Docker File
Docker 鏡像原理
思考:
- Docker 鏡像本質是什么?
- Docker 中一個 centos 鏡像為什么只有 200MB,而一個 centos 操作系統的 iso 文件要幾個 G ?
- Docker 中一個 tomcat 鏡像為什么有 500MB,而一個 tomcat 安裝包只有 70 多 MB ?
操作系統組成:
- 進程調度子系統
- 進程通信子系統
- 內存管理子系統
- 設備管理子系統
- 文件管理子系統
- 網絡通信子系統
- 作業控制子系統
Linux 文件系統由 bootfs 和 rootfs 兩部分組成:
- bootfs:包含 bootloader(引導加載程序)和 kernel(內核)。
- rootfs:root 文件系統,包含的就是典型 Linux 系統中的 /dev,/proc,/bin,/etc 等標准目錄和文件。
- 不同的 linux 發行版,其 bootfs 基本一樣,而 rootfs 則不同,如 ubuntu,centos 等。
Docker 鏡像:
- Docker 鏡像是由特殊的文件系統疊加而成。
- 最底端是 bootfs,其使用的是宿主機的 bootfs 。
- 第二層是 root 文件系統rootfs,稱為 base image 。
- 再往上可以疊加其他的鏡像文件。
- 統一文件系統(Union File System)技術能夠將不同的層整合成一個文件系統,為這些層提供了一個統一的視角,這樣就隱藏了多層的存在,在用戶的角度看來,只存在一個文件系統。
- 一個鏡像可以放在另一個鏡像的上面。位於下面的鏡像稱為父鏡像,最底部的鏡像成為基礎鏡像。
- 當從一個鏡像啟動容器時,Docker 會在最頂層加載一個讀寫文件系統作為容器。
總結:
- Docker 鏡像本質是什么?
- 是一個分層文件系統。
- Docker 中一個 centos 鏡像為什么只有 200MB,而一個 centos 操作系統的 iso 文件要幾個 G ?
- Centos 的 iso 鏡像文件包含 bootfs 和 rootfs,而 docker 的 centos 鏡像復用操作系統的 bootfs,只有 rootfs 和其他鏡像層。
- Docker 中一個 tomcat 鏡像為什么有 500MB,而一個 tomcat 安裝包只有 70 多 MB ?
- 由於 docker 中鏡像是分層的,tomcat 雖然只有 70 多 MB,但他需要依賴於父鏡像和基礎鏡像,所有整個對外暴露的 tomcat 鏡像大小 500 多 MB 。
Dockerfile
鏡像制作
Docker 鏡像如何制作?
- 方法一:容器轉為鏡像
docker commit 容器id 鏡像名稱:版本號
docker save -o 壓縮文件名稱 鏡像名稱:版本號
docker load –i 壓縮文件名稱
- 方法二:使用 Dockerfile
Dockerfile 概念
什么是 Dockerfile ?
- Dockerfile 是一個文本文件,包含了一條條的指令。
- 每一條指令構建一層,基於基礎鏡像,最終構建出一個新的鏡像。
- 對於開發人員:可以為開發團隊提供一個完全一致的開發環境。
- 對於測試人員:可以直接拿開發時所構建的鏡像或者通過 Dockerfile 文件構建一個新的鏡像開始工作。
- 對於運維人員:在部署時,可以實現應用的無縫移植。
- 官方 Dockerfile 文件參考:https://github.com/CentOS/CentOS-Dockerfiles
Dockerfile 主要組成部分:
- 基礎鏡像信息:如 FROM centos:6.8
- 制作鏡像時的操作命令:如 RUN yum insatll openssh-server -y
- 容器啟動時的執行命令:如 CMD ["/bin/bash"]
Dockerfile 常用命令
- FROM:指定基礎鏡像。
- MAINTAINER:指定維護者信息。
- RUN:制作鏡像過程中的操作命令。
- ADD:加載主機文件(會自動解壓)。
- WORKDIR:設置當前工作目錄。
- VOLUME:設置卷,掛載並映射主機目錄。
- EXPOSE:指定對外的端口。
- CMD:指定容器啟動后的要執行的命令。
- COPY:用於將本地文件或者目錄拷貝進容器鏡像。
- ENV :環境變量。
- ENTRYPOINT:容器啟動后執行的命令。
FROM 命令
使用 Dockerfile 做定制的鏡像,都是從基礎鏡像開始的。這個基礎鏡像可以是各種版本的操作系統、或帶着中間件的操作系統、或是自定義的操作系統鏡像。本地有就從本地直接用,本地沒有會去下載。
好習慣是將 Dockerfile 文件放在集中的位置,最好是共享存儲里面。然后以項目名稱創建目錄,以 tag 名創建子目錄。
[root@localhost ~]# mkdir dockerfile
[root@localhost ~]# cd dockerfile/
[root@localhost dockerfile]# mkdir nginx
[root@localhost dockerfile]# cd nginx
[root@localhost nginx]# mkdir v1from
[root@localhost nginx]# cd v1from/
[root@localhost v1from]# vi Dockerfile
注意創建 Dockerfile 名稱就只能叫做“Dockerfile”,不然的話需要用 -f 參數指定 Dockerfile,看個人習慣。如果使用清晰的目錄結構,建議使用 Dockerfile 命名。
這個 Dockerfile 只有一行 from 命令,即引用 nginx 這個基礎鏡像,剩余的動作完全沒有。這並沒有實際意義,除了演示不會有人這么做 Dockerfile 的。
# 構建鏡像
docker build -t nginx:v1from .
注意:
- 最后的“.”,代表從本目錄的 Dockerfile 執行,也可以使用絕對路徑。
- 打鏡像的時候最后鏡像名稱和t ag 都由自己規划,即“:”前后都是自定義。
- 如果名稱和 tag 和之前版本重復,相當於提交新版本並覆蓋原有鏡像。
使用新做的 nginx:v1from 鏡像啟動容器:
RUN 命令
用於執行后面跟着的命令行命令。有兩種格式:
RUN <命令行命令>
RUN ["可執行文件", "參數1", "參數2", ...]
仍然以 nginx 為基礎鏡像,這次增加一個 RUN 命令:
說明:可以到 docker.com 上查看 nginx 鏡像的說明文檔,找到這個鏡像中 nginx 默認 web 目錄的位置為:/usr/share/nginx/html/,並使用 echo 命令打印一句話並重定向到該目錄下的 index.html 文件中。
容器啟動后,再通過 nginx 看到這個靜態頁面查看命令執行結果。
可見默認文件 index.html 的內容已被修改。
注意事項:
- 命令行命令就是 shell 命令,常用於此的命令包括:yum install/apt-get、wget、tar 等,當然也可以用下一節學習的 COPY 命令,拷貝腳本到容器中,然后用 RUN 執行。
- RUN 命令僅在 docker build 過程中生效,鏡像完成后,docker run 后 RUN 腳本或命令則不再生效(可以用 yum 命令理解,已經在 docker build 的時候安裝相應的東西進入鏡像,啟動容器的時候已經用到 yum 安裝的內容,自然不用再安裝一遍)。
- RUN 是會觸發鏡像分層的,不合理的 RUN 會增加鏡像大小。看以下四個例子:
# docker build -t nginx:3runwithoutrm .
FROM centos
RUN yum install -y wget
RUN wget http://nginx.org/download/nginx-1.19.10.tar.gz
RUN tar -zxvf nginx-1.19.10.tar.gz
# docker build -t nginx:1runwithoutrm .
FROM centos
RUN yum install wget && \
wget http://nginx.org/download/nginx-1.19.10.tar.gz && \
tar -zxvf nginx-1.19.10.tar.gz
# docker build -t nginx:4runwithrm .
FROM centos
RUN yum install wget
RUN wget http://nginx.org/download/nginx-1.19.10.tar.gz
RUN tar -zxvf nginx-1.19.10.tar.gz
RUN rm -f nginx-1.19.10.tar.gz
# docker build -t nginx:1runwithrm .
FROM centos
RUN yum install wget && \
wget http://nginx.org/download/nginx-1.19.10.tar.gz && \
tar -zxvf nginx-1.19.10.tar.gz && \
rm -f nginx-1.19.10.tar.gz
結論:
- RUN 下載的文件,在同一個 RUN 中進行了刪除,才能減小鏡像大小(寫時復制和聯合掛載的特性)。
- RUN 的個數並不是越少越好,細分后的 RUN 更容易在構建時候被當做 cache 利用到,一個 RUN 里過多的 && 連接命令則很難被復用。
COPY 命令
COPY 命令用於將本地文件或者目錄拷貝進容器鏡像。
如下圖,構建 dockerfile,目錄 web,web 下有一個 a.html 文件,文件中一句話。
啟動容器並從瀏覽器中查看目錄下的文件:
注意事項:
- COPY 命令在拷貝目錄的時候,和 cp 的習慣不一致,會把目錄下面的內容拷走,而不拷目錄本身,COPY a /path/to/ 相當於 cp a/* /path/to/。
- COPY 命令在拷貝的時候,如果容器中最后一級目錄不存在,會新建該目錄並拷貝。綜合上邊一條,如果想要正確的把目錄 a 拷貝到 /path/to/ 下,正確的寫法是 COPY a /path/to/a(此時 /path/to 目錄存在,/path/to/a 不存在)。
CMD 命令
CMD 用於容器啟動時后執行命令,注意和 RUN 的區別(RUN 用於構建的時候執行命令)。
在沒有 ENTRYPOINT 的情況下,CMD 就是容器用來啟動前台用戶進程的命令。
格式:
CMD <shell 命令>
CMD ["可執行文件或命令", "參數1", "參數2", …]
這里推薦第二種寫法,不然的話第一種可執行文件只能用 sh 腳本。
Dockerfile 說明:
- yum 安裝的 nginx,默認頁面存放路徑依然是 /usr/share/nginx/html;
- 該例子首先拷貝一個靜態頁面 default.html 到默認路徑中;
- 然后新建一個靜態頁面路徑,將 modify.html 文件拷貝到新建的路徑中;
- COPY 一份修改后的配置文件到 /nginx 下;
- 最后用 CMD 啟動 nginx,注意 -g daemon off; 是容器啟動必須加的參數(別漏寫“;”),以避免 nginx 后台啟動,否則 nginx 后台啟動將導致容器直接退出。
1)docker run 時不加啟動參數
注意,此處 Dockerfile 中未加入 EXPOSE,需要指定后端端口,用小寫 -p 啟動:
docker run -d -p 80:80 nginx:v4cmd
2)docker run 時加啟動參數
docker run -d -p 81:80 nginx:v4cmd nginx -g "daemon off;" -c /nginx/nginx.conf
可以看到,若啟動時帶了運行參數,則會覆蓋掉 Dockerfile 中的 CMD 執行內容,以新的命令和參數啟動。
CMD 注意事項:
- CMD 如果有多個,只有最后一個生效。
- CMD 會被傳入的啟動參數覆蓋,這個特性在測試時很有用。
ENTRYPOINT 命令
和 CMD 是同類命令,可以單獨使用,也可以和 CMD 配合使用。
單獨使用 ENTYRPOINT 格式:
ENTYRPOINT ["命令或者可執行腳本", "參數一", "參數二", …]
單獨使用 ENTRYPOINT 時,和 CMD 不同,其參數不會被 docker run 時傳入的啟動參數所覆蓋,但是 docker run 傳入的啟動參數會作為 ENTRYPOINT 指定命令的參數傳入。
Dockerfile 示例內容:
FROM centos
RUN yum install -y nginx
COPY default.html /usr/share/nginx/html
RUN mkdir -p /nginx/html
COPY modify.html /nginx/html
COPY nginx.conf /nginx
ENTRYPOINT ["nginx","-g","daemon off;"]
1)docker run 時不加啟動參數
2)docker run 時加啟動參數
docker run -d -p 81:80 nginx:v5entrypoint -g "daemon off;" -c /nginx/nginx.conf
可以看到,modify.html 生效,但是這次傳入的參數並不是完整的 nginx -g "daemon off;" -c /nginx/nginx.conf 命令,而是僅僅只有參數,並且參數覆蓋了鏡像中 ENTRYPOINT 的參數。
ENTRYPOINT 和 CMD 混合使用
若兩個命令同時存在,CMD 本身不再運行命令,其內容變成 ENTRYPOINT 的參數,同時 CMD 的內容依然可以被 docker run 時候的啟動參數覆蓋,進而傳遞給 ENTRYPOINT。
換句話說,兩者組合,可以完成定參和變參的混合定義。
Dockerfile 示例:
FROM centos
RUN yum install -y nginx
COPY default.html /usr/share/nginx/html
RUN mkdir -p /nginx/html
COPY modify.html /nginx/html
COPY nginx.conf /nginx
ENTRYPOINT ["nginx","-g","daemon off;","-c"]
CMD ["/etc/nginx/nginx.conf"]
1)docker run 時不帶參數
2)docker run 時帶參數
實際使用中,ENTRYPOINT 和 CMD 組合的方式會更常見一些。
VOLUME 命令
實現效果和 docker run -v 掛載主機目錄一樣。
寫在 Dockerfile 中的好處是,萬一在 docker run 時候忘記了 -v 參數,數據也不會丟失。
另外 docker run -v 指令會覆蓋 Dockerfile 的 VOLUME 指令。
案例一:自定義 centos7 鏡像
需求:自定義 centos7 鏡像
- 默認登錄路徑為 /usr
- 可以使用 vim
實現步驟:
-
文件內容:
- 定義父鏡像:FROM centos:7
- 定義作者信息:MAINTAINER juno xxxx@xxxx.com
- 執行安裝 vim 命令: RUN yum install -y vim
- 定義默認的工作目錄:WORKDIR /usr
- 定義容器啟動執行的命令:CMD /bin/bash
-
通過 dockerfile 構建鏡像:
docker bulid –f dockerfile文件路徑 –t 鏡像名稱:版本
案例二:發布 SpringBoot 項目
實現步驟:
-
文件內容:
- 定義父鏡像:FROM java:8
- 定義作者信息:MAINTAINER juno xxxx@xxxx.com
- 將jar包添加到容器:ADD springboot.jar app.jar
- 定義容器啟動執行的命令:CMD java–jar app.jar
-
通過 dockerfile 構建鏡像:
docker bulid –f dockerfile文件路徑 –t 鏡像名稱:版本