美文网首页
【在开发中使用docker】

【在开发中使用docker】

作者: 傻白甜心 | 来源:发表于2017-12-15 23:57 被阅读0次
    《Docker开发指南中文版》

    此章节为《Docker开发指南》的第五章—— 在开发中使用docker 的个人读书笔记

    本章节内容如下:

    1. 先创建如下目录结构
    2. 构建Hello World应用
    3. 绑定挂载(bind mount)
    4. 添加uWSGI
    5. 指定用户运行程序
    6. 善用配置文件和辅助脚本
    7. 通过Compose实现自动化
    8. 使用Compose的工作流程

    想要创建一个简单的Hello World的Web应用程序

    1. 先创建如下目录结构

    identidock/
    └── app
            └── identidock.py

    # ps:可以用tree命令查看某一目录结构
     tree identidock/
    

    如果没有tree命令,可以通过install命令,如

    # ubuntu
    apt-get install tree
    # Redhat/CentOS
    yum install tree
    # MacOS
    brew install tree
    

    identidock.py中的内容为如下:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        return "Hello World!\n"
    
    if __name__ == '__main__':
        app.run(debug=True,host='0.0.0.0')
    

    以上代码的含义是:

    1. 对Flask初始化,建立一个人应用程序对象
    2. 创建一个制定URL的route,当这个URL收到请求时候,函数hello_world被调用
    3. 初始化Web服务器,这里用0.0.0.0

    在identidock目录下创建Dockerfile,内容如下:

    FROM python:3.4
    
    RUN pip install Flask==0.10.1
    WORKDIR /app
    COPY app /app
    
    CMD ["python","identidock.py"]
    

    2. 构建Hello World应用

    cd identidock
    docker build -t identidock
    '''
    Sending build context to Docker daemon  3.584kB
    Step 1/5 : FROM python:3.4
    3.4: Pulling from library/python
    f49cf87b52c1: Pull complete
    7b491c575b06: Pull complete
    b313b08bab3b: Pull complete
    51d6678c3f0e: Pull complete
    09f35bd58db2: Pull complete
    bf6320e45c26: Pull complete
    37e455739c2f: Pull complete
    faaf9f6fee47: Pull complete
    Digest: sha256:ad36d0d99389948d3ebf47740d8cff26e46f725fa3a315a653ff4a842721edba
    Status: Downloaded newer image for python:3.4
     ---> 17929a018b26
    Step 2/5 : RUN pip install Flask==0.10.1
     ---> Running in 4d146d9c3047
    Collecting Flask==0.10.1
      Downloading Flask-0.10.1.tar.gz (544kB)
    Collecting Werkzeug>=0.7 (from Flask==0.10.1)
      Downloading Werkzeug-0.13-py2.py3-none-any.whl (311kB)
    Collecting Jinja2>=2.4 (from Flask==0.10.1)
      Downloading Jinja2-2.10-py2.py3-none-any.whl (126kB)
    Collecting itsdangerous>=0.21 (from Flask==0.10.1)
      Downloading itsdangerous-0.24.tar.gz (46kB)
    Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask==0.10.1)
      Downloading MarkupSafe-1.0.tar.gz
    Building wheels for collected packages: Flask, itsdangerous, MarkupSafe
      Running setup.py bdist_wheel for Flask: started
      Running setup.py bdist_wheel for Flask: finished with status 'done'
      Stored in directory: /root/.cache/pip/wheels/b6/09/65/5fcf16f74f334a215447c26769e291c41883862fe0dc7c1430
      Running setup.py bdist_wheel for itsdangerous: started
      Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
      Stored in directory: /root/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129a
      Running setup.py bdist_wheel for MarkupSafe: started
      Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
      Stored in directory: /root/.cache/pip/wheels/88/a7/30/e39a54a87bcbe25308fa3ca64e8ddc75d9b3e5afa21ee32d57
    Successfully built Flask itsdangerous MarkupSafe
    Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, Flask
    Successfully installed Flask-0.10.1 Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.13 itsdangerous-0.24
     ---> 1df249ca6b11
    Removing intermediate container 4d146d9c3047
    Step 3/5 : WORKDIR /app
     ---> 5375ff27929b
    Removing intermediate container 8788e4bc91aa
    Step 4/5 : COPY app /app
     ---> 1ed911ed49c0
    Step 5/5 : CMD python identidock.py
     ---> Running in ee6a3a632d37
     ---> 4e57748b28e5
    Removing intermediate container ee6a3a632d37
    Successfully built 4e57748b28e5
    Successfully tagged identidock:latest
    '''
    
    docker run -d -p 5000:5000 identidock
    #d0a15b29e165e8042c8fb0afff7c8118917362eef011d1e2a900650d42d83512
    

    测试

    curl localhost:5000
    #Hello World!
    

    如果是Mac/Window的Docker Machine来运行的话

    curl $(docker-machine ip default):5000
    #Hello World!
    

    目前流程的问题:即使代码少许改变,也要重新创建镜像,并且启动容器


    3. 绑定挂载(bind mount)

    docker stop $(docker ps -lq)
    #0c75444e8f5f
    docker rm $(docker ps -lq)
    

    停止并删除上次运行的容器(加入刚刚的例子不是最后一个运行的容器)

    docker rum -d -p 5000:5000 -v "$PWD"/app:/app identidock
    

    通过docker ps找出它的ID,代码挂载到/app,并且启动一个新的容器,-v 绝对路径,$PWD节省输入字数,提高可移植性

    验证

    curl localhost:5000
    #Hello World!
    

    通过sed将World直接替换为Docker

    #linux下为sed -i 's/World/Docker/' app/identidock.py 
    #加入''保证在Unix可以执行(Unix sed -i强制备份)
    sed -i '' s/World/Docker/ app/identidock.py
    curl localhost:5000
    #Hello Docker!
    

    是的,docker一定程度上代替了virtualenv的功能,virtualenv是一个隔离多个python环境的工具,但是仍可以在docker容器后使用virtualenv


    4. 添加uWSGI

    uWSGI是一个可直接用于生产环境的应用服务器,可以部署在Web服务器(例如nginx)后,使用uWSGI而不是Flask作为Web服务器可以让你的容器更加灵活,适用于各种环境,只需改动Dockerfile的2行就可以将容器过度到uWSGI:

    FROM python:3.4
    
    RUN pip install Flask==0.10.1 uWSGI==2.0.8
    WORKDIR /app
    COPY app /app
    
    CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py",\
    "--callable","app","--stats","0.0.0.0:9191"]
    

    以上内容包括:

    1. 添加uWSGI到python pip安装列表
    2. 创建一个uWSGI命令,启动监听9090端口的http服务器,并从/app/identidock.py运行app,还早9191端口定义了一个数据统计服务器,其实我们还可以在运行docker run时候重新定义CMD内容
    docker build -t identidock .
    docker run -d -p 9090:9090 -p 9191:9191 indetidock
    curl localhost:9090
    #Hello Docker
    

    问题:程序不是在命令行执行的,localhost:9191看不到;以root身份运行服务器,存在安全漏洞


    5. 指定用户运行程序

    添加以下#add标注的内容:

    FROM python:3.4
    
    #add 1
    RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
    
    RUN pip install Flask==0.10.1 uWSGI==2.0.8
    WORKDIR /app
    COPY app /app
    
    #add 2
    EXPOSE 9090 9191
    #add 3
    USER uwsgi
    
    CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py",\
    "--callable","app","--stats","0.0.0.0:9191"]
    
    1. 添加了uwsgi用户组,用户名
    2. 显式地声明容器监听端口
    3. 指定uwsgi用户进行操作

    docker build -t identidock .
    docker run identidock woami
    

    创建新镜像,并执行(注意,这里将Web服务器的CMD命令替换为whoami,这个命令会返回它在容器运行的名称)

    docker run -d -P --name port-test identidock
    docker port port-test
    curl localhost:32769
    

    -P 随机选择端口,--name声明为port-name
    docker port 询问指定name的端口号
    通过curl访问



    6. 善用配置文件和辅助脚本

    在Dockerfile的相同目录下,添加一个cmd.sh的脚本(注意等号左右不能有空格,《Docker开发指南》书上P66有点坑)

    #!/bin/bash
    set -e
    
    if [ "$ENV"='DEV' ]; then
      echo "Running Development Server"
      exec python "identidock.py"
    else
      echo "Running Production Server"
      exec uwsgi --http 0.0.0.0:9090 --wsgi-file /app/identidock.py \
                 --callable app --stats 0.0.0.0:9191
    fi
    

    以上代码的内容为:若ENV变量为DEV,那么它将运行调试Web服务器,exec命令的目的是为了避免创建一个新进程,以确保uwsgi进程可以收到所有信号(如SIGTERM),而不是被父进程所拦截。
    另:一般pip的安装内容可放在requirements.txt文件,而uWSGI的配置可以放在.ini文件中
    接下来需要更新Dockerfile,以下内容包括:

    1. 将脚本放进容器
    2. 从CMD指令调用它
    FROM python:3.4
    
    RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
    RUN pip install Flask==0.10.1 uWSGI==2.0.8
    WORKDIR /app
    COPY app /app
    #add 1
    COPY cmd.sh /
    
    EXPOSE 9090 9191
    USER uwsgi
    
    #change 1
    CMD ["/cmd.sh"]
    

    需要关掉仍在运行的旧容器,下面命令将停止并删除主机上所有容器

    # 停止
    docker stop $(docker ps -q)
    
    # 删除(请谨慎使用)
    docker rm $(docker ps -aq)
    

    现在可以重建附有这个脚本的镜像

    chmod +x cmd.sh
    docker build -t identidock .
    docker run -e "ENV=DEV" -p 5000:5000 identidock
    '''
    Running Development Server
     * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 130-199-939
    '''
    

    以-e "ENV=DEV"将会得到一个开发服务器,否则得到一个生产环境的服务器


    7. 通过Compose实现自动化

    Docker Compose在你装工具箱的时候,已经装了
    我们在identidock目录下创建一个名为docker-compose.yml的文件,并写入以下内容(书上P68没有缩紧正确,一直报错ERROR: In file './docker-compose.yml', service 'ports' must be a mapping not an array.,查了半天,应该是以下这么写):

    identidock:
      build: .
      ports:
        - 5000:5000
      environment:
        ENV: DEV
      volumes:
        - ./app:/app
    
    1. 第一行声明构建的容器名称,1个YAML文件可以定义多个容器
    2. build告诉Compose,这个容器的镜像是当前目录(.)的Dockerfile
    3. ports相当于docker run -p
    4. environment相当于docker run -e,将变量ENV设为DEV
    5. volumes相当于docker run -v,将当前目录的app挂载到容器的根目录下

    然后,运行

    docker-compose up
    '''
    Building identidock
    Step 1/9 : FROM python:3.4
     ---> 17929a018b26
    Step 2/9 : RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
     ---> Using cache
     ---> c76ad080b658
    Step 3/9 : RUN pip install Flask==0.10.1 uWSGI==2.0.8
     ---> Using cache
     ---> 346587bcb96d
    Step 4/9 : WORKDIR /app
     ---> Using cache
     ---> 92e7a0d285be
    Step 5/9 : COPY app /app
     ---> 88cb5b326607
    Step 6/9 : COPY cmd.sh /
     ---> ccf9abdc80a6
    Step 7/9 : EXPOSE 9090 9191
     ---> Running in 5984985fe2a0
     ---> 85f12e9d6aa9
    Removing intermediate container 5984985fe2a0
    Step 8/9 : USER uwsgi
     ---> Running in 993f145eb935
     ---> 2df3e353cdec
    Removing intermediate container 993f145eb935
    Step 9/9 : CMD /cmd.sh
     ---> Running in 6d5b1f62f4de
     ---> ee711916b3d7
    Removing intermediate container 6d5b1f62f4de
    Successfully built ee711916b3d7
    Successfully tagged identidock_identidock:latest
    WARNING: Image for service identidock was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
    Creating identidock_identidock_1 ...
    Creating identidock_identidock_1 ... done
    Attaching to identidock_identidock_1
    identidock_1  | Running Development Server
    identidock_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
    identidock_1  |  * Restarting with stat
    identidock_1  |  * Debugger is active!
    identidock_1  |  * Debugger PIN: 130-199-939
    '''
    

    (由于为之前未删除identidock,因此它创建的容器名称叫做identidock_1),在另一新终端下运行测试

    curl localhost:5000
    

    得到

    Hello Docker!
    

    同时,原本终端会多一行服务端的信息

    identidock_1  | 172.17.0.1 - - [15/Dec/2017 15:35:38] "GET / HTTP/1.1" 200 -
    

    当应用程序完毕,可以按Ctrl+C来停止
    如要切换到uWSGI服务器,需要修改YAML的environment和ports值。或者专门产生一个新的YAML,并在执行docker-compose使用-f或者COMPOSE_FILE环境指定它。


    8. 使用Compose的工作流程

    以下是compose常用命令:

    command usage
    up 启动所有在Compose文件中定义的容器,并把日志信息汇集在一起,通常与-d使用在后台运行
    build 重新构建由Dockerfile的镜像。除非镜像不存在,否则up命令不会执行构建的动作,因此需要更新镜像时候,便使用这个命令
    ps 获得由Compose管理的容器状态信息
    run 启动一个容器,并运行一个一次性的命令。被连接的容器会同时启动,除非使用了--no-deps参数
    logs 汇集由Compose管理的容器日志,并以彩色输出
    stop 停止容器,但不会删
    rm 删除已经停止的容器

    关于完整的命令,请参考Docker网站的信息:
    https://docs.docker.com/compose/reference/

    相关文章

      网友评论

          本文标题:【在开发中使用docker】

          本文链接:https://www.haomeiwen.com/subject/bvulwxtx.html