zoukankan      html  css  js  c++  java
  • Python Django撸个WebSSH操作Kubernetes Pod(下)- 终端窗口自适应Resize

    追求完美不服输的我,一直在与各种问题斗争的路上痛并快乐着

    上一篇文章Django实现WebSSH操作Kubernetes Pod最后留了个问题没有解决,那就是terminal内容窗口的大小没有办法调整,这会导致的一个问题就是浏览器上可显示内容的区域太小,当查看/编辑文件时非常不便,就像下边这样,红色可视区域并没有被用到

    RESIZE_CHANNEL

    前文说到kubectl exec有两个参数COLUMNSLINES可以调整tty内容窗口的大小,命令如下:

    kubectl exec -i -t $1 env COLUMNS=$COLUMNS LINES=$LINES bash
    

    这实际上就是将COLUMNSLINES两个环境变量传递到了容器内,由于Kubernetes stream底层也是通过kubernetes exec实现的,所以我们在启动容器时也将这两个变量传递进去就可以了,就像这样

    exec_command = [
        "/bin/sh",
        "-c",
        'export LINES=20; export COLUMNS=100; '
        'TERM=xterm-256color; export TERM; [ -x /bin/bash ] '
        '&& ([ -x /usr/bin/script ] '
        '&& /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) '
        '|| exec /bin/sh']
    

    添加了export LINES=20; export COLUMNS=100;,可以实现改变tty的输出大小,但这有个问题就是只能在建立链接时指定一次,不能动态的更新,也就是在一次websocket会话的过程中,如果页面大小改变了,后端输出的LINES和COLUMNS是无法随着改变的

    在解决问题的过程中发现官方源码中有个RESIZE_CHANNEL的配置,同样可以控制窗口的大小,使用方法如下:

    cont_stream = stream(api_instance.connect_get_namespaced_pod_exec,
                         name=pod_name,
                         namespace=self.namespace,
                         container=container,
                         command=exec_command,
                         stderr=True, stdin=True,
                         stdout=True, tty=True,
                         _preload_content=False
                         )
    
    cont_stream.write_channel(4, json.dumps({"Height": int(rows), "Width": int(cols)}))
    

    这样我们就可以修改stream输出的窗口大小了

    xterm.js fit

    一顿操作后,打开页面,咦?怎么页面不行,原来窗口的调整不仅需要调整stream输出数据的窗口大小,前端页面也要跟着一并调整

    这里用到了xterm.js的另一个组件fit,fit可以调整终端大小的colsrows适配父级元素

    首先调整terminal块的宽度和高度为整个页面可视区域的大小,要让整个可视区域为终端窗口

    document.getElementById('terminal').style.height = window.innerHeight + 'px';
    

    然后引入fit组件,在term初始化之后执行fit操作

    <script src="/static/plugins/xterm/xterm.js"></script>
    <script src="/static/plugins/xterm/addons/fit/fit.js"></script>
    <script>
      // 修改terminal的高度为body的高度
      document.getElementById('terminal').style.height = window.innerHeight + 'px';
    
      var term = new Terminal({cursorBlink: true});
      term.open(document.getElementById('terminal'));
    
      // xterm fullscreen config
      Terminal.applyAddon(fit);
      term.fit();
    
      console.log(term.cols, term.rows);
    </script>
    

    fit之后就可以通过term.colsterm.rows取到xterm.js根据字体大小自动计算过的colsrows的值了,然后把这两个值传递给kubernetes,kubernetes再根据这两个值输出窗口大小,这样前后端匹配就完美了

    数据传递

    xterm.js可以通过如下的方法动态的将colsrows传递给后端

    term.on('resize', size => {
      socket.send('resize', [size.cols, size.rows]);
    })
    

    但当窗口由大变小时,之前输出的内容会有样式错乱,我为了方便直接在WebSocket连接建立时采用url传参的方式把colsrows两个值传递给后端,kubernetes根据这两个值来设置输出内容的窗口大小,这样做的缺点是不会随着前端页面的变化动态的去调整后端stream输出窗口的大小,不过问题不大,如果页面调整大小,刷新下页面重新建立连接就可以啦,具体实现如下

    首先需要修改的就是WebSocket的url地址

    前端增加term.colsterm.rows两个参数的传递

    var socket = new WebSocket(
    'ws://' + window.location.host + '/pod/{{ name }}/'+term.cols+'/'+term.rows);
    

    Routing增加两个参数的解析

    re_path(r'^pod/(?P<name>w+)/(?P<cols>d+)/(?P<rows>d+)$', SSHConsumer),
    

    Consumer解析URL将对应参数传递给Kubernetes stream

    class SSHConsumer(WebsocketConsumer):
        def connect(self):
            self.name = self.scope["url_route"]["kwargs"]["name"]
            self.cols = self.scope["url_route"]["kwargs"]["cols"]
            self.rows = self.scope["url_route"]["kwargs"]["rows"]
    
            # kube exec
            self.stream = KubeApi().pod_exec(self.name, cols=self.cols, rows=self.rows)
            kub_stream = K8SStreamThread(self, self.stream)
            kub_stream.start()
    
            self.accept()
    

    最后Kubernetes stream接收参数并修改窗口大小

        def pod_exec(self, RAND, container="", rows=24, cols=80):
            api_instance = client.CoreV1Api()
    
            exec_command = [
                "/bin/sh",
                "-c",
                'TERM=xterm-256color; export TERM; [ -x /bin/bash ] '
                '&& ([ -x /usr/bin/script ] '
                '&& /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) '
                '|| exec /bin/sh']
    
            cont_stream = stream(api_instance.connect_get_namespaced_pod_exec,
                                 name=pod_name,
                                 namespace=self.namespace,
                                 container=container,
                                 command=exec_command,
                                 stderr=True, stdin=True,
                                 stdout=True, tty=True,
                                 _preload_content=False
                                 )
    
            cont_stream.write_channel(4, json.dumps({"Height": int(rows), "Width": int(cols)}))
    
            return cont_stream
    

    至此,每次WebSocket连接建立,前后端就会有一样的输出窗口大小,问题解决~


    扫码关注公众号查看更多实用文章

    相关文章推荐阅读:

  • 相关阅读:
    OC 消息机制本质
    Lua中的闭包
    Lua中的函数
    Lua中的语句
    Lua中的表达式
    Lua中的类型与值
    Unity3D之通过C#使用Advanced CSharp Messenger
    C#中sealed关键字
    C#委托delegate、Action、Func、predicate 对比用法
    Unity3D之IOS&Android收集Log文件
  • 原文地址:https://www.cnblogs.com/37Y37/p/12579408.html
Copyright © 2011-2022 走看看