使用 Docker Compose 構(gòu)建容器

前言


Docker 常用指令詳解 一文中介紹過使用 docker run 命令配合各種復(fù)雜的選項(xiàng)完成對(duì) 容器 的構(gòu)建和運(yùn)行,但是這么長串的命令真的不是很好記也容易敲錯(cuò)。Docker Compose 是一個(gè)用來定義和運(yùn)行復(fù)雜應(yīng)用的 Docker 工具,以 yaml 格式的數(shù)據(jù)來保存 容器配置,使用更簡單的命令完成對(duì) 容器 的管理。此外 docker-compose.yml 還起到一個(gè)說明文檔的作用, 一切配置在里面顯得一目了然,就不用另外單獨(dú)去寫部署文檔了。

安裝與卸載


1. 安裝 Docker Compose ( 官方文檔 )
# curl方式安裝(推薦)
# 源站(https://github.com/docker/compose/releases)太慢了,這里用daocloud上的鏡像,版本號(hào)可自行修改
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
# 對(duì)于curl安裝方式
sudo rm /usr/local/bin/docker-compose

# 對(duì)于pip安裝方式
sudo pip uninstall docker-compose

使用方法 ( 參考 )


docker-compose [選項(xiàng)] [子命令]
命令選項(xiàng)列表
選項(xiàng) 說明
-f 指定配置文件, 默認(rèn)為 ./docker-compose.yml
-p 設(shè)置項(xiàng)目名, 默認(rèn)為配置文件上級(jí)目錄名
--verbose 輸出詳細(xì)信息
-H 指定docker服務(wù)器, 相當(dāng)于 docker -H
子命令列表
子命令 說明
build 構(gòu)建或重建服務(wù)依賴的鏡像 ( 配置文件指定 build 而不是 image )
config 校驗(yàn)文件并顯示解析后的配置
images 列出容器使用的鏡像
events 監(jiān)控服務(wù)下容器的事件
logs 顯示容器的輸出內(nèi)容 相當(dāng)于 docker logs
port 打印綁定的開放端口
ps 顯示當(dāng)前項(xiàng)目下的容器,加 -p 選項(xiàng)來指定項(xiàng)目名 相當(dāng)于 docker ps
help 命令幫助
pull 拉取服務(wù)用到的鏡像 相當(dāng)于 docker pull
up 項(xiàng)目下創(chuàng)建服務(wù)并啟動(dòng)容器,如果指定了項(xiàng)目名,其他操作也要帶上項(xiàng)目名參數(shù)。容器名格式:[ 項(xiàng)目名 ]_[ 服務(wù)名 ]_[ 序號(hào) ]
down 移除 up 命令創(chuàng)建的容器、網(wǎng)絡(luò)、掛載點(diǎn)、鏡像
pause 暫停服務(wù)下所的容器
unpause 恢復(fù)服務(wù)下所有暫停的容器
rm 刪除服務(wù)下停止的容器
exec 在服務(wù)下啟動(dòng)的容器中運(yùn)行命令 相當(dāng)于 docker exec,
run 服務(wù)下創(chuàng)建并運(yùn)行容器 相當(dāng)于 docker run ,與 up 命令的區(qū)別在于端口要另外映射,且不受 start/stop/restart/kill 等命令影響,容器名格式:[ 項(xiàng)目名 ]_[ 服務(wù)名 ]_run_[ 序號(hào) ]
scale 設(shè)置服務(wù)的容器數(shù)目, 變多就新增, 變少就刪除
start 開啟服務(wù) ( up 命令創(chuàng)建的所有容器 ) 相當(dāng)于 docker start
stop 停止服務(wù) ( up 命令創(chuàng)建的所有容器 ) 相當(dāng)于 docker stop
restart 重啟服務(wù) ( up 命令創(chuàng)建的所有容器 ) 相當(dāng)于 docker restart
kill 向服務(wù)發(fā)送信號(hào) ( up 命令創(chuàng)建的所有容器 ) 相當(dāng)于 docker kill

docker-compose.yml 語法 ( 參考、進(jìn)階用法 )


  • 示例結(jié)構(gòu):
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] 的內(nèi)容即可, 如果要指定語法版本則要從最外層開始寫。

示例


以我去年寫的一篇 websocket 文章 中的項(xiàng)目作為示例

在當(dāng)前目錄下創(chuàng)建 docker-compose.yml

# tomcat版
demo-websocket-tomcat:
  # 指定用于構(gòu)建鏡像的Dockerfile路徑, 值為字符串
  build: '.'
  # 設(shè)置容器用戶名(鏡像中已創(chuàng)建),默認(rèn)root
  user: user_docker
  # 設(shè)置容器主機(jī)名
  hostname: docker-anyesu
  # 容器內(nèi)root賬戶是否擁有宿主機(jī)root賬戶的所有權(quán)限 [參考](http://blog.csdn.net/halcyonbaby/article/details/43499409)
  privileged: false
  # always (當(dāng)容器退出時(shí)docker自動(dòng)重啟它)
  # on-failure:10 (當(dāng)容器非正常退出, 最多自動(dòng)重啟10次, 10之后不再重啟)
  restart: always
  # 容器的網(wǎng)絡(luò)連接類型,anyesu_net是創(chuàng)建的自定義網(wǎng)橋
  net: anyesu_net
  # 掛載點(diǎn),設(shè)置與宿主機(jī)之間的路徑映射
  volumes:
  - /usr/anyesu/docker/tomcat-logs:/usr/anyesu/tomcat/logs
  # :ro表示只讀,默認(rèn)為:rw
  #- /usr/anyesu/docker/tomcat-logs:/usr/anyesu/tomcat/logs:ro
  # 與宿主機(jī)之間的端口映射
  ports:
  - 8080:8080
  # 設(shè)置容器dns, 如果設(shè)置了net項(xiàng)則此項(xiàng)失效, 暫不清楚原因, 不過可以使用本地文件映射為容器的/etc/resolv.conf文件。
  dns: 8.8.8.8
  # 設(shè)置容器環(huán)境變量
  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
  # 覆蓋容器啟動(dòng)后默認(rèn)執(zhí)行的命令
  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

再創(chuàng)建 Dockerfile 文件, 用于構(gòu)建鏡像

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 && \
    # 設(shè)置時(shí)區(qū)
    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下自帶項(xiàng)目
    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

# 設(shè)置環(huán)境變量
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

# 啟動(dòng)命令(前臺(tái)程序)
CMD ["catalina.sh", "run"]

接著就可以構(gòu)建并運(yùn)行了

# 創(chuàng)建一個(gè)名為anyesu_net、網(wǎng)段為172.18.0.0的網(wǎng)橋(docker默認(rèn)創(chuàng)建的網(wǎng)段為172.17.0.0)
docker network create --subnet=172.18.0.0/16 anyesu_net

# 子命令 up 選項(xiàng):
# -d 后臺(tái)運(yùn)行
# --build 重新構(gòu)建依賴的鏡像(如果docker-compose.yml中指定了build項(xiàng)的話)
# --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 訪問這兩個(gè)項(xiàng)目了 ( 注意防火墻開放這兩個(gè)端口哦 )

說明:
  • 上面的兩個(gè)項(xiàng)目分別展示了 docker 的兩種使用方式:
  1. 環(huán)境和應(yīng)用打包到一個(gè)鏡像中作為一個(gè)整體
  2. 環(huán)境和應(yīng)用獨(dú)立,可以自由組合
  • 關(guān)于 docker-compose 的 build 操作網(wǎng)上的介紹比較少,有幾點(diǎn)要注意的:
  1. 配置文件中 build 參數(shù)內(nèi)容指定 Dockerfile 文件的路徑, 貌似在高版本中可以是 map 類型
  2. build 操作需要使用 sudo 提權(quán),否則會(huì)報(bào)一些奇怪的錯(cuò)誤
  • 將日志文件映射到宿主機(jī)目錄上方便查看,如果源碼不想打包到鏡像中也可以做映射。還有一個(gè)小技巧,用空目錄映射鏡像已有目錄達(dá)到刪除的效果,如 tomcat/webapps 下的自帶應(yīng)用是沒用的,可以用這個(gè)方法清除。

  • JVM 參數(shù) 可以配置在環(huán)境變量 JAVA_OPTS 中。使用 nodejs 的全局安裝 ( -g ) 時(shí)別忘了設(shè)置 NODE_PATH

  • command 只能運(yùn)行一條命令,如果要運(yùn)行多條就使用 sh -c "...", 實(shí)際命令用 && 隔開做參數(shù)傳入

  • websocket 的 demo源碼 有不少同學(xué)反映項(xiàng)目配置不起來,研究下上面的 Dockerfiledocker-compose.yml 應(yīng)該就能對(duì)項(xiàng)目的結(jié)構(gòu)和依賴有更清晰的了解了。

  • demo-websocket-tomcat 項(xiàng)目的配置中, 出于 安全 考慮,使用普通用戶登錄。宿主機(jī)的 /usr/anyesu/tomcat/logs 目錄要設(shè)置 777 權(quán)限,否則 tomcat 無法寫日志。

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

遇到的坑:

之前一個(gè)原本啟動(dòng)只要 10 秒的小項(xiàng)目在容器中有時(shí)候重啟要好幾分鐘甚至可能會(huì)一直啟不來,在日志中找到下面這段內(nèi)容:

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

關(guān)鍵的一句就是: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [624,301] milliseconds
這個(gè)步驟竟然用了10分鐘!查了一下發(fā)現(xiàn)是 docker 容器下 隨機(jī)數(shù)與熵池策略 有問題。
解決方法如下,個(gè)人更推薦第二種:

  • 打開 $JAVA_PATH/jre/lib/security/java.security,將
    securerandom.source=file:/dev/random 替換為 securerandom.source=file:/dev/urandom
  • 啟動(dòng)容器時(shí),使用 JVM 參數(shù)JAVA_OPTS=-Djava.security.egd=file:/dev/./urandom

系列文章


Docker 學(xué)習(xí)總結(jié)

Docker 常用指令詳解

使用 Dockerfile 構(gòu)建鏡像

Docker Daemon 連接方式詳解

Docker 下的網(wǎng)絡(luò)模式


轉(zhuǎn)載請(qǐng)注明出處:http://m.itdecent.cn/p/ee8e7d2eb645

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容