最近在作一系列的 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 數量進行調整,在寫腳本時,要當心父鏡像編譯錯誤的問題。性能