美文网首页
容器管理利器: Web Terminal简介

容器管理利器: Web Terminal简介

作者: 猴子精h | 来源:发表于2017-11-10 15:46 被阅读2764次

一. 前言

在微服务大行其道的今天,容器恰巧又是微服务的主要载体,所以我们操作的对象也由最开始的「物理机」到「虚拟机」再到今天的「容器」。由于这些载体的变更,我们的使用方式也需要随之发生一些改变。比如一个最常用的登入操作,「虚拟机」下我们可能通过 ssh 的方式 ,但如果是容器呢?ssh 的方式就需要在每个容器中都运行一个 sshd 进程,这种做法可行但略显繁琐,也不太符合一个容器只运行一个进程的思想。
那么有没有一个即方便快捷又安全的登入方式呢?

有,通过 Web Terminal 的方式,通过 Web 的方式即可以避免对客户端的依赖又能够实现用户权限控制。目前,有很多开源的 Web Terminal 的项目,基本上都是通过 ssh 代理的方式调用并返回一个 shell 的虚拟终端(pty)。

ssh_proxy.png

二. 实现容器的 Web Terminal

2.1 架构图

docker ws.png

2.2 前端 Web Termianl 页面

Linux 终端返回的内容会带很多特殊的字符,比如我输入一个 ls 指令,终端返回的结果如下:

'l'
's'
'\r\n'
'\x1b[0;0mRUNNING_PID\x1b[0m  \x1b[1;34mbin\x1b[0m          \x1b[1;34mconf\x1b[0m         \x1b[1;34mlib\x1b[0m\r\nbash-4.3# '

这样我们就需要自己做穷举处理了,这里推荐使用一款的模拟 Terminal 的 JavaScript 库 xterm.js。这个库已经帮我们做了这些复杂操作。

<!doctype html>
  <html>
    <head>
      <link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
      <script src="node_modules/xterm/dist/xterm.js"></script>
    </head>
    <body>
      <div id="terminal"></div>
      <script>
        var term = new Terminal();
        term.open(document.getElementById('terminal'));
        
        term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
      </script>
    </body>
  </html>

可以看到它已经将 \x1B[1;3;31mxterm.js\x1B[0m 这些特殊字符变成了红色:

xterm-example.png

2.3 调用 Docker Daemon API 返回 Shell 虚拟终端

前提,我们需要自行开启 Docker Daemon 的远程调用。

在平常的命令行操作下,我们经常会使用 docker exec -i -t <container_id> /bin/sh 来模拟一个 Shell 的伪终端。所以,我们就通过 API 调用的方式来实现同样的操作:

  1. 先调用 execCreate 来创建一个 Exec 。在调用时,需要指定TtyAttachStdinAttachStdoutAttachStderr 参数均为 true,Cmd 参数为 bash,这样才能获得 bash 进程的标准输入输出和错误;

    request

    POST /v1.24/containers/e90e34656806/exec HTTP/1.1
    Content-Type: application/json
    
    {
       "AttachStdin": true,
      "AttachStdout": true,
      "AttachStderr": true,
      "Cmd": ["sh"],
      "DetachKeys": "ctrl-p,ctrl-q",
      "Tty": true
       ...
    }
    
  1. 如果调用 execCreate 成功,调用请求会返回该 Exec 的 ID,根据这个 ID 继续调用execStart 接口。在调用时,需要指定 Detach 为 False,Tty 为 True,这样才会返回一个 HTTP 的 stream:

    request

    POST /v1.24/exec/e90e34656806/start HTTP/1.1
    Content-Type: application/json
    
    {
     "Detach": false,
     "Tty": true
    }
    

    response:

    HTTP/1.1 200 OK
    Content-Type: application/vnd.docker.raw-stream
    
    
    {{ STREAM }}
    

2.4 d-terminal

d-terminal 是这个系统的核心,它分成两个部分:

  1. 一部分用于处理用户端的输入和输出,像 top 这样的监控命令需要服务端定时推送数据给客户端,这里使用了 WebSocket,以及存储和展示后端 Docker Dameon 主机的 IP 和 container_id。
  2. 另一部分用于调用 Docker Daemon 返回虚拟终端。对于终端来说,通常是你输入一个字符就会立马返回,直到你输入一个 "归位键" 终端才会把你说输入的字符拼接成一个字符串并发送给 shell 解释器,并将 shell 解释器的结果返回。为了提升使用流畅性,新启用了一个线程去调用 Docker Daemon API,也可以使用像 epoll 这样的多路复用技术来实现。
main.png
@sockets.route('/echo')
def echo_socket(ws):
    ...
    # 调用 Docker API 返回一个虚拟终端
    docker_daemon_sock = get_tty()._sock
    # 启动一个与 Docker Daemon 通讯的子线
    docker_daemon_sock_thd = DockerDaemonSock(ws, docker_daemon_sock)
    docker_daemon_sock_thd.start()

    while not ws.closed:
        message = ws.receive() # 接收 terminal 的输入
        # 将用户的输入发送那个 docker daemon
        docker_daemon_sock.send(bytes(message, encoding='utf-8'))
        

# 子线程 DockerDaemonSock 类
class DockerDaemonSock(threading.Thread):
    def __init__(self, ws, docker_daemon_sock):
        super(DockerDaemonSock, self).__init__()
        self.ws = ws
        self.docker_daemon_sock = docker_daemon_sock

    def run(self):
        while not self.ws.closed:
            try:
                # 接收 docker daemon 的返回
                resp = self.docker_daemon_sock.recv(2048)
                if resp:
                    # 将 docker daemon 的返回发送给前端 terminal
                    self.ws.send(str(resp, encoding='utf-8'))
                else:
                    print("docker socket closed.")
                    self.ws.close()
            except Exception as e:
                print("docker termial socket error: {}".format(e))
                self.ws.close()

三. 总结

上述仅仅是描述了一个最基本的实现,完全是为了抛砖引玉,后续可以通过在中间层添加一些扩展,比如,用户权限的分配,与自己环境中的容器编排引擎集成等,最终作为 Pass 平台的一个基础的组成部分。

最后,上述的 demo 可去 github 具体查看。效果如下:

image

四. 参考

相关文章

  • 容器管理利器: Web Terminal简介

    一. 前言 在微服务大行其道的今天,容器恰巧又是微服务的主要载体,所以我们操作的对象也由最开始的「物理机」到「虚拟...

  • Web容器简介

    Web容器管理JavaEE应用中Web页面、servlet和一些EJB组件(Java EE 7规范中新增)的执...

  • react-Redux 初步了解

    Redux 简介是 JavaScript 状态容器,提供可预测化的状态管理 reac 设计思想-> web 应用是...

  • 渐行渐远的Servlet

    Servlet简介 Servlet 是一种基于 Java 技术的 Web 组件,用于生成动态内容,由容器管理。类似...

  • Tomcat容器等级

    Tomcat容器分为四个等级,Servlet容器管理Context容器,一个Context容器对应一个Web工程。

  • ServletContext--当前web应用

    1,servletcontext简介:web容器启动时他会为每个web应用程序都创建一ServletContext...

  • SpringBoot--开发实战--SpringSession会

    一、Spring-session简介   在传统单机web应用中,一般使用tomcat/jetty等web容器时,...

  • Terminal利器——tldr

    tldr全称too long,don’t read. 历史背景 对于很多使用终端的Linux和Mac用户,相信大家...

  • SpringBoot--开发实战--Jetty(四十二)

    一、Jetty简介   Jetty 是一个开源的servlet容器,它为基于Java的web容器,例如JSP和se...

  • jsp & servlet 精华一页纸

    servlet/jsp 本质上是一种java编程协议/规范, 存在于web容器中,由web容器解析,管理其生命周期...

网友评论

      本文标题:容器管理利器: Web Terminal简介

      本文链接:https://www.haomeiwen.com/subject/mlwbmxtx.html