# python flask项目部署到服务器上,并制作docker镜像

### 部署python web项目到服务器（使用docker）

> **参考 : https://blog.51cto.com/fish/6023519**

#### (一)创建虚拟环境

    创建目录
    mkdir mytest
    cd mytest
    
    [root@python mytest]# pwd
    /root/mytest
    
    创建虚拟环境
    [root@python mytest]# python3 -m venv myvenv
    
    激活虚拟环境
    [root@python mytest]# source myvenv/bin/activate
    
    一旦激活虚拟环境后,将进入到该虚拟环境下的shell界面,如下:
    (myvenv) [root@python mytest]# 
    (myvenv) [root@python mytest]# ll
    total 16
    -rw-r--r--. 1 root root 222 Mar 31 11:14 demo.py
    -rw-r--r--. 1 root root 307 Mar 31 13:26 Dockerfile
    -rw-r--r--. 1 root root 282 Mar 31 13:38 gunicorn.conf.py
    drwxr-xr-x. 5 root root 100 Mar 31 11:45 myvenv
    -rw-r--r--. 1 root root 261 Mar 31 13:03 requirements.txt
    (myvenv) [root@python mytest]# 
    

#### (二)创建flask项目

> 在文件夹mytest下，创建一个demo.py启动文件

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return 'hello docker & flask & 爱看书的小沐.'
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0", debug=True)
    

> 在该环境下执行demo.py文件, 由于没有flask环境将报错

    (myvenv) [root@python mytest]# python demo.py
    ...
    
    安装flask
    (myvenv) [root@python mytest]# python install flask
    ...
    
    再次运行demo.py
    (myvenv) [root@python mytest]# python demo.py 
     * Serving Flask app 'demo' (lazy loading)
     * Environment: production
       WARNING: This is a development server. Do not use it in a production deployment.
       Use a production WSGI server instead.
     * Debug mode: on
     * Running on all addresses.
       WARNING: This is a development server. Do not use it in a production deployment.
     * Running on http://10.0.0.101:5000/ (Press CTRL+C to quit)
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 128-311-800
     
    监听5000端口,可以正常跑起来
    

#### (三)安装 [gunicorn](https://so.csdn.net/so/search?q=gunicorn&spm=1001.2101.3001.7020) gevent, 提高性能

    (myvenv) [root@python mytest]# pip install gunicorn gevent flask
    
    在mytest目录下创建gunicorn.conf.py配置文件
    (myvenv) [root@python mytest]# vim gunicorn.conf.py
    
    文件内容如下:
    workers = 5    # 定义同时开启的处理请求的进程数量，根据网站流量适当调整
    worker_class = "gevent"   # 采用gevent库，支持异步处理请求，提高吞吐量
    bind = "0.0.0.0:5000"   #端口随便写，但是注意是否已经被占用。netstap -lntp
    

> 启动gunicorn,如下

    (myvenv) [root@python mytest]# gunicorn demo:app -c gunicorn.conf.py
    [2023-03-31 14:08:57 +0800] [58874] [INFO] Starting gunicorn 20.1.0
    [2023-03-31 14:08:57 +0800] [58874] [INFO] Listening at: http://0.0.0.0:3000 (58874)
    [2023-03-31 14:08:57 +0800] [58874] [INFO] Using worker: gevent
    [2023-03-31 14:08:57 +0800] [58877] [INFO] Booting worker with pid: 58877
    [2023-03-31 14:08:57 +0800] [58878] [INFO] Booting worker with pid: 58878
    [2023-03-31 14:08:57 +0800] [58879] [INFO] Booting worker with pid: 58879
    [2023-03-31 14:08:57 +0800] [58880] [INFO] Booting worker with pid: 58880
    [2023-03-31 14:08:57 +0800] [58881] [INFO] Booting worker with pid: 58881
    

#### (四)将项目部署到docker上

> **(1)** 执行如下命令, 生成python项目所依赖的包文件, 将其写入到requirements.txt文件中

    (myvenv) [root@python mytest]# pip freeze > requirements.txt
    (myvenv) [root@python mytest]# ll
    total 16
    -rw-r--r--. 1 root root 222 Mar 31 11:14 demo.py
    -rw-r--r--. 1 root root 282 Mar 31 13:38 gunicorn.conf.py
    drwxr-xr-x. 5 root root 100 Mar 31 11:45 myvenv
    drwxr-xr-x. 2 root root  69 Mar 31 14:08 __pycache__
    -rw-r--r--. 1 root root 261 Mar 31 13:03 requirements.txt
    (myvenv) [root@python mytest]# cat requirements.txt 
    click==8.0.4
    dataclasses==0.8
    Flask==2.0.3
    gevent==22.10.2
    greenlet==2.0.2
    gunicorn==20.1.0
    importlib-metadata==4.8.3
    itsdangerous==2.0.1
    Jinja2==3.0.3
    MarkupSafe==2.0.1
    typing_extensions==4.1.1
    Werkzeug==2.0.3
    zipp==3.6.0
    zope.event==4.6
    zope.interface==5.5.2
    (myvenv) [root@python mytest]# 
    

> **(2)** 创建Dockerfile文件, 用于构建自己的镜像

    (myvenv) [root@python mytest]# vim Dockerfile
    (myvenv) [root@python mytest]# cat Dockerfile
    FROM python:3.8
    
    WORKDIR /project/
    
    COPY requirements.txt ./
    RUN python -m pip install --upgrade pip
    RUN pip install --ignore-requires-python dataclasses==0.8
    RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    #将当前所有文件拷贝到要制作的docker镜像中
    COPY . .
    
    CMD ["gunicorn", "demo:app", "-c", "./gunicorn.conf.py"]
    
    

> **(3)** 配置docker镜像加速地址

    [root@python mytest]#  mkdir -p /etc/docker
    [root@python mytest]#  tee /etc/docker/daemon.json <<-'EOF'
    {
        "registry-mirrors": [
    	"https://o6ul5754.mirror.aliyuncs.com",
    	"https://ung2thfc.mirror.aliyuncs.com",
    	"https://registry.docker-cn.com",
    	"http://hub-mirror.c.163.com",
    	"https://docker.mirrors.ustc.edu.cn"
    	]
    }
    EOF
    [root@python mytest]#  cat /etc/docker/daemon.json
    {
        "registry-mirrors": [
    	"https://o6ul5754.mirror.aliyuncs.com",
    	"https://ung2thfc.mirror.aliyuncs.com",
    	"https://registry.docker-cn.com",
    	"http://hub-mirror.c.163.com",
    	"https://docker.mirrors.ustc.edu.cn"
    	]
    }
    [root@python mytest]#  systemctl daemon-reload
    [root@python mytest]#  systemctl restart docker
    
    验证配置是否生效
    [root@python mytest]# docker info
    Client:
     Context:    default
     Debug Mode: false
     Plugins:
      buildx: Docker Buildx (Docker Inc.)
        Version:  v0.10.4
        Path:     /usr/libexec/docker/cli-plugins/docker-buildx
      compose: Docker Compose (Docker Inc.)
        Version:  v2.17.2
        Path:     /usr/libexec/docker/cli-plugins/docker-compose
      scan: Docker Scan (Docker Inc.)
        Version:  v0.23.0
        Path:     /usr/libexec/docker/cli-plugins/docker-scan
    
    Server:
     Containers: 5
      Running: 0
      Paused: 0
      Stopped: 5
     Images: 3
     Server Version: 23.0.2
     Storage Driver: overlay2
      Backing Filesystem: xfs
      Supports d_type: true
      Using metacopy: false
      Native Overlay Diff: true
      userxattr: false
     Logging Driver: json-file
     Cgroup Driver: cgroupfs
     Cgroup Version: 1
     Plugins:
      Volume: local
      Network: bridge host ipvlan macvlan null overlay
      Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
     Swarm: inactive
     Runtimes: io.containerd.runc.v2 runc
     Default Runtime: runc
     Init Binary: docker-init
     containerd version: 1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f
     runc version: v1.1.4-0-g5fd4c4d
     init version: de40ad0
     Security Options:
      seccomp
       Profile: builtin
     Kernel Version: 3.10.0-957.el7.x86_64
     Operating System: CentOS Linux 7 (Core)
     OSType: linux
     Architecture: x86_64
     CPUs: 1
     Total Memory: 1.934GiB
     Name: python
     ID: IP2S:J2XT:35RB:KK7O:HFQ5:FMME:HNT2:LYFR:74GT:7CWE:GXGM:446B
     Docker Root Dir: /var/lib/docker
     Debug Mode: false
     Registry: https://index.docker.io/v1/
     Experimental: false
     Insecure Registries:
      127.0.0.0/8
     Registry Mirrors:
      https://o6ul5754.mirror.aliyuncs.com/
      https://ung2thfc.mirror.aliyuncs.com/
      https://registry.docker-cn.com/
      http://hub-mirror.c.163.com/
      https://docker.mirrors.ustc.edu.cn/
     Live Restore Enabled: false
    

> **(4)** 制作docker镜像

    [root@python mytest]# docker build -t myapp:1.0 .
    [+] Building 18.5s (12/12) FINISHED                                                                                                                                                               
     => [internal] load build definition from Dockerfile                                                                                                                                         0.0s
     => => transferring dockerfile: 406B                                                                                                                                                         0.0s
     => [internal] load .dockerignore                                                                                                                                                            0.0s
     => => transferring context: 2B                                                                                                                                                              0.0s
     => [internal] load metadata for docker.io/library/python:3.8                                                                                                                                0.2s
     => [1/7] FROM docker.io/library/python:3.8@sha256:4c4e6735f46e7727965d1523015874ab08f71377b3536b8789ee5742fc737059                                                                          0.0s
     => [internal] load build context                                                                                                                                                            0.2s
     => => transferring context: 370.95kB                                                                                                                                                        0.2s
     => CACHED [2/7] WORKDIR /project/                                                                                                                                                           0.0s
     => CACHED [3/7] COPY requirements.txt ./                                                                                                                                                    0.0s
     => CACHED [4/7] RUN python -m pip install --upgrade pip                                                                                                                                     0.0s
     => [5/7] RUN pip install --ignore-requires-python dataclasses==0.8                                                                                                                          2.3s
     => [6/7] RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple                                                                                                   12.5s
     => [7/7] COPY . .                                                                                                                                                                           2.2s 
     => exporting to image                                                                                                                                                                       1.2s 
     => => exporting layers                                                                                                                                                                      1.1s 
     => => writing image sha256:4afe28f18625b8bf5cdcb9785d158e9917561d1d6de1367e71bf4fa3d40fc407                                                                                                 0.0s 
     => => naming to docker.io/library/myapp:1.0                                                                                                                                                 0.0s 
    [root@python mytest]# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
    myapp         1.0       4afe28f18625   59 minutes ago   1.01GB
    hello-world   latest    feb5d9fea6a5   18 months ago    13.3kB
    

> **(5)** 运行该镜像成docker容器

    [root@python mytest]# docker run -d -p 5000:5000 myapp:1.0
    a91f1d816659e5abcd74a34d4bfc2a049a60c98d0a797a49202a7cc83bbc0778
    [root@python mytest]# 
    

> 浏览器访问 http://10.0.0.101:5000 页面可以正常访问并获取到数据

> **(6)** 如果修改代码, 需要重新制作该镜像文件, 例如修改demo.py文件如下

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        return 'hello docker 666'
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0", debug=True, threaded=True)
    
    

> 同时修改端口5000为3000; 重新制作docker镜像文件

    [root@python mytest]# docker build -t myapp:1.1 .  # 这次打的标签tag版本为1.1
    [root@python mytest]# docker build -t myapp:1.1 .
    [+] Building 16.6s (12/12) FINISHED                                                                                                                                                               
     => [internal] load build definition from Dockerfile                                                                                                                                         0.0s
     => => transferring dockerfile: 406B                                                                                                                                                         0.0s
     => [internal] load .dockerignore                                                                                                                                                            0.0s
     => => transferring context: 2B                                                                                                                                                              0.0s
     => [internal] load metadata for docker.io/library/python:3.8                                                                                                                               15.2s
     => [1/7] FROM docker.io/library/python:3.8@sha256:4c4e6735f46e7727965d1523015874ab08f71377b3536b8789ee5742fc737059                                                                          0.0s
     => [internal] load build context                                                                                                                                                            0.2s
     => => transferring context: 370.93kB                                                                                                                                                        0.2s
     => CACHED [2/7] WORKDIR /project/                                                                                                                                                           0.0s
     => CACHED [3/7] COPY requirements.txt ./                                                                                                                                                    0.0s
     => CACHED [4/7] RUN python -m pip install --upgrade pip                                                                                                                                     0.0s
     => CACHED [5/7] RUN pip install --ignore-requires-python dataclasses==0.8                                                                                                                   0.0s
     => CACHED [6/7] RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple                                                                                             0.0s
     => [7/7] COPY . .                                                                                                                                                                           0.7s
     => exporting to image                                                                                                                                                                       0.5s
     => => exporting layers                                                                                                                                                                      0.5s
     => => writing image sha256:c7df86df2dd926af25bf4194e18abbd5af873ef25ba0ed5c5cbf686d8f2df62d                                                                                                 0.0s
     => => naming to docker.io/library/myapp:1.1                                                                                                                                                 0.0s
    [root@python mytest]# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
    myapp         1.1       c7df86df2dd9   7 seconds ago    1.01GB
    myapp         1.0       4afe28f18625   11 minutes ago   1.01GB
    hello-world   latest    feb5d9fea6a5   18 months ago    13.3kB
    [root@python mytest]# docker run -d -p 3000:3000 myapp:1.1
    254176fcad70aebde1bb80b120b7b1f715ea1c67ce2a4d3e949b5be1bec13abc
    [root@python mytest]#
    

> 再次访问 http://10.0.0.101:3000 已经正常访问

#### (五) 保存镜像文件到指定目录下

    查看当前镜像
    [root@python mytest]# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
    check_tools   1.4       1175ff06f5c3   21 hours ago    1.06GB
    check_tools   1.3       908c5ec78290   22 hours ago    1.04GB
    check_tools   1.2       c584b78ae39d   22 hours ago    1.04GB
    check_tools   1.1       770dd461a2cf   22 hours ago    1.04GB
    check_tools   1.0       2692e79d33a2   6 days ago      1.01GB
    myapp         1.1       c7df86df2dd9   7 days ago      1.01GB
    myapp         1.0       4afe28f18625   7 days ago      1.01GB
    hello-world   latest    feb5d9fea6a5   18 months ago   13.3kB
    hello-world   latest    feb5d9fea6a5   18 months ago   13.3kB
    
    保存镜像到指定目录下
    [root@python mytest]# docker save 1175ff06f5c3>/root/check_tools.tar #check_tools.tar为打包的文件
    
    查看保存的镜像文件 check_tools.tar
    [root@python mytest]# ll /root/
    total 1075124
    -rw-------.  1 root root       1650 Oct 22  2020 anaconda-ks.cfg
    -rw-r--r--.  1 root root 1083920896 Apr  6 16:10 check_tools.tar
    drwxr-xr-x.  4 root root        140 Apr  7 13:16 mytest
    drwxr-xr-x. 17  501  501       4096 Jan 12  2021 Python-3.6.4
    -rw-r--r--.  1 root root   16992824 Jan 12  2021 Python-3.6.4.tar.xz
    drwxr-xr-x.  2 root root         66 Jan 20  2021 test
    drwxr-xr-x.  3 root root         21 Jan 20  2021 virtualenv_1
    [root@python mytest]# 
    

#### (六) 在另一台主机上加载镜像文件

> 将上述步骤生成的`check_tools.tar`文件拷贝到另一台机器上进行加载

    [root@localhost ~]#  docker load < check_tools.tar        # check_tools.tar 为文件名称
    
    查看镜像
    [root@localhost ~]# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
    check_tools   1.4       1175ff06f5c3   21 hours ago    1.06GB
    [root@localhost ~]# 
    
    后台启动该镜像服务
    [root@localhost ~]# docker ps
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    [root@localhost ~]# docker run -d -p 3000:3000 check_tools:1.4
    22870b5ee2f07c2faf85b9f29bbec078355b9dd510154abb6a2f299a0078792d
    [root@localhost ~]# docker ps
    CONTAINER ID   IMAGE             COMMAND                  CREATED          STATUS          PORTS                                       NAMES
    22870b5ee2f0   check_tools:1.4   "gunicorn check_tool…"   35 seconds ago   Up 34 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   flamboyant_keller
    [root@localhost ~]#  netstat -tunlp
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      7037/sshd           
    tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      14811/docker-proxy  
    tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      7125/master         
    tcp6       0      0 :::22                   :::*                    LISTEN      7037/sshd           
    tcp6       0      0 :::3000                 :::*                    LISTEN      14815/docker-proxy  
    tcp6       0      0 ::1:25                  :::*                    LISTEN      7125/master         
    udp        0      0 127.0.0.1:323           0.0.0.0:*                           6325/chronyd        
    udp6       0      0 ::1:323                 :::*                                6325/chronyd        
    [root@localhost ~]# 
    

#### (七)逻辑卷映射

> 为避免每次修改代码后, 都要重新制作镜像文件, 采用逻辑卷映射: 将宿主机的某个目录映射到镜像文件中的某个目录, 以后只需要宿主机中的对应文件就可以

    逻辑卷映射 check_tool_data
    [root@python mytest]# docker run -p 3000:3000 -v check_tool_data:/project/ check_tools:1.7
    [2023-04-07 07:14:49 +0000] [1] [INFO] Starting gunicorn 20.1.0
    [2023-04-07 07:14:49 +0000] [1] [INFO] Listening at: http://0.0.0.0:3000 (1)
    [2023-04-07 07:14:49 +0000] [1] [INFO] Using worker: gevent
    [2023-04-07 07:14:49 +0000] [8] [INFO] Booting worker with pid: 8
    [2023-04-07 07:14:49 +0000] [9] [INFO] Booting worker with pid: 9
    [2023-04-07 07:14:49 +0000] [10] [INFO] Booting worker with pid: 10
    [2023-04-07 07:14:49 +0000] [11] [INFO] Booting worker with pid: 11
    [2023-04-07 07:14:50 +0000] [12] [INFO] Booting worker with pid: 12
    ^C[2023-04-07 07:17:02 +0000] [1] [INFO] Handling signal: int
    [2023-04-07 07:17:02 +0000] [8] [INFO] Worker exiting (pid: 8)
    [2023-04-07 07:17:02 +0000] [9] [INFO] Worker exiting (pid: 9)
    [2023-04-07 07:17:02 +0000] [12] [INFO] Worker exiting (pid: 12)
    [2023-04-07 07:17:02 +0000] [11] [INFO] Worker exiting (pid: 11)
    [2023-04-07 07:17:02 +0000] [10] [INFO] Worker exiting (pid: 10)
    [2023-04-07 07:17:03 +0000] [1] [INFO] Shutting down: Master
    
    查找逻辑卷位置
    [root@python mytest]# find / -name check_tool_data
    /var/lib/docker/volumes/check_tool_data
    [root@python mytest]# cd /var/lib/docker/volumes/check_tool_data
    [root@python _data]# ll
    total 24
    -rw-r--r--. 1 root root 7112 Apr  6 16:03 check_tools.py
    -rw-r--r--. 1 root root  222 Mar 31 11:14 demo.py
    -rw-r--r--. 1 root root  389 Apr  6 16:04 Dockerfile
    -rw-r--r--. 1 root root  281 Apr  6 15:27 gunicorn.conf.py
    drwxr-xr-x. 2 root root   71 Apr  7 15:14 json_file
    drwxr-xr-x. 5 root root  100 Apr  7 15:14 myvenv
    drwxr-xr-x. 2 root root   76 Apr  7 15:14 __pycache__
    -rw-r--r--. 1 root root  280 Apr  6 15:39 requirements.txt
    [root@python _data]# 
    

> 之后只需要修改 `/var/lib/docker/volumes/check_tool_data/check_tools.py`文件就可以了, 修改代码重新启动容器就可以加载到修改后的代码了
> 
>     [root@python mytest]# docker run -p 3000:3000 -v check_tool_data:/project/ check_tools:1.7
>     

您可