Docker 映象構建之 Dockerfile
阿新 • • 發佈:2020-08-28
在 Docker 中構建映象最常用的方式,就是使用 `Dockerfile`。Dockerfile 是一個用來構建映象的文字檔案,文字內容包含了一條條構建映象所需的指令和說明。官方文件:https://docs.docker.com/engine/reference/builder/
## Dockerfile 常用指令
### FROM
語法:`FROM :`
指明構建的新映象是來自於哪個基礎映象,如果沒有選擇 tag,那麼預設值為 latest。
```shell
FROM centos:7
```
> 如果不以任何映象為基礎,那麼寫法為:FROM scratch。官方說明:scratch 映象是一個空映象,可以用於構建 busybox 等超小映象,可以說是真正的從零開始構建屬於自己的映象。
### ~~MAINTAINER~~(deprecated)
語法:`MAINTAINER `
指明映象維護者及其聯絡方式(一般是郵箱地址)。官方說明已過時,推薦使用 LABEL。
```shell
MAINTAINER mrhelloworld
```
### LABEL
語法:`LABEL = = = ...`
功能是為映象指定標籤。也可以使用 LABEL 來指定映象作者。
```shell
LABEL maintainer="mrhelloworld.com"
```
### RUN
語法:`RUN `
構建映象時執行的 Shell 命令,比如構建的新映象中我們想在 /usr/local 目錄下建立一個 java 目錄。
```shell
RUN mkdir -p /usr/local/java
```
### ADD
語法:`ADD ... `
拷貝檔案或目錄到映象中。src 可以是一個本地檔案或者是一個本地壓縮檔案,壓縮檔案會自動解壓。還可以是一個 url,如果把 src 寫成一個 url,那麼 ADD 就類似於 wget 命令,然後自動下載和解壓。
```shell
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
```
### COPY
語法:`COPY ... `
拷貝檔案或目錄到映象中。用法同 ADD,只是不支援自動下載和解壓。
```shell
COPY jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
```
### EXPOSE
語法:`EXPOSE [/...]`
暴露容器執行時的監聽埠給外部,可以指定埠是監聽 TCP 還是 UDP,如果未指定協議,則預設為 TCP。
```shell
EXPOSE 80 443 8080/tcp
```
> 如果想使得容器與宿主機的埠有對映關係,必須在容器啟動的時候加上 -P 引數。
### ENV
語法:`ENV ` 新增單個,`ENV = ...` 新增多個。
設定容器內環境變數。
```shell
ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
```
### CMD
語法:
- `CMD ["executable","param1","param2"]`,比如:`CMD ["/usr/local/tomcat/bin/catalina.sh", "start"]`
- `CMD ["param1","param2"] `,比如:`CMD [ "echo", "$JAVA_HOME" ]`
- `CMD command param1 param2`,比如:`CMD echo $JAVA_HOME`
啟動容器時執行的 Shell 命令。在 Dockerfile 中只能有一條 CMD 指令。如果設定了多條 CMD,只有最後一條 CMD 會生效。
```shell
CMD ehco $JAVA_HOME
```
> 如果建立容器的時候指定了命令,則 CMD 命令會被替代。假如映象叫 `centos:7`,建立容器時命令是:`docker run -it --name centos7 centos:7 echo "helloworld"` 或者 `docker run -it --name centos7 centos:7 /bin/bash`,就不會輸出 `$JAVA_HOME` 的環境變數資訊了,因為 CMD 命令被 `echo "helloworld"`、`/bin/bash` 覆蓋了。
### ENTRYPOINT
語法:
- `ENTRYPOINT ["executable", "param1", "param2"]`,比如:`ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh", "start"]`
- `ENTRYPOINT command param1 param2`,比如:`ENTRYPOINT ehco $JAVA_HOME`
啟動容器時執行的 Shell 命令,同 CMD 類似,不會被 docker run 命令列指定的引數所覆蓋。在 Dockerfile 中只能有一條 ENTRYPOINT 指令。如果設定了多條 ENTRYPOINT,只有最後一條 ENTRYPOINT 會生效。
```shell
ENTRYPOINT ehco $JAVA_HOME
```
> - 如果在 Dockerfile 中同時寫了 ENTRYPOINT 和 CMD,並且 CMD 指令不是一個完整的可執行命令,那麼 CMD 指定的內容將會作為 ENTRYPOINT 的引數;
> - 如果在 Dockerfile 中同時寫了 ENTRYPOINT 和 CMD,並且 CMD 是一個完整的指令,那麼它們兩個會互相覆蓋,誰在最後誰生效
### WORKDIR
語法:`WORKDIR /path/to/workdir`
為 RUN、CMD、ENTRYPOINT 以及 COPY 和 AND 設定工作目錄。
```shell
WORKDIR /usr/local
```
### VOLUME
指定容器掛載點到宿主機自動生成的目錄或其他容器。一般的使用場景為需要持久化儲存資料時。
```shell
# 容器的 /var/lib/mysql 目錄會在執行時自動掛載為匿名卷,匿名卷在宿主機的 /var/lib/docker/volumes 目錄下
VOLUME ["/var/lib/mysql"]
```
> 一般不會在 Dockerfile 中用到,更常見的還是在 docker run 的時候通過 -v 指定資料卷。
## 構建映象
Dockerfile 檔案編寫好以後,真正構建映象時需要通過 `docker build` 命令。
`docker build` 命令用於使用 `Dockerfile` 建立映象。
```shell
# 使用當前目錄的 Dockerfile 建立映象
docker build -t mycentos:7 .
# 通過 -f Dockerfile 檔案的位置建立映象
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .
```
- `-f`:指定要使用的 Dockerfile 路徑;
- `--tag, -t`:映象的名字及標籤,可以在一次構建中為一個映象設定多個標籤。
### 關於 . 理解
我們在使用 `docker build` 命令去構建映象時,往往會看到命令最後會有一個 `.` 號。它究竟是什麼意思呢?
很多人以為是用來指定 `Dockerfile` 檔案所在的位置的,但其實 `-f` 引數才是用來指定 `Dockerfile` 的路徑的,那麼 `.` 號究竟是用來做什麼的呢?
`Docker` 在執行時分為 `Docker 引擎(服務端守護程序)` 和 `客戶端工具`,我們日常使用各種 `docker 命令`,其實就是在使用 `客戶端工具` 與 `Docker 引擎` 進行互動。
當我們使用 `docker build` 命令來構建映象時,這個構建過程其實是在 `Docker 引擎` 中完成的,而不是在本機環境。如果在 `Dockerfile` 中使用了一些 `ADD` 等指令來操作檔案,如何讓 `Docker 引擎` 獲取到這些檔案呢?
這裡就有了一個 `映象構建上下文` 的概念,當構建的時候,由使用者指定構建映象時的上下文路徑,而 `docker build` 會將這個路徑下所有的檔案都打包上傳給 `Docker 引擎`,引擎內將這些內容展開後,就能獲取到上下文中的檔案了。
舉個栗子:我的宿主機 jdk 檔案在 /root 目錄下,Dockerfile 檔案在 /usr/local/dockerfile 目錄下,檔案內容如下:
```shell
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
```
那麼構建映象時的命令就該這樣寫:
```shell
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root
```
再舉個栗子:我的宿主機 jdk 檔案和 Dockerfile 檔案都在 /usr/local/dockerfile 目錄下,檔案內容如下:
```shell
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
```
那麼構建映象時的命令則這樣寫:
```shell
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 .
```
## Dockerfile 實踐
接下來我們通過基礎映象 `centos:7`,在該映象中安裝 jdk 和 tomcat 以後將其製作為一個新的映象 `mycentos:7`。
建立目錄。
```shell
mkdir -p /usr/local/dockerfile
```
編寫 Dockerfile 檔案。
```shell
vi Dockerfile
```
Dockerfile 檔案內容如下:
```shell
# 指明構建的新映象是來自於 centos:7 基礎映象
FROM centos:7
# 通過映象標籤聲明瞭作者資訊
LABEL maintainer="mrhelloworld.com"
# 設定工作目錄
WORKDIR /usr/local
# 新映象構建成功以後建立指定目錄
RUN mkdir -p /usr/local/java && mkdir -p /usr/local/tomcat
# 拷貝檔案到映象中並解壓
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
ADD apache-tomcat-9.0.37.tar.gz /usr/local/tomcat
# 暴露容器執行時的 8080 監聽埠給外部
EXPOSE 8080
# 設定容器內 JAVA_HOME 環境變數
ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
ENV PATH $PATH:$JAVA_HOME/bin
# 啟動容器時啟動 tomcat 並檢視 tomcat 日誌資訊
ENTRYPOINT /usr/local/tomcat/apache-tomcat-9.0.37/bin/startup.sh && tail -f /usr/local/tomcat/apache-tomcat-9.0.37/logs/catalina.out
```
構建映象。
```shell
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/
[root@localhost ~]# docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/
Sending build context to Docker daemon 191.4MB
Error response from daemon: Dockerfile parse error line 1: unknown instruction: NTOS:7
[root@localhost ~]# vi /usr/local/dockerfile/Dockerfile
[root@localhost ~]# vi /usr/local/dockerfile/Dockerfile
[root@localhost ~]# docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root/
Sending build context to Docker daemon 191.4MB
Step 1/10 : FROM centos:7
---> 7e6257c9f8d8
Step 2/10 : LABEL maintainer="mrhelloworld.com"
---> Running in 4c561fed28a5
Removing intermediate container 4c561fed28a5
---> b536fc4e4290
Step 3/10 : WORKDIR /usr/local
---> Running in 50141816c10e
Removing intermediate container 50141816c10e
---> 030f9db632da
Step 4/10 : RUN mkdir -p /usr/local/java && mkdir -p /usr/local/tomcat
---> Running in d1f8f4e008c8
Removing intermediate container d1f8f4e008c8
---> 68773de3525a
Step 5/10 : ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
---> 92f410a9e1ba
Step 6/10 : ADD apache-tomcat-9.0.37.tar.gz /usr/local/tomcat
---> 8e0576fccd4e
Step 7/10 : EXPOSE 8080
---> Running in bb6c4ef8f4e1
Removing intermediate container bb6c4ef8f4e1
---> 01edd4710cc1
Step 8/10 : ENV JAVA_HOME /usr/local/java/jdk-11.0.6/
---> Running in 722c2d369a2f
Removing intermediate container 722c2d369a2f
---> ef5172fb1dd6
Step 9/10 : ENV PATH $PATH:$JAVA_HOME/bin
---> Running in eaa287937565
Removing intermediate container eaa287937565
---> 0347db73b904
Step 10/10 : ENTRYPOINT /usr/local/tomcat/apache-tomcat-9.0.37/bin/startup.sh && tail -f /usr/local/tomcat/apache-tomcat-9.0.37/logs/catalina.out
---> Running in 8f93d36f4de2
Removing intermediate container 8f93d36f4de2
---> 1674e0191270
Successfully built 1674e0191270
Successfully tagged mycentos:7
```
## 映象構建歷史
```shell
docker history 映象名稱:標籤|ID
docker history mycentos:7
```
![](https://mrhelloworld.com/resources/articles/docker/image-20200817200725799.png " ")
## 使用構建的映象建立容器
```shell
# 建立容器
docker run -di --name mycentos7 -p 8080:8080 mycentos:7
# 進入容器
docker exec -it mycentos7 /bin/bash
# 測試 java 環境變數
[root@dcae87df010b /]# java -version
java version "11.0.6" 2020-01-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.6+8-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.6+8-LTS, mixed mode)
# 訪問 http://192.168.10.10:8080/ 看到頁面說明環境 OK!
```
![](https://mrhelloworld.com/resources/articles/docker/image-20200812190553942.png " ")
> 太棒了,Dockerfile 構建映象的方式你也學會了,再接再厲學習一下 Docker 映象的備份恢復遷移,go ~
![](https://user-gold-cdn.xitu.io/2020/5/1/171cf87f564bc82e?w=433&h=133&f=gif&s=333013)
本文采用 `知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議`。
大家可以通過 `分類` 檢視更多關於 `Docker` 的文章。