Skip to content

使用 Docker Compose 构建容器 #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
anyesu opened this issue Apr 26, 2019 · 0 comments
Open

使用 Docker Compose 构建容器 #11

anyesu opened this issue Apr 26, 2019 · 0 comments

Comments

@anyesu
Copy link
Owner

anyesu commented Apr 26, 2019

前言


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

安装与卸载


1. 安装 Docker Compose ( 官方文档 )
# curl方式安装(推荐)
# 源站(https://github.com/docker/compose/releases)太慢了,这里用daocloud上的镜像,版本号可自行修改
sudo 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安装方法](http://www.cnblogs.com/lzj0218/p/5753675.html))
sudo pip install docker-compose
2. 卸载 Docker Compose
# 对于curl安装方式
sudo rm /usr/local/bin/docker-compose

# 对于pip安装方式
sudo 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: {}

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

示例


以我去年写的一篇 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.94/bin/apache-tomcat-7.0.94.tar.gz && \
    tar -zxvf apache-tomcat-7.0.94.tar.gz && \
    mv apache-tomcat-7.0.94 tomcat && \
    # 清空webapps下自带项目
    rm -r tomcat/webapps/* && \
    rm apache-tomcat-7.0.94.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-compose 的 build 操作网上的介绍比较少,有几点要注意的:
  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

@anyesu anyesu changed the title 使用 Docker-compose 构建容器 使用 Docker Compose 构建容器 Apr 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant