1. 程式人生 > >Docker構建nginx+uwsgi+flask映象(二)

Docker構建nginx+uwsgi+flask映象(二)

Dockerfile搭建環境並打包應用

在上一章Docker構建nginx+uwsgi+flask映象(一)的學習中,我們學會用命令列一句一句在alpine環境中搭建nginx+uwsgi+flask服務,但這體現不了Docker為我們帶來的便利,而本章,我們將通過Dockerfile來製作基礎映象和打包應用,因此會有兩個Dockerfile檔案。

我們先來寫第一個Dockerfile檔案,這個檔案負責搭建執行環境,執行環境需要包括:nginx、uwsgi、Python3:

# 配置基礎映象
FROM alpine:3.8

# 新增標籤說明
LABEL author="moshangguang" email="
[email protected]
" purpose="nginx+uwsgi+Python3基礎映象" # 配置清華映象地址 RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.8/main/" > /etc/apk/repositories # 更新升級軟體 RUN apk add --update --upgrade # 安裝軟體 RUN apk add --no-cache nginx python3 uwsgi uwsgi-python3 # 升級pip,這一步同時會在/usr/bin/目錄下生成pip可執行檔案 RUN pip3 install --no-cache-dir --upgrade pip # 建立軟連結 RUN ln -s /usr/bin/python3 /usr/bin/python

     

上面的安裝軟體相比之前少了個vim,因為之前我們是在容器內部編輯配置檔案,所以需要這個命令,但現在我們的配置檔案都是預先在容器之外編輯好在通過docker拷貝到容器內部,所以也就無需安裝vim。另外建立軟連線時,我們只建立了從python到python3的軟連線,沒有建立pip到pip3的軟連結,因為/usr/bin/pip在安裝軟體時已經生成。

執行docker build命令,生成映象,執行docker images命令可以看到生成nginx_uwsgi_py3映象,標籤為alpine3.8:

[[email protected] docker]# docker build -t nginx_uwsgi_py3:alpine3.8 .
Sending build context to Docker daemon 4.096 kB
……
Successfully built 63be35fe36ca
[
[email protected]
docker]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx_uwsgi_py3 alpine3.8 63be35fe36ca 5 minutes ago 60 MB

  

於是,我們完成了第一個Dockerfile檔案,這個Dockerfile檔案可以為我們搭建我們所需要的執行環境映象。

我們可以把構建好的基礎映象推送到我們的Docker hub倉庫,先用docker login登入之後

[[email protected] docker]# docker login -u moshangguang -p 123456
Login Succeeded

  

tips:上面登入密碼是假的哈。

然後為我們的映象打上tag,之後推送映象。

[[email protected] docker]# docker tag 687445ba4c7f moshangguang/nginx_uwsgi_py3:alpine3.8
[[email protected] docker]# docker images
REPOSITORY                       TAG                   IMAGE ID            CREATED             SIZE
moshangguang/nginx_uwsgi_py3     alpine3.8             63be35fe36ca        22 minutes ago      60 MB
nginx_uwsgi_py3                  alpine3.8             63be35fe36ca        22 minutes ago      60 MB
[[email protected] docker]# docker push moshangguang/nginx_uwsgi_py3:alpine3.8
The push refers to a repository [docker.io/moshangguang/nginx_uwsgi_py3]
……
alpine3.8: digest: sha256:412ec97c1c51dffeee6b924494bc size: 1154

  

有了基礎映象,我們就可以開始編寫我們的應用了,這裡先給出我們應用的目錄結構(web_app在github上的地址):

[[email protected] docker]# tree web_app
web_app
├── app
│   ├── app.py
│   ├── requirements.txt
│   └── uwsgi.ini
├── Dockerfile
└── nginx.conf

1 directory, 5 files

  

最外層的web_app目錄包含一個app目錄,和兩個檔案,分別是Dockerfile、nginx.conf,注意,web_app下的Dockerfile檔案和之前的Dockerfile檔案不同,這裡的Dockerfile檔案是用來打包應用的。nginx.conf檔案在打包應用時會拷貝到容器中,作為nginx啟動的配置。

app目錄下有三個檔案,分別是:app.py、requirements.txt和uwsgi.ini。我們唯一不熟悉的就是requirements.txt,這個檔案用來存放我們Python應用所需要的庫,如flask、flask_sqlalchemy等等。在打包應用時會執行pip命令讀取這個檔案的內容,安裝我們所需要的庫。

這裡,我們列印下requirements.txt的內容:

[[email protected] app]# cat requirements.txt 
flask
flask_sqlalchemy

  

app.py檔案也略做修改,新增兩個路由/hello和/world:

from flask import Flask

app = Flask(__name__)


@app.route('/hello')
def hello():
    return 'Hello!!!\n'


@app.route('/world')
def world():
    return 'World!!!\n'


@app.route('/')
def hello_world():
    return 'Hello World!!!\n'


if __name__ == '__main__':
    app.run()

  

上一章中,我們在uwsgi.ini檔案中將uwsgi-socket配置繫結到本機的9000埠,同時在nginx.conf檔案中設定uwsgi_pass,將請求轉發到9000埠,這樣的做法顯得有些累贅,如果以後我不想用9000埠,意味著我需要改兩個地方。那麼,有沒有辦法讓uwsgi自動獲取繫結到一個埠,而nginx.conf又能獲取到uwsgi所繫結的埠呢?肯定是有的:

uwsgi.ini

[uwsgi]
uwsgi-socket    = /tmp/uwsgi.sock
chmod-socket    = 777
callable        = app
plugin          = python3
wsgi-file       = app.py
buffer-size     = 65535
processes       = %(%k * 2)
threads         = %(%k * 20)
disable-logging = true

    

上面的uwsgi.ini檔案中,我們不再將uwsgi-socket這個配置項繫結到特定的一個埠,而是指定了一個檔案,這個檔案是Unix套接字,即通過檔案系統(而非網路地址)進行定址和訪問的套接字。配置uwsgi-socket之後,還需要配置chmod-socket,Unix socket是個檔案,所以會受到Unix系統的許可權限制,可以配置成660或者777,使得uwsgi客戶端能夠訪問這個Unix socket檔案,這裡配置為777。

這裡新增兩個優化引數:processes和threads,分別是開啟的程序數和執行緒數,而%k是魔數變數,代表CPU核數,如果我們是雙核CPU,那這裡的processes和threads分別為4和40,即有4個程序,每個程序有40個執行緒。disable-logging的意思一目瞭然,代表不記錄請求資訊的日誌,只記錄錯誤以及uwsgi內部訊息到日誌中。

最後,我們再來看下nginx.conf需要做改動的地方,其實也就是http模組下的server:

server {
  listen 6666;
  charset utf-8;
  client_max_body_size 75M;
  location / {
	include uwsgi_params;
	uwsgi_pass unix:///tmp/uwsgi.sock;
        ……
  }
}

  

其實改動的地方也只有一個uwsgi_pass,原先我們是直接繫結在9000埠上,而現在我們要指向uwsgi-socket所指向的Unix套接字。這樣,nginx就可以自動將請求轉發給uwsgi所監聽的套接字了。

這裡給出nginx.conf全部的內容:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
worker_rlimit_nofile 20480;


events {
  use epoll;
  worker_connections 20480;
  multi_accept on;
}


http {
    include       /etc/nginx/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"';
    #請求量級大建議關閉acccess_log
    #access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  300s;
    client_header_timeout 300s;
    client_body_timeout 300s;

    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_types text/html application/javascript application/json;

    include /etc/nginx/conf.d/*.conf;

    server {
      listen 6666;
      charset utf-8;
      client_max_body_size 75M;
      location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/uwsgi.sock;
        uwsgi_send_timeout 300;
        uwsgi_connect_timeout 300;
        uwsgi_read_timeout 300;
      }
    }
}

  

最後,我們來看下用於打包應用的Dockerfile:

# 使用基礎映象庫
FROM moshangguang/nginx_uwsgi_py3:alpine3.8

# 建立工作路徑
RUN mkdir /app

# 指定容器啟動時執行的命令都在app目錄下執行
WORKDIR /app

# 替換nginx的配置
COPY nginx.conf /etc/nginx/nginx.conf

# 將本地app目錄下的內容拷貝到容器的app目錄下
COPY ./app/ /app/

# pip讀取requirements.txt內容安裝所需的庫
RUN pip install -r /app/requirements.txt -i  https://pypi.tuna.tsinghua.edu.cn/simple some-package --no-cache-dir

# 啟動nginx和uwsgi
ENTRYPOINT nginx -g "daemon on;" && uwsgi --ini /app/uwsgi.ini

  

上面的每一條命令都有註釋,這裡就不再多作介紹了。

現在,讓我們來打包web_app應用吧!將工作目錄移到web_app目錄下,執行docker build命令,建立映象:

[[email protected] web_app]# docker build -t web_app .
Sending build context to Docker daemon 24.58 kB
……
Successfully built 88212eefb0b4

 

檢視剛剛建立的web_app映象:

[[email protected] web_app]# docker images
REPOSITORY                       TAG                   IMAGE ID            CREATED              SIZE
web_app                          latest                88212eefb0b4        About a minute ago   79.9 MB

  

根據映象啟動一個容器,容器內部的nginx監聽的是6666埠,而宿主機則用9999埠接收請求,再轉發到容器內部的6666埠:

[[email protected] web_app]# docker run -p 9999:6666 -d web_app
a8cd1104dfc994637011ebd9dd9160d62eab64b1c9bb6ceb9266c092eb425452

  

這裡,測試容器內的應用是否能正常處理使用者的請求:

 

 

 

 

 

到此為止,我們便完成了用Docker構建基礎映象,並打包應用了。