owned this note
owned this note
Published
Linked with GitHub
# 六角鼠年鐵人賽 Week 5 - Build a suitale Image by DockerFile
==大家好,我是 "為了拿到金角獎盃而努力著" 的文毅青年 - Kai==
### 夜歸鹿門山歌 孟浩然
:::info
山寺鐘鳴晝已昏,漁梁渡頭爭渡喧。
人隨沙岸向江村,餘亦乘舟歸鹿門。
鹿門月照開煙樹,忽到龐公棲隱處。
巖扉鬆徑長寂寥,惟有幽人自來去。
:::
## DockerFile 介紹
DockerFile 是建置一個 Image 的源頭,負責定義底層映像檔、支援操作指令、加入維護者、容器啟動指令等內容。
上述的說明很難懂,可以把它想成 DockerFile 是一個設計圖,而經由 DockerFile 建置出的 Image 就是工廠,可以生產出許多的 Container。今天如果工廠想要生產不同的東西,DockerFile 只要更動部分的設計,如同工廠增刪不同的配備,就可以開始生產其他產品。
DockerFile 作為 Docker Image 的源頭,其帶來的優勢有以下幾點:
### 文字檔方便攜帶
DockerFile 僅是由一行一行的指令整理而成的文字檔,因此它幾乎不占用任何資源,檔案輕巧方便攜帶與交換。
### 提供建置訊息
DockerFile本身是個可閱讀的檔案,由許多操作指令一行一個動作的方式寫成,當然這跟Linux操作有很大的關聯。
除了用來建置成 Image 外,也因其獨特的格式而成為了很好的文件,開發者可以透過 DockerFile 了解該 Image 完整的建置流程,避免掉無謂的動作而損耗資源。
雖然在 Docker Hub 上挑選公開且評分高的 Image,基本上不會遇到胡亂搞的設計,但如果今天拿到一個不知來源的 Image,或者對其在建置上有不確定性時,DockerFile 就是個很重要的說明文件。
而對於一般常用 word、excel 紀錄環境佈署過程的工程師來說,一份 DockerFile 既省了開發的工,也省了寫文件的工(愉悅~)。
### 客製化
從網路上直接抓 Image 的方式好處是方便、快速,但在專案上卻不一定能夠完全滿足需求,而透過建置專屬 DockerFile 的方式,便可輕鬆建置完全媒合專案的 Image。
## DockerFile Commands
#### 建立 DockerFile
```xml
touch [DockerFile Name]
Kai 的操作指令為
touch testDockerFile_1
```
:::info
使用 Linux 指令 touch 建立空檔案
**P.S. touch 另外的功能為變更檔案或目錄的時間戳記**
:::
---
#### 編譯檔案
```
vim [DockerFile Name]
{I},{O},{A},{R}
(Edit...)
{ESC}
:wq
```

:::info
使用 Linux 指令 vim 開啟編譯文檔的視窗
按下 i,o,a (插入),r (取代) 隨意一個按鍵開啟編輯模式
(編輯...)
按下 ESC 離開編輯模式
輸入 :w(寫入) q(離開) 離開 vim 回到主畫面
:::
```
# 我只是註解
# DockerFile 開頭必須是 FROM 指定一個底層映像檔
FROM tomcat:7.0.100-jdk8-corretto
# 映像檔維護者, 可以當成作者
MAINTAINER KAI
# LABEL 設定映像檔的 Metadata
LABEL description="Kai's example" version="1.0" owner="Kai"
```

:::info
**#**
在 DockerFile 中為註解符號,使用在句子最前頭,可以讓後續輸入的文字僅保留訊息功能,而不會被編譯器讀取為須執行的程式碼
**FROM [Docker Image Name:TAG]**
DockerFile 開頭第一句必須是此指令,指定該 DockerFile 的底層映像檔,而後續的操作就會在該映像檔上的寫入層進行處理,包含建立各個 Layer 等動作
**MAINTAINER [Editor Name]**
為描述此映像檔的維護者名稱
**LABEL [<key>=value]**
設定映像檔的 Metadata 資訊,有許多種的<key>可選用,可在這部分寫入較多關於此 DockerFile 的訊息
:::
---
#### RUN - Build 時的指令
:::info
**RUN <shell commands>**
**RUN ["executable","param","param"...]**
在 build Image 時的指令,可以使用一般常見的 shell 指令和 exec 指令兩種下法讓開發者建置環境
想了解其中差異的人可以參考官網 [**Run 的解說**](https://docs.docker.com/engine/reference/builder/#run)
簡單來說 EXEC 的指令下法可以避免一些預設的 shell 指令,而選擇想要的 shell 指令進行操作,此外還須注意 EXEC 執行的程式碼,必須使用 **雙引號** 的格式框住指令
以下分享一些小技巧與觀念:
1. 使用 \ 符號進行換行,避免過長的文字影響閱讀
2. 使用 && 串聯 多個 RUN 指令,因為每一個 RUN 都會建立一個 Layer,使用 && 可以避免建置過多 Layer
:::
---
#### CMD - 執行 Container 時的指令
:::info
**CMD ["executable","param","param"]**
**CMD <shell commands>**
**CMD ["param","param"]**
在 Container 建立時要下的指令,官方推薦使用 EXEC 的方式進行操作,另注意第三種為有使用 ENTRYPOINT 參數時適用的指令
:::
:::danger
- CMD 在 DockerFile 中只能存在一行,若有多行不影響運行,但只有最後一行 CMD 指令會生效
- ENTRYPOINT 可設定預留參數在 Image 中,若有 CMD 指令,可透過執行後的 CMD 指令改變它
:::
---
#### ENTRYPOINT - 執行 Container 時的指令
:::info
**ENTRYPOINT ["param","param"...]**
在 Container 建立時要下的指令,可設定預留參數在 Image 中,待啟動 Container 時執行,若有 CMD 參數,則會自動用 CMD 參數取代原有參數
:::
:::danger
- 與 CMD 一樣,在 DockerFile 中只能存在一行,若有多行不影響運行,只有最後一行 ENTRYPOINT 指令會生效
:::
:::warning
**CMD 與 ENTRYPOINT 範例**
若在 DockerFile 中寫入下列指令
```
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["World"]
```
而在啟動時,若我們輸入下列指令會有不同的結果
```
docker run -it <image>
return: Hello World
docker run -it <image> Docker
return: Hello Docker
```
:::
---
#### ADD 和 COPY - 增加與複製檔案
:::info
**COPY [--chown=<user>:<group>] <src>,<src>,... <dest>**
**COPY [--chown=<user>:<group>] ["<src>","<src>",... "<dest>"]**
**ADD [--chown=<user>:<group>] <src>,<src>,... <dest>**
**ADD [--chown=<user>:<group>] ["<src>","<src>",... "<dest>"]**
兩個指令都是用來將檔案放置到指定位置的指令,不同在於:
- ADD 支援 URL 路徑
- 檔案為壓縮檔時,使用 ADD 會在放置後自動解壓縮,COPY 則不會 (一般建議以 COPY 為搬運指令)
:::
---
#### EXPOSE - 監聽 port
:::info
**EXPOSE <port>/<protocol>,<port>/<protocol>,...**
設定用來監聽 port 的指令,預設為 TCP,可手動更正為 UDP
定義的 port 並不會在 Container 啟動時一併啟用連接埠,須使用 -p 或 -P 參數啟用
使用大寫 -P 會在啟用時,啟動所有 EXPOSE 定義連接埠號,並且動態、隨機關連到主機的連接埠號
```
docker run -P [Docker Image]
```
-p 小寫可在啟用時,自行設定與主機關聯的連接埠號
```
docker run -p 8080:8081/tcp -p 8080:8081/udp [Docker Image]
```
:::
---
#### ENV - 環境指令
:::info
**ENV <key> <value>**
**ENV <key>=<value>**
用來設置環境變數的指令,兩種寫法都可以,且在 Container 建立後亦可使用
:::
---
#### VOLUME - 掛載目錄指令
:::info
**VOLUME ["/data"]**
VOLUME 值可以是 JSON 也可以是純粹的文字,要注意是無法指定本機對應目錄,目錄為自動生成,可以使用 docker inspect VOLUME 查詢目錄資訊
:::
---
#### WORKDIR - 設定工作目錄指令
:::info
**WORKDIR /path/work_dir**
設定工作目錄路徑,設定好後,前頭提到的 RUN、CMD、ENTRYPOINT、COPY、ADD 就會在此路徑下執行操作
:::
---
### USER - 運行 Container 時的使用者名稱或ID
:::info
**USER <user>**
**USER <UID>**
設定 USER 後,後續的操作便會以該名 USER 的權限進行操作,若 USER 的 權限不足以操作某些指令,會造成執行中錯誤。
若 USER 不存在,則在運行時的指定會因失敗而停止
:::
---
#### ARG - 在建置映像檔時可傳入的參數
:::info
**ARG <name>**
提供 build Image 時的參數,無法在運行 Container 時執行,這是與 ENV 最大的不同
:::
---
#### ONBUILD - Base Image Build Trigger
:::info
這個設置可以讓你 build 出來的 Image 在被別人當作 底層映像檔時,執行完 FROM 之後會優先執行你寫入的 ONBUILD 指令,後續才是他人寫的指令
:::
## Build Image
在介紹了許多會寫入 DockerFile 中的指令後,終於要來講如何用 DockerFile 建置 Image了 (累)
```
docker build [DockerFile Name]
docker build -t [Image Name:TAG]
docker build -f [path] -t [Image Name:TAG]
```
:::info
-t 的參數為寫入 Image Name 和 TAG
-f 的參數為指定 Image 建置完成後放置的位置
:::
Image 建置好後,就可以開始使用 Image 產出許多 Container 來運行了!
## 結語
:::danger
DockerFile 的內部指令基本就上述這些,詳細的一些內容可能 Kai 這邊也還沒學到,等後續學到了,我會再特別開一篇新的或是更新舊內容的方式做回饋~
下篇將會分享 **使用 DockerFile 編寫建置 Tomcat + MySQL 環境** 的學習心得
[六角鼠年鐵人賽 Week 6 - 使用 DockerFile 建置 Tomcat + MySQL 環境](/csz4vaWTTgGbnw_5QmQrjQ)
:::
首頁 [Kai 個人技術 Hackmd](/2G-RoB0QTrKzkftH2uLueA)
###### tags: `Docker`,`w3HexSchool`