前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >php docker web终端的折腾 原

php docker web终端的折腾 原

作者头像
domain0
发布2018-08-01 18:24:04
6940
发布2018-08-01 18:24:04
举报
文章被收录于专栏:运维一切运维一切

#php是天下最好的语言,没有之一。 我非常喜欢php,我听过一个高手的讲座,讲php的编译原理,发现如果就php语言开发而言的确技术上有高低之分。一比较才发现自己差了十万八千里,自己不努力是不行的。好了不多说了,我来说说今天我分享的话题。 ##背景 2015年,我开始接触docker,那个时候发现一个管理平台shipyard,shipyard中有一个可以直接在浏览器上操作的web终端很吸引我,我想自己实现这样的一个服务,我翻看了其中的技术细节,发现有一个shipyard/controller/api/hijack.go中一堆的操作,当我开始用php模拟来写的时候,我发现我对php的认识还处于低级阶段,看起来很一个很艰难的工作。

##整体的架构

浏览器上主要的通信技术就是websocket技术,我以前玩过一个原型. ##前端代码

代码语言:javascript
复制
< !doctype html > <title > term.js < /title>
<!--
  term.js
  Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
-->
<style>
  html {
    background: #555;
  }

  h1 {
    margin-bottom: 20px;
    font: 20px/1.5 sans - serif;
} < /style>
<h1>term.js</h1 > <script src = "http://apps.bdimg.com/libs/term.js/0.0.2/term.js" > </script>
<script>
;(function() {
  window.onload = function() {
    var socket = new WebSocket('ws:/ / myblog.com / wsapp / '); 

    socket.onopen = function() {
      var term = new Terminal({
      cols: 80,
      rows: 24,
      useStyle: true,
      screenKeys: true,
      cursorBlink: false
      });

      term.on('data ', function(data) {
      socket.send(data);
      });

      term.on('title ', function(title) {
        document.title = title;
      });
      term.open(document.body);
      term.write('\x1b[31mWelcome to term.js ! \x1b[m\r\n ');

      socket.onmessage =  function(message) {
        term.write(message.data);
      }    

      socket.onclose = function() {
        term.destroy();
      }
    socket.onerror = function() {
        console.log("connect error");
        term.destroy();
    }
    }
  };
}).call(this);
</script>

这里用到了term.js的一个前端的终端组件,传送门 所有的通信都是走的websocket ##nginx的配置

代码语言:javascript
复制
        location /wsapp/ {
            proxy_pass http://127.0.0.1:9501;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }

没什么好讲的,这里就是连接到一个websocket的服务而已 ##后端服务

代码语言:javascript
复制
< ?php
function create_exec($container_id = '9e75217f2a89') {
    $url = "http://0.0.0.0:2375/containers/".$container_id."/exec";
    $post_data = ["AttachStdin" = >true, "AttachStdout" = >true, "AttachStderr" = >true, "Cmd" = >["/bin/bash"], "DetachKeys" = >"ctrl-p,ctrl-q", "Privileged" = >true, "Tty" = >true, "User" = >"root:root"];
    $header = ["Content-Type: application/json"];
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data, true));
    $output = curl_exec($ch);
    curl_close($ch);
    return $output;
}
//websocket 执行
$server = new swoole_websocket_server("127.0.0.1", 9501);
$client = null;
$exec_id = null;
$host = "192.168.254.128:2375";
$re = null;
$server - >on('open',
function(swoole_websocket_server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
    global $exec_id;
    global $re;
    $re = $request;
    $exec = json_decode(create_exec(), true);
    $exec_id = $exec['Id'];
    print_r("exec id:".$exec_id);
    //socket async
    global $client;
    $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
    $client - >on("connect",
    function(swoole_client $cli) {
        global $exec_id;
        global $host;
        $url = '/exec/'.$exec_id."/start";
        $data = '{"Detach":false, "Tty":true}';
        $out = "POST ${url} HTTP/1.1\r\n";
        $out. = "Remote Address:${host}\r\n";
        $out. = "Content-Type: application/json\r\n";
        $out. = "Connection: Upgrade\r\n";
        $out. = "Content-Length: ".strlen($data)."\r\n";
        $out. = "Upgrade: tcp\r\n\r\n";
        $out. = $data."\r\n\r\n";
        $cli - >send($out);
    });
    $client - >on("receive",
    function(swoole_client $cli, $data) {
        echo "Receive docker daemon: $data";
        //接受到的docker daemon数据发送到web客户端
        global $server;
        global $re;
        $server - >push($re - >fd, $data);
        //$cli->send(str_repeat('A', 100)."\n");
    });
    $client - >on("error",
    function(swoole_client $cli) {
        echo "error\n";
    });
    $client - >on("close",
    function(swoole_client $cli) {
        echo "Connection close\n";
    });
    //连接docker
    $client - >connect('192.168.254.128', 2375);
});
$server - >on('message',
function(swoole_websocket_server $server, $frame) {
    //接收到的web客户端数据发送给docker daemon
    global $client;
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $client - >send($frame - >data);
    //$server->push($frame->fd, "this is server");
});
$server - >on('close',
function($ser, $fd) {
    echo "client {$fd} closed\n";
});
$server - >start();

docker的exec这里要先通过exec接口获取一个执行id,然后通过这个id,调用start接口才能进行服务,不过docker的start接口这里的返回并不是一个curl能处理的请求,而是一个流,而且是一个可读可写的流,所以这里必须采用socket的方式来进行服务发送,还有一个需要注意的,就是这个socket的通信并不能采用同步的方式进行,因为如果同步阻塞数据就没办法读写了,读的时候程序直接卡住了,没办法继续下去了。这就是php需要的异步io技术。 swoole这块最大的问题就是参数的传递,我不得不定义好多global的变量,不然异步socket和websocket服务嵌套的用,代码看起来好丑陋。

ps: 我查了一下,php从5.3开始解决了这个问题,php这个版本之后支持闭包参数传递,有了use关键字的支持,另外对this关键字进行了改装,可以有效的简化这种写法,我闲的时候玩玩 ##最后,看看成果

参考资料:https://hui.lu/shi-yong-tornado-cao-zuo-docker-api/#webssh-with-frontend

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017/01/15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档