Stories

Detail Return Return

四位一體水溶交融,Docker一拖三Tornado6.2 + Nginx + Supervisord非阻塞負載均衡容器式部署實踐 - Stories Detail

原文轉載自「劉悦的技術博客」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/py... 安裝項目的所有依賴。

最後,將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/...\_Tornado6\_Supervisor\_Python3.8

原文轉載自「劉悦的技術博客」 https://v3u.cn/a_id_203

user avatar u_17353607 Avatar nihaojob Avatar oeasy Avatar wszgrcy Avatar huangmingji Avatar rk405264704 Avatar ichu Avatar songxianling1992 Avatar codexiaosheng Avatar zhuyundataflux Avatar reddish Avatar zengjingaiguodekaomianbao Avatar
Favorites 39 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.