---
# System prepended metadata

title: Dockerfile 詳細說明
tags: [docker]

---

###### tags: `docker`

# Dockerfile 詳細說明

- [FROM](#FROM)
- [ENV](#ENV) | [ARG](#ARG)
- [ADD](#ADD) | [COPY](#COPY)
- [RUN](#RUN) | [CMD](#CMD) | [ENTRYPOINT](#ENTRYPOINT)
- [LABEL](#LABEL)
- [EXPOSE](#EXPOSE)
- [WORKDIR](#WORKDIR)
- [HEALTHCHECK](#HEALTHCHECK)

## 指令說明

### FROM
```dockerfile
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]    
```
- 用來指定要引進的基礎image，會先從本地搜尋，若無則到網路上的[DockerHub](https://hub.docker.com/)找。可以用自定義的image檔。
- Ex:
    ```dockerfile
    FROM mariadb
    ```
  此指令就是指令指定引入mariadb當作基礎image。
### ENV
```dockerfile
ENV <key>=<value> ...
```
- 新增環境變數到該image裡。
- Ex:
    ```dockerfile
    ENV my_env=your_variable
    ```
    ![]()
    <img src="https://i.imgur.com/PP0xN01.png" width="75%"/>
    在裡面就可以呼叫了
### ARG
```dockerfile
ARG <name>[=<default value>]
```
- ARG在build時候是可以從外部以--build-arg帶入的變數，讓build的動作可結合外部的指令給定一些建構時候所需的參數。
- Ex:
    ```dockerfile
    ARG MY_FILE
    ADD ./${MY_FILE} /
    ```
    ```shell
    docker build -build-arg MY_FILE=./file.txt .
    ```
    用-build-arg把MY_FILE這個變數帶入Dockerfile裡來使用
- ARG不能在CMD或ENTRYPOINT裡使用。
>在ENV與ARG參數的設定上，可以透過下面方式增加一些變化...
>    - ${variable:-word} 代表，如果variable有給值，則以variable設定的文字為主，如未設定，則以word字串為主。
>    - ${variable:+word} 代表，如果variable有給值，則值為word；如果variable未給值，則最後結果為空字串(empty)。

### ADD
```dockerfile
ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
```
- src: 不限於本地路徑，可以用網址之類的。
- Ex: 
    ```dockerfile
    ADD http://example.com/font.js /opt/
    ADD my_big_lib.tar.gz /var/lib/myapp
    ```
    第1行示範從遠端下載檔案到/opt/，第2行則示範使用ADD傳送tar檔會自動解壓縮到目的地(僅限於tar檔)。

### COPY
```dockerfile
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
```
- COPY與ADD類似，但COPY只支援本地路徑
- Ex:
    ```dockerfile
    COPY ./file.txt /home/user/folder
    ```
    上面範例則是將本地路徑的file.txt複製到image的/home/user/folder
### RUN
```dockerfile
RUN [command,param1,param2,...]
RUN command param1, param2 ...
```
- 支援shell格式或exec格式
- 每次執行RUN都會多建立一層layer，所以盡量減少執行RUN的次數以增加效能。Ex:
    ```dockerfile
    RUN ["apt-get", "update"]
    RUN ["apt-get", "-y", "install", "cron"]
    RUN crontab -l | { cat; echo "0 4 * * * ${JAVA_EXE} -jar ${CRON_FILE} >> ${CRON_PATH}/cronlog.log";} | crontab -
    ```
    下面的寫法相對上面還要好
    ```dockerfile
    RUN apt-get update \
        && apt-get -y install cron \
        && crontab -l | { cat; echo "0 4 * * * ${JAVA_EXE} -jar ${CRON_FILE} >> ${CRON_PATH}/cronlog.log";} | crontab -
    ```
> COPY ,ADD ,RUN三者在執行時皆會多產生一層layer，layer越多效能就越差，所以能越少越好。
### CMD
```dockerfile
CMD ["command", "param1", "param2"] #exec form
CMD command param1 param2 #shell form
CMD ["param1","param2"]
```
- CMD 主要用途是為執行容器(container)時提供預設值，預設值可以是一個可執行檔(executable)以及其需要的參數；也可以只是單純提供參數的部分，不過這種用法一定要搭配 ENTRYPOINT 使用，也就是 CMD 只是作為 ENTRYPOINT 所需的預設參數。
- exex form:使用exec來執行
	- Ex:`CMD ["/usr/bin/wc","--help"]`實際上會長這樣`exec /usr/bin/wc --help`
		> <p style="color: red; font-weight:bold">exec 會替代當前shell並重新啟動一個shell，所以無法使用現有的環境變數</p>
- shell form: 使用`/bin/sh -c`來執行
	- Ex: `CMD echo "This is a test." | wc -`實際上會長這樣`/bin/sh -c echo "This is a test." | wc -`

### ENTRYPOINT

```dockerfile
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
```
- exec form與shell form需要注意的點和CMD一樣。
### LABEL
```dockerfile
LABEL <key>=<value> <key>=<value> <key>=<value> ...
```
- 用於記載作者、版本之類的metadata。
### EXPOSE
```dockerfile
EXPOSE <port> [<port>/<protocol>...]
```
- EXPOSE 指令通知 Docker container在運行時監聽指定的port。可以指定port監聽TCP還是UDP，不指定則預設為TCP。
- 此指令僅僅聲明要使用的port，並不會自動將port映射出來。
### WORKDIR
```dockerfile
WORKDIR /path/to/workdir
```
- WORKDIR 指令為 跟在它後面的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令指定當前工作目錄。如果 WORKDIR不存在，即使它沒有在任何後續的指令中使用，也會被建立。

### HEALTHCHECK
```dockerfile
HEALTHCHECK [OPTIONS] CMD command
```
- healthCheck用於檢查container是否運行中。
- option:
    ```dockerfile
    --interval=DURATION (default: 30s)
    --timeout=DURATION (default: 30s)
    --start-period=DURATION (default: 0s)
    --retries=N (default: 3)
    ```
- healthCheck會先檢查然後等interval時間後再次檢查，若單次檢查時間超過timeout時間，則該次檢查視為失敗。
- 當累積失敗次數達retries次時，則回傳unhealthy並關閉container。
- Ex: 
    ```dockerfile
    HEALTHCHECK --interval=5m --timeout=3s \
      CMD curl -f http://localhost/ || exit 1
    ```
## 參考文獻
[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
[《Docker —— 從入門到實踐­》正體中文版](https://philipzheng.gitbook.io/docker_practice)
[linux exec用法總結](https://www.796t.com/p/1390349.html)