最近在作一系列的 docker 的镜像编译脚本时,想到能不能经过并行编译加快速度,查了一下资料,最后经过 shell 的 job control 实现了并行编译多个 docker 镜像。docker
具体要实现的目标包括:shell
首先,若是要在脚本文件中启用 job control ,须要在脚本文件开头处,加上如下的代码:ubuntu
#!/bin/bash # 容许脚本使用 job control set -m
shell 的 job control 容许之后台方式,执行程序时带 '&' 会让进程在后台运行,可经过 jobs
指令检查有多少后台任务在执行中,经过 wait
指令等待进程结束,并立刻经过 $?
得到 wait 目标进程的返回值。数组
这一系列的 docker 镜像是存在依赖关系的,依赖的关系容易处理,直接查 Dockerfile 的 FROM
指令,处理其父镜像的 tag 后获得依赖关系:bash
all_dockerfiles=$(find <目录> -name Dockerfile -type f) # 关联数组保存镜像名和对应的路径 declare -A all_images=() # 保存镜像名和对应的父镜像名 declare -A depends=() for dockerfile in ${all_dockerfiles[@]}; do image_dir=${dockerfile%/*} image_name=${image_dir##*/} all_images[${image_name}]=${image_dir} parent_image_name=$(cat ${dockerfile} | awk -F '[ :]' '/^FROM/{print $2}') # 这里可作个判断,当镜像名不在目录中时可去除,如 ubuntu:14.04 之类的父镜像不须要记录 depends[${image_name}]=${parent_image_name} done
有了 all_images 和 depends 两个关联数组,就能够根据这两个数据,结合 job control 来进行并行处理。在决定是否编译一个镜像前,须要判断其父镜像是否编译成功,所以,增长两个数组分别记录编译成功和编译失败的镜像名,使用一个数组记录当前在编译中的镜像名。网络
# 容许的最大进程数量 workers=4 # 已编译的镜像 declare -a builts=() # 错误的镜像 declare -a errors=() # 正在运行的任务,[pid]=镜像名 declare -A runnings=() while [ ${#all_images[@]} -gt 0 ] || [ ${#runnings[@] -gt 0 } ]; do # 检查是否有编译任务已完成 if [ $(jobs | wc -l) -lt ${#runnings[@]} ]; then # 检查是哪一个编译任务已完成 for pid in ${!runnings[@]}; do if ! (jobs -l | grep ${pid} > /dev/null); then # 获取进程返回值 wait ${pid} return_code=$? # 成功仍是失败 if [ ${return_code} -ne 0 ]; then errors=( ${errors[@]} ${runnings[${pid}]} ) else builts=( ${builts[@]} ${runnings[${pid}]} ) fi unset runnings[${pid}] fi done fi # 加入新的编译任务, 从 all_images 中挑一个镜像 for image_name in ${!all_images[@]}; do # 是否还有 worker 可用 if [ ${#runnings[@]} -ge ${workers} ]; then break fi # 父镜像是否已编译 parent_image_name=${depends[${image_name}]} # 父镜像失败则本镜像失败 for i in ${errors[@]}; do if [ ${i} == ${parent_image_name} ]; then errors=( ${errors[@]} ${image_name} ) unset all_images[${image_name}] continue fi done # 父镜像成功,则本镜像可开始编译 for i in ${builts[@]}; do if [ ${i} == ${parent_image_name} ]; then docker build -q ${all_images[${image_name}]} -t "${image_name}:latest" & runnings[$!]=${image_name} unset all_images[${image_name}] fi done done # sleep 一会 sleep 0.2 done
worker 的数量可根据网络的性能和主机的 cpu 数量进行调整,在写脚本时,要当心父镜像编译错误的问题。性能