一、Docker 簡介
1、什么是 Docker
Docker 是使用最廣泛的開源容器引擎,它徹底釋放了計算虛擬化的威力,極大提高了應用的運行效率,降低了雲計算資源供應的成本! 使用 Docker,可以讓應用的部署、測試和分發都變得前所未有的高效和輕松!
Docker 使用 Google 公司推出的 Go 語言 進行開發實現,基於 Linux 內核的 cgroup,namespace,以及 AUFS 類的 Union FS 等技術,對進程進行封裝隔離,屬於操作系統層面的虛擬化技術。由於隔離的進程獨立於宿主和其它的隔離的進程,因此也稱其為容器。
Docker 在容器的基礎上,進行了進一步的封裝,從文件系統、網絡互聯到進程隔離等等,極大的簡化了容器的創建和維護。使得 Docker 技術比虛擬機技術更為輕便、快捷。
2、為什么要用 Docker
① 更高效的利用系統資源:由於容器不需要進行硬件虛擬以及運行完整操作系統等額外開銷,Docker 對系統資源的利用率更高。
② 更快速的啟動時間:Docker 容器應用,由於直接運行於宿主內核,無需啟動完整的操作系統,因此可以做到秒級、甚至毫秒級的啟動時間。
③ 一致的運行環境:Docker 的鏡像提供了除內核外完整的運行時環境,確保了應用運行環境一致性。
④ 持續交付和部署:使用 Docker 可以通過定制應用鏡像來實現持續集成、持續交付、部署。一次創建或配置,可以在任意地方正常運行。
⑤ 更輕松的遷移:Docker 確保了執行環境的一致性,使得應用的遷移更加容易。Docker 可以在很多平台上運行,無論是物理機、虛擬機、公有雲、私有雲,甚至是筆記本,其運行結果是一致的。
3、Docker 基本組成
① 鏡像(Images)
Docker 鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時准備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
Docker 設計時,充分利用 Union FS 的技術,將其設計為分層存儲的架構,Docker 鏡像由多層文件系統聯合組成。鏡像構建時,會一層層構建,前一層是后一層的基礎。每一層構建完就不會再發生改變,后一層上的任何改變只發生在自己這一層。
② 容器(Container)
鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的 類 和 實例 一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等。
容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行於屬於自己的獨立的 命名空間。因此容器可以擁有自己的 root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶 ID 空間。容器內的進程是運行在一個隔離的環境里,使用起來,就好像是在一個獨立於宿主的系統下操作一樣。
每一個容器運行時,是以鏡像為基礎層,在其上創建一個當前容器的存儲層,我們可以稱這個為容器運行時讀寫而准備的存儲層為容器存儲層。容器存儲層的生存周期和容器一樣,容器消亡時,容器存儲層也隨之消亡。因此,任何保存於容器存儲層的信息都會隨容器刪除而丟失。
按照 Docker 最佳實踐的要求,容器不應該向其存儲層內寫入任何數據,容器存儲層要保持無狀態化。所有的文件寫入操作,都應該使用 數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。
數據卷的生存周期獨立於容器,容器消亡,數據卷不會消亡。因此,使用數據卷后,容器可以隨意刪除、重新 run ,數據卻不會丟失。
③ 鏡像倉庫(Registry)
鏡像倉庫是一個集中的存儲、分發鏡像的服務。一個 Docker Registry 中可以包含多個倉庫(Repository);每個倉庫可以包含多個標簽(Tag);每個標簽對應一個鏡像。
通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標簽就常用於對應該軟件的各個版本。我們可以通過 <倉庫名>:<標簽> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標簽,將以 latest 作為默認標簽。
最常使用的 Registry 公開服務是官方的 Docker Hub,這也是默認的 Registry,並擁有大量的高質量的官方鏡像。用戶還可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 鏡像,可以直接使用做為私有 Registry 服務。
二、Docker 安裝
Docker 版本包含 社區版和企業版,我們日常使用社區版足夠。Docker 社區版各個環境的安裝參考官方文檔:https://docs.docker.com/install/。后面的學習在 Linux CentOS7 上測試。
1、CentOS7 安裝步驟
CentOS 安裝參考官方文檔:https://docs.docker.com/install/linux/docker-ce/centos/
① 卸載舊版本
# yum remove docker docker-common docker-selinux
② 安裝依賴包
# yum install -y yum-utils device-mapper-persistent-data lvm2
③ 安裝 Docker 軟件包源
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
④ 安裝 Docker CE
# yum install docker-ce
⑤ 啟動 Docker 服務
# systemctl start docker
⑥ 設置開機啟動
# systemctl enable docker
⑦ 驗證安裝是否成功
# docker -v
# docker info
2、Docker 命令
通過 --help 參數可以看到 docker 提供了哪些命令,可以看到 docker 的用法是 docker [選項] 命令 。
命令有兩種形式,Management Commands 是子命令形式,每個命令下還有子命令;Commands 是直接命令,相當於子命令的簡化形式。
繼續查看 Management Commands 有哪些子命令,例如查看 image 的子命令。docker image ls 等同於 docker images,docker image pull 等同於 docker pull。
三、鏡像管理
1、鏡像簡介
鏡像包含了一個軟件的運行環境,是一個不包含Linux內核而又精簡的Linux操作系統,一個鏡像可以創建N個容器。
鏡像是一個分層存儲的架構,由多層文件系統聯合組成。鏡像構建時,會一層層構建,前一層是后一層的基礎。
從下載過程中可以看到,鏡像是由多層存儲所構成。下載也是一層層的去下載,並非單一文件。下載過程中給出了每一層的 ID 的前 12 位。並且下載結束后,給出該鏡像完整的 sha256 的摘要,以確保下載一致性。
通過 docker history <ID/NAME> 查看鏡像中各層內容及大小,每層會對應着 Dockerfile 中的一條指令。
由於 Docker 鏡像是多層存儲結構,並且可以繼承、復用,因此不同鏡像可能會因為使用相同的基礎鏡像,從而擁有共同的層。由於 Docker 使用 Union FS,相同的層只需要保存一份即可,因此實際鏡像硬盤占用空間很可能要比這個列表鏡像大小的總和要小的多。
2、鏡像管理
Docker 運行容器前需要本地存在對應的鏡像,如果鏡像不存在本地,Docker 會從鏡像倉庫下載,默認是 Docker Hub 公共注冊服務器中的倉庫。
Docker Hub 是由 Docker 公司負責維護的公共注冊中心,包含大量的優質容器鏡像,Docker 工具默認從這個公共鏡像庫下載鏡像。下載的鏡像如何使用可以參考官方文檔。地址:https://hub.docker.com/explore
如果從 Docker Hub 下載鏡像非常緩慢,可以先配置鏡像加速器,參考:https://www.daocloud.io/mirror
Linux下通過以下命令配置鏡像站:
# curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io # systemctl restart docker
① 搜索鏡像 :docker search <NAME> [選項]
OFFICIAL:是否官方版本;
② 下載鏡像 :docker pull [選項] [Docker Registry地址]<倉庫名>:<標簽>
Docker Registry地址:地址的格式一般是 <域名/IP>[:端口號] 。默認地址是Docker Hub。
倉庫名:倉庫名是兩段式名稱,既 <用戶名>/<軟件名> 。對於 Docker Hub,如果不給出用戶名,則默認為 library ,也就是官方鏡像。
③ 列出本地鏡像 :docker images [選項]
TAG:標簽版本;IMAGE ID:鏡像ID;SIZE:鏡像大小;
查看虛懸鏡像(鏡像既沒有倉庫名,也沒有標簽,顯示為 <none>):docker images -f dangling=true
默認的 docker images 列表中只會顯示頂層鏡像,如果希望顯示包括中間層鏡像在內的所有鏡像:docker images -a
只列出鏡像ID:docker images -q
列出部分鏡像:docker images redis
以特定格式顯示:docker images --format "{{.ID}}: {{.Repository}}"
④ 給鏡像打 Tag :docker tag <IMAGE ID> [<用戶名>/]<鏡像名>:<標簽>
鏡像的唯一標識是其 ID 和摘要,而一個鏡像可以有多個標簽。
⑤ 刪除本地鏡像 :docker rmi [選項] <鏡像1> [<鏡像2> ...]
<鏡像> 可以是鏡像短 ID、鏡像長 ID、鏡像名或者鏡像摘要。
刪除鏡像的時候,實際上是在要求刪除某個標簽的鏡像。所以首先需要做的是將滿足我們要求的所有鏡像標簽都取消,這就是我們看到的Untagged 的信息。
鏡像是多層存儲結構,因此在刪除的時候也是從上層向基礎層方向依次進行判斷刪除。鏡像的多層結構讓鏡像復用變得非常容易,因此很有可能某個其它鏡像正依賴於當前鏡像的某一層。這種情況,不會觸發刪除該層的行為。直到沒有任何層依賴當前層時,才會真實的刪除當前層。
除了鏡像依賴以外,還需要注意的是容器對鏡像的依賴。如果有用這個鏡像啟動的容器存在(即使容器沒有運行),那么同樣不可以刪除這個鏡像。如果這些容器是不需要的,應該先將它們刪除,然后再來刪除鏡像。
⑥ 批量刪除鏡像
刪除所有虛懸鏡像:docker rmi $(docker images -q -f dangling=true)
刪除所有倉庫名為 redis 的鏡像:docker rmi $(docker images -q redis)
刪除所有在 mysql:8.0 之前的鏡像:docker rmi $(docker images -q -f before=mysql:8.0)
⑦ 導出鏡像:docker save -o <鏡像文件> <鏡像>
可以將一個鏡像完整的導出,就可以傳輸到其它地方去使用。
⑧ 導入鏡像:docker load -i <鏡像文件>
四、容器管理
1、創建容器
啟動容器有兩種方式,一種是基於鏡像新建一個容器並啟動,另外一個是將在終止狀態(stopped)的容器重新啟動。Docker 容器非常輕量級,很多時候可以隨時刪除和新創建容器。
創建容器的主要命令為 docker run (或 docker container run),常用參數如下:
當利用 docker run 來創建容器時,Docker 在后台運行的標准操作包括:
- 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載
- 利用鏡像創建並啟動一個容器
- 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層
- 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去
- 從地址池配置一個 ip 地址給容器
- 執行用戶指定的應用程序
- 執行完畢后容器被終止
① 創建並進入容器:docker run -ti <IMAGE ID OR NAME> /bin/bash
創建容器,通過 -ti 參數分配一個 bash 終端,並進入容器內執行命令。退出容器時容器就被終止了。
② 容器后台運行:docker run -d <IMAGE ID OR NAME>
容器是否會長久運行,是和 docker run 指定的命令有關,即容器內是否有前台進程在運行,和 -d 參數無關。一個容器必須有一個進程來守護容器才會在后台長久運行。
例如通過 -d 參數后台運行 centos,容器創建完成后將退出。而通過 -ti 分配一個偽終端后,容器內將有一個 bash 終端守護容器,容器不會退出。
③ 進入容器:docker exec -ti <CONTAINER ID> bash
④ 指定端口:docker run -d -p <宿主機端口>:<容器內端口> <IMAGE ID>
通過 -p 參數指定宿主機與容器內的端口映射,如果不指定宿主機端口,將會隨機映射一個宿主機端口。映射好端口后,宿主機外部就可以通過該端口訪問容器了。
⑤ 宿主機重啟時自動重啟容器:docker run -d --restart always <IMAGE ID>
宿主機重啟時,docker 容器默認不會自動啟動,可以通過 --restart=always 設置自動重啟。
2、容器資源限制
容器資源限制主要是對內存的限制和使用CPU數量的限制,常用選項如下:
① 使用例子
1) 限制容器最多使用500M內存和100M的Swap,並禁用OOM Killer。注意 --memory-swap 的值為 --memory 的值加上 Swap 的值。
docker run -d --name nginx_1 --memory="500m" --memory-swap="600m" --oom-kill-disable nginx
如果 memory 小於等於 memory-swap,表示不使用Swap;如果 memory-swap="-1" 表示可以無限使用Swap;如果不設置 memory-swap,其值默認為 memory 的2倍。
2) 允許容器最多使用一個半的CPU:docker run -d --name nginx_2 --cpus="1.5" nginx
3) 允許容器最多使用 50% 的CPU:docker run -d --name nginx_3 --cpus=".5" nginx
② 查看容器資源使用統計:docker stats <CONTAINER ID>
通過 docker stats 可以實時查看容器資源使用情況。如果不對容器內存或CPU加以限制,將默認使用物理機所有內存和CPU。通常情況下,為了安全一定要限制容器內存和CPU,否則容器內程序遭到攻擊,可能無限占用物理機的內存和CPU。
3、容器常用命令
① 停止容器:docker stop <CONTAINER ID>
② 重啟容器:docker start <CONTAINER ID>
③ 刪除容器
刪除已停止的容器:docker rm <CONTAINER ID>
強制刪除運行中的容器:docker rm -f <CONTAINER ID>
批量刪除Exited(0)狀態的容器:docker rm $(docker ps -aq -f exited=0)
批量刪除所有容器:docker rm -f $(docker ps -aq)
④ 查看容器日志:docker logs <CONTAINER ID OR NAMES>
⑤ 查看容器詳細信息:docker inspect <CONTAINER ID>
⑥ 列出或指定容器端口映射:docker port <CONTAINER ID>
⑦ 顯示一個容器運行的進程:docker top <CONTAINER ID>
⑧ 拷貝文件/文件夾到容器:docker cp <dir> <CONTAINER ID>:<dir>
⑨ 查看容器與鏡像的差異:docker diff <CONTAINER ID>
五、管理應用程序數據
容器刪除后,里面的數據將一同被刪除,因此需要將容器內經常變動的數據存儲在容器之外,這樣刪除容器之后數據依然存在。
Docker 提供三種方式將數據從宿主機掛載到容器中:
- volumes:由 Docker 管理的數據卷,是宿主機文件系統的一部分(/var/lib/docker/volumes)。是保存數據的最佳方式。
- bind mounts:將宿主機上的文件或目錄掛載到容器中,通常在容器需要使用宿主機上的目錄或文件時使用,比如搜集宿主機的信息、掛載宿主機上的 maven 倉庫等。
- tmpfs:掛載存儲在主機系統的內存中,而不會寫入主機的文件系統。如果不希望將數據持久存儲在任何位置,可以使用 tmpfs,同時避免寫入容器可寫層提高性能。這種方式使用比較少。
1、Volume
① 管理卷
創建數據卷:docker volume create <volume_name>
查看數據卷列表:docker volume ls
查看數據卷詳細信息:docker volume inspect <volume_name>
創建的數據卷默認在宿主機的 /var/lib/docker/volumes/ 下
② 創建容器時指定數據卷
1) 通過 --mount 方式:--mount src=<數據卷名稱>,dst=<容器內的數據目錄>,注意逗號之間不能有空格
docker run -d --name nginx_1 --mount src=nginx_html,dst=/usr/share/nginx/html nginx
2) 通過 -v 方式:-v <數據卷名稱>:<容器內的數據目錄>
docker run -d --name nginx_2 -v nginx_html:/usr/share/nginx/html nginx
以上兩種方式,--mount 更加通用直觀,-v 是老語法的方式。如果對應的數據卷不存在,將自動創建數據卷。如果掛載目標在容器中非空,則該目錄現有內容會自動同步到數據卷中。
③ 刪除數據卷:docker volume rm <volume_name>
數據卷是獨立於容器的生命周期的,刪除容器是不會刪除數據卷的,除非刪除數據卷,刪除數據卷之后數據也就丟失了。
如果數據卷正在被某個容器使用,將不能被刪除,需要先刪除使用此數據卷的所有容器之后才能刪除數據卷。
④ Volume 特點及使用場景
- 多個容器可以同時掛載相同的卷,可用於多個運行容器之間共享數據。
- 當容器停止或被刪除后,數據卷依然存在。當明確刪除卷時,卷才會被刪除。
- 可以將容器的數據存儲在遠程主機或其它存儲上。
- 將數據從一台 docker 主機遷移到另一台主機時,先停止容器,然后備份卷的目錄 /var/lib/docker/volumes
2、Bind Mounts
① 創建容器時綁定數據卷
1) 通過 --mount 方式:--mount type=bind,src=<宿主機目錄>,dst=<容器內的數據目錄>,注意逗號之間不能有空格
docker run -d --name nginx_1 --mount type=bind,src=/data/nginx/html,dst=/usr/share/nginx/html nginx
2) 通過 -v 方式:-v <宿主機目錄>:<容器內的數據目錄>
docker run -d --name nginx_2 -v /data/nginx/html:/usr/share/nginx/html nginx
以上兩種方式,如果源文件/目錄不存在,不會自動創建容器,會拋出錯誤。與 volume 方式相比,如果掛載目標在容器中非空,則該目錄現有內容將被隱藏,可以理解成使用宿主機的目錄覆蓋了容器中的目錄,新增的數據會同步。
② Bind Mounts 特點及使用場景
- 從主機共享配置文件到容器。默認情況下,掛載主機 /etc/resolv.conf 到每個容器,提供 DNS 解析。
- 在 docker 主機上的開發環境和容器之間共享源代碼。例如,可以將 maven target 目錄掛載到容器中,每次在 docker 主機上構建maven項目時,容器都可以訪問構建好的項目包。
- 當 docker 主機的文件或目
- 錄結構保證與容器所需的綁定掛載一致時,例如容器中需要統計主機的一些信息,可以直接將主機的某些目錄直接掛載給容器使用。
六、容器網絡
1、Docker 網絡模式
① bridge:--net=bridge
默認網絡,docker 啟動后會創建一個 docker0 網橋,默認創建的容器會添加到這個網橋中。
創建容器時不指定網絡模式,默認使用 bridge 方式,通過容器網絡配置可以看到會默認分配一個 docker0 的內網IP。
② host:--net=host
容器不會獲得一個獨立的 network namespace,而是與主機共用一個。這就意味着容器不會有自己的網卡信息,而是使用宿主機的。容器除了網絡,其它都是隔離的。
可以看到 host 網絡模式下,容器網絡配置與宿主機是一樣的。那么容器內應用的端口將占用宿主機的端口。
③ none:--net=none
獲取獨立的 network namespace,但不為容器進行任何網絡配置,需要我們手動配置。應用場景比較少。
④ container:--net=container:<CONTAINER NAME/ID>
與指定的容器使用同一個 network namespace,具有同樣的網絡配置信息,兩個容器除了網絡,其它都是隔離的。
1) 創建容器,並映射 9090 端口到容器的 80 端口,進入容器內,通過 netstat -antp 可以看到沒有端口連接信息
2) 創建 nginx 容器,並使用 net_container 容器的網絡。可以看到 net_container 容器內已經在監聽 nginx 的80端口了,而且通過 映射的 9090 端口可以訪問到 nginx 服務。
⑤ 自定義網絡
與默認的bridge原理一樣,但自定義網絡具備內部DNS發現,可以通過容器名或者主機名進行容器之間網絡通信
首先創建一個自定義網絡,創建兩個容器並加入到自定義網絡,在容器中就可以互相連通。
2、容器網絡訪問原理
當 Docker 啟動時,會自動在主機上創建一個 docker0 虛擬網橋(其上有一個 docker0 內部接口),實際上是Linux 的一個 bridge,可以理解為一個軟件交換機。它在內核層連通了其他的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網絡。
每次創建一個新容器的時候,Docker 從可用的地址段中選擇一個空閑的 IP 地址分配給容器的 eth0 端口。使用本地主機上 docker0 接口的 IP 作為所有容器的默認網關。
當創建一個 Docker 容器的時候,同時會創建一對 veth pair 接口(當數據包發送到一個接口時,另外一個接口也可以收到相同的數據包)。這對接口一端在容器內,即 eth0 ;另一端在本地並被掛載到 docker0 網橋,名稱以 veth 開頭。通過這種方式,主機可以跟容器通信,容器之間也可以相互通信。Docker 就創建了在主機和所有容器之間的一個虛擬共享網絡。
七、制作鏡像
1、Dockerfile
鏡像是多層存儲,每一層是在前一層的基礎上進行的修改;Dockerfile 是一個文本文件,其內包含了一條條的指令(Instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。
我們所使用的鏡像基本都是來自於 Docker Hub 的鏡像,直接使用這些鏡像是可以滿足一定的需求,而當這些鏡像無法直接滿足需求時,我們就需要定制這些鏡像。
編寫 Dockerfile 時,主要會用到如下一些指令:
Dockerfile 中每一個指令都會建立一層,在其上執行后面的命令,執行結束后, commit 這一層的修改,構成新的鏡像。對於有些指令,盡量將多個指令合並成一個指令,否則容易產生非常臃腫、非常多層的鏡像,不僅僅增加了構建部署的時間,也很容易出錯。而且Union FS 是有最大層數限制的,比如 AUFS不得超過 127 層。因此鏡像構建時,一定要確保每一層只添加真正需要添加的東西,任何無關的東西都應該清理掉。
① FROM
一個 Dockerfile 中 FROM 是必備的指令,並且必須是第一條指令。
格式:FROM <鏡像名稱>[:<TAG>]
Docker 還存在一個特殊的鏡像,名為 scratch 。這個鏡像並不實際存在,它表示一個空白的鏡像。如果以 scratch 為基礎鏡像的話,意味着不以任何鏡像為基礎,接下來所寫的指令將作為鏡像第一層開始存在。
② RUN
RUN 指令是用來執行命令行命令的。由於命令行的強大能力, RUN 指令在定制鏡像時是最常用的指令之一。
多個 RUN 指令盡量合並成一個,使用 && 將各個所需命令串聯起來。Dockerfile 支持 Shell 類的行尾添加 \ 的命令換行方式,以及行首 # 進行注釋的格式。
格式:
- shell 格式: RUN <命令> ,就像直接在命令行中輸入的命令一樣。
- exec 格式: RUN ["可執行文件", "參數1", "參數2"] ,這更像是函數調用中的格式。
③ COPY
COPY 指令將從構建上下文目錄中 <源路徑> 的文件/目錄復制到新的一層的鏡像內的 <目標路徑> 位置。<源路徑> 可以是多個,甚至可以是通配符。<目標路徑> 可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑。
使用 COPY 指令,源文件的各種元數據都會保留。比如讀、寫、執行權限、文件變更時間等。
格式:
- COPY <源路徑>,... <目標路徑>
- COPY ["<源路徑1>",... "<目標路徑>"]
④ ENV
設置環境變量,無論是后面的其它指令,如 RUN ,還是運行時的應用,都可以直接使用這里定義的環境變量。
格式:
- ENV <key> <value>
- ENV <key1>=<value1> <key2>=<value2>...
⑤ USER
USER 用於切換到指定用戶,這個用戶必須是事先建立好的,否則無法切換。
格式: USER <用戶名>
⑥ EXPOSE
EXPOSE 指令是聲明運行時容器提供服務端口,這只是一個聲明,在運行時並不會因為這個聲明應用就會開啟這個端口的服務。
格式:EXPOSE <端口1> [<端口2>...]
⑦ HEALTHCHECK
HEALTHCHECK 指令是告訴 Docker 應該如何判斷容器的狀態是否正常。通過該指令指定一行命令,用這行命令來判斷容器主進程的服務狀態是否還正常,從而比較真實的反應容器實際狀態。
當在一個鏡像指定了 HEALTHCHECK 指令后,用其啟動容器,初始狀態會為 starting ,在 HEALTHCHECK 指令檢查成功后變為 healthy ,如果連續一定次數失敗,則會變為 unhealthy
格式:
- HEALTHCHECK [選項] CMD <命令> :設置檢查容器健康狀況的命令,CMD 命令的返回值決定了該次健康檢查的成功與否: 0 - 成功; 1 - 失敗;。
- HEALTHCHECK NONE :如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉其健康檢查指令
HEALTHCHECK 支持下列選項:
- --interval=<間隔> :兩次健康檢查的間隔,默認為 30 秒;
- --timeout=<時長> :健康檢查命令運行超時時間,如果超過這個時間,本次健康檢查就被視為失敗,默認 30 秒;
- --retries=<次數> :當連續失敗指定次數后,則將容器狀態視為unhealthy ,默認 3 次。
⑧ WORKDIR
使用 WORKDIR 指令可以來指定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,該目錄需要已經存在, WORKDIR 並不會幫你建立目錄。
在Dockerfile 中,兩行 RUN 命令的執行環境是不同的,是兩個完全不同的容器。每一個 RUN 都是啟動一個容器、執行命令、然后提交存儲層文件變更。因此如果需要改變以后各層的工作目錄的位置,那么應該使用 WORKDIR 指令。
格式:WORKDIR <工作目錄路徑>
⑨ VOLUME
容器運行時應該盡量保持容器存儲層不發生寫操作,對於數據庫類需要保存動態數據的應用,其數據庫文件應該保存於卷(volume)中。為了防止運行時用戶忘記將動態文件所保存目錄掛載為卷,在 Dockerfile 中,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。
格式:
- VOLUME ["<路徑1>", "<路徑2>"...]
- VOLUME <路徑>
這里的路徑會在運行時自動掛載為匿名卷,任何向此路徑中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。
⑩ CMD
CMD 指令用於指定默認的容器主進程的啟動命令。
格式:
- shell 格式: CMD <命令>
- exec 格式: CMD ["可執行文件", "參數1", "參數2"...]
在運行時可以指定新的命令來替代鏡像設置中的這個默認命令,跟在鏡像名后面的是 command ,運行時會替換 CMD 的默認值;例如:docker run -ti nginx /bin/bash,"/bin/bash" 就是替換命令。
在指令格式上,一般推薦使用 exec 格式,這類格式在解析時會被解析為 JSON數組,一定要使用雙引號,而不要使用單引號。如果使用 shell 格式的話,實際的命令會被包裝為 sh -c 的參數的形式執行,如:CMD echo $HOME 會被包裝為 CMD [ "sh", "-c", "echo $HOME" ],實際就是一個 shell 進程。
注意:Docker 不是虛擬機,容器中的應用都應該以前台執行。對於容器而言,其啟動程序就是容器應用進程,容器就是為了主進程而存在的,主進程退出,容器就失去了存在的意義,從而退出。啟動程序時,應要求其以前台形式運行。
⑪ ENTRYPOINT
ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動程序及參數。 ENTRYPOINT 在運行時也可以替代,不過比 CMD 要略顯繁瑣,需要通過 docker run 的參數 --entrypoint 來指定。
格式:
- shell 格式: ENTRYPOINT <命令>
- exec 格式: ENTRYPOINT ["可執行文件", "參數1", "參數2"...]
當指定了 ENTRYPOINT 后, CMD 的含義就發生了改變,不再是直接的運行其命令,而是將 CMD 的內容作為參數傳給 ENTRYPOINT 指令。ENTRYPOINT 中的參數始終會被使用,而 CMD 的額外參數可以在容器啟動時動態替換掉。
例如: ENTRYPOINT ["/bin/echo", "hello"] 容器通過 docker run -ti <image> 啟動時,輸出為:hello 容器通過 docker run -ti <image> docker 啟動時,輸出為:hello docker 將Dockerfile修改為: ENTRYPOINT ["/bin/echo", "hello"] CMD ["world"] 容器通過 docker run -ti <image> 啟動時,輸出為:hello world 容器通過 docker run -ti <image> docker 啟動時,輸出為:hello docker
2、構建基礎鏡像
以構建 nginx 基礎鏡像為例看如何使用 Dockerfile 構建鏡像。
① 編寫 Dockerfile
# 基於 centos:8 構建基礎鏡像 FROM centos:8 # 作者 MAINTAINER jiangzhou.bo@vip.163.com # 安裝編譯依賴的 gcc 等環境,注意最后清除安裝緩存以減小鏡像體積 RUN yum install -y gcc gcc-c++ make \ openssl-devel pcre-devel gd-devel \ iproute net-tools telnet wget curl \ && yum clean all \ && rm -rf /var/cache/yum/* # 編譯安裝 nginx RUN wget http://nginx.org/download/nginx-1.17.4.tar.gz \ && tar zxf nginx-1.17.4.tar.gz \ && cd nginx-1.17.4 \ && ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module \ && make && make install \ && rm -rf /usr/local/nginx/html/* \ && echo "hello nginx !" >> /usr/local/nginx/html/index.html \ && cd / && rm -rf nginx-1.17.4* # 加入 nginx 環境變量 ENV PATH $PATH:/usr/local/nginx/sbin # 拷貝上下文目錄下的配置文件 COPY ./nginx.conf /usr/local/nginx/conf/nginx.conf # 設置工作目錄 WORKDIR /usr/local/nginx # 暴露端口 EXPOSE 80 # daemon off: 要求 nginx 以前台進行運行 CMD ["nginx", "-g", "daemon off;"]
② 構建鏡像
命令:docker build -t <鏡像名稱>:<TAG> [-f <Dockerfile 路徑>] <上下文目錄>
- 上下文目錄:鏡像構建的上下文。Docker 是 C/S 結構,docker 命令是客戶端工具,一切命令都是使用的遠程調用形式在服務端(Docker 引擎)完成。當構建的時候,用戶指定構建鏡像上下文的路徑,docker build 命令得知這個路徑后,會將路徑下的所有內容打包,然后上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包后,展開就會獲得構建鏡像所需的一切文件。這樣在 COPY 文件的時候,實際上是復制上下文路徑下的文件。一般來說,應該將 Dockerfile 以及所需文件置於一個空目錄下,並指定這個空目錄為上下文目錄。
- Dockerfile 路徑:可選,缺省時會尋找當前目錄下的 Dockerfile 文件,如果是其它名稱的 Dockerfile,可以使用 -f 參數指定文件路徑。
在 nginx 目錄下包含了構建需要的 Dockerfile 文件及配置文件,docker build 執行時,可以清晰地看到鏡像的構建過程。構建成功后,就可以在本地看到已經構建好的鏡像。
基於構建的鏡像啟用容器並測試:
③ 構建應用鏡像
構件好基礎鏡像之后,以這個基礎鏡像來構建應用鏡像。
首先編寫 Dockerfile:將項目下的 html 目錄拷貝到 nginx 目錄下
FROM bojiangzhou/nginx:v1
COPY ./html /usr/local/nginx/html
項目鏡像構建成功后,運行鏡像,可以看到內容已經被替換了。我們構建好的項目鏡像就可以傳給其他用戶去使用了。
八、鏡像倉庫
1、Habor
鏡像倉庫是集中存放鏡像的地方。目前 Docker 官方維護了一個公共倉庫 Docker Hub,大部分需求,都可以通過在 Docker Hub 中直接下載鏡像來實現。
有時候使用 Docker Hub 這樣的公共倉庫可能不方便,用戶可以創建一個本地倉庫供私人使用。docker-registry 是官方提供的工具,可以用於構建私有的鏡像倉庫。
Habor 是由 VMWare 公司開源的容器鏡像倉庫,Habor 在 docker-registry 上進行了相應的企業級擴展,從而獲得了更加廣泛的應用。這章學習如何使用 Habor 搭建本地私有倉庫。
Harbor 安裝及配置要求參考:Installation and Configuration Guide
Harbor 使用文檔參考:User Guide
安裝 Harbor 前,需確保硬件條件至少 2核4G內存40G的硬盤,本機需已安裝 docker、docker-compose、openssl。
2、安裝 Docker Compose
Docker Compose 是 Docker 官方編排(Orchestration)項目之一,負責快速在集群中部署分布式應用。使用 Docker Compose 可以輕松、高效的管理容器,它是一個用於定義和運行多容器 Docker 的應用程序工具。它允許用戶通過一個單獨的 docker-compose.yml 模板文件來定義一組相關聯的應用容器為一個項目。Harbor 也是基於 docker compose 編排部署的。
① 安裝 Docker Compose 可以通過下面命令自動下載適應版本的 Compose,並為安裝腳本添加執行權限
# curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # chmod +x /usr/local/bin/docker-compose
② 檢測安裝是否成功
# docker-compose -v
3、安裝 Harbor
① 首先從 github 下載二進制包,選擇一個最新版本安裝:https://github.com/goharbor/harbor/releases
# wget https://storage.googleapis.com/harbor-releases/release-1.9.0/harbor-offline-installer-v1.9.1.tgz
② 解壓
# tar zxvf harbor-offline-installer-v1.9.1.tgz # cd harbor
③ 修改配置文件
# vim harbor.yml
主要修改 hostname 為可訪問的域名或IP、修改管理員密碼
④ 安裝
# ./prepare # ./install.sh
Harbor 安裝成功后,實際上就是通過 docker compose 啟動了相關組件的容器
⑤ Harbor 生命周期管理
harbor 基於 docker compose 編排部署,因此可以使用 docker-compose 命令來管理 harbor 的生命周期。
首先切到 harbor 根目錄下
停止 harbor:docker-compose stop
重啟 harbor:docker-compose start
修改配置:
# #先停止 harbor # docker-compose down -v # #修改 harbor.yml # vim harbor.yml # #運行 prepare 填充配置 # ./prepare # #啟動harbor # docker-compose up -d
4、Harbor 簡單使用
① 訪問 Harbor
訪問配置的 hostname 進入 harbor 管理頁面。具體如何使用可參考官方文檔:User Guide
通過新建項目創建一個項目(bojiangzhou)用於測試。公開項目不需登錄就可以下載鏡像,私有的需要登錄;上傳鏡像則都需要登錄才能上傳鏡像。
推送鏡像的命令格式可以參考如下:
② 創建用戶
若想上傳鏡像,需要用戶登錄,首先在用戶管理創建用戶:
接着在項目中添加用戶成員:
③ 前置配置
上傳鏡像之前,首先需要將 harbor 倉庫配置為 docker 授信的倉庫,否則將被拒絕連接:
vim /etc/docker/daemon.json # 配置如下內容 { "registry-mirrors": ["http://f1361db2.m.daocloud.io"], "insecure-registries": ["192.168.31.54"] }
之后重啟 docker 和 harbor:
#systemctl restart docker # cd /docker-test/harbor # docker-compose up -d
④ 鏡像倉庫登錄退出
登錄:docker login registry
退出:docker logout registry
⑤ 上傳鏡像
首先需要給鏡像打 Tag:docker tag SOURCE_IMAGE[:TAG] 192.168.31.54/bojiangzhou/IMAGE[:TAG]
接着推送鏡像:docker push 192.168.31.54/bojiangzhou/IMAGE[:TAG]
可以看到鏡像已經推送到鏡像倉庫了:
下載倉庫鏡像:docker pull 192.168.31.54/bojiangzhou/nginx-app:1.0.0
九、圖形化管理
Portainer 是一個開源、輕量級 Docker 管理用戶頁面,基於 Docker API,管理 Docker 主機。一般單機下不會使用圖形管理頁面,這里簡單了解下就好。
1、安裝 Portainer
① 創建數據卷:docker volume create portainer_volume
② 啟動 Portainer 容器:docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_volume:/data portainer/portainer
2、訪問 Portainer
訪問 ip:9000,即可進入 Portainer 管理頁面,首先需要創建一個管理用戶
接着連接本機
連接本地進入后就可以進行容器、鏡像等的管理了。
--------------------------------------------------------------------------------------------------------------------------------