iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

本文目標:

  • 學習 docker
  • 淺談 docker image optimization

進入正題

前一篇文章已經有向各位介紹容器化技術的用途,這篇文章會帶大家使用 Docker 去建構容器化的應用程式。
在 Docker 的世界中,會有許多的 image file,這些 image file 都是開發者撰寫 Dockerfile 後使用 docker build 產生而成的。

將 dockerfile 放置在需要打包的專案資料夾後,輸入:

docker build .

就可以將應用程式打包成 docker image 囉!
不過,這樣做會產生出一個沒有 tag 的 docker image,當 image 的數量變多時會變得不容易辨識。
為了方便 docker image 的識別與管理,開發者在編譯 image 時通常會使用 -t 為 image 打上 tag name:

docker build -t free5gc/amf:latest

如果想查詢 local 上存放了多少 image,也可以使用以下命令進行檢查:

docker image ls

image 與 container 的關係


image 與 container 就好比作業系統中 program 與 process 的關係,當 image 被 docker 執行後,就會產生出一個運作應用程式的 container。

要執行 local 環境的 docker image,可以使用以下命令:

docker run <IMAGE_NAME> --name <CONTAINER_NAME>

上面的 --name flag 其實是可選的,它會以我們輸入的 <CONTAINER_NAME> 為 container 命名。
實際上,docker 還提供了很多不同的 flag 供開發者使用:

  • --privileged:privileged mode
  • --pull:啟動前是否要從遠端的 docker registry 重新 pull image("always"|"missing"|"never"
  • --volume:將 host 的指令路徑對應到 container 內的指定路徑
  • --net:讓容器連接到 docker network

補充:

如何撰寫 Dockerfile

網路上有很多 Dockerfile 的範例可以參考,這邊以 free5gc/free5gc-compose 為例:

FROM free5gc/base:latest AS builder
FROM alpine:3.15

LABEL description="Free5GC open source 5G Core Network" \
    version="Stage 3"

ENV F5GC_MODULE amf
ARG DEBUG_TOOLS

# Install debug tools ~ 100MB (if DEBUG_TOOLS is set to true)
RUN if [ "$DEBUG_TOOLS" = "true" ] ; then apk add -U vim strace net-tools curl netcat-openbsd ; fi

# Set working dir
WORKDIR /free5gc
RUN mkdir -p config/ log/ support/TLS/ ${F5GC_MODULE}/

# Copy executable and default certs
COPY --from=builder /free5gc/${F5GC_MODULE} ./${F5GC_MODULE}
COPY --from=builder /free5gc/support/TLS/${F5GC_MODULE}.pem ./support/TLS/
COPY --from=builder /free5gc/support/TLS/${F5GC_MODULE}.key ./support/TLS/

# Move to the binary path
WORKDIR /free5gc/${F5GC_MODULE}

# Config files volume
VOLUME [ "/free5gc/config" ]

# Certificates (if not using default) volume
VOLUME [ "/free5gc/support/TLS" ]

# Exposed ports
EXPOSE 8000
  • FROM 關鍵字會載入該容器所需的執行環境,以 amf 的環境來看,就需要 alpine (一個輕量化的 Linux 作業系統,使用它可以方便開發者在容器執行期間進行除錯)以及 free5gc/base(它是一個基於 ubuntu 的 image,我們在這個 image 上安裝 dependencies 並且編譯 NF 的執行檔案)。
  • LABEL 關鍵字可以讓開發者新增該容器的資訊。
  • ENV 關鍵字可以幫助我們定義一些變數,讓 Docker 執行剩餘指令時可以參考該變數的內容。
  • ARG 關鍵字可以供我們定義 docker build 時需要傳入的參數。
docker build -t NF_AMF --no-cache --build-arg DEBUG_TOOLS=true .
  • WORKDIR 關鍵字可以讓 Docker 知道需要移動到哪一個 Path 作為當前的工作目錄。
  • VOLUME 關鍵字可以供我們建立一個 Docker volume,每一個 Docker volume 都會 mapping 到電腦中的實體檔案系統上,所以資料是可以永久保存的,不怕 container 停止運作以後遺失資料。
  • EXPOSE 關鍵字可以告訴 Docker 要讓 Container 的哪一個 PORT 提供對外服務。

補充資訊[1]:Docker volume

除了像是剛剛那樣在 dockerfile 內使用 VOLUME 關鍵字,還可以在 docker run 時使用 -v 參數建立 volume:

docker run -it -v /Users/ianchen/storage:/storage ...
  • 前者 /Users/ianchen/storage 為電腦的實體位址,後者為 container 的 path。
  • -it 參數可以讓 container 運作以後進入互動模式。

此外,使用 docker volume ls 可以列出目前已經存在的 volume:

補充資訊[2]:檢查 volume 的掛載位址

如果將情境換成在沒有指定實體路徑的情況下就建立 volume:

docker run -it -v /storage ...

可以透過以下方式檢查 volume 的實體位址:

docker inspect -f '{{.Mounts}}' CONTAINER_ID

補充資訊[3]:Container 之間共享 volume

假設已經有一個運作中並帶有 volume 的 Container: container1,若我們希望 container2 可以與 container1 共享 volume,可以使用以下指令:

docker run -it --volumes-from container1 --name=container2 ...

補充資訊[4]:檢查 container

使用 docker container ls 可以查詢正在運作的 container。
使用 docker ps -a 可以列出所有的 container。

docker container 的主要功能是管理 container,除了 list(ls)以外,還有 remove(rm)、create、kill、exec 等功能。

補充資訊[5]:Docker image 的最佳化

我們在撰寫 Dockerfile 的時候,如果希望應用程式運作在 ubuntu 的環境上,那麼我們可能會以官方提供的 ubuntu image 作為 Dockerfile 的基礎環境,接著在上面使用 apt 安裝應用程式所需要的套件(git、make、node.js、mongodb 等等),當 image 的規模到達一個程度,我們又需要頻繁的更新 image file 時,就會碰到一些問題:

  • 編譯時間變慢
  • image 肥大

這幾個問題可以依靠幾種方式改善,不過,在提出改善方式之前,我們必須先知道一件事:docker image 有 layer 以及 cache 的概念!
我們在 Dockerfile 上面使用的每一行命令(如:COPYRUN)對 docker 來說都是一層層 layer:

圖片出處:https://www.metricfire.com/blog/how-to-build-optimal-docker-images/

以上圖為例,Docker 在編譯 image 時會一層層 layer 進行階段式的編譯,並且每一層的編譯結果都會被 cache:

  • 如果 higher layer 的行為改變,前面的 layer 不需要重新編譯。
  • 但如果是中間的 layer 發生改變,即使後面的 layer 不變,仍需要重新編譯(因為中間的結果不一樣會導致最終的結果跟著改變)

了解 layer 的概念後,就來談談如何最佳化 image 吧!

1. 精挑細選基本環境:如果應用程式一定要執行在 linux 上,可以評估是否需要用到 ubuntu image?還是 alpine 這類的 image 就能達成需求,這兩者的 size 可是差了數十倍呢!

2. 編譯過程中的檔案是否需要保留?像是 apt install 這類的都會保留安裝檔案在系統內,如果非必要,應該在安裝完畢後主動清除這些檔案以節省 image 的大小。

3. layer 的拆分:很多人提倡應該盡可能將功能類似的命令組合起來,以 apt install 為例,如果我的容器需要 3 個套件,我可以使用三個 RUN apt install 完成,也可以選擇使用 RUN apt install -y ... 一次安裝所有套件,當套件的數量非常大,一次性安裝時可能會帶來一個問題(如果 image 使用的套件數量需要增減,那麼編譯時這個 layer 的 cache 將變得不可用)。

4. 將功能類似的命令組合起來:這部分需要與第三點提到的情況一起考慮進去,但像是 lscdmv 這種指令,就可以毫不猶豫地將它們合併在一塊囉!

5. 拆解 image:以本文使用的範例 free5gc-compose 來說,就是一個很好的例子!我們將 NF 的執行檔案編譯都交給一個 image 來做,其他的 NF container 只需要運作一個 alpine linux 上,並且在 image 編譯期間把 base image 內部的 NF 執行檔複製進來就好了。如果把 NF 執行檔案的編譯工作交給各別的 image,那麼空間的使用率會有數十倍到數百倍的差異。

總結

這篇文章簡單的介紹 Docker 的使用方式,也順便介紹一下很實用的最佳化技巧,network 相關的技術會在下一篇文章中提到,考慮到系列文會接著介紹 docker-compose,所以我並不會花很多時間在 docker 指令的介紹(怎麼建立負責的網路模型、一些進階的使用技巧),如果對這些指令有興趣,可以直接翻閱 Docker 的官方文件

References


上一篇
DevOps 與核心網路
下一篇
Docker Network 介紹
系列文
5G 核心網路與雲原生開發之亂彈阿翔36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言