Docker 鏡像多階段構建實戰總結
一 背景
通常情況下,構建鏡像通常會採用兩種方式:
-
將全部組件及其依賴庫的編譯、測試、打包等流程封裝進一個 Docker 鏡像中。採用這種方式往往忽略了以下這些問題:
- Dockefile 特別長,可維護性降低。
- 鏡像的層次多,體積大,部署時間長。
- 源代碼存在洩漏的風險。
- 分散到多個 Dockerfile。事先在一個 Dockerfile 將項目及其依賴庫編譯測試打包好後,再將其拷貝到運行環境中,這種方式需要我們編寫兩個 Dockerfile 和一些編譯腳本才能將其兩個階段自動整合起來,這種方式雖然可以很好地規避第一種方式存在的風險,但明顯部署過程較複雜。
為了解決以上這些問題,Docker v17.05 開始支持多鏡像階段構建 (multistage builds)。只需要編寫一個 Dockerfile 即可。通過一段簡單的 C 語言代碼的編譯、執行來具體演示。demo.c 的內容如下:
# include<stdio.h>
int main()
{
printf("%s\n","This is a demo!");
return 0;
}
二 實踐步驟
2.1 只通過一個 Dockerfile 來構建【方案一】
查看對應的 Dockerfile:
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo.c .
RUN yum install -y gcc && \
gcc -v
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc && \
cp demo /usr/local/bin/
CMD ["demo"]
感興趣的小夥伴可以直接將上面的 Dockerfile 和 docker-entrypoint.sh 在本地構建目錄創建,執行 docker build -t redis:6.0.5-buster
進行嘗試。
2.2 多個 Dockerfile 實現多階段構建【方案二】
多階段構建一般需要多個 Dockerfile 來完成,由於我們只需要源碼編譯後的產物。所以我們第一個階段可以直接使用上文中鏡像構建後的產物。第二階段的 Dockerfile 內容如下:
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo /usr/local/bin
CMD ["demo"]
執行構建腳本 bash build.sh
, build.sh
的內容如下:
#!/bin/bash
cd stage-1
docker create --name bin demo:1.0
cd ../stage-2
docker cp bin:/usr/local/bin/demo .
docker rm -f bin
docker build -t demo:2.0 .
構建後得到的 Docker 容器運行結果:
$ docker run --rm -it demo:1.0
This is a demo!
$ docker run --rm -it demo:2.0
This is a demo!
兩個容器的環境變量:
$ docker run --rm -it demo:1.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=a52af1bec0af
TERM=xterm
VERSION=1.0
HOME=/root
$ docker run --rm -it demo:2.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=f6618fd1244b
TERM=xterm
VERSION=2.0
HOME=/root
2.3 一個 Dockerfile 實現多階段構建【方案三】
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo.c .
RUN yum install -y gcc && \
gcc -v
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc && \
cp demo /usr/local/bin/
FROM centos:7.8.2003
COPY --from=0 /usr/local/bin/demo /usr/local/bin/demo
CMD ["demo"]
這種方式構建的 Docker 容器運行結果:
$ docker run --rm -it demo:3.0
This is a demo!
$ docker run --rm -it demo:3.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=7839b3d568db
TERM=xterm
HOME=/root
三個鏡像大小對比:
$ docker images|grep demo
demo 3.0 8766031d380a 39 seconds ago 203MB
demo 2.0 7d9c479cb421 10 minutes ago 203MB
demo 1.0 af331209572f 38 minutes ago 350MB
三 總結
- 3.1 通過觀察,方案一構建得到鏡像遠比方案二和方案三大得多,方案二和方案三的鏡像一樣大小。
- 3.2 方案三並不會繼承第一階段構建的鏡像的環境變量等配置,僅僅是複製了第一階段的構建成果,需要特別注意。