四位一体水溶交融,Docker一拖三Tornado6.2 + Nginx + Supervisord非阻塞负载均衡容器式部署实践
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_203
容器,又见容器。Docker容器的最主要优点就在于它们是可移植的。一套服务,其所有的依赖关系可以捆绑到一个独立于Linux内核、平台分布或部署模型的主机版本的单个容器中。此容器可以传输到另一台运行Docker的主机上,并且在没有兼容性问题的情况下执行。而传统的微服务架构会将各个服务单独封装为容器,虽然微服务容器化环境能够在给定数量的基础架构内实现更高的工作负载密度,但是,在整个生产环境中创建、监视和销毁的容器需求总量呈指数级增长,从而显著增加了基于容器管理环境的复杂性。
藉此,本次我们将服务化零为整,将Tornado服务和Nginx服务器以及配套的监控管理程序Supervisor集成到一个单独的容器中,将其高度可移植性最大化地发挥。
Docker具体安装流程请移玉步到:一寸宕机一寸血,十万容器十万兵|Win10/Mac系统下基于Kubernetes(k8s)搭建Gunicorn+Flask高可用Web集群
整体容器内的系统架构如图所示:

首先,创建项目目录 mytornado:
mkdir mytornado
这里web服务框架我们使用业内著名的非阻塞异步框架Tornado6.2,创建一个服务的入口文件main.py
import json
import tornado.ioloop
import tornado.web
import tornado.httpserver
from tornado.options import define, options
define('port', default=8000, help='default port', type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, Tornado")
def make_app():
return tornado.web.Application([
(r"/", IndexHandler),
])
if __name__ == "__main__":
tornado.options.parse_command_line()
app = make_app()
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
这里运行端口我们通过命令行传参的方式进行监听,方便多进程服务的启动。
之后,创建 requirements.txt 项目依赖文件:
tornado==6.2
接下来,创建Nginx的配置文件 tornado.conf
upstream mytornado {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
}
server {
listen 80;
location / {
proxy_pass http://mytornado;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这里Nginx监听80端口,反向代理到本地系统的8000和8001端口,这里我们使用默认的负载均衡方案:轮询,如果有其他需求,可以按照其他的方案进行修改:
1、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
upstream backserver {
server 192.168.0.14;
server 192.168.0.15;
}
2、权重 weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream backserver {
server 192.168.0.14 weight=3;
server 192.168.0.15 weight=7;
}
3、ip_hash( IP绑定)
上述方式存在一个问题就是说,在负载均衡系统中,假如用户在某台服务器上登录了,那么该用户第二次请求的时候,因为我们是负载均衡系统,每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的。
我们可以采用ip_hash指令解决这个问题,如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream backserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
4、fair(第三方插件)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backserver {
server server1;
server server2;
fair;
}
5、url_hash(第三方插件)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
下面我们编写Supervisor的配置文件supervisord.conf:
[supervisord]
nodaemon=true
[program:nginx]
command=/usr/sbin/nginx
[group:tornadoes]
programs=tornado-8000,tornado-8001
[program:tornado-8000]
command=python3.8 /root/mytornado/main.py --port=8000
# 执行目录
directory=/root/mytornado
# 自动重启
autorestart=true
# 启动supervisor时,程序自启动
autostart=true
# 日志
stdout=/var/log/tornado-8000.log
redirect_stderr=true
loglevel=info
[program:tornado-8001]
command=python3.8 /root/mytornado/main.py --port=8001
# 执行目录
directory=/root/mytornado
# 自动重启
autorestart=true
# 启动supervisor时,程序自启动
autostart=true
# 日志
stdout=/var/log/tornado-8001.log
redirect_stderr=true
loglevel=info
Supervisor 是专门用来在类 Unix 系统上监控管理进程的工具,发布于 2004 年,它对应的角色分别为 Supervisorctl 和 Supervisord。后者的主要作用是启动配置好的程序、响应 Supervisorctl 发过来的指令以及重启退出的子进程,而前者是 Supervisor 的客户端,它以命令行的形式提供了一系列参数,来方便用户向 Supervisord 发送指令,常用的有启动、暂停、移除、更新等命令。
这里我们主要使用Supervisor针对Tornado服务进行监控和管理,这里默认的项目目录为/root/mytornado/
进程配置两个,分别对应nginx的监听端口:8000和8001
最后,编写容器配置文件Dockerfile:
FROM yankovg/python3.8.2-ubuntu18.04
RUN sed -i "s@/archive.ubuntu.com/@/mirrors.163.com/@g" /etc/apt/sources.list \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get update --fix-missing -o Acquire::http::No-Cache=True
RUN apt install -y nginx supervisor pngquant
# application
RUN mkdir /root/mytornado
WORKDIR /root/mytornado
COPY main.py /root/mytornado/
COPY requirements.txt /root/mytornado/
RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
# nginx
RUN rm /etc/nginx/sites-enabled/default
COPY tornado.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/tornado.conf /etc/nginx/sites-enabled/tornado.conf
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
# supervisord
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# run
CMD ["/usr/bin/supervisord"]
这里基础镜像选择预装了Python3.8的Ubuntu18,兼具了小体积和可扩展的特性,添加apt-get的安装源之后,分别安装Nginx以及Supervisor。
随后,依照Supervisor配置文件内所书,在容器内部创建项目目录/root/mytornado/
并且将上面编写好的main.py以及requirements.txt复制到容器内部,运行pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ 安装项目的所有依赖。
最后,将tornado.conf和supervisord.conf也拷贝到对应的配置路径中,分别启动Nginx和Supervisor服务。
编写好之后,在项目根目录的终端内运行命令打包镜像:
docker build -t 'mytornado' .
首次编译会等待一小会儿,因为需要下载基础镜像服务:
liuyue:docker_tornado liuyue$ docker build -t mytornado .
[+] Building 16.2s (19/19) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 37B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/yankovg/python3.8.2-ubuntu18.04:latest 15.9s
=> [internal] load build context 0.0s
=> => transferring context: 132B 0.0s
=> [ 1/14] FROM docker.io/yankovg/python3.8.2-ubuntu18.04@sha256:811ad1ba536c1bd2854a42b5d6655fa9609dce1370a6b6d48087b3073c8f5fce 0.0s
=> CACHED [ 2/14] RUN sed -i "s@/archive.ubuntu.com/@/mirrors.163.com/@g" /etc/apt/sources.list && rm -rf /var/lib/apt/lists/* 0.0s
=> CACHED [ 3/14] RUN apt install -y nginx supervisor pngquant 0.0s
=> CACHED [ 4/14] RUN mkdir /root/mytornado 0.0s
=> CACHED [ 5/14] WORKDIR /root/mytornado 0.0s
=> CACHED [ 6/14] COPY main.py /root/mytornado/ 0.0s
=> CACHED [ 7/14] COPY requirements.txt /root/mytornado/ 0.0s
=> CACHED [ 8/14] RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ 0.0s
=> CACHED [ 9/14] RUN rm /etc/nginx/sites-enabled/default 0.0s
=> CACHED [10/14] COPY tornado.conf /etc/nginx/sites-available/ 0.0s
=> CACHED [11/14] RUN ln -s /etc/nginx/sites-available/tornado.conf /etc/nginx/sites-enabled/tornado.conf 0.0s
=> CACHED [12/14] RUN echo "daemon off;" >> /etc/nginx/nginx.conf 0.0s
=> CACHED [13/14] RUN mkdir -p /var/log/supervisor 0.0s
=> CACHED [14/14] COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:2dd8f260882873b587225d81f7af98e1995032448ff3d51cd5746244c249f751 0.0s
=> => naming to docker.io/library/mytornado 0.0s
打包成功后,运行命令查看镜像信息:
docker images
可以看到镜像总大小不到1g:
liuyue:docker_tornado liuyue$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mytornado latest 2dd8f2608828 4 hours ago 828MB
接着让我们来启动容器:
docker run -d -p 80:80 mytornado
通过端口映射技术,将容器内的80端口服务映射到宿主机的80端口。
输入命令查看服务进程:
docker ps
显示正在运行:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
60e071ba2a36 mytornado "/usr/bin/supervisord" 6 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp frosty_lamport
liuyue:docker_tornado liuyue$
此时我们打开浏览器访问 http://127.0.0.1

没有任何问题。
同时可以根据运行中的容器id来选择进入对应的容器:
liuyue:docker_tornado liuyue$ docker exec -it 60e071ba2a36 /bin/sh
#
在容器内部我们可以看到项目的所有文件:
# pwd
/root/mytornado
# ls
main.py requirements.txt
#
重要的是,可以使用Supervisor对既有的Tornado进程进行管理操作,
查看所有进程:
supervisorctl status
根据配置文件,我们容器内运行着三个服务:
# supervisorctl status
nginx RUNNING pid 10, uptime 0:54:28
tornadoes:tornado-8000 RUNNING pid 11, uptime 0:54:28
tornadoes:tornado-8001 RUNNING pid 12, uptime 0:54:28
依据服务名称,对服务进行停止操作:
# supervisorctl stop tornadoes:tornado-8001
tornadoes:tornado-8001: stopped
# supervisorctl status
nginx RUNNING pid 10, uptime 0:55:52
tornadoes:tornado-8000 RUNNING pid 11, uptime 0:55:52
tornadoes:tornado-8001 STOPPED Dec 28 08:47 AM
#
再次启动:
# supervisorctl start tornadoes:tornado-8001
tornadoes:tornado-8001: started
# supervisorctl status
nginx RUNNING pid 10, uptime 0:57:09
tornadoes:tornado-8000 RUNNING pid 11, uptime 0:57:09
tornadoes:tornado-8001 RUNNING pid 34, uptime 0:00:08
#
如果服务进程意外终止,Supervisor可以对其进行拉起操作,满血复活:
# ps -aux | grep python
root 1 0.0 0.1 55744 20956 ? Ss 07:58 0:01 /usr/bin/python /usr/bin/supervisord
root 11 0.0 0.1 102148 22832 ? S 07:58 0:00 python3.8 /root/mytornado/main.py --port=8000
root 34 0.0 0.1 102148 22556 ? S 08:48 0:00 python3.8 /root/mytornado/main.py --port=8001
root 43 0.0 0.0 11468 1060 pts/0 S+ 08:51 0:00 grep python
# kill -9 34
# supervisorctl status
nginx RUNNING pid 10, uptime 1:00:27
tornadoes:tornado-8000 RUNNING pid 11, uptime 1:00:27
tornadoes:tornado-8001 RUNNING pid 44, uptime 0:00:16
如果愿意,也可以将编译好的镜像提交到Dockerhub上,这样可以做到随时使用随时拉取,不需要每次都进行编译操作,这里我已经将镜像推送到云端,需要的话可以直接拉取使用:
docker pull zcxey2911/mytornado:latest
Dockerhub的具体操作流程请参见:利用DockerHub在Centos7.7环境下部署Nginx反向代理Gunicorn+Flask独立架构
结语:诚然,Docker容器技术消除了线上线下的环境差异,保证了服务生命周期的环境一致性标准化。开发者使用镜像实现标准开发环境的构建,开发完成后通过封装着完整环境和应用的镜像进行迁移,藉此,测试和运维人员可以直接部署软件镜像来进行测试和发布,大大简化了持续集成、测试和发布的过程,但是我们也不得不正视容器技术现有阶段的劣势,那就是性能的损耗,Docker容器对CPU和内存的使用几乎没有任何开销,但它们会影响I/O和OS交互。这种开销是以每个I/O操作的额外周期的形式出现的,所以小I/O比大I/O遭受的损失要大得多。这种开销增加了I/O延迟,减少了用于有用工作的CPU周期,从而限制了吞吐量。也许不久的将来,随着内核技术的提升,该缺陷会被逐步解决,最后奉上项目地址,与君共觞:https://github.com/zcxey2911/Docker_Tornado6_Supervisor_Python3.8
原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_203
四位一体水溶交融,Docker一拖三Tornado6.2 + Nginx + Supervisord非阻塞负载均衡容器式部署实践的更多相关文章
- nginx反向代理和负载均衡的简单部署
1. 安装 1) 从Nginx官网下载页面(http://nginx.org/en/download.html)下载Nginx最新版本(目前是1.5.13版本)安装包: 2) ...
- Nginx反向代理,负载均衡,redis session共享,keepalived高可用
相关知识自行搜索,直接上干货... 使用的资源: nginx主服务器一台,nginx备服务器一台,使用keepalived进行宕机切换. tomcat服务器两台,由nginx进行反向代理和负载均衡,此 ...
- Nginx 服务器 之Nginx与tomcat实现负载均衡
本文讲解我们如何使用Nginx做反向带服务器,实现nginx与tomcat服务器集群做负载均衡. 一.nginx与tomcat实现负载均衡 1.在/usr/local/ngnix/conf 创建 ...
- Nginx代理功能与负载均衡详解
序言 Nginx的代理功能与负载均衡功能是最常被用到的,关于nginx的基本语法常识与配置已在上篇文章中有说明,这篇就开门见山,先描述一些关于代理功能的配置,再说明负载均衡详细. Nginx代理服务的 ...
- Nginx+Tomcat+Redis实现负载均衡、资源分离、session共享
Nginx+Tomcat+Redis实现负载均衡.资源分离.session共享 CentOS安装Nginx http://centoscn.com/CentosServer/www/2013/0910 ...
- Nginx做NodeJS应用负载均衡配置实例
这篇文章主要介绍了Nginx做NodeJS应用负载均衡配置实例,本文直接给出配置实例,需要的朋友可以参考下. 负载均衡可以把用户的请求分摊到多个服务器上进行处理,从而实现了对海量用户的访问支持.负载均 ...
- Nginx+Keepalived主主负载均衡服务器
Nginx+keepalived主主负载均衡服务器测试实验环境: 主Nginx之一:192.168.11.27主Nginx之二:192.168.11.28Web服务器一:192.168.11.37We ...
- nginx : TCP代理和负载均衡的stream模块
一直以来,Nginx 并不支持tcp协议,所以后台的一些基于TCP的业务就只能通过其他高可用负载软件来完成了,比如Haproxy. 这算是一个nginx比较明显的缺憾.不过,在1.90发布后这个认知将 ...
- 【转】Nginx+Tomcat搭建高性能负载均衡集群
最近对负载均衡比较感兴趣,研究了公司的负载均衡的配置,用的是阿里的SLB,相当于不用运维,只需要在后台进行简单的配置就能完成Tomcat的负载均衡,索性在网上找了几篇文章去尝试搭建一个集群,然而很多都 ...
随机推荐
- 无线:WPA
WPA全名为Wi-Fi Protected Access,有WPA和WPA2两个标准,是一种保护无线电脑网络(Wi-Fi)安全的系统,它是应研究者在前一代的系统有线等效加密(WEP)中找到的几个严重的 ...
- form表单与css选择器
目录 form表单 action属性 input标签 lable标签 select标签 textarea标签 补充 网络请求方式 CSS简介 CSS基本选择器 组合选择器 属性选择器 分组与嵌套 伪类 ...
- 双webview模式,子窗口打不开或者无法切换
iOS 真机调试时,发现window.open 无效.可以结合plusReady里面不执行一起参考,博主在当时遇到这个问题只查询了资料,而后并没有来得及自己亲自验证以下方法的可行性.来日再遇上mui的 ...
- Golang可重入锁的实现
Golang可重入锁的实现 项目中遇到了可重入锁的需求和实现,具体记录下. 什么是可重入锁 我们平时说的分布式锁,一般指的是在不同服务器上的多个线程中,只有一个线程能抢到一个锁,从而执行一个任务.而我 ...
- 详解TCP四次挥手(断开TCP连接过程)
在讲述TCP四次挥手,即断开TCP连接的过程之前,需要先介绍一下TCP协议的包结构. TCP协议包结构: 这里只对涉及到四次挥手过程的字段做解释 (1) 序号(Sequence number) 我们通 ...
- golang 方法接收者
[定义]: golang的方法(Method)是一个带有receiver的函数Function,Receiver是一个特定的struct类型,当你将函数Function附加到该receiver, 这个 ...
- AtCoder ABC 250 总结
AtCoder ABC 250 总结 总体 连续若干次一样的结果:30min 切前 4 题,剩下卡在 T5 这几次卡在 T5 都是一次比一次接近, 什么 dp 前缀和打挂,精度被卡,能水过的题连水法都 ...
- CSS 技术
浏览本篇文章前可以先看之前的前端网页介绍和html常用标签以便更容易理解 本文目录: 目录 CSS 技术介绍 CSS 语法规则 CSS 和 HTML 的结合方式 第一种: 第二种 第三种 CSS 选择 ...
- C语言- 基础数据结构和算法 - 栈的顺序存储
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- Multiparty Cardinality Testing for Threshold Private Set-2021:解读
本文记录阅读该论文的笔记. 本文基于阈值加法同态加密方案提出了一个新的允许\(N\)方检查其输入集的交集是否大于\(n-t\)的IPSI方案,该协议的通信复杂度为\(O(Nt^2)\). 注意:\(N ...