Dockerfile 實踐及梳理


Dockerfile 是一個文本文件,我們可以通過組合一條條的指令 (Instruction),來構建滿足我們需求的 Docker 鏡像

文檔

Best practices for writing Dockerfiles

Reference

Dockerfile 指令詳解

簡單上手

使用 Dockerfile 構建SpringBoot 工程的鏡像

  1. 新建 SpringBoot 項目,默認的端口是 8080 ,新建 Controller 和 Mapping
@RestController
public class HelloController {
    @GetMapping("hello")
    public String hello() {
        return "hello world!";
    }
}

啟動項目,訪問 http://localhost:8080/hello 測試

  1. 打 jar 包
    注意,需要在 pom 中添加 spring-boot-maven-plugin 插件,否則運行 jar 包時會提示:沒有主清單屬性
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
#打包
mvn package

target 目錄下就可以找到 .jar 文件,我這里的文件名為:demo-0.0.1-SNAPSHOT.jar
在 Linux 新建 ~/springboot 文件夾,並將 jar 包上傳到這個文件夾下

  1. 新建 Dockerfile
    在這個文件下新建 Dockerfile 文件
# 基於 openjdk:8-jre 這個基礎鏡像進行構建
FROM openjdk:8-jre

# 這里的 demo-0.0.1- SNAPSHOT.jar 要對應上傳的 jar 包名稱
# 將 本地 jar包 復制到容器內
COPY demo-0.0.1-SNAPSHOT.jar  app.jar

# 開放 8080 端口
EXPOSE 8080

# 運行命令、參數
ENTRYPOINT ["java","-jar"]
CMD ["app.jar"]

保存文件,退出編輯器

  1. 編譯 Docker 鏡像
# build 是構建 Docker 鏡像的命令
# -t 指定鏡像的 tag
# 名稱:demo 版本:v1.0
# 最后的 . 表示 build context 目錄為當前目錄,目的是為了找到 所需的 jar 包
docker build -t demo:v1.0 .
  1. 啟動容器
# 前台啟動剛構建的 SpringBoot 容器
# -p 映射容器8080端口 到宿主機的 8080 上
docker run -p 8080:8080 demo:v1.0
  1. 測試
    訪問 Linux 的8080 端口,注意替換為自己的 Linux 的地址,並開放 8080 端口

http://192.168.43.161:8080/hello

build context

Dockerfile 默認會使用它自己所在的目錄作為 context,通過 docker 執行構建命令后,Docker daemon 會拷貝 context 目錄下的所有文件,所以 context 目錄不要放置項目無關的文件,或者可以使用 .dockerignore 定義忽略文件,也可以指定 context 路徑

# build 命令通過 Dockerfile 構建鏡像
# 指定 ~/dockerfile 為 build context
docker build ~/dockerfile
# 不需要添加文件到 context 可以使用 -
docker build -

可以通過 stdin 的方式,避免生產 Dockerfile 文件,直接 build 鏡像

docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

除了可以指定 context外,還可以通過-f 指定 Dockerfile 所在的路徑

docker build  -f dockerfiles/Dockerfile .

最佳實踐

非常推薦官方的 Dockerfile最佳實踐:Best practices for writing Dockerfiles

  1. 每個容器單一職責,有利於橫向拓展和復用
  2. 舊版強調減少層數以提高性能,現在只有 RUN, COPY, ADD 這幾個命令會創建層,其他命令只會創建中間層。並且只有使用到資源最終會被拷貝到最終鏡像
  3. 多個參數按字母順序排列,並使用空格和 \ 進行分割,提高可讀性
  4. --no-cache 不使用緩存,默認 build 過程中如果檢查到有可重用的鏡像層則使用。從基礎鏡像開始,每一條命令逐一檢查,如果命令不一樣則緩存失效。使用 ADDCOPY 則會校驗使用到的文件校驗和是否相同,除了這兩個命令,其他則不會通過文件變化來決定是否匹配緩存,而是僅通過命令本身是否一致來判斷是否匹配緩存,比如:RUN apt-get -y update會改變容器內的文件,但是也只使用這個命令匹配緩存,而不會通過文件的變動。一旦緩存失效,后續都會產生新的鏡像層

Dockerfile 指令 (instructions)

FROM

Dockerfile 的第一個命令一般都是 FROM,通過這個指定該鏡像的 Base Image,推薦基礎鏡像:alpine,因為它完整且輕量,如果不需要 Base Image 可以用 FROM scratch,代表該鏡像基於一個空鏡像進行構建

RUN

由於上面提到的緩存匹配原則,RUN apt-get update 命令可能會導致直接使用了原來緩存的鏡像層,而沒有執行該命令獲取最新的軟件列表,可以使用 RUN apt-get update && apt-get install -y 來使緩存失效
可以使用 \ 分割,提高可讀性:

RUN apt-get update && apt-get install -y \
    curl

CMD

指定容器啟動時運行的命令,通常默認采用的格式:CMD ["executable", "param1", "param2"…],如:

CMD ["perl", "-de0"]

這樣使用 docker run -it 命令進入容器時,就會默認進入 shell 界面

EXPOSE

指定容器需要監聽的端口

ENV

可以使用 ENV 更新 PATH 環境變量,例如

ENV PATH=/usr/local/nginx/bin:$PATH

注意!每一個 ENV 指令都會創建一個新的中間層 (intermediate layer),如果使用 ENV 設置了變量,在未來的層 unset 了變量,那么它在 unset 之前依然是可用的。為了防止這種情況,我們應該用 RUN 進行環境變量的 設置和取消

ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER

ADD or COPY

兩個命令功能相似,優先使用COPY,它的作用只是將本地文件拷貝到容器內,而 ADD 則有其他特性,比如:自動將本地 tar 文件提取到鏡像中、遠程URL
如果多個步驟需要使用不同的文件,應該單獨 COPY,而不是一次性 COPY,這樣部分文件變化不會導致所有的緩存都失效
避免使用 ADD 通過 URL 獲取包,可以使用 curl 或者 wget,這樣可以在提取后刪除文件,避免鏡像多一層,還可以通過管道,就不需要再手動刪除中間文件

RUN mkdir -p /usr/src/things \
    && curl -SL https://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

ENTRYPOINT

使用 ENTRYPOINT 設置主命令,還可以用 CMD 設置默認的可選參數

ENTRYPOINT ["s3cmd"]
CMD ["--help"]

運行編譯鏡像,指定名稱為:s3cmd,運行容器

docker run s3cmd

默認會運行 s3cmd 並帶上 --help 參數,即:顯示該命令的幫助

運行下面命令:

docker run s3cmd ls s3://mybucket

ls s3://mybucket 會覆蓋默認可選參數 --help

如果需要覆蓋 ENTRYPOINT,需要使用 --entrypoint 參數

VOLUME

暴露鏡像中可變和用戶可修改的數據,比如:存儲文件、配置文件,比如:

VOLUME /data

設置的目錄會在容器運行時自動掛載為匿名卷,如果沒有設置,就會寫入容器存儲層

USER

如果不需要使用 sudo ,可以通過 USER 切換到非 root 用戶,例如:

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

WORKDIR

WORKDIR 指令可以來指定工作目錄,不存在會自動創建
Dockerfile 不同於 Shell,下面的命令其實是不同的層,第一條的 cd 不會影響第二條命令,最終運行結束會導致在 /app 下找不到 world.txt 文件

RUN cd /app
RUN echo "hello" > world.txt

應該使用:

WORKDIR /app
RUN echo "hello" > world.txt

參考資料

使用 Dockerfile 定制鏡像

利用構建緩存機制縮短Docker鏡像構建時間

Dockerfile: ENTRYPOINT和CMD的區別


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM