一、docker鏡像
1.什么是鏡像
????????容器解決應用開發、測試和部署的問題,而鏡像解決應用部署環境問題。鏡像是一個只讀的容器模板, 打包了應用程序和應用程序所依賴的文件系統以及啟動容器的配置文件,是啟動容器的基礎。鏡像所打 包的文件內容就是容器的系統運行環境——rootfs(根文件系統或根目錄)。容器與鏡像類似對象與類的關系。
2.docker鏡像原理
1. 分層: Docker鏡像采用分層的方式構建,每一個鏡像都由一組鏡像組合而成。每一個鏡像層都可 以被需要的鏡像所引用,實現了鏡像之間共享鏡像層的效果。這樣的分層設計在鏡像的上傳與下載 過程當中有效的減少了鏡像傳輸的大小,在傳輸過程當中本地或注冊中心只需要存在一份底層的基 礎鏡像層即可,真正被保存和下載的內容是用戶構建的鏡像層。而在構建過程中鏡像層通常會被緩 存以縮短構建過程。
2. 寫時復制:底層鏡像層在多個容器間共享,每個容器啟動時不需要復制一份鏡像文件,而是將所有 需要的鏡像層以只讀的方式掛載到一個掛載點,在只讀層上再覆蓋一層讀寫層。在容器運行過程中 產生的新文件將會寫入到讀寫層,被修改過的底層文件會被復制到讀寫層并且進行修改,而老文件 則被隱藏。
3. 聯合掛載:docker采用聯合掛載技術,在同一個掛載點同時掛載多個文件系統,從而使得容器的根 目錄看上去包含了各個鏡像層的所有文件。如下圖:
?4. 內容尋址:根據鏡像層內容計算校驗和,生成一個內容哈希值,并使用該值來充當鏡像層ID、索引 鏡像層。內容尋址提高了鏡像的安全性,在pull、push和load、save操作后檢測數據的完整性。另 外基于內容哈希來索引鏡像層,對于來自不同構建的鏡像層,只要擁有相同的內容哈希值,就能被 不同的鏡像所引用。
? ? ? ? docker的鏡像和容器的關系不是類和對象的關系而是相同的引用,以下是詳細講解:
一、“類與對象” 類比的局限性
1.?類與對象的本質特征
- 類(Class):是抽象模板,定義屬性和方法,不占用實際資源;
- 對象(Object):是類的實例化結果,每個對象獨立擁有數據空間,修改對象不會影響類或其他對象。
- 核心邏輯:對象是類的 “復制”,彼此隔離。
2.?Docker 鏡像與容器的實際機制
- 鏡像(Image):是只讀的文件系統快照,由多層只讀層疊加而成(基于 UnionFS);
- 容器(Container):是鏡像的運行實例,在鏡像頂層添加一個可寫層(
container layer
)。- 核心邏輯:容器并非鏡像的 “復制”,而是共享鏡像的底層只讀層,僅添加獨立的可寫層。
二、“相同引用” 的本質:分層存儲與共享機制
1.?UnionFS 分層架構
Docker 使用 UnionFS(如 Overlay2)實現鏡像和容器的分層存儲:
?
- 鏡像層(只讀):多個鏡像可共享底層基礎層(如
ubuntu:latest
的文件系統),類似 Linux 的硬鏈接,多個引用指向同一物理存儲;- 容器層(可寫):每個容器在鏡像頂層添加一個專屬的可寫層,用于存儲運行時修改(如寫入文件、環境變量變更)。
2.?“相同引用” 的具體體現
- 底層共享:當多個容器基于同一鏡像啟動時,它們共享鏡像的所有只讀層(即底層文件系統的引用相同),而非各自復制一份鏡像數據。例如:
bash
# 啟動兩個容器基于同一鏡像 docker run -d ubuntu:22.04 sleep 3600 docker run -d ubuntu:22.04 sleep 3600
這兩個容器的底層文件系統(如/bin/bash
、/etc
)實際指向同一份物理存儲,僅各自的可寫層獨立。- 空間效率:這種共享機制大幅節省磁盤空間,避免鏡像復制的冗余(類似 Linux 系統中多個進程共享同一可執行文件的內存映射)。
3.?與 “類與對象” 的關鍵差異
對比維度 類與對象關系 鏡像與容器關系 數據隔離性 對象完全獨立,無共享數據 容器共享鏡像的只讀層數據 修改影響 修改對象不影響類或其他對象 容器修改僅作用于自身可寫層 資源占用 每個對象獨立占用內存 多個容器共享底層鏡像資源 創建邏輯 對象是類的 “實例化復制” 容器是鏡像的 “引用 + 擴展”
?3. docker鏡像關鍵概念
????????1. registry:注冊中心,用來保存docker鏡像,其中包括鏡像的層次結構和關于鏡像的元數據。
????????2. repository:倉庫,即由具有某個功能的Docker鏡像的所有迭代版本構成的鏡像組。
????????3. manifest:Docker鏡像元數據文件,在pull、push、save和load中作為鏡像結構和基礎信息的描 述文件。在鏡像被pull或者load到Docker宿主機時,manifest被轉化為本地的鏡像配置文件config
????????4. image:鏡像,用來存儲一組相關的元數據信息,主要包括鏡像的架構(如amd64)、鏡像默認配 置信息、構建鏡像的容器配置信息、包含所有鏡像層的rootfs。
????????5. layer:鏡像層,是docker用來管理鏡像的中間概念,鏡像是由鏡像層組成的,單個鏡像層可以被 多個鏡像和容器共享。
????????6. dockerfile:是一個鏡像制作過程的定義,文檔包含了鏡像制作的所有命令和完整操作流程
1. Registry(注冊中心)
定義與作用
Registry 是 Docker 生態中的鏡像存儲服務,負責集中保存 Docker 鏡像的完整數據(包括鏡像層、元數據、清單等)。它是鏡像的「云端倉庫」,支持鏡像的上傳(
push
)、下載(pull
)、刪除等操作。核心特性
- 存儲結構:以分層方式存儲鏡像數據,支持內容尋址(通過鏡像層的哈希值索引),避免重復存儲;
- 元數據管理:保存鏡像的?
manifest
(清單)、標簽(tag)、版本信息等;- 多租戶支持:可部署公共 Registry(如 Docker Hub)或私有 Registry(如 Harbor、AWS ECR)。
如何查看與使用?
公共 Registry(Docker Hub):
訪問?hub.docker.com?可瀏覽官方倉庫(如?nginx
、ubuntu
)和用戶倉庫。例如,搜索?nginx
?會顯示其所有標簽(latest
、alpine
?等)。私有 Registry 操作:
若部署了私有 Registry(如 Harbor),需先登錄:docker login my-private-registry.com:5000 # 輸入賬號密碼
上傳鏡像到私有 Registry:
docker tag nginx:alpine my-private-registry.com/my-team/nginx:alpine # 重命名鏡像為私有倉庫路徑 docker push my-private-registry.com/my-team/nginx:alpine # 上傳
2. Repository(倉庫)
定義與作用
Repository(倉庫)是 Registry 中的鏡像邏輯分組單元,由同一功能鏡像的所有迭代版本(不同標簽)組成。例如,
nginx
?倉庫包含?nginx:latest
、nginx:alpine
、nginx:1.25
?等不同標簽的鏡像。核心特性
- 標簽(Tag):倉庫中的每個鏡像版本通過標簽區分(如?
latest
?是默認標簽);- 命名規則:倉庫名通常為?
[用戶/組織名]/[鏡像名]
(如?docker/library/nginx
?是官方倉庫,myuser/myapp
?是個人倉庫)。如何查看與使用?
查看倉庫中的標簽:
- 公共倉庫:在 Docker Hub 頁面直接查看標簽列表(如?nginx 標簽頁);
- 私有倉庫:通過命令行或 Registry 管理界面(如 Harbor 的 Web 界面)查看。
拉取倉庫中的特定標簽:
docker pull nginx:alpine # 拉取 nginx 倉庫的 alpine 標簽鏡像
3. Manifest(清單)
定義與作用
Manifest 是鏡像的元數據描述文件,JSON 格式,記錄鏡像的核心信息,包括:
?
- 鏡像的配置文件(
config
)的哈希值;- 所有鏡像層(
layers
)的哈希值列表(按堆疊順序);- 鏡像的架構(如?
amd64
)、操作系統(如?linux
)等元數據。核心作用
- 鏡像傳輸的「地圖」:當執行?
docker pull
?或?docker push
?時,首先傳輸 Manifest,通過其中的層哈希值確認需要下載 / 上傳的層;- 本地鏡像的「索引」:下載后,Manifest 會被轉換為本地鏡像的配置文件(
config.json
),記錄鏡像的運行時默認配置(如入口命令、環境變量)。如何查看與使用?
查看遠程鏡像的 Manifest(需鏡像未開啟內容信任):
docker manifest inspect nginx:alpine
輸出示例(簡化):
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.v2+json","config": {"mediaType": "application/vnd.docker.container.image.v1+json","size": 7023,"digest": "sha256:abc123..." # 鏡像配置文件的哈希},"layers": [{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 2815,"digest": "sha256:def456..." # 第一層的哈希},{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 1234,"digest": "sha256:ghi789..." # 第二層的哈希}] }
查看本地鏡像的 Manifest(需通過?
docker inspect
?間接獲取):docker inspect nginx:alpine | jq '.[0].RootFS' # 查看鏡像層的哈希列表
4. Image(鏡像)
定義與作用
Image(鏡像)是 Docker 容器的只讀模板,由以下部分組成:
- 配置信息(
config.json
):包括鏡像的架構、默認命令(CMD
)、入口點(ENTRYPOINT
)、環境變量(ENV
)等;- 根文件系統(RootFS):由多個只讀鏡像層(
layer
)堆疊而成,定義容器的初始文件系統;- 元數據:如鏡像創建時間、作者、標簽等。
核心特性
- 不可變性:鏡像一旦構建完成,內容不可修改(修改需生成新鏡像);
- 分層結構:基于內容尋址的鏡像層共享機制,節省存儲(詳見下文?
layer
?部分)。如何查看與使用?
查看本地鏡像列表:
docker images # 顯示所有本地鏡像(倉庫名、標簽、鏡像 ID、大小等)
查看鏡像詳細信息:
docker inspect nginx:alpine # 輸出鏡像的完整配置(JSON 格式)
關鍵字段:
Config
:鏡像的默認運行配置(如?Cmd
、Env
);RootFS.Layers
:鏡像層的哈希列表(與 Manifest 中的?layers
?一致);Created
:鏡像的創建時間。
5. Layer(鏡像層)
定義與作用
Layer(鏡像層)是鏡像的最小存儲單元,對應 Dockerfile 中一條指令(如?
RUN
,?COPY
)的文件系統變更。每個層是一個只讀的?tar
?壓縮包,包含該指令對文件系統的修改(新增、刪除、修改的文件 / 目錄)。核心特性
- 內容尋址:每個層的哈希值由其內容(文件數據 + 元數據)計算生成(SHA256),相同內容的層會被不同鏡像共享;
- 堆疊順序:層按 Dockerfile 指令順序堆疊,上層覆蓋下層的同名文件;
- 可寫層隔離:容器運行時,會在鏡像層頂部添加一個可寫層(
container layer
),所有運行時修改僅作用于該層。如何查看與使用?
查看鏡像的層歷史:
docker history nginx:alpine
輸出示例:
?IMAGE CREATED CREATED BY SIZE COMMENT sha256:abc123 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B <missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80/tcp 0B <missing> 2 weeks ago /bin/sh -c apt-get update && apt-get install… 123MB ...
每行對應一個鏡像層,
CREATED BY
?顯示該層對應的 Dockerfile 指令,SIZE
?是層的大小。查看層的具體內容(需借助工具):
可通過?docker image inspect
?獲取層的哈希,然后到 Docker 存儲目錄(如?/var/lib/docker/overlay2
)查看層的實際文件:# 獲取鏡像層的哈希列表 docker inspect nginx:alpine | jq '.[0].RootFS.Layers' # 輸出類似:["sha256:def456...", "sha256:ghi789..."]# 查看層的內容(需 root 權限) ls /var/lib/docker/overlay2/def456.../diff # 該目錄是層的解壓內容
6. Dockerfile
定義與作用
Dockerfile 是鏡像構建的腳本文件,包含一系列指令(如?
FROM
,?RUN
,?COPY
),定義了鏡像的構建步驟和配置。Docker 通過解析 Dockerfile 逐行執行指令,生成鏡像層,最終構建出完整鏡像。核心指令與示例
常見 Dockerfile 指令:
FROM
:指定基礎鏡像(如?FROM ubuntu:22.04
);RUN
:執行命令(如?RUN apt-get update
);COPY
:復制文件到鏡像(如?COPY app/ /app/
);CMD
:定義容器默認啟動命令(如?CMD ["nginx", "-g", "daemon off;"]
);EXPOSE
:聲明容器監聽的端口(如?EXPOSE 80
)。如何使用?
編寫 Dockerfile(示例:構建一個簡單的 Nginx 鏡像):
# 基礎鏡像 FROM nginx:alpine# 復制自定義配置到鏡像 COPY nginx.conf /etc/nginx/nginx.conf# 聲明端口(僅文檔作用) EXPOSE 80# 容器啟動時執行的命令(繼承自基礎鏡像的 CMD)
構建鏡像:
docker build -t my-nginx:v1 . # -t 指定鏡像標簽,. 表示 Dockerfile 在當前目錄
驗證構建結果:
docker images | grep my-nginx # 查看新構建的鏡像 docker history my-nginx:v1 # 查看鏡像層是否包含 COPY 指令生成的層
總結:概念間的協同關系
- 構建階段:用戶編寫?
Dockerfile
,Docker 逐行執行指令生成?layer
(鏡像層),最終構建為?image
(鏡像);- 存儲階段:鏡像的?
manifest
(清單)描述其層結構和元數據,鏡像與層被存儲在?registry
(注冊中心)的?repository
(倉庫)中;- 分發與運行階段:用戶通過?
docker pull
?從 Registry 下載鏡像(基于 Manifest 拉取層),啟動容器時鏡像的只讀層與容器的可寫層合并,形成運行環境。
?4. 鏡像管理命令
????????Docker 鏡像的全生命周期管理(構建、查看、標記、導出 / 導入、刪除)依賴一系列核心命令。以下按功能分類,詳細講解每個命令的作用、常用選項、操作示例及注意事項,幫助你高效管理鏡像。
一、鏡像構建:
docker build
功能
通過解析?
Dockerfile
?腳本,逐層構建鏡像。支持分層緩存、參數傳遞、資源限制等高級特性,是鏡像創建的標準方式(推薦替代?docker commit
)。常用選項(標?為高頻使用)
選項 說明 -t, --tag
??指定鏡像的標簽(格式: 倉庫名:標簽
,如?myapp:v1
);可多次使用設置多個標簽。-f, --file
??顯式指定? Dockerfile
?路徑(默認當前目錄的?Dockerfile
)。--no-cache
??禁用構建緩存(強制重新構建所有層,用于解決緩存導致的鏡像內容不同步)。 --build-arg
設置構建時變量(需在? Dockerfile
?中用?ARG
?聲明)。--pull
構建前嘗試拉取基礎鏡像的最新版本(避免使用本地舊版本)。 --rm
構建成功后刪除中間容器(默認? true
,節省磁盤空間)。--force-rm
無論構建是否成功,總是刪除中間容器(避免中間容器殘留)。 操作示例
假設當前目錄有?
Dockerfile
(內容如下):# 基礎鏡像 FROM nginx:alpine# 復制自定義配置到鏡像 COPY nginx.conf /etc/nginx/nginx.conf# 聲明端口(僅文檔作用) EXPOSE 80# 容器啟動命令(繼承自基礎鏡像)
構建命令:
?# 基本構建(標簽為 my-nginx:v1,使用當前目錄的 Dockerfile) docker build -t my-nginx:v1 .# 顯式指定 Dockerfile 路徑(假設文件在 ./docker/ 目錄) docker build -t my-nginx:v1 -f ./docker/Dockerfile .# 禁用緩存并設置構建變量(假設 Dockerfile 中有 ARG VERSION) docker build -t my-nginx:v1 --no-cache --build-arg VERSION=1.25 .
注意事項
- 構建上下文(PATH):命令中的?
.
?表示構建上下文路徑,Docker 會將該路徑下的所有文件打包傳給 Docker 守護進程(避免在上下文中放入大文件,否則會影響構建速度)。- 緩存機制:默認情況下,若某層的內容未變(哈希匹配),則直接復用緩存層,加速構建。若需強制更新某層(如?
RUN apt-get update
),需用?--no-cache
。- 中間容器:構建過程中會生成臨時容器(用于執行?
RUN
?指令),默認構建成功后刪除(--rm=true
),失敗時保留(便于調試)。
二、鏡像查看:
docker images
功能
列出本地已下載或構建的鏡像,支持過濾、格式化輸出,可查看鏡像的標簽、大小、創建時間等信息。
常用選項(標?為高頻使用)
選項 說明 -a, --all
??顯示所有鏡像(包括中間層鏡像,默認過濾中間層)。 --filter
??過濾鏡像(如? dangling=true
?查看懸空鏡像,label=key=value
?按標簽過濾)。--format
自定義輸出格式(使用 Go 模板,如? {{.Repository}}:{{.Tag}}
)。-q, --quiet
僅顯示鏡像 ID(用于腳本自動化)。 --digests
顯示鏡像的摘要(SHA256 哈希,用于驗證鏡像完整性)。 操作示例
# 查看所有本地鏡像(含標簽、大小等) docker images# 查看所有鏡像(含中間層) docker images -a# 過濾懸空鏡像(無標簽、未被引用的鏡像,通常是構建失敗的殘留) docker images --filter dangling=true# 僅顯示鏡像 ID docker images -q# 格式化輸出(顯示倉庫:標簽 及 大小) docker images --format "{{.Repository}}:{{.Tag}} \t {{.Size}}"
注意事項
- 懸空鏡像(Dangling Image):指無標簽(
<none>:<none>
)且未被其他鏡像引用的鏡像,通常由覆蓋標簽(如?docker tag old-image new-tag
)或構建失敗導致,可通過?docker image prune
?清理。- 中間層鏡像:構建過程中生成的臨時層(
docker images -a
?可見),默認被過濾,可通過?--no-cache
?避免生成冗余中間層。
三、鏡像刪除:
docker rmi
功能
刪除本地鏡像。若鏡像被容器引用(即使容器已停止),需先刪除容器或強制刪除鏡像。
常用選項
選項 說明 -f, --force
強制刪除鏡像(即使被容器引用,需先停止容器)。 --no-prune
不刪除該鏡像的中間層(默認會刪除未被其他鏡像引用的中間層)。 操作示例
# 刪除指定標簽的鏡像 docker rmi my-nginx:v1# 強制刪除被容器引用的鏡像(需先停止容器) docker stop my-container # 停止容器 docker rmi -f my-nginx:v1 # 強制刪除鏡像# 刪除多個鏡像(通過鏡像 ID 或標簽) docker rmi my-nginx:v1 nginx:alpine# 刪除所有無標簽的懸空鏡像(等價于 docker image prune) docker rmi $(docker images -q --filter dangling=true)
注意事項
- 依賴關系:若鏡像被其他鏡像引用(如作為基礎鏡像),需先刪除依賴它的鏡像,否則無法刪除。
- 容器關聯:運行中的容器依賴的鏡像無法刪除(會報錯?
conflict: unable to remove repository reference
),需先停止并刪除容器(docker rm
)。
四、鏡像標記:
docker tag
功能
為本地鏡像添加或修改標簽,方便分類管理(如標記為測試版、生產版)或推送至不同倉庫(如私有 Registry)。
操作示例
# 為鏡像添加新標簽(原標簽保留) docker tag my-nginx:v1 my-nginx:latest # 同時存在 my-nginx:v1 和 my-nginx:latest# 標記鏡像到私有 Registry(以便推送) docker tag my-nginx:v1 private-registry.com/my-team/my-nginx:v1# 覆蓋已有標簽(原標簽指向的鏡像變為懸空鏡像) docker tag my-nginx:v1 my-nginx:old # 原 my-nginx:old 若存在,會變為 <none>:<none>
注意事項
- 標簽的意義:標簽是鏡像的「別名」,不影響鏡像本身(鏡像的唯一標識是?
IMAGE ID
)。- 推送準備:推送鏡像到私有 Registry 前,需通過?
docker tag
?將鏡像重命名為?私有倉庫路徑:標簽
(如?private-registry.com/user/repo:tag
)。
五、鏡像導出與導入:
docker save
?/?docker load
功能
docker save
:將本地鏡像打包為?tar
?文件,用于離線傳輸或備份。docker load
:從?tar
?文件中導入鏡像到本地(恢復備份或傳輸后的鏡像)。操作示例
# 導出單個鏡像為 tar 文件(包含鏡像層和元數據) docker save -o my-nginx.tar my-nginx:v1# 導出多個鏡像(用空格分隔) docker save -o all-images.tar my-nginx:v1 nginx:alpine# 導入 tar 文件到本地鏡像庫(會保留原標簽) docker load -i my-nginx.tar
注意事項
- 文件大小:
tar
?文件包含鏡像的所有層,體積較大(與鏡像大小一致)。- 跨平臺兼容性:導出的鏡像僅適用于相同架構(如?
amd64
)的 Docker 環境,跨架構需重新構建。
六、歸檔文件創建鏡像:
docker import
功能
從?
tar
?歸檔文件(如?docker export
?導出的容器文件系統)創建鏡像。與?docker save
?不同,docker import
?會丟失鏡像的層信息,生成一個單層鏡像。操作示例
# 從容器導出文件系統(得到容器的當前文件系統快照) docker export my-container > container.tar# 從歸檔文件創建鏡像(無層信息,僅單層) docker import -c "CMD [\"nginx\", \"-g\", \"daemon off;\"]" container.tar my-nginx:imported
注意事項
- 適用場景:僅用于從容器文件系統快照恢復鏡像(如遷移容器狀態),不推薦替代?
docker build
(無法利用分層緩存)。- 元數據丟失:導入的鏡像會丟失原鏡像的構建歷史(
docker history
?僅顯示一層),且無法復用其他鏡像的層。
七、容器創建鏡像:
docker commit
功能
從運行中的容器創建新鏡像(將容器的可寫層保存為鏡像層)。適用于快速保存容器的臨時狀態,但不推薦用于正式鏡像構建(無法復現構建過程)。
常用選項
選項 說明 -a, --author
鏡像作者(如? --author "Nick <nick@example.com>"
)。-m, --message
提交說明(類似 Git 的? commit -m
)。-p, --pause
提交時暫停容器(默認? true
,避免提交過程中容器狀態變化)。操作示例
# 啟動容器并修改內容(如修改 nginx 配置) docker run -it --name my-container nginx:alpine /bin/bash echo "server { listen 80; }" > /etc/nginx/nginx.conf # 修改配置 exit# 從容器提交新鏡像 docker commit -a "Nick" -m "Custom nginx config" my-container my-nginx:committed
注意事項
- 不可復現性:
commit
?生成的鏡像無法通過?Dockerfile
?復現,后續修改需再次?commit
,難以維護。- 鏡像體積:每次?
commit
?會生成新層,可能導致鏡像體積膨脹(推薦用?docker build
?分層構建)。
總結:命令使用場景建議
需求 推薦命令 原因 標準鏡像構建 docker build
可復現、支持分層緩存、易于維護。 臨時保存容器狀態 docker commit
快速保存,但不推薦長期使用。 鏡像離線傳輸 / 備份 docker save
?+?docker load
保留鏡像層信息,支持恢復原鏡像。 從容器文件系統創建鏡像 docker import
僅用于遷移容器狀態,無法復用分層機制。 鏡像標簽管理 docker tag
靈活標記,便于分類和推送。 鏡像刪除 docker rmi
清理冗余鏡像,注意依賴關系。
5.? 鏡像倉庫命令
????????Docker 鏡像的全生命周期管理不僅包括本地構建與查看,還涉及遠程倉庫交互(登錄、拉取、推送)、鏡像分享(公共倉庫 / 本地導出 / 私有倉庫)等關鍵操作。以下結合命令詳解、操作示例與最佳實踐,完整梳理這一流程。
一、遠程倉庫認證:
docker login
?與?docker logout
核心作用
docker login
:登錄到 Docker 鏡像倉庫(如 Docker Hub 或私有 Registry),獲取上傳 / 下載權限;docker logout
:退出當前登錄的倉庫,避免憑證泄露。命令格式與選項
# 登錄(默認 Docker Hub) docker login [OPTIONS] [SERVER]# 登出(默認 Docker Hub) docker logout [OPTIONS] [SERVER]
常用選項:
-u, --username
:登錄用戶名;-p, --password
:登錄密碼(或 Token);- (注意:直接使用?
-p
?可能泄露密碼,推薦交互式輸入或使用?--password-stdin
?從標準輸入讀取)操作示例
# 登錄 Docker Hub(交互式輸入密碼) docker login # 輸入用戶名后,輸入密碼(隱藏輸入)# 非交互式登錄(僅用于腳本,需謹慎保管密碼) docker login -u "myuser" -p "mypassword"# 登錄私有 Registry(如 localhost:5000) docker login http://localhost:5000 -u "testuser" -p "testpwd"# 退出 Docker Hub docker logout# 退出私有 Registry docker logout http://localhost:5000
注意事項
- 登錄憑證默認存儲在?
~/.docker/config.json
(Linux/macOS)或?%USERPROFILE%\.docker\config.json
(Windows),包含倉庫地址和加密后的密碼;- 若私有 Registry 使用自簽名證書,需配置 Docker 信任該證書(或通過?
--insecure-registry
?跳過校驗,見后文私有倉庫部分)。
二、鏡像遠程操作:
docker pull
?與?docker push
核心作用
docker pull
:從遠程倉庫下載鏡像到本地;docker push
:將本地鏡像上傳到遠程倉庫(需先登錄)。命令格式與選項
# 拉取鏡像(默認 Docker Hub) docker pull [OPTIONS] NAME[:TAG|@DIGEST]# 推送鏡像(默認 Docker Hub) docker push [OPTIONS] NAME[:TAG]
常用選項:
?
-a, --all-tags
:拉取倉庫中所有標簽的鏡像(如?docker pull -a nginx
?拉取?nginx:latest
、nginx:alpine
?等);--disable-content-trust
:跳過鏡像內容校驗(默認開啟,確保鏡像未被篡改);操作示例
# 拉取 Docker Hub 的 nginx:latest 鏡像(默認標簽為 latest) docker pull nginx# 拉取指定標簽的鏡像(如 nginx:alpine) docker pull nginx:alpine# 拉取指定摘要(Digest)的鏡像(確保內容絕對一致) docker pull nginx@sha256:abc123... # 從 manifest 中獲取摘要# 推送本地鏡像到 Docker Hub(需先登錄) docker push myuser/myapp:v1# 推送鏡像到私有 Registry(需先打標簽為私有倉庫路徑) docker tag myapp:v1 localhost:5000/myuser/myapp:v1 docker push localhost:5000/myuser/myapp:v1
注意事項
- 拉取鏡像時,若本地已有相同哈希的鏡像層,會直接復用(基于內容尋址),節省下載時間;
- 推送鏡像時,僅上傳本地不存在于遠程倉庫的鏡像層(增量上傳),提升效率;
- 若鏡像被標記為多個標簽(如?
myapp:v1
?和?myapp:latest
),推送時需分別推送每個標簽。
三、鏡像搜索:
docker search
核心作用
在 Docker Hub 搜索公開鏡像,支持過濾(如官方鏡像、高星鏡像),快速定位所需鏡像。
命令格式與選項
docker search [OPTIONS] TERM
常用選項:
--automated
:僅顯示自動構建的鏡像(由 Dockerfile 自動構建,非手動?commit
);--no-trunc
:顯示完整的鏡像描述(默認截斷);-f, --filter
:過濾條件(如?stars=10
?表示收藏數≥10);操作示例
# 搜索名稱包含 "nginx" 的鏡像 docker search nginx# 搜索收藏數≥10 的 nginx 鏡像 docker search -f stars=10 nginx# 僅顯示自動構建的 nginx 鏡像 docker search --automated nginx# 顯示完整描述(不截斷) docker search --no-trunc nginx
輸出示例:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 20000 [OK] bitnami/nginx Bitnami nginx Docker Image 1500 [OK] ...
四、鏡像分層與聯合掛載實踐(以 Nginx 為例)
Docker 鏡像的分層機制是其輕量化的核心,通過?
docker save
、解壓鏡像包和查看?manifest
,可以直觀理解鏡像層的結構。操作步驟
運行 Nginx 容器:
docker run -d -p 81:80 --name mynginx1 nginx:latest
進入容器查看文件系統:
docker exec -it mynginx1 /bin/bash ls /etc/nginx # 查看 Nginx 配置文件(來自鏡像層) exit
保存鏡像為 tar 歸檔:
docker save -o nginx.tar nginx:latest
解壓并查看鏡像層:
mkdir nginx-layers && tar -xvf nginx.tar -C nginx-layers
解壓后會得到多個文件:
manifest.json
:鏡像的清單文件(記錄層哈希、配置文件路徑);config.json
:鏡像的配置文件(記錄鏡像元數據);- 多個?
sha256-xxx.tar
?文件:每個文件對應一個鏡像層(壓縮的文件系統變更)。分析?
manifest.json
(簡化示例):[{"Config": "config.json","RepoTags": ["nginx:latest"],"Layers": ["sha256:layer1.tar", # 基礎層(如 Alpine Linux)"sha256:layer2.tar", # Nginx 安裝層"sha256:layer3.tar" # 其他配置層]} ]
對比?
docker commit
?生成的鏡像:
- 運行容器并修改文件(如添加?
test.txt
):docker exec mynginx1 touch /test.txt
- 提交容器為新鏡像:
docker commit -m "Add test.txt" mynginx1 mynginx:v1
- 保存并解壓?
mynginx:v1
?的鏡像包,會發現?manifest.json
?中新增了一個層(對應?test.txt
?的變更),而底層的 Nginx 層與原鏡像完全一致(哈希相同,證明層復用)。關鍵結論
- 鏡像由多個只讀層堆疊而成,相同內容的層會被不同鏡像共享(節省存儲);
docker commit
?會生成新層(記錄容器可寫層的變更),但無法復現構建過程(不推薦用于生產)。
五、鏡像分享的三種方式
方式 1:公共倉庫(Docker Hub)分享
適用于公開分享鏡像,需注冊 Docker Hub 賬號并創建倉庫。
?操作步驟:
?
創建 Docker Hub 倉庫:
登錄?hub.docker.com,點擊?Create Repository
,輸入倉庫名(如?myuser/mynginx
)。本地鏡像打標簽:
docker tag mynginx:v1 myuser/mynginx:v1
推送鏡像到 Docker Hub:
docker login # 登錄 Docker Hub docker push myuser/mynginx:v1
拉取驗證(任意設備):
docker pull myuser/mynginx:v1
方式 2:本地導出 / 導入(
docker save
?+?docker load
)適用于離線傳輸或備份鏡像(無需網絡)。
?操作步驟:
?
導出鏡像為 tar 文件:
docker save -o myimages.tar mynginx:v1 mysql:5.7 # 導出多個鏡像
傳輸文件(通過 U 盤、局域網等)。
導入鏡像到目標設備:
docker load -i myimages.tar # 導入后鏡像標簽和層信息完整保留
方式 3:私有注冊中心分享
適用于企業內部私有鏡像管理,需搭建私有 Registry 并配置認證。
?搭建步驟(以本地 Registry 為例):
?
啟動 Registry 容器(無認證版本):
docker run -d -p 5000:5000 --name registry --restart=always registry:2
驗證 Registry 狀態:
curl http://localhost:5000/v2/_catalog # 應返回 {"repositories":[]}(空倉庫)
配置認證(可選):
使用?htpasswd
?生成密碼文件(需安裝?apache2-utils
):sudo apt install apache2-utils # Linux htpasswd -Bbn testuser testpwd > /path/to/auth/htpasswd # 生成密碼文件
啟動帶認證的 Registry:
docker run -d -p 5000:5000 --name registry --restart=always \-v /path/to/data:/var/lib/registry \ # 數據卷(持久化存儲)-v /path/to/auth:/auth \ # 密碼文件路徑-e "REGISTRY_AUTH=htpasswd" \ # 啟用 htpasswd 認證-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \registry:2
配置 Docker 信任私有 Registry(解決 HTTPS 校驗問題):
- 編輯 Docker 服務配置(Linux):
sudo vim /lib/systemd/system/docker.service
- 在?
ExecStart
?行末尾添加?--insecure-registry localhost:5000
(允許不安全連接):ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry localhost:5000
- 重啟 Docker 服務:
sudo systemctl daemon-reload sudo systemctl restart docker
推送與拉取鏡像:
# 登錄私有 Registry docker login http://localhost:5000 -u testuser -p testpwd# 打標簽 docker tag mynginx:v1 localhost:5000/myuser/mynginx:v1# 推送 docker push localhost:5000/myuser/mynginx:v1# 拉取(其他設備需配置相同的 --insecure-registry) docker pull localhost:5000/myuser/mynginx:v1
?二、 dockerfile
1.dockerfile是什么
????????Dockerfile是一個創建鏡像所有命令的文本文件, 包含了一條條指令和說明, 每條指令構建一層, 通過 docker build命令,根據Dockerfile的內容構建鏡像,因此每一條指令的內容, 就是描述該層如何構建.有了 Dockefile, 就可以制定自己的docker鏡像規則,只需要在Dockerfile上添加或者修改指令, 就可生成 docker 鏡像。
2.?dockerfile解決了什么問題
????????Dockerfile 包含了鏡像制作的完整操作流程,其他開發者可以通過 Dockerfile 了解并復現制作過程 Dockerfile 中的每一條指令都會創建新的鏡像層,這些鏡像可以被 Docker Daemon 緩存。再次制作鏡 像時,Docker 會盡量復用緩存的鏡像層(using cache),而不是重新逐層構建,這樣可以節省時間和 磁盤空間 Dockerfile 的操作流程可以通過docker image history [鏡像名稱]查詢,方便開發者查看變更記錄。
3.?docker build 構建流程
一、Docker build 核心流程
第一步:準備上下文(Context)并處理文件
打包上下文文件
docker build
?會將指定的上下文目錄(context)中的文件打包,發送給 Docker 引擎(Docker daemon)。- 若上下文中存在?
.dockerignore
?文件,會按規則過濾掉不需要上傳的文件(如日志、臨時文件等)。例外規則
- 若?
.dockerignore
?文件中包含?.dockerignore
?或?Dockerfile
?本身,這兩個文件不會被過濾,仍會被上傳。鏡像標簽驗證
- 若指定了鏡像標簽(
-t
?參數),會先驗證倉庫名(repository)和標簽(tag)的格式是否合法。第二步:發送構建請求至 Docker 引擎
- 通過 HTTP 請求向 Docker server 發送構建指令,請求中包含上下文信息(打包后的文件)。
第三步:Docker 引擎執行鏡像構建
解壓上下文文件
- 在臨時目錄中解壓上下文文件,作為構建的基礎環境。
解析 Dockerfile 指令
- 讀取并解析?
Dockerfile
?中的每條指令,按指令類型(如?RUN
、COPY
、ADD
?等)分發到對應模塊處理。逐層構建鏡像層
- 對每條指令,創建臨時容器并執行指令,然后將容器狀態提交(commit)為一個新的鏡像層。
- 例如:
RUN apt-get update
?會生成一個包含更新操作的鏡像層。合并鏡像層
- 所有指令執行完畢后,合并所有鏡像層,最后一次 commit 生成的鏡像 ID 即為最終鏡像 ID。
二、構建緩存機制(提升效率的關鍵)
1. 緩存默認行為
- Docker 會自動緩存已構建的鏡像層,下次構建時若發現指令未變,直接復用緩存,無需重新執行。
- 可通過?
--no-cache=true
?參數禁用緩存(如?docker build --no-cache=true -t myimage .
)。2. 緩存匹配規則
- 基礎檢查:遍歷緩存中的鏡像層,檢查構建指令是否與當前指令完全一致(如?
RUN
?命令的參數)。- 特殊指令處理:
- 對于?
ADD
、COPY
?指令,除了指令本身,還會校驗文件的校驗和(checksum),若文件內容不同則緩存失效。- 忽略容器內文件檢查:
- 例如?
RUN apt-get update
?執行后,容器內文件更新,但緩存策略不會校驗這些文件,僅依賴指令本身和文件校驗和。三、查看鏡像構建歷史
- 命令:
docker history <鏡像ID或標簽>
- 作用:顯示鏡像各層的構建指令、大小、時間等信息,便于調試和優化構建流程。
4.?關鍵字
指令 | 說明 |
---|---|
FROM | 設置鏡像使用的基礎鏡像 |
MAINTAINER | 設置鏡像的作者 |
RUN | 編譯鏡像時運行的腳本 |
CMD | 設置容器的啟動命令 |
LABEL | 設置鏡像標簽 |
EXPOSE | 設置鏡像暴露的端口 |
ENV | 設置容器的環境變量 |
ADD | 編譯鏡像時復制上下文中的文件到鏡像中 |
COPY | 編譯鏡像時復制上下文中的文件到鏡像中 |
ENTRYPOINT | 設置容器的入口程序 |
VOLUME | 設置容器的掛載卷 |
USER | 設置運行 RUN、CMD、ENTRYPOINT 的用戶名 |
WORKDIR | 設置 RUN、CMD、ENTRYPOINT、COPY、ADD 指令的工作目錄 |
ARG | 設置編譯鏡像時加入的參數 |
ONBUILD | 設置鏡像的 ONBUILD 指令 |
STOPSIGNAL | 設置容器的退出信號量 |
5.?dockerfile 實踐(go)
一、基本語法實踐案例
案例目標:使用 Golang 構建一個可執行程序并打包為鏡像。
1. 案例準備
# 創建項目目錄 mkdir example1 && cd example1
2. Dockerfile 內容
FROM golang:1.18 # 指定基礎鏡像 ENV env1=env1value env2=env2value # 設置環境變量 MAINTAINER nick # 鏡像作者 LABEL hello=1.0.0 # 鏡像標簽 RUN git clone https://gitee.com/nickdemo/helloworld.git # 克隆代碼 WORKDIR helloworld # 設置工作目錄 RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct # 配置代理 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . # 編譯程序 EXPOSE 80 # 暴露端口 CMD ["./app", "--param1=p1", "--param2=p2"] # 容器啟動命令
3. 構建與運行
# 構建鏡像 docker build -t hello:1.0.0 -f Dockerfile . # 運行容器 docker run -p 80:80 -d --name hello hello:1.0.0
二、Docker Build 上下文實踐
案例目標:理解上下文(Context)對構建的影響。
1. 素材準備
mkdir example2 && cd example2 # 下載 Nginx 源碼和應用代碼 curl https://nginx.org/download/nginx-1.21.6.tar.gz > nginx-1.21.6.tar.gz git clone https://gitee.com/nickdemo/helloworld
2. 無上下文優化的 Dockerfile(低效)
FROM golang:1.18 ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 RUN git clone https://gitee.com/nickdemo/helloworld.git # 從網絡克隆,不使用本地上下文 WORKDIR helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
3. 優化后的上下文 Dockerfile(高效)
FROM golang:1.18 ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 COPY ./helloworld /go/src/helloworld # 從上下文復制本地代碼 WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
4. 實踐操作
- 忽略文件測試:在?
example2
?中創建?.dockerignore
,添加?helloworld
,觀察構建時是否跳過該目錄。- 上下文路徑測試:修改?
COPY
?路徑為上下文外的路徑(如?../other
),觀察構建錯誤。三、多階段構建實踐
案例目標:分離編譯環境與運行環境,減小鏡像體積。
1. 素材準備
mkdir example3 && cd example3 curl https://nginx.org/download/nginx-1.21.6.tar.gz > nginx-1.21.6.tar.gz git clone https://gitee.com/nickdemo/helloworld
2. 基礎多階段構建(無別名)
# 第一階段:編譯環境 FROM golang:1.18 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 第二階段:運行環境(使用 Alpine 輕量級系統) FROM alpine:latest ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 WORKDIR /app/ COPY --from=0 /go/src/helloworld/app ./ # 從第一階段復制可執行文件 EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
3. 帶別名的多階段構建(更清晰)
# 第一階段:命名為 stage0 FROM golang:1.18 as stage0 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 第二階段:運行環境 FROM alpine:latest ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 WORKDIR /app/ COPY --from=stage0 /go/src/helloworld/app ./ EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
4. 構建命令
docker build -t hello:1.0.0 -f Dockerfile .
四、ADD 與 COPY 指令對比實踐
案例目標:理解?
ADD
?與?COPY
?的功能差異。1. 基礎功能對比
FROM alpine:latest WORKDIR /soft/# COPY 僅復制文件(不支持解壓/URL) COPY nginx-1.21.6.tar.gz ./copy/ # 復制本地文件 COPY srcDir/ ./destDir/ # 復制目錄內容(不含目錄本身)# ADD 支持解壓和 URL ADD https://nginx.org/download/nginx-1.21.6.tar.gz ./add/ # 從 URL 下載(不解壓) ADD nginx-1.21.6.tar.gz ./add/ # 解壓本地壓縮包
2. 多階段構建中的應用
# 編譯階段 FROM golang:1.18 as stage0 COPY ./helloworld /go/src/helloworld/ # 推薦用 COPY 復制本地文件 WORKDIR /go/src/helloworld RUN go build -o app .# 運行階段 FROM alpine:latest WORKDIR /app/ COPY --from=stage0 /go/src/helloworld/app ./
五、CMD 與 ENTRYPOINT 指令實踐
案例目標:理解容器啟動命令的不同配置方式。
1. CMD 指令案例
FROM alpine:latest WORKDIR /app COPY app ./ EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"] # 默認啟動命令# 運行時覆蓋 CMD: # docker run -d hello:1.0.0 ./app --param1=1 --param2=2
2. ENTRYPOINT 指令案例
FROM alpine:latest WORKDIR /app COPY app ./ EXPOSE 80 ENTRYPOINT ["./app"] # 固定入口程序 CMD ["--param1=p1", "--param2=p2"] # 為 ENTRYPOINT 提供默認參數# 運行時追加參數: # docker run -d hello:1.0.0 --param1=1 --param2=2 # 替換入口程序: # docker run --entrypoint sh hello:1.0.0
六、Build Arg 實踐
案例目標:動態傳遞構建參數。
?FROM golang:1.18 as s0 WORKDIR /go/src/app RUN go env -w GOPROXY=$http_proxy # 使用預定義參數FROM alpine:latest as s1 ARG wd=./app # 定義默認參數 ARG label=myapp # 鏡像標簽參數 ARG tag=1.0.0 # 鏡像版本參數 RUN echo "工作目錄: $wd, 標簽: $label, 版本: $tag" WORKDIR $wd LABEL $label=$tag# 構建命令(傳遞參數): # docker build --build-arg http_proxy=https://proxy... --build-arg wd=/home/app --tag hello:1.0.0 .
七、Target 與 Cache-From 實踐
案例目標:加速多階段構建,復用緩存
FROM golang:1.18 as s0 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go build -o app .FROM alpine:latest as s1 WORKDIR /app COPY --from=s0 /go/src/helloworld/app ./# 分步構建: # 1. 構建第一階段并保存 docker build -t prehello:1.0.0 --target s0 . # 2. 從緩存構建第二階段 docker build -t hello:1.0.0 --cache-from prehello:1.0.0 --target s1 .
八、ONBUILD 指令實踐
案例目標:定義基礎鏡像的下游構建行為。
1. 定義基礎鏡像
FROM golang:1.18 ONBUILD ADD ./helloworld /go/src/helloworld/ # 下游構建時觸發 ONBUILD WORKDIR /go/src/helloworld ONBUILD RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct ONBUILD RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 構建基礎鏡像 docker build -t mygolang:1.0.0 .
2. 使用基礎鏡像構建應用
FROM mygolang:1.0.0 as s0 # 自動觸發 ONBUILD 指令 FROM alpine:latest as s1 WORKDIR /app COPY --from=s0 /go/src/helloworld/app ./ EXPOSE 80 ENTRYPOINT ["./app"]# 構建應用鏡像(無需重復編寫編譯步驟) docker build -t hello:1.0.0 .
6. dockerfile實踐(c語言)
一、項目準備
1. 目錄結構
創建項目目錄?
c-app-docker
,包含:
src/
:存放 C 源文件Dockerfile
:構建鏡像的核心文件2. 編寫 C 程序
在?
src/
?目錄下創建?main.c
,實現一個簡單的 HTTP 服務(使用?libmicrohttpd
?庫)// src/main.c #include <stdio.h> #include <microhttpd.h>#define PORT 8080static int handle_request(void *cls, struct MHD_Connection *connection,const char *url, const char *method,const char *version, const char *upload_data,size_t *upload_data_size, void **con_cls) {const char *response = "Hello, Docker C App!\n";struct MHD_Response *mhd_response;int ret;mhd_response = MHD_create_response_from_buffer(strlen(response), (void *)response, MHD_RESPMEM_PERSISTENT);ret = MHD_queue_response(connection, MHD_HTTP_OK, mhd_response);MHD_destroy_response(mhd_response);return ret; }int main() {struct MHD_Daemon *daemon;daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, PORT, NULL, NULL,&handle_request, NULL, MHD_OPTION_END);if (NULL == daemon) {fprintf(stderr, "Failed to start MHD daemon\n");return 1;}printf("Server running on port %d...\n", PORT);getchar(); // 阻塞進程,避免退出MHD_stop_daemon(daemon);return 0; }
二、Dockerfile 編寫(多階段構建優化)
使用?多階段構建?分離編譯環境與運行環境,最終鏡像僅保留運行所需的最小依賴,體積更小、更安全。
Dockerfile
?內容:# ================================================ # 階段1:編譯環境(使用 GCC 編譯 C 程序) # ================================================ FROM gcc:13 as builder# 設置工作目錄 WORKDIR /app# 安裝依賴庫(libmicrohttpd 用于 HTTP 服務) RUN apt-get update && \apt-get install -y libmicrohttpd-dev && \rm -rf /var/lib/apt/lists/*# 復制 C 源文件到容器 COPY src/main.c ./# 編譯 C 程序(生成可執行文件 app) RUN gcc -o app main.c -lmicrohttpd# ================================================ # 階段2:運行環境(僅保留運行所需的最小依賴) # ================================================ FROM alpine:3.19# 安裝運行時依賴(libmicrohttpd 庫) RUN apk add --no-cache libmicrohttpd# 設置工作目錄 WORKDIR /app# 從編譯階段復制可執行文件(僅保留最終程序) COPY --from=builder /app/app ./# 暴露服務端口(與 C 程序中定義的 PORT 一致) EXPOSE 8080# 啟動命令:運行編譯后的可執行文件 CMD ["./app"]
三、鏡像構建與運行
1. 構建鏡像
在項目根目錄(
c-app-docker
)下執行構建命令:docker build -t c-http-server:1.0 .
說明:
-t c-http-server:1.0
:指定鏡像名稱為?c-http-server
,標簽為?1.0
。- 多階段構建會自動跳過中間階段(僅保留最后一個?
FROM
?后的內容),最終鏡像體積遠小于編譯環境。2. 運行容器
docker run -p 8080:8080 --name c-server -d c-http-server:1.0
參數說明:
?
-p 8080:8080
:將容器的 8080 端口映射到宿主機的 8080 端口。--name c-server
:指定容器名稱為?c-server
。-d
:后臺運行容器。3. 驗證服務
通過瀏覽器或命令行訪問宿主機的 8080 端口:
curl http://localhost:8080
預期輸出:
Hello, Docker C App!
四、關鍵步驟說明
1. 多階段構建的優勢
- 體積更小:編譯階段使用?
gcc:13
(約 1.2GB),運行階段使用?alpine:3.19
(約 5MB),最終鏡像僅包含運行所需的?libmicrohttpd
?庫和可執行文件(總大小約 20MB)。- 更安全:運行環境不包含編譯器、調試工具等冗余組件,減少攻擊面。
2. 依賴管理
- 編譯階段:通過?
apt-get
?安裝?libmicrohttpd-dev
(包含頭文件和編譯庫)。- 運行階段:通過?
apk add
?安裝?libmicrohttpd
(僅運行時庫,無開發文件)。3. 緩存優化
COPY src/main.c ./
?放在?RUN gcc
?前,避免源文件修改時重復安裝依賴(Docker 按指令順序緩存,若?COPY
?后修改?main.c
,僅?RUN gcc
?會重新執行)。五、擴展實踐建議
1. 靜態編譯(可選)
若需完全獨立的可執行文件(不依賴宿主機庫),可在編譯階段添加?
-static
?參數# 階段1 編譯命令改為: RUN gcc -o app main.c -lmicrohttpd -static
注意:
libmicrohttpd
?可能不支持靜態編譯,需根據實際庫的特性調整。2. 健康檢查
在?
Dockerfile
?中添加健康檢查,確保服務正常運行:HEALTHCHECK --interval=5s --timeout=3s \CMD curl -f http://localhost:8080/ || exit 1
3. 環境變量配置
若需動態配置端口或參數,可使用?
ENV
?和?ARG
?指令:# 在階段2添加: ARG PORT=8080 ENV APP_PORT=$PORT EXPOSE $APP_PORT CMD ["./app", "--port", "$APP_PORT"]
構建時指定參數
docker build -t c-http-server:1.0 --build-arg PORT=8090 .
?0voice · GitHub?