使用Docker-compose构建容器

作者: anyesu | 来源:发表于2017-06-30 02:47 被阅读620次

    前言


    docker常用指令详解一文中介绍过使用 docker run 命令配合各种复杂的选项完成对容器的构建和运行,但是这么长串的命令真的不是很好记也容易敲错。Docker Compose 是一个用来定义和运行复杂应用的 Docker 工具,以 yaml 格式的数据来保存容器配置,使用更简单的命令完成对容器的管理。此外 docker-compose.yml 还起到一个说明文档的作用, 一切配置在里面显得一目了然,就不用另外单独写部署文档了。

    1. 安装 Docker Compose(官方文档)
    # curl方式安装(推荐)
    # 如果权限不够,先执行 sudo -i 切换到root帐号
    curl -L https://get.daocloud.io/docker/compose/releases/download/1.14.0/docker-compose-`uname -s`-`uname -m` > /tmp/docker-compose
    chmod +x /tmp/docker-compose
    sudo mv /tmp/docker-compose /usr/local/bin/docker-compose
    
    # pip方式安装(需要python,[pip安装方法]())
    sudo pip install docker-compose
    
    2. 卸载 Docker Compose
    # 对于curl安装方式
    sudo rm /usr/local/bin/docker-compose
    
    # 对于pip安装方式
    pip uninstall docker-compose
    

    使用方法(参考)


    docker-compose [选项] [子命令]
    
    命令选项列表
    选项 说明
    -f 指定配置文件, 默认为 ./docker-compose.yml
    -p 设置项目名, 默认为配置文件上级目录名
    --verbose 输出详细信息
    -H 指定docker服务器, 相当于 docker -H
    子命令列表
    子命令 说明
    build 构建或重建服务依赖的镜像(配置文件指定build而不是image)
    config 校验文件并显示解析后的配置
    images 列出容器使用的镜像
    events 监控服务下容器的事件
    logs 显示容器的输出内容 相当于 docker logs
    port 打印绑定的开放端口
    ps 显示当前项目下的容器,加-p参数指定项目名 相当于 docker ps
    help 命令帮助
    pull 拉取服务用到的镜像 相当于 docker pull
    up 项目下创建服务并启动容器,如果指定了项目名,其他操作也要带上项目名参数。容器名格式:[项目名]\_[服务名]\_[序号]
    down 移除 up 命令创建的容器、网络、挂载点、镜像
    pause 暂停服务下所的容器
    unpause 恢复服务下所有暂停的容器
    rm 删除服务下停止的容器
    exec 在服务下启动的容器中运行命令 相当于 docker exec,
    run 服务下创建并运行容器 相当于 docker run ,与 up 命令的区别在于端口要另外映射,且不受start/stop/restart/kill等命令影响,容器名格式:[项目名]\_[服务名]\_run\_[序号]
    scale 设置服务的容器数目,多增少删
    start 开启服务(up命令创建的所有容器) 相当于 docker start
    stop 停止服务(up命令创建的所有容器) 相当于 docker stop
    restart 重启服务(up命令创建的所有容器) 相当于 docker restart
    kill 像服务发送信号(up命令创建的所有容器) 相当于 docker kill
    docker-compose.yml 语法(参考进阶用法)

    示例结构:

    networks: {}
    services:
      [service-name-1]:
        image: ...
        network_mode: bridge
        ports:
          \- 8080:8080/tcp
        ...
      ...
      [service-name-N]:
        image: ...
        network_mode: bridge
        ports:
          \- 8080:8080/tcp
        ...
    # docker-compose语法版本
    version: '2.1'
    volumes: {}
    

    一般只写第二层内容即可, 如果要指定语法版本则要从最外层开始写。

    示例


    以我去年写的一篇websocket文章中的项目作为示例

    在当前目录下创建 docker-compose.yml

    # tomcat版
    demo-websocket-tomcat:
      # 指定用于构建镜像的Dockerfile路径, 值为字符串
      build: '.'
      # 设置容器用户名(镜像中已创建),默认root
      user: user_docker
      # 设置容器主机名
      hostname: docker-anyesu
      # 容器内root账户是否拥有宿主机root账户的所有权限 [参考](http://blog.csdn.net/halcyonbaby/article/details/43499409)
      privileged: false
      # always (当容器退出时docker自动重启它)
      # on-failure:10 (当容器非正常退出, 最多自动重启10次, 10之后不再重启)
      restart: always
      # 容器的网络连接类型,anyesu_net是创建的自定义网桥
      net: anyesu_net
      # 挂载点,设置与宿主机之间的路径映射
      volumes:
      - /usr/anyesu/docker/tomcat-logs:/usr/anyesu/tomcat/logs
      # :ro表示只读,默认为:rw
      #- /usr/anyesu/docker/tomcat-logs:/usr/anyesu/tomcat/logs:ro
      # 与宿主机之间的端口映射
      ports:
      - 8080:8080
      # 设置容器dns, 如果设置了net项则此项失效, 暂不清楚原因, 不过可以使用本地文件映射为容器的/etc/resolv.conf文件。
      dns: 8.8.8.8
      # 设置容器环境变量
      environment:
        JAVA_OPTS: -Djava.security.egd=file:/dev/./urandom
    
    # nodejs版
    demo-websocket-nodejs:
      # 使用镜像
      image: node:alpine
      privileged: false
      restart: always
      volumes:
      - /usr/anyesu/docker/websocket-master/Nodejs-Websocket:/usr/anyesu/node
      ports:
      - 3000:8080
      - 3002:3002
      # 覆盖容器启动后默认执行的命令
      command: sh -c "npm install ws@1.1.0 express -g && node /usr/anyesu/node/server.js"
      environment:
        NODE_PATH: /usr/local/lib/node_modules
    

    再创建 Dockerfile 文件, 用于构建镜像

    FROM alpine:latest
    MAINTAINER anyesu
    
    RUN echo -e "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/main\n\
    https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/community" > /etc/apk/repositories && \
        # 设置时区
        apk --update add ca-certificates && \
        apk add tzdata && \
        ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        echo "Asia/Shanghai" > /etc/timezone && \
        # 安装jdk
        apk add openjdk7=7.121.2.6.8-r0 && \
        # 安装wget
        apk add wget=1.18-r1 && \
        tmp=/usr/anyesu/tmp && \
        mkdir -p $tmp && \
        cd /usr/anyesu && \
        # 下载tomcat
        wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-7/v7.0.78/bin/apache-tomcat-7.0.78.tar.gz && \
        tar -zxvf apache-tomcat-7.0.78.tar.gz && \
        mv apache-tomcat-7.0.78 tomcat && \
        # 清空webapps下自带项目
        rm -r tomcat/webapps/* && \
        rm apache-tomcat-7.0.78.tar.gz && \
        cd $tmp && \
        # 下载websocket-demo源码
        wget https://github.com/anyesu/websocket/archive/master.zip && \
        unzip master.zip && \
        proj=$tmp/websocket-master/Tomcat-Websocket && \
        src=$proj/src && \
        tomcatBase=/usr/anyesu/tomcat && \
        classpath="$tomcatBase/lib/servlet-api.jar:$tomcatBase/lib/websocket-api.jar:$proj/WebRoot/WEB-INF/lib/fastjson-1.1.41.jar" && \
        output=$proj/WebRoot/WEB-INF/classes && \
        mkdir -p $output && \
        # 编译java代码
        /usr/lib/jvm/java-1.7-openjdk/bin/javac -sourcepath $src -classpath $classpath -d $output `find $src -name "*.java"` && \
        # 拷贝到tomcat
        mv $proj/WebRoot $tomcatBase/webapps/ROOT && \
        rm -rf $tmp && \
        apk del wget && \
        # 清除apk缓存
        rm -rf /var/cache/apk/* && \
        # 添加普通用户
        addgroup -S group_docker && adduser -S -G group_docker user_docker && \
        # 修改目录所有者
        chown user_docker:group_docker -R /usr/anyesu
    
    # 设置环境变量
    ENV JAVA_HOME /usr/lib/jvm/java-1.7-openjdk
    ENV CATALINA_HOME /usr/anyesu/tomcat
    ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
    
    # 暴露端口
    EXPOSE 8080
    
    # 启动命令(前台程序)
    CMD ["catalina.sh", "run"]
    

    接着就可以构建并运行了

    # 创建一个名为anyesu_net、网段为172.18.0.0的网桥(docker默认创建的网段为172.17.0.0)
    docker network create --subnet=172.18.0.0/16 anyesu_net
    
    # 子命令 up 选项:
    # -d 后台运行
    # --build 重新构建依赖的镜像(如果docker-compose.yml中指定了build项的话)
    # --force-recreate 重启容器, 即使配置和镜像都没有改变
    wget https://github.com/anyesu/websocket/archive/master.zip
    unzip master.zip
    sed -i 's$8082$3002$' ./websocket-master/Nodejs-Websocket/server.js
    sed -i 's$8082$3002$' ./websocket-master/Nodejs-Websocket/js/chat.js
    sudo docker-compose up -d --build --force-recreate
    

    之后就可以通过 ip:8080ip:3000 访问两个项目了(注意防火墙开放这两个端口哦)

    说明:
    • 上面的两个项目分别展示了docker的两种使用方式:
    1. 环境和应用打包到一个镜像中作为一个整体
    2. 环境和应用独立,可以自由组合
    • 关于 docker-composebuild 操作网上的介绍比较少,有几点要注意的:
    1. 配置文件中 build 参数内容指定 Dockerfile 路径, 貌似在高版本中可以是map类型
    2. build 操作需要使用 sudo 提权,否则会报一些奇怪的错误
    • 将日志文件映射到宿主机目录上方便查看,如果源码不想打包到镜像中也可以做映射。还有一个小技巧,用空目录映射镜像已有目录达到删除的效果,如 tomcat/webapps 下的自带应用是没用的,可以用这个方法清除。

    • JVM参数 可以配置在环境变量 JAVA_OPTS 中,nodejs全局安装 ( -g ) 别忘了设置 NODE_PATH

    • command 只能运行一条命令,如果要运行多条就使用 sh -c "..." 实际命令 && 隔开做参数传入

    • websocket的demo源码有不少同学反映项目配置不起来,研究下上面的 Dockerfiledocker-compose.yml 应该就能对项目的结构和依赖有更清晰的了解。

    • demo-websocket-tomcat 项目的配置中, 出于安全考虑,使用普通用户登录。宿主机的 /usr/anyesu/tomcat/logs 目录要设置 777 权限,否则 tomcat 无法写日志。

    • 容器的配置信息, hosts , dns 配置文件等可以在 /var/lib/docker/containers/[容器id]/ 下查看。

    遇到的坑:

    之前一个原本启动只要10秒的小项目在容器中有时候重启要好几分钟甚至可能会一直起不来,在中找到下面一段日志

    INFO: Deploying web application directory /usr/anyesu/tomcat/webapps/ROOT
    Jun 10, 2017 3:03:28 PM org.apache.catalina.startup.TldConfig execute
    INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
    Jun 10, 2017 3:14:01 PM org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom
    INFO: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [624,301] milliseconds.
    Jun 10, 2017 3:14:05 PM org.apache.catalina.startup.HostConfig deployDirectory
    INFO: Deployment of web application directory /usr/fuyou/tomcat/webapps/ROOT has finished in 644,588 ms
    Jun 10, 2017 3:14:05 PM org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["http-bio-8080"]
    Jun 10, 2017 3:14:05 PM org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["ajp-bio-8009"]
    Jun 10, 2017 3:14:05 PM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 644776 ms

    关键的一句就是: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [624,301] milliseconds
    这个步骤竟然用了10分钟!查了一下发现是docker容器下随机数与熵池策略有问题。
    解决方法如下,个人更推荐第二种

    • 打开 $JAVA_PATH/jre/lib/security/java.security
      securerandom.source=file:/dev/random 替换为 securerandom.source=file:/dev/urandom
    • 使用 JVM参数 JAVA_OPTS=-Djava.security.egd=file:/dev/./urandom

    系列文章


    Docker 学习总结

    Docker 常用指令详解

    使用Dockerfile构建镜像

    Docker Daemon连接方式详解

    Docker下的网络模式


    转载请注明出处:http://www.jianshu.com/p/ee8e7d2eb645

    相关文章

      网友评论

        本文标题:使用Docker-compose构建容器

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