前言:
最近有個項目需要上線,是python中sanic網絡異步框架寫的,並且要求使用docker+nginx來部署項目實現負載均衡,於是乎百度了sanic項目部署,基本上都是基於docker+gunicorn部署sanic項目這篇文章,里面講的也稍稍微有些簡略,不過對於小白特別不友好,按步驟操作肯定是不行的,因為文章中只舉了很小很小的一個例子,感覺更像demo。而小白可能只是臨時接受部署任務,按部就班的操作是會出現很多錯誤的。現在就來排排坑。(建議先看一遍再動手部署)
一、Dockerfile文件放在哪?
這是sanic項目的總目錄,首先明確一點我的主運行文件是run.py,所以Dockerfile需要放在和我主文件相同地方,這樣執行build指令就可以直接在當前目錄下創建。可以直接使用vim指令在該項目目錄下創建Dockerfile,記住文件名一定要相同
二、Dockerfile里面寫什么?為什么要寫
FROM taoliu/gunicorn3
WORKDIR /temp1
ADD . /temp1
RUN pip install --upgrade pip
RUN pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
EXPOSE 9010
CMD gunicorn run:app --bind 0.0.0.0:9010 --worker-class sanic.worker.GunicornWorker
下面一個個來解釋
FROM:表示這個鏡像是基於什么創建的,也就是基礎鏡像,這里使用taoliu/gunicorn3表示我使用的是gunicorn3來運行我的鏡像,可以理解為項目需要一個環境來運行。而這個鏡像是國內的,所以下載會較快
WORKDIR:表示工作目錄是在docker中的哪里,可以稍微理解一下docker是個獨立的系統,就像安裝在Windows中虛擬機虛擬的linux,和外面的Windows命令和目錄是不相互關聯的。而這個目錄也是我們后面執行命令所在的目錄。
ADD:這里使用ADD . /temp1,前面的“.”表示將當前所在目錄的所有文件全部放到docker中的/temp1這里要和上面的WORKDIR設置的一樣,命名按照自己喜歡的來,這里是測試就用temp1代替
RUN:就相當於開始運行指令(可以理解為在linux中輸入命令行,但其實這是在docker環境中輸入的)了,docker中的系統一些python依賴的庫是不存在的,所以我們開始先更新一下docker中的pip,再由pip獲取項目所需鏡像,這里我整理到了固定的文件內即requirements.txt,推薦使用國內鏡像。這樣下載較快,我使用的是阿里雲鏡像。
阿里雲:http://mirrors.aliyun.com/pypi/simple/
豆瓣:http://pypi.douban.com/simple/
清華大學:https://pypi.tuna.tsinghua.edu.cn/simple/
中國科學技術大學:http://pypi.mirrors.ustc.edu.cn/simple/
華中科技大學:http://pypi.hustunique.com/
EXPOSE:設置鏡像暴露端口,記錄容器啟動時監聽哪些端口,容器啟動時,Docker Daemon會掃描鏡像中暴露的端口,如果加入-P參數,Docker Daemon會把鏡像中所有暴露端口導出,並為每個暴露端口分配一個隨機的主機端口(暴露端口是容器監聽端口,主機端口為外部訪問容器的端口)
注意:EXPOSE只設置暴露端口並不導出端口,只有啟動容器時使用-P/-p才導出端口,這個時候才能通過外部訪問容器提供的服務
CMD:設置容器的啟動命令,也就是當我們啟動容器的時候執行的命令,我的項目端口是0.0.0.0:9010,文件是run,所以使用gunicorn3啟動時,命令為上述文件中。需要注意的是Dockerfile中只能有一條CMD命令,如果寫多了則最后一條生效
三、build鏡像
上面的步驟處理完之后就可以build出新鏡像了,使用下面命令:
sudo docker build -t sanic_item .
這里千萬要加后面的點,代表當前路徑下創建鏡像
四、使用run還是使用create+start
在對於新技術肯定要經過較多的測試才能很好的掌握,如果使用下面這條命令:
sudo docker run --name sanic1 -p 8080:8080 sanic_item
--name:為容器起個別名,這樣可以使用這個別名操作容器,而不需要用隨機的容器ID來進行操作
-p:來指定端口,前面的端口是linux的,后面的端口是docker中項目運行的端口,兩個可以一樣可以不一樣。但是為了方便開發及測試,建議盡量一樣.
后面就是鏡像名了。而這條run語句就等於create+start即創建並開啟容器。這里說一下容器是基於鏡像來運行的。關於容器和鏡像的關系要深入docker中了解。這里不詳細介紹。這條語句的弊端就是第一次使用的部署可以使用,但是如果第二次還使用,那么就會不停的創建新容器。在不了解的情況下,以為run指令只會運行鏡像。沒想到docker中是根據鏡像來創建容器再運行。濫用run指令的結果圖:
可以看出很多是基於一個鏡像來創建的容器。其實第一次使用完run之后便可使用一些經常使用的命令:
sudo docker stop sanic1 #停止當前運行的容器,前提是run指令時有--name來指代名字
sudo docker start sanic1 #只需要啟動就行,不需要再用run或create
sudo docker ps #查看當前運行鏡像
sudo docker ps -a #查看當前容器狀態,正在運行的容器PORTS列會有參數,上圖就是沒有容器正在運行
sudo docker rmi 鏡像ID/別名 #需要先使用ps指令找到要刪除的鏡像ID再刪除
sudo docker rm 容器ID/別名 #比如刪除sanic1這個容器sudo docker rm sanic1
五、使用nginx實現負載均衡
首先明確一點一個端口不能同時被兩個程序所占用。就打個比方來說,現在的項目前端發來的請求訪問的是8080端口,如果是單機版項目,直接運行監聽8080即可。但是目前遇到的情況是項目部署在docker上,還要啟動nginx來監聽。也就會造成這個端口有兩個進程在監聽,這時候只能啟動一個。並且我們要部署多個后台服務。所以首先考慮nginx。我的nginx配置文件如下(注意看中文注釋部分)
http { #代表HTTP協議其他的協議需要其他定義
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' #這里規定的是日志的輸出格式,建議打開
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server{
listen 8080;#監聽8080端口
server_name localhost;
access_log /usr/logs/host1.access.log main;#開啟日志功能,只要訪問這個端口的都會有輸出,具體的名字和目錄自己指定,main為上面定義的日志格式
location /{
proxy_pass http://localhost; #對於發送到8080端口的請求可以進行轉發
}
}
server { #nginx默認服務,這里只需要放入前端界面即可
listen 80;
server_name localhost;
#charset koi8-r;
access_log /usr/logs/host.access.log main; #不同端口我建立了不同日志,必須要保證日志所在目錄有效才會創建,同時日志我這里沒設置無緩存刷出,若要看見實時消息的才設置
root /usr/local/dist2/;
# location / {
# root html;
# index index.html index.htm;
location /{
index index.html index.htm;
}
}
upstream localhost{#這里是實現負載均衡的策略,我的上一篇博客中介紹過為什么使用這種方法
ip_hash;
server 0.0.0.0:9010 weight=1;
server 0.0.0.0:9011 weight=1;
server 0.0.0.0:9012 weight=1;
}
}#這里代表HTTP協議的請求即定義的服務完成
可以看到我們對於8080端口的請求,轉發到了9010,9011,9012三個端口,那么docker需要准備三個不同鏡像,來創建三個不同容器,這樣才能實現nginx的代理。對於sanic項目只需要改一下運行端口即可,其余部分相同,Dockerfile中的端口也要與其對應。比如我當前的sanic項目改了三個不同端口為9010,9011,9012。(對應上面講的Dockerfile也要變),然后build三個不同鏡像為sanic_item1,sanic_item2,sanic_item3。然后啟動三個命令窗口輸入下面的指令
sudo docker run --name sanic1 -p 9010:9010 sanic_item1
sudo docker run --name sanic2 -p 9011:9011 sanic_item2
sudo docker run --name sanic3 -p 9012:9012 sanic_item3
便能運行三個容器了。
注意:這里介紹的是縱向擴展的負載均衡架構,也是一個介紹。如果多台其實只要改ip地址即可,端口可以不改變。這里所說的docker+nginx實現負載均衡,不需要Nginx部署在docker內部,Nginx只是一個請求轉發的工具,如果是遇到上千萬級請求或上億級,超過了nginx的負荷才需要部署多台。主要的壓力還是在后端和前端處理和解析數據上。
總結:
這篇博客主要是目前使用sanic項目部署的資料特別少,而這么少的資料中整理起來,以及遇到的一些bug很難查找。所以稍微講了一下部署項目遇到的坑和解決方案。建議先通讀一遍,腦子里大概有個印象再上手部署。
補充一點:為什么要部署在docker上,因為Docker有點像Git,也就是你創建完的鏡像,可以上傳到DockerHub中讓別人或者開發組的其他人下載,實現一次部署就能到處運行,emmm,我覺得這才是最主要的。不然上述部署過程完全能夠在linux中實現。