Dockerfile 指令詳解

GitHub Page:https://blog.cloudli.top/posts/Dockerfile-指令詳解/java

FROM

FROM 命令指定基礎鏡像。在構建鏡像時,基礎鏡像必須指定,所以在 DockerfileFROM 是必備指令且必須是第一條指令。node

Docker Hub 上有不少經常使用的高質量官方鏡像,有一些是應用和服務類的鏡像,如 nginxmysqlredis 等;也有一些是用於運行各類語言應用的鏡像,如 openjdkpythonnode 等。python

若是找不到應用的官方鏡像,能夠基於操做系統鏡像構建一個。Docker Hub 上提供了不少操做系統鏡像。mysql

FROM ubuntu
...

RUN

RUN 指令是用來執行命令行命令的。RUN 指令的格式有兩種:nginx

  • shell 格式:RUN <命令>,就像直接在命令行中輸入命令同樣。redis

    RUN java -jar app.jar
  • exec 格式:RUN ["可執行文件", "參數1", "參數2"]sql

    RUN ["java", "-jar", "app.jar"]

Dockerfile 中,每個指令都會在鏡像上創建一層,因此對於多個命令行,不要寫多個 RUN 指令。docker

對於多個命令,可使用這樣的寫法:shell

FROM ubuntu

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

對於多個命令,使用 && 鏈接起來,只用一個 RUN 指令執行,這樣就只會構建一層。數據庫

COPY

COPY 指令用來將宿主的文件目錄複製到鏡像中。有兩種格式:

  • COPY [--chown=<user>:<group>] <源路徑>... <目標路徑>

    COPY app.jar /usr/src/
  • COPY [--chown=<user>:<group>] ["源路徑1", ... "目標路徑"]

    COPY ["app.jar", "config.yml", "/usr/src"]

對於多個路徑參數,最後一個爲目標路徑,其餘都是源路徑。目標路徑 能夠是絕對路徑,也能夠是相對於工做目錄的路徑(工做目錄能夠用 WORKDIR 來指定)。目標路徑若是不存在,在複製文件前會先建立。

CMD

CMD 是容器啓動命令,它指定了容器啓動後要執行的程序。與 RUN 指令類似有兩種形式:

  • shell 格式:CMD <命令>

    CMD echo 'Hello, world!''
  • exec 格式:CMD ["可執行文件", "參數1", "參數2", ...]

    CMD [ "sh", "-c", "echo 'Hello, world!'" ]

還有一種參數列表格式:CMD ["參數1", "參數2", ...]。在指定了 ENTRYPOINT 指令後,能夠用 CMD 指定參數。

在使用 CMD 時,程序必須之前臺運行,Docker 不是虛擬機,容器沒有後臺服務的概念。若是使用 CMD 運行一個後臺程序,那麼容器在命令執行完就會退出。

CMD java -jar app.jar &

以上命令讓 app.jar 在後臺運行,容器啓動後就會馬上退出。Docker 容器與守護線程很類似,當全部前臺程序退出後,容器就會退出。

CMD 指定的命令能夠在運行時替換,跟在鏡像名稱後面的參數將替換鏡像中的 CMD

docker run app echo $HOME

以上命令運行容器時使用 echo $HOME 替換掉鏡像中的啓動命令。

ENTRYPOINT

ENTRYPOINT 的格式與 CMD 同樣有兩種格式。

它和 CMD 同樣都是指定容器啓動的程序和參數,但稍有區別。當指定了 ENTRYPOINT 後,CMD 的內容將做爲參數加到 ENTRYPOINT 後面。

也就是變成了:

<ENTRYPOINT> "<CMD>"

ENTRYPOINT 可讓鏡像像命令同樣使用,當僅僅使用 CMD 時,run 命令中鏡像名後面的參數會替換 CMD 的內容。使用 ENTRYPOINT 後,這些參數將附加到原來命令的後面。

FROM alpine
ENTRYPOINT [ "ls" ]

使用以上 Dockerfile 構建的鏡像運行容器:

docker run app -al

-al 參數將附加到 ENTRYPOINT 指定的命令後面,當容器啓動時執行的是 ls -al

ENV

ENV 指令用來設置環境變量,格式有兩種:

  • ENV <key> <value
  • ENV <key1>=<value1> <key2>=<value2>

環境變量在後面的其它指令中能夠經過 $key 來使用:

FROM ubuntu
ENV VERSION="8-jre"

RUN apt-get update \
    && apt-get install -y openjdk-$VERSION
...

ARG

ARG 指令指定構建參數,與 ENV 效果同樣,都是設置環境變量。不一樣的是,ARG 設置的構建參數,在容器運行時不存在。

格式:ARG <key>[=<默認值>],能夠指定默認值,也能夠不指定。

FROM alpine
ARG NAME="Hello, Docker!"

RUN echo $NAME
CMD echo $NAME

對於以上 Dockerfile,在構建時能夠看到輸出,可是在運行容器時沒有輸出。

ARG 設置的參數能夠在構建命令中指定:docker build --build-arg <key>=<value>

VOLUME

VOLUME 指令用來定義匿名卷。

  • VOLUME <路徑>
  • VOLUME ["路徑1", "路徑2", ...]

對於數據庫類須要保持數據的應用,其文件應該保存於卷(volume)中,在 Dockerfile 中,能夠事先將指定的目錄掛載爲匿名卷。

VOLUME /data

這裏 /data 目錄在容器運行時自動掛載爲匿名卷,任何寫入 /data 中的數據都不會記錄到容器的存儲層。在運行時能夠覆蓋這個掛載設置:

docker run -v dbdir:/data

以上命令將 dbdir 目錄掛載到了 /data,替換了 Dockerfile 中的掛載配置。

EXPOSE

EXPOSE 指令指定容器運行時暴露的端口。格式:EXPOSE <端口1> [<端口2> ...]

FROM ubuntu
EXPOSE 8080

RUN apt-get update \
    && apt-get install -y tomcat8
...

以上 Dockerfile 安裝了 tomcat 應用,在運行容器時會暴露 8080 端口。

EXPOSE 只是指定了容器暴露的端口,並不會在宿主機進行端口映射。在使用 docker run -P 時,會自動隨機映射 EXPOSE 指定的端口,也可使用 -p 指定端口:docker run -p <宿主端口>:<容器端口>

WORKDIR

WORKDIR 指令指定工做目錄,即指定當前目錄,相似於 cd 命令,之後各層的當前目錄都是 WORKDIR 指定的目錄。若是目錄不存在,會自動建立。格式:WORKDIR <目錄路徑>

不能把 Dockerfile 當成 Shell 腳原本寫:

RUN cd /src/app
RUN java -jar app.jar

以上操做中第二行的工做目錄並非 /src/app,兩個指令不在同一層,第一個 RUN 指令的 cd 操做和第二個沒有任何關係。所以要切換目錄,應該使用 WORKDIR 來指定。

USER

USER 指令指定當前用戶。與 WORKDIR 類似,會影響之後的層。USER 改變執行 RUNCMDENTRYPOINT 的用戶。格式:USER <用戶名>[:<用戶組>]

USER 指定的用戶和組必須是事先建立好的,不然沒法切換。

# 添加用戶
RUN groupadd -r redis \
    && useradd -r -g redis redis
USER redis
ENTRYPOINT ["reids-server"]

ONBUILD

ONBUILD 指令後面跟的是其它指令,它在當前鏡像構建時不會被執行,只有以當前鏡像爲基礎鏡像去構建下一級鏡像時纔會被執行。格式:ONBUILD <其它指令>

FROM openjdk:8-jre-alpine
WORKDIR /app
ONBUILD COPY ./app.jar /app
...

這個 Dockerfile 在構建時不會執行 ONBUILD

FROM my-jre
...

假設以前構建的鏡像名是 my-jre,以上 Dockerfile 構建鏡像時,原來的 ONBUILD 將執行。

相關文章
相關標籤/搜索