zoukankan      html  css  js  c++  java
  • 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="XXX@qq.com"  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:

    [root@ docker]# docker build -t nginx_uwsgi_py3:alpine3.8 .
    Sending build context to Docker daemon 4.096 kB
    ……
    Successfully built 63be35fe36ca
    [root@ 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登录之后

    [root@ docker]# docker login -u moshangguang -p 123456
    Login Succeeded
    

      

    tips:上面登录密码是假的哈。

    然后为我们的镜像打上tag,之后推送镜像。

    [root@ docker]# docker tag 687445ba4c7f moshangguang/nginx_uwsgi_py3:alpine3.8
    [root@ 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
    [root@iZj6c0zloujsauztjjhyhgZ 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上的地址):

    [root@ 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的内容:

    [root@ 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!!!
    '
    
    
    @app.route('/world')
    def world():
        return 'World!!!
    '
    
    
    @app.route('/')
    def hello_world():
        return 'Hello World!!!
    '
    
    
    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命令,创建镜像:

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

    查看刚刚创建的web_app镜像:

    [root@ 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端口:

    [root@ web_app]# docker run -p 9999:6666 -d web_app
    a8cd1104dfc994637011ebd9dd9160d62eab64b1c9bb6ceb9266c092eb425452
    

      

    这里,测试容器内的应用是否能正常处理用户的请求:

     

    到此为止,我们便完成了用Docker构建基础镜像,并打包应用了。

  • 相关阅读:
    alg--动态规划(dynamic planning)
    alg--分治法
    汇编-理解call,ret
    汇编--实验7
    leetCode笔记--binary tree
    leetCode笔记--(1)
    C#获取当前路径的方法如下
    VS2013 快捷键 与 RESHARPER 冲突
    使用Visual Studio 2013进行单元测试--初级篇
    VS 插件
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/10220860.html
Copyright © 2011-2022 走看看