兜兜    2019-06-13 13:40:49    2019-06-20 11:00:42   

   docker channels xterm.js websocket

### 流程 #### `浏览器-->nginx-->静态页面-->ws请求-->nginx-->daphne(django-channels)` &nbsp; ### 环境 `centos 7` `nginx 1.12` `python 3.6` `daphne 2.3.0` `django 2.2.2` `docker 4.0.1` `channels 2.2.0` `channels-redis 2.4.0` &nbsp; ### 一、django-channels的websocket实现 #### 创建虚拟环境 ```bash pip install virtualenv cd /data/app/ virtualenv venv source venv/bin/activate ``` #### 安装相关软件包 ```bash pip install -u django channels asgi_redis channels_redis ``` #### 创建项目 ```bash django-admin startproject xtermWS ``` #### 创建app ```bash cd xtermWS python manage.py startapp dockerCmd ``` #### 项目结构如下 ```bash xtermWS ├── dockerCmd │   ├── admin.py │   ├── apps.py │   ├── consumers.py #类似view,需要手动创建 │   ├── __init__.py │   ├── models.py │   ├── routing.py #类似urls,需要手动创建 │   └── views.py ├── manage.py └── xtermWS ├── asgi.py #类似wsgi,需要手动创建 ├── __init__.py ├── routing.py #类似urls,需要手动创建 ├── settings.py ├── urls.py └── wsgi.py ``` #### 配置项目settings.py ```bash vim xtermWS/settings.py ``` ```bash ALLOWED_HOSTS = ['*'] INSTALLED_APPS = [ 'channels', # channels 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'dockerCmd.apps.DockercmdConfig', #新创建的dockerCmd ] ... TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #项目模板路径 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] #asgi配置 ASGI_APPLICATION = 'xtermWS.routing.application' #日志配置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'normal': { 'format': '%(levelname)s | %(asctime)s | app: %(module)s pid: %(process)d th: %(thread)d | %(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'normal', } }, 'loggers': { 'django': { 'handlers': ['console'], 'level': 'INFO', 'propagate': True, }, }, } #channel_redis配置 CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { 'hosts': [('127.0.0.1', 6379)], }, }, } ``` #### 创建xtermWS/routing.py ```bash vim xtermWS/routing.py ``` ```python from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter import dockerCmd.routing application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter( dockerCmd.routing.websocket_urlpatterns ) ), }) ``` #### 创建dockerCmd/routing.py ```bash vim dockerCmd/routing.py ``` ```python from django.urls import re_path from dockerCmd import consumers websocket_urlpatterns = [ re_path(r'^ws/(?P<cid>[^/]+)$', consumers.CommandConsumer), ] ``` #### 创建dockerCmd/consumers.py ```bash vim dockerCmd/consumers.py ``` ```python from channels.generic.websocket import WebsocketConsumer import docker import threading import logging logger = logging.getLogger('django') class CommandConsumer(WebsocketConsumer): def connect(self): self.container_id = self.scope['url_route']['kwargs']['cid'] self.accept() self.client=docker.APIClient() #推送logs self.send(text_data=self.client.logs(self.container_id,stdout=True, stderr=True).decode('utf-8')) #self.send(text_data=self.client.attach(container_id,stderr=True,stdout=True,demux=True)) self.socket=self.client.attach_socket(self.container_id, params={'stdin': 1, 'stream': 1}) #开启线程获取stdout,stdin,logs数据stream数据 self.stop_thread=False self.t = threading.Thread(target=self.send_stream_log) self.t.start() def disconnect(self, close_code): #关闭线程 #关闭socket self.stop_thread=True self.socket._sock.send('stop\r\n'.encode('utf-8')) #socket关闭 self.socket.close() #容器关闭删除 self.client.stop(self.container_id) self.client.wait(self.container_id) self.client.remove_container(self.container_id) #client关闭 self.client.close() def receive(self, text_data): self.socket._sock.send(text_data.encode('utf-8')) logger.info('CommandConsumer:receive') def send_stream_log(self): for b in self.client.attach(self.container_id,stderr=True,stdout=True,stream=True,demux=True): logger.info(b) if self.stop_thread: break if b[0]: self.send(text_data=b[0].decode('utf-8')) if b[1]: self.send(text_data=b[1].decode('utf-8')) logger.info('退出线程') ``` #### 创建asgi.py ```bash vim xtermWS/asgi.py ``` ```python import os import django from channels.routing import get_default_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xtermWS.settings") django.setup() application = get_default_application() ``` &nbsp; #### 二、docker安装redis ```bash docker run -d -p 6379:6379 --name redis redis ``` &nbsp; ### 三、supervisor管理daphne #### 安装supervisor ```bash yum install -y supervisor ``` #### 创建配置文件 ```bash vim /etc/supervisord.d/daphne.ini ``` ```bash [program:daphne] directory=/data/app/xtermWS/ environment=PATH="/data/app/venv/bin" command=/data/app/venv/bin/daphne xtermWS.asgi:application -b 0.0.0.0 -p 9000 autostart=true autorestart=true stdout_logfile=/var/daphne/daphne_ws.log redirect_stderr=true ``` #### 创建日志目录 ```bash mkdir /var/daphne ``` #### 启动supervisord ```bash supervisord -c /etc/supervisord.conf ``` #### 启动/停止daphne ```bash supervisorctl start daphne supervisorctl stop daphne ``` &nbsp; ### 四、nginx安装配置 #### 安装nginx ```bash yum install nginx -y ``` #### 配置nginx ```bash vim /etc/nginx/nginx.conf ``` ```ini ... upstream channels-backend-prod { server 127.0.0.1:9000; } server{ listen 80; server_name domain; location /ws { proxy_pass http://channels-backend-prod; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } } ... ``` #### 启动nginx ```bash systemctl start nginx ``` &nbsp; ### 五、xterm.js安装配置 #### 安装xterm.js ```bash npm install xterm ``` #### 拷贝到项目静态文件目录 ```bash cp -r node_modules/xterm xtermWS/xtermWS/static/ ``` &nbsp; ### 六、前端配置 ```html ... //引入xtrem的css和js文件 <link rel="stylesheet" href="{% static 'xterm/xterm.css' %}" /> <script src="{% static 'xterm/xterm.js' %}" ></script> <script src="{% static 'xterm/addons/attach/attach.js' %}" ></script> ... //显示终端窗口 <div id="terminal-container"> </div> ... <script type="text/javascript"> ... $(document).ready(function(){ // 声明一个websocket变量 var term_websocket; Terminal.applyAddon(attach); const term = new Terminal({ windowsMode:true, rows:10 }); //关闭之前的websocket if (typeof(term_websocket) != "undefined"){ term_websocket.close(); } const container = document.getElementById('terminal-container'); term.open(container); const protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; const port = location.port ? `:${location.port}` : ''; const socketUrl = `${protocol}${location.hostname}${port}/ws/容器id`; term_websocket = new WebSocket(socketUrl); term_websocket.onopen = (ev) => { term.attach(term_websocket); }; term_websocket.onclose = function(ev){ console.log('Connection closed.'); }; }); ... </script> ```

©著作权归作者所有:来自ynotes.cn笔记作者兜兜的原创作品,如需转载,请注明出处:https://ynotes.cn/blog/article_detail/180

文章分类: 编程语言     个人分类: 默认

收藏


0 条评论
按时间正序 按时间倒序