2.1 容器的生命週期
這個章節,我們將會先從使用容器,啟動容器、執行容器到最後刪除容器,在跟著實作的同時,也可以思考一下 Docker 的運作模式,最後會找到一個清晰的脈絡,漸漸地就可以對 Docker 有更高的熟悉度。
啟動容器
我們建立一個 nginx 的容器作為開頭:
$ docker container run --publish 80:80 nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/10/01 07:38:25 [notice] 1#1: using the "epoll" event method
2022/10/01 07:38:25 [notice] 1#1: nginx/1.23.1
2022/10/01 07:38:25 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/10/01 07:38:25 [notice] 1#1: OS: Linux 5.10.124-linuxkit
2022/10/01 07:38:25 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/10/01 07:38:25 [notice] 1#1: start worker processes
2022/10/01 07:38:25 [notice] 1#1: start worker process 31
2022/10/01 07:38:25 [notice] 1#1: start worker process 32
2022/10/01 07:38:25 [notice] 1#1: start worker process 33
接著打開你的瀏覽器輸入:http://localhost,就能看到以下的畫面,恭喜您運行了人生第一個 Docker 容器。
相較以往如果要在 macOS 上運行 nginx,需要使用 brew install nginx
的方式,還會將執行檔留存在電腦上,使用 Docker 啟動可以說是輕鬆無負擔。
退出非背景執行容器
接著回到終端機的畫面,會發現其似乎停滯了,這些文字都是運行 nginx 的輸出,至於要如何把運行中的容器退出呢?在非背景執行的情況下,可以採用 Ctrl + C
的方式來退出容器。
2022/10/01 07:43:52 [notice] 33#33: exiting
2022/10/01 07:43:52 [notice] 33#33: exit
2022/10/01 07:43:52 [notice] 32#32: exiting
2022/10/01 07:43:52 [notice] 32#32: exit
2022/10/01 07:43:52 [notice] 31#31: exiting
2022/10/01 07:43:52 [notice] 31#31: exit
2022/10/01 07:43:52 [notice] 1#1: signal 17 (SIGCHLD) received from 32
2022/10/01 07:43:52 [notice] 1#1: worker process 32 exited with code 0
2022/10/01 07:43:52 [notice] 1#1: worker process 33 exited with code 0
2022/10/01 07:43:52 [notice] 1#1: signal 29 (SIGIO) received
2022/10/01 07:43:52 [notice] 1#1: signal 17 (SIGCHLD) received from 31
2022/10/01 07:43:52 [notice] 1#1: worker process 31 exited with code 0
2022/10/01 07:43:52 [notice] 1#1: exit <- 輸入 Ctrl + C
$
列出運行中的容器
輸入 docker container list
指令可以列出運行中的容器:
$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
空無一物,是因為剛剛的 nginx 容器已經被退出了, 並不在運行容器的名單中。
列出包含退出狀態的容器
加入 --all
的參數能夠列出包含退出狀態的容器,也可以透過下方指令看到剛剛的 nginx 容器:
$ docker container list --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
273a9357234e nginx "/do.." 9 mi... Exited char..
啟動退出狀態的容器
對於退出狀態的容器,我們並不需要使用 docker container run
的方式來啟動,而是可以直接對容器執行 docker container start
來喚醒。
$ docker container start 273a93
273a93
這裡後面帶入的 273a93 則為容器獨一無二的 ID,在您電腦上所執行的 nginx 容器和我的 ID 是不會相同的。
而您本機的 nginx 容器 ID 則在 docker container list --all
時可以看到,只需要輸入前幾碼讓 Docker 能夠比對並找到要啟動的容器即可。
這時候回到瀏覽器輸入 http://localhost 又會看到 nginx 重新被啟動了!
退出背景執行的容器
透過 docker container start
的方式,會發現容器自己進入了背景執行,這時候 Ctrl + C 的方式沒有辦法退出容器,我們可以透過 docker container stop
來退出容器。
$ docker container stop 273a93
273a93
刪除退出狀態的容器
刪除容器其實有兩種做法,這邊先介紹要如何刪除一個已經進入退出狀態的容器,在上一小節,我們退出了在背景執行的 nginx 容器,接下來我們要刪除它,完整一個容器的生命週期。
所以我們輸入 docker container rm
這段指令搭配容器的 ID,這邊的 rm 意指 remove 的意思。
$ docker container rm 273a93
273a93
接著為了證實容器被刪除這件事,我們再次列出包含退出狀態的所有容器吧!
$ docker container list --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
背景執行容器
在上一個小節我們重新運作停止的 nginx 容器後,發現其進入背景執行的狀態,那要如何在一開始啟動容器時就指定進入背景狀態呢?
只要在原先的指令上加入 --detach
的參數,便能夠使容器在一開始就進入背景執行狀態。
$ docker container run --publish 80:80 --detach nginx
5cef016820f622def62a46d59f4a9c2e812b616168558492d8bb6864db2f661c
強制刪除正在運行中的容器
前面介紹過的刪除容器只能應用在退出狀態的容器,若是我們直接對運作中的容器執行刪除指令,會出現以下的錯誤訊息:
$ docker container rm 5cef01
Error response from daemon: You cannot remove a running container 5cef016820f622def62a46d59f4a9c2e812b616168558492d8bb6864db2f661c. Stop the container before attempting removal or force remove
內容提示我們,請先退出再刪除,或是使用 force remove 的方式,只要加入 --force
的參數,就能夠強制刪除運行中的容器。
$ docker container rm --force 5cef01
5cef01
接著為了證實容器被刪除這件事,我們再次列出所有包含退出的容器吧!
$ docker container list --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
替容器命名
在之前的範例中,我們都是使用 Docker 所提供的 ID 在對容器進行操作,但這在實務上顯然不太實際,畢竟每一次的 ID 都是不一樣的,很難精準的操控容器。
所以我們也可以透過 --name
的參數替容器命名,如下方示範,我們把容器命名為 nginx,方便我們之後的操作:
$ docker container run --name nginx --publish 80:80 --detach nginx # 不換行
ce6d6951713d3fec44849bd643c4aea6dabc3548a650fa015e4c451821ed4677
列出容器來看看吧!
$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce6d6951... nginx "/do.." 2 min.. Up.... 0.0.0.0:80->80/tcp nginx
接著我們可以把前面所學到的指令都套用在這個容器的名字上,就不需要再使用容器的 ID ,而是他的名字,下方為操作的範例,諸如此類:
$ docker container stop nginx
nginx
$ docker container rm --force nginx
nginx
$ docker container start nginx
nginx
觀看容器內的 Logs
在學會背景執行容器後,會遇到沒辦法觀看 Logs ( 事件紀錄 ) 的情況發生,這時候很簡單,接續上一個小節,我們對 nginx 這個容器執行 docker container logs
的指令。
$ docker container logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/08/30 16:23:35 [notice] 1#1: using the "epoll" event method
2022/08/30 16:23:35 [notice] 1#1: nginx/1.23.1
2022/08/30 16:23:35 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/08/30 16:23:35 [notice] 1#1: OS: Linux 5.10.104-linuxkit
2022/08/30 16:23:35 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/08/30 16:23:35 [notice] 1#1: start worker processes
2022/08/30 16:23:35 [notice] 1#1: start worker process 32
2022/08/30 16:23:35 [notice] 1#1: start worker process 33
2022/08/30 16:23:35 [notice] 1#1: start worker process 34
$
但這只是單純地把 Logs 印出來,在開發時,我們需要一直持續追蹤 Logs 的進展,這時候加上 --follow
的參數,就能夠達到想要的效果。
$ docker container logs --follow nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/08/30 16:23:35 [notice] 1#1: using the "epoll" event method
2022/08/30 16:23:35 [notice] 1#1: nginx/1.23.1
2022/08/30 16:23:35 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/08/30 16:23:35 [notice] 1#1: OS: Linux 5.10.104-linuxkit
2022/08/30 16:23:35 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/08/30 16:23:35 [notice] 1#1: start worker processes
2022/08/30 16:23:35 [notice] 1#1: start worker process 32
2022/08/30 16:23:35 [notice] 1#1: start worker process 33
2022/08/30 16:23:35 [notice] 1#1: start worker process 34
# 這邊會像是當機一樣,等待新的 Logs 產生
同理,要離開只需要執行 Ctrl + C 的指令就能夠離開輸出的畫面。
以上為 Docker 容器最基本的幾種操作方式,可以說是涵蓋了新手會使用到的基本操作指令,而在實際使用過後,想必對於這一切是感到霧煞煞,接下來我們將會針對剛剛的操作做講解。
啟動容器時發生了什麼?
在本章的最開頭,我們啟動了 nginx 的容器,而您的電腦在幾秒鐘的時間就有了 nginx 的服務,接著我們透過終端機透露出的端倪來一步步解析究竟發生了什麼事。
$ docker container run --publish 80:80 nginx
Unable to find image 'nginx:latest' locally <- 找不到
latest: Pulling from library/nginx
...
Logs 的第一行顯示出了 Docker 在本地端找不到 nginx:latest 這個映像檔,所以從 library/nginx 這邊拉取了 nginx:latest 這個映像檔。
也就是從預設的映像檔儲存庫 ( DockerHub ) 中拉出了這個映像檔到本地端。
如果您本身也使用過 Git 這個工具,應該對於這個動作不感到陌生,只是這邊的儲存庫預設是 DockerHub 的 nginx 倉庫,所以不需要指定從哪裡取得映像檔。
最基本的運作流程如下圖:
上述的啟動流程並不包含了 --publish 80:80
這個部份,所以我們再把圖片中的流程畫得更細節一些:
讓我們在用文字搭配上圖再敘述一次流程,解釋清楚整個啟動容器的流程:
圖片中的第一步:
先確認本地端是否有指令中指定的映像檔,若是存在直接跳至第三步根據指令啟動容器;若是不存在,則是移動至第二步。
圖片中的第二步:
從預設的映像檔儲存庫中下載指令中指定的映像檔至本地端。
圖片中的第三步:
根據指令中的映像檔啟動容器。
圖片中的第四步:
根據指令中的額外參數加入設定。
但在真實的運作流程中,第三步以及第四步 會是結合在一起的,並不會分開執行,只是為了讓新手們更好理解整個容器啟動的流程才會這樣繪製。
列出容器時的資訊
在前面實際操作時時常常使用到的 docker container list
,讓我們來看看這個指令透露出什麼值得參考的資訊吧!
$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ce6d6951... nginx "/do.." 2 min.. Up.... 0.0.0.0:80->80/tcp nginx
CONTAINER ID:
我們在執行容器時,Docker 會賦予容器一個獨一無二的 ID,以便 Docker 能夠辨識出容器的差別。
IMAGE:
啟動時指令中所指定的映像檔名稱。
COMMAND:
容器啟動時的啟動指令,這和 Docker 映像檔篇 的 CMD 指令息息相關,也可以在啟動容器時傳入不同的啟動指令來取代原先的指令,後面也會提到。
CREATED:
何時啟動的容器。
STATUS:
總共有 created、restarting、running、removing、paused、exited 以及 dead 七種狀態,根據字面上的意思也很好理解,總之就是目前容器的狀態。
PORTS:
啟動時指令中指定容器的 port 該對應到本機的哪個 port。
NAMES:
這個容器的名字,如果沒有替容器命名則會是由 Docker 隨機分配取名。
如何 重新啟動容器卻不要背景執行?
在剛剛練習容器的生命週期時,我們發現重新啟動退出狀態的容器會自動改成背景執行,這是 Docker 預設的行為,只要是 docker container start
的指令,都會預設改為背景執行模式。
其實我們可以使用 --attach
的方式在重新啟動時直接將應用程式的 Logs 顯示出來。
$ docker container start --attach nginx
docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: IPv6 listen already enabled
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/10/01 08:58:48 [notice] 1#1: using the "epoll" event method
2022/10/01 08:58:48 [notice] 1#1: nginx/1.23.1
2022/10/01 08:58:48 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/10/01 08:58:48 [notice] 1#1: OS: Linux 5.10.124-linuxkit
2022/10/01 08:58:48 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/10/01 08:58:48 [notice] 1#1: start worker processes
2022/10/01 08:58:48 [notice] 1#1: start worker process 25
2022/10/01 08:58:48 [notice] 1#1: start worker process 26
2022/10/01 08:58:48 [notice] 1#1: start worker process 27
# 等待中
容器生命週期練習
每一個章節都會提供一些小小的練習來熟悉本章的內容,通常會比較難一點;若是想不出來也不需要灰心,有些時候可能是指令不熟悉導致,可以往前翻閱來加強記憶喔,亦或是翻到後面的解答章節也可以幫助你解惑喔,記得 --help
以及 docs.docker.com 會是你最好的夥伴。
- 請你在背景執行三個不同的服務,分別是 nginx、postgres、httpd ( apache ),並且分別給容器命名
- nginx 要執行在 80:80,postgres 要執行在 5432:5432,httpd 則要執行在 8080:80
- 當你在啟動 postgres 容器時,需要給予環境變數
--env POSTGRES_PASSWORD=mysecretpassword
才能夠正確啟動 - 使用
docker container logs
的指令來確認服務都有正常啟動 - 停止並刪除上述三個容器
- 使用
docker container list --all
來確認容器都有徹底刪除
容器生命週期練習解答
- 請你在背景執行三個不同的服務,分別是 nginx、postgres、httpd ( apache ),並且分別給容器命名;nginx 要執行在 80:80,postgres 要執行在 5432:5432,httpd 則要執行在 8080:80,以及啟動 postgres 容器時,需要給予環境變數
--env POSTGRES_PASSWORD=mysecretpassword
由於讀者們在練習時,本機應該是沒有 postgre、httpd 這些映像檔,所以會重現容器啟動時所發生的一切,可以回想一下剛剛的流程,或是往回翻閱,是不是如你所想呢?
$ docker container run --detach --name nginx --publish 80:80 nginx
b9ba4eadf1896ea33d6a01ba6e6ac009a5e2e5b4ae93e94879f2843f8ac536d1
$ docker container run --detach --name pg --publish 5432:5432 --env POSTGRES_PASSWORD=mysecretpassword postgres
3b7b5f7a1ef24a58603e77dd6e2e2873b79f96a5efbaea877139941f40ad5bbb
$ docker container run --detach --name apache --publish 8080:80 httpd
91298103ea23f0275ddfbd3f062791f32bbd187dac9bffe7d5acaaa33435b820
- 使用
docker container logs
的指令來確認服務都有正常啟動。
$ docker container logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is n...
/docker-entrypoint.sh: Looking for shell scripts in ...
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-o...
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx...
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx...
$ docker container logs pg
PostgreSQL init process complete; ready for start up.
2022-08-27 12:23:37.566 UTC [1] LOG: starting Postre..
2022-08-27 12:23:37.566 UTC [1] LOG: listening on IP..
2022-08-27 12:23:37.566 UTC [1] LOG: listening on IPv..
2022-08-27 12:23:37.574 UTC [1] LOG: listening on Un..
2022-08-27 12:23:37.584 UTC [60] LOG: database syst..
2022-08-27 12:23:37.594 UTC [1] LOG: database system...
$ docker container logs apache
AH00558: httpd: Could not reliably determine the serve..
AH00558: httpd: Could not reliably determine the ser...
[Sat Aug 27 12:22:50.192367 2022] [mpm_event:notice] [pid 1:tid 1405....]
[Sat Aug 27 12:22:50.194186 2022] [core:notice] [pid 1:tid 140564....]
- 停止並刪除上述三個容器,停止以及刪除的指令都可以搭配複數的容器,不需要一個一個打。
---- 先退出,再刪除 ----
$ docker container stop nginx pg apache
nginx
pg
apache
$ docker container rm nginx pg apache
nginx
pg
apache
---- 強制刪除 ----
$ docker container rm --force nginx pg apache
nginx
pg
apache
- 使用
docker container list --all
來確認容器都有徹底刪除。
$ docker container list --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES