專題 - 使用 podman 進行 container 管理
上次更新日期 2021/12/21
docker 是容器管理的最早企劃之一,所以很多的管理大多以 docker 為依據來進行的。但是, docker 必須要使用 root 的身份去啟動與管理,對於某些特別注意資安的朋友來說,感覺就是怕怕的。 因此,Red Hat 推出了 podman 搭配 runc 來進行輕量級虛擬化,也就是容器的管理與運行! 這玩意兒挺有趣的,透過一般帳號就可以來運作與管理容器,更有趣的是,這傢伙可以透過 systemd 來管理! 因此,玩了 podman 連 systemd 都有進一步的理解!相當有趣喔!
- 什麼是 podman 與安裝 container-tools
- 簡易的操作 podman
- 簡易的倉儲與映像檔查詢管理
- 稍進階 container 管理:port mapping, volume...
- 透過 pod 管理容器類群
- 一般帳號的 systemd 管理容器的開機啟動行為
什麼是 podman 與安裝 container-tools
- 容器三要件:
- 基本上,無論是 docker、podman 等,都是用來管理容器 (container) 的相關軟體,而容器的運作, 大多離不開倉儲 (registry)、映像檔 (image)、容器 (container) 三者,這三個的關係已經在 docker 章節講過一次, 這裡再次強調一下!你得要回去前面的章節好好瞧一瞧喔!
- 映像檔 (image):
容器都是從映像檔喚起的,所以這裡的『映像檔 (image)』並不只是 VM 的硬碟而已,所謂的映像檔包含了: 系統函式庫、指令執行檔、相關編譯函式庫、各種服務配置檔案、啟動腳本與配置等。另外,映像檔是不能更改的! 映像檔的打包需要依循 OCI (Open Container Initiative image) 規格來設計才行。 - 服務使用容器來設計的概念:
基本上,過去我們學的底層伺服器,大多是一部主機啟用之後,在上面啟動多個服務,針對不同的需求來設計。 新的依據容器的設計理念,則是任何服務都使用單一的容器去啟動,然後透過不同容器間的介接來達成服務間的溝通。 由於所有的服務都是獨立的,因此,任何一個服務出問題時,並不會影響到其他服務的運作,當然你的主機就會活的比較好。 另外,也由於所有的服務都在獨立的容器上,所以搬移時,就會顯的非常簡單。
- podman 是什麼?
- podman 是一套 Red Hat 提供的容器管理工具組,這個工具組包含了數個工具:
- podman:可以用來管理映像檔與容器本身
- skopeo:用來檢查、複製、刪除與簽核映像檔
- buildah:用來建立新的映像檔
- podman 可以在非 root 環境下執行
前面提到的 docker 得要使用 root 的身份去管理,所以所有的容器都是 root 的權限!對於某些人來說,實在不太喜歡。 podman 可以在一般帳號身份來管理容器!而且也能夠管理由 docker 所建立的映像檔與容器,相當值得了解一番。 不過,由於身份並非是 root,因此,如果容器需要使用到網路埠口時,就需要特別留意了!因為一般帳號不能啟動小於 1024 以下的埠口啊!。這也就是說,如果你要使用 podman 管理的容器去啟動小於 1024 以下的埠口時,你可能有兩個選擇:- 使用 root 的身份去操作 podman,就可以避過這個問題。不過,當然啦!有人就是不喜歡使用 root 啟動啊!
- 用一般帳號啟動 podman,並且開啟 port 到大於 1024 的埠口,然後透過防火牆的 port mapping 功能, 指向非正規埠口即可。
- podman 是一套 Red Hat 提供的容器管理工具組,這個工具組包含了數個工具:
- 安裝 container-tools 模組:
- client 系統的環境準備:
由於我們的 Server 系統已經安裝了 docker 了,而 docker 與 podman 是互相衝突的 (兩個都在管理容器), 所以,這次我們同時啟動 server 與 client,其中 client 用來操作 podman。但是 server 必須要能夠提供網路才行。 因此,請在 client 上面,根據 server 的網路環境,設定好你的 client 網路!。 - 安裝容器管理軟體:
雖然系統上面預設安裝有 podman 了,不過,我們可以透過 yum 的模組功能 (module) 來找到更多的應用! 如下所示:# yum module list Name Stream Profiles Summary 389-ds 1.4 389 Directory Server (base) ant 1.10 [d] common [ Java build tool container-tools rhel8 [d][e] common [ Most recent (rolling) versions of podman.. d] ependencies such as container-selinux bu.. container-tools 3.0 common [ Stable versions of podman 3.0, buildah 1.. d] -selinux built and tested together, and .. freeradius 3.0 [d] server [ High-performance and highly configurable.. d] ....
想要安裝相關的 module 也挺簡單的,如上所示,我們想要安裝的是 container-tools,你只要安裝不要給予版本, yum 會主動的給最新版!所以,就這樣做即可:# yum module install container-tools
系統就會主動的安裝好所需要的模組了!相當簡單方便!
- client 系統的環境準備:
- 實做練習:
- 在使用 yum 列出的模組中,找到最新版本的 python,並且安裝該 python 版本。
- 再次檢查 yum 的模組, python39 是否多了 [e] 的顯示?
- 可以使用『 yum module info python39 』列出更多的詳細資訊
簡易的操作 podman
- 第一次操作 podman 管理容器:
- 使用一般帳號開始查詢所需要的映像檔:
如前所述, podman 可以讓用戶直接操作容器,所以請使用一般帳號 (誰都可以) 來操作才能發現 podman 的好處。 然後想想看前一章 docker 的處理方式,你會想到應該要先查詢有沒有映像檔在倉儲內才對吧!所以, 繼續讓我們來看看有沒有 rockylinux 呢?$ podman search rockylinux INDEX NAME DESCRIPTION STARS OFFICIAL docker.io docker.io/rockylinux/rockylinux 45 docker.io docker.io/library/rockylinux The official build of Rocky. 0 [OK] docker.io docker.io/106061/rockylinux_cicd For CI/CD RockyLinux 0 ....
看起來有兩個挺不錯的 rockylinux 可以使用~一個是喜好 (STARS) 比較多,另一個則是官方 (OFFICIAL) 推出的!我們直接拿第一個來安裝好了!要注意喔,即使是 podman,很多的 image 也是從 docker.io 這個公開的倉儲來的! - 開始下載映像檔:
找到正確的映像檔,再來就是下載他到本機上!$ podman pull docker.io/rockylinux/rockylinux Trying to pull docker.io/rockylinux/rockylinux:latest... Getting image source signatures Copying blob 72a2451028f1 done Copying config a1e37a3cce done Writing manifest to image destination Storing signatures a1e37a3cce8f954b7a802d41974c7cd8dbe8c529c7d9a253fba1d6cd679230f1 $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/rockylinux/rockylinux latest a1e37a3cce8f 4 weeks ago 211 MB
很快的,將需要的映像檔抓下來,也能夠進行第一次的觀察!另外,從上面的結果,你也可以發現到映像檔的命名大致上是這樣的:- docker.io/rockylinux/rockylinux:latest
- 倉儲名稱/建立者或單位名稱/實際映像檔名稱:版本(tag)
- 開始啟動 container:
啟動容器也是使用 podman 啦!方法跟 docker 幾乎一模一樣!連參數設計都相同!假設我要設定一個名為 rocky1 的容器名稱, 使用的就是剛剛抓下來的映像檔,就這樣做即可:$ podman run -it --name rocky1 docker.io/rockylinux/rockylinux [root@71875f3ccaae /]# ls /etc/yum.repos.d/ Rocky-AppStream.repo Rocky-Debuginfo.repo Rocky-Extras.repo Rocky-Media.repo Rocky-Plus.repo Rocky-RT.repo ... [root@71875f3ccaae /]# yum whatprovides '*bin/ps' procps-ng-3.3.15-6.el8.i686 : System and process monitoring utilities Repo : baseos Matched from: Provide : /bin/ps .... [root@71875f3ccaae /]# yum install procps-ng net-tools iproute [root@71875f3ccaae /]# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 19352 3584 pts/0 Ss 15:08 0:00 /bin/bash root 70 0.0 0.1 51864 3668 pts/0 R+ 15:14 0:00 ps aux [root@71875f3ccaae /]# ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: tap0: <BROADCAST,UP,LOWER_UP&t; mtu 65520 qdisc fq_codel state UNKNOWN group default.. link/ether f2:eb:43:03:21:61 brd ff:ff:ff:ff:ff:ff inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0 valid_lft forever preferred_lft forever inet6 fe80::f0eb:43ff:fe03:2161/64 scope link valid_lft forever preferred_lft forever [root@71875f3ccaae /]# [ctrl]-p [crtl]-q $ <==這樣就回到原本的 host 狀態,而不是 container 中!
- 觀察 container 與取回 container 控制:
跟 docker 也是差不多,要觀察 podman 時,使用 podman ps 或者是加上 -a 的參數,都可以看到運作中或者是全部的 podman container 喔!$ podman ps CONTAINER ID IMAGE COMMAND CREATED .. NAMES 71875f3ccaae docker.io/rockylin... /bin/bash 16 minutes ago.. rocky1
目前僅有一個 container 在運作,我們也剛剛脫離 rocky1 啦!現在,讓我們繼續取得這個 container 之後, 直接關閉他吧。$ podman attach rocky1 [root@71875f3ccaae /]# exit $ podman ps -a CONTAINER ID IMAGE .. COMMAND .. STATUS .. NAMES 71875f3ccaae docker.io/rockylinux/r.. /bin/bash .. Exited (0) 20 seconds ago .. rocky1
- 使用一般帳號開始查詢所需要的映像檔:
- 管理既有的 container 方式:
- 用 start 與 attach 重新啟動與連接之前建立的 container :
以剛剛建立的 rocky1 為例,當我們離開 container (exit 指令下達後) 之後,事實上這個 container 還是存在的 若需要再次的啟動,那就使用底下的方式來處理:$ podman ps -a CONTAINER ID IMAGE .. COMMAND .. STATUS .. NAMES 71875f3ccaae docker.io/rockylinux/r.. /bin/bash .. Exited (0) 20 seconds ago .. rocky1 $ podman start rocky1 rocky1 $ podman attach rocky1 [root@71875f3ccaae /]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 1 0 0 80 0 - 4810 - pts/0 00:00:00 bash 0 R 0 12 1 0 80 0 - 12435 - pts/0 00:00:00 ps [root@71875f3ccaae /]# [crtl]-p [crtl]-q $ podman ps CONTAINER ID IMAGE .. COMMAND .. STATUS .. NAMES 71875f3ccaae docker.io/rockylinux/.. /bin/bash .. Up 2 minutes ago .. rocky1
這樣就啟動上次建立的 container 了!而且也可以跟 container 互動!取得操作方式! - 用 exec 執行 container 內的指令:
假設 container 執行的指令並不是 /bin/bash 時,例如啟動 apache 的 container ,啟動的是服務, 而不是 bash 啊!那麼你想要跟 container 互動,就得要使用 exec 來處理才行:$ podman exec [-it] 容器名稱 指令列 $ podman exec rocky1 ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 19240 3740 pts/0 Ss+ 17:14 0:00 /bin/bash root 23 0.0 0.1 44676 3428 ? Rs 17:31 0:00 ps aux $ podman exec -it rocky1 bash [root@71875f3ccaae /]# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 19240 3740 pts/0 Ss+ 17:14 0:00 /bin/bash root 45 0.1 0.1 19240 3648 pts/1 Ss 17:33 0:00 bash root 57 0.0 0.2 51864 3800 pts/1 R+ 17:33 0:00 ps aux [root@71875f3ccaae /]# exit $ podman ps CONTAINER ID IMAGE .. COMMAND .. STATUS .. NAMES 71875f3ccaae docker.io/rockylinux/r.. /bin/bash .. Up 20 minutes ago .. rocky1
你會發現,我們使用 ps 時,podman 會跑去 container 裡面執行指令,執行完畢就結束了!並不會停留在 container 內。 但如果是可以有互動的 bash 指令,那加上 -it 之後,你就會在 container 內停留操作 bash, 並且此時就有兩個 bash 在 container 內!因此,你離開這個 bash 時,並不會干擾到原本的 bash 程序! 所以,這個 container 還是會持續存在記憶體內!並不會關閉的! - 用 logs 呼叫出 container 目前的終端內容:
有個比較有趣的 podman 指令,那就是 logs !這個 logs 會將 container 的所有操作行為都列出來! 如果我們想要看看 rocky1 從啟動到目前為止的操作行為,可以這樣做:$ podman logs rocky1 .... [root@71875f3ccaae /]# exit exit [root@71875f3ccaae /]# ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 4 S 0 1 0 0 80 0 - 4810 - pts/0 00:00:00 bash 0 R 0 12 1 0 80 0 - 12435 - pts/0 00:00:00 ps $
內容相當龐大,自己瞧瞧就好了!上面所有的資料都只是顯示而已,相當有趣喔! - 用 cp 將 container 內的檔案複製出來:
某些時刻,你可能需要將 container 內的資料複製到 host 上面,此時,可以透過 podman cp 來處理!$ podman cp rocky1:/etc/hosts . $ ll hosts -rw-r--r--. 1 student student 288 Dec 19 01:14 hosts $ podman cp rocky1:/var/lib . $ ll -d lib
- 用 stop 關閉正在運作中的 container:
這個就簡單了!關閉 container 而已!$ podman ps ... (你會看到 rocky1 正在活動中) $ podman stop rocky1 rocky1 $ podman ps
這樣關閉了 rocky1 囉!
- 用 start 與 attach 重新啟動與連接之前建立的 container :
- 實做練習:
- 找一下名為 httpd 的 image,最好是由 redhat.com 這個倉儲內的 ubi8 這個單位所推出的較佳。
- 將上述的 image 抓到本地端來 (偶而會顯示 TLS timeout,重新執行幾次即可)
- 利用此 image 建立名為 myhttpd 的容器名稱,請使用 podman 搭配 create 指令操作測試。 不要進入互動界面 (不要 -it 之意。)
- 觀察上述容器是否活動中?若沒有在活動中,請啟動該容器,但是同樣不要進入該容器
- 能否使用 attach 連接到 myhttpd 呢?使用 podman 似乎無法離開該 container,或許只能關閉了。
- 承上,如果關閉了,請重新啟動。
- 嘗試使用 exec 的方式,再次執行一個 bash 在 myhttpd 容器內。
- 在容器內以 ps aux 查詢啟動的程序有哪些?
- 在容器內查看一下 /etc/system-release 的結果為何?
- 在容器內離開 bash !
- 嘗試刪除 myhttpd 這個容器!
- 不保留 container 的方式,透過 --rm 功能:
- container 一般存在的角色:
基本上,container 都是用到才啟動!然後,相關的資料通通放置在某個 host 目錄下,關閉 container 時, 大部分連同 container 本身都是可以刪除的!目的不在永遠存在啦!有需要才多開幾個容器來支援這樣。 所以如同上面練習最後一題,我們很可能在每次停止 container 之後,都要手動刪除該容器呢! - 在關閉時直接刪除容器的方法: --rm:
要實做容器關閉就刪除該容器也很簡單,加上 --rm 在 podman run 的階段即可!例如,再啟動一個名為 rocky2 的容器,
使用 rockylinux 映像檔,然後離開 bash 時,觀察看看容器有沒有被刪除呢?
$ podman run -it --rm --name rocky2 rockylinux/rockylinux [root@92eb4fed4bc1 /]# cat /etc/system-release Rocky Linux release 8.5 (Green Obsidian) [root@92eb4fed4bc1 /]# exit $ podman ps -a
你會發現到,基本上,沒有 rocky2 容器的存在!你離開 bash,該容器就主動被刪除了!這就是 --rm 的功效! 要注意喔,很多容器的使用,就是加上這個動作的!
- container 一般存在的角色:
簡易的倉儲與映像檔查詢管理
- 預設的映像檔倉儲:
- 倉儲的位置:
在 RHEL 衍生的 Linux distributions 上面,預設的 podman 會有底下的映像檔倉儲:- "registry.fedoraproject.org"
- "registry.access.redhat.com"
- "registry.centos.org"
- "docker.io"
- 映像檔倉儲的設定檔:
相關的設定檔其實都放置到 /etc/containers/registries* 裡面,我們先來看看主要的設定檔:$ grep -v '^#' /etc/containers/registries.conf unqualified-search-registries = ["registry.fedoraproject.org", "registry.access.redhat.com", "registry.centos.org", "docker.io"] short-name-mode = "permissive"
從上面也看得出來,主要設定就是這幾個!其他的設定檔也都瞧瞧就好!預設值已經很好! 除非你有額外想要安裝的倉儲時,再來修改這些設定檔即可。 - 查看既有的 registry 設定以及相關設定值:
事實上,使用 podman 就可以看到很多資訊了!$ podman info .... registries: search: - registry.fedoraproject.org - registry.access.redhat.com - registry.centos.org - docker.io store: configFile: /home/student/.config/containers/storage.conf containerStore: number: 1 ..... volumePath: /home/student/.local/share/containers/storage/volumes .....
這樣能夠看到預設的倉儲位置,以及未來儲存資料時,預設的資料放置目錄哩!
- 倉儲的位置:
- 搜尋映像檔的其他特殊選項:
- 選擇特定的倉儲來搜尋:
通常我們都直接使用 podman search 直接找映像檔關鍵字。如果你有特別的需求,需要在特定的軟體倉儲搜尋時, 舉例來說,我們知道 rockylinux 應該不會出現在 redhat 相關的倉儲...那就可以這樣做:$ podman help search $ podman search docker.io/rockylinux INDEX NAME DESCRIPTION docker.io docker.io/rockylinux/rockylinux docker.io docker.io/library/rockylinux The official build of Rocky Linux docker.io docker.io/106061/rockylinux_cicd For CI/CD RockyLinux
意思是倉儲名稱之後加上關鍵字,就可以取得相關的映像檔名稱囉!不是太困難~那如果你只想要某些特定的關鍵字, 例如僅列出 5 筆資料,或者是只列出官方 (official) 的映像檔,也可以這樣:$ podman search docker.io/rockylinux --limit 5 $ podman search docker.io/rockylinux --filter is-official=true
過濾 (filter) 可以家的選項有:- --filter stars=數量
- --filter is-automated=[true|false]
- --filter is-official=[true|false]
- 查看下載的容器資訊:
使用 podman 裡頭的 image 管理中,可以透過 inspect 來查看一些映像檔的資訊喔:$ podman image inspect docker.io/rockylinux/rockylinux [ { .... "Created": "2021-11-15T21:47:03.053188004Z", "Config": { "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "WorkingDir": "/", "Created": "2021-11-15T21:47:03.053188004Z", ....
- 選擇特定的倉儲來搜尋:
- 實做練習:
- 找出 registry.access.redhat.com 裡面關於 python 的容器有哪些?
- 請下載最新的 python,應該要下載的是 ubi8 的組織所打包的版本。
- 列出 python 這個映像檔的詳細資訊,並且特別查看比較重要的 Config 部份資料, 包括 Ports 對應、環境變數、實際啟動指令 (Cmd) 等資訊
- 刪除此映像檔。
稍進階 container 管理:port mapping, volume...
- 設定好 port mapping 功能:
- 觀察一個 httpd 的服務方式:
我們前面的練習當中,有下載過 httpd 的映像檔,觀察一下映像檔的內容吧!$ podman inspect registry.access.redhat.com/ubi8/httpd-24 ..... "Config": { "User": "1001", "ExposedPorts": { "8080/tcp": {}, "8443/tcp": {} .... "Entrypoint": [ "container-entrypoint" ], "Cmd": [ "/usr/bin/run-httpd" ], .....
看起來這個映像檔似乎會啟用一個 8080 以及 8443 的埠口給容器使用喔! 同時啟動 container 時,會主動喚醒 /usr/bin/run-httpd 這個腳本程式就是了。 - 使用 daemon 的方式啟動容器:
我們前面都是使用 podman run -it... 來啟動一個容器,事實上,如果以上面 httpd-24 的映像檔為例, 他就主要提供一個 web service,而不是要跟你直接互動啊!所以,你當然可以直接讓該容器啟動在背景上!$ podman run -d --name myhttpd registry.access.redhat.com/ubi8/httpd-24 $ podman ps CONTAINER ID IMAGE .. COMMAND .. STATUS PORTS NAMES 1198d20a0eaa registry.access.redhat... /usr/bin/run-http... .. Up 11 secon myhttpd
這樣就執行了一個不需要互動,且持續在執行當中的容器了! - 讓本機可以連上虛擬機器來取得網頁服務 -p 功能!
現在想一想,上面的容器就是 httpd 提供的一個服務,所以,你得要使用瀏覽氣去測試他對吧。 好!請問 IP 在哪裡?請問 port 在哪裡?哈哈!沒錯!其實你根本無法在 container 之外的環境連接到該網頁服務! 所以,這樣根本無法提供網際網路實際處理的。沒關係,我們可以跟 docker 一樣,透過『 -p 本機埠口:容器埠口』 來設定好連線的!$ podman stop myhttpd $ podman rm myhttpd $ podman run -d --name myhttpd -p 8080:8080 registry.access.redhat.com/ubi8/httpd-24 $ podman port -l 8080/tcp -> 0.0.0.0:8080 $ curl http://localhost:8080 $ podman port -a a2668e3a2eac 8080/tcp -> 0.0.0.0:8080
- 修改 myhttpd 的首頁內容:
雖然上面已經可以順利的從 host 去取得網頁資料,不過,我們還是嘗試來修改一下首頁好了! 免得誤會是跑去哪裡了~$ podman exec -it myhttpd bash bash-4.4$ echo 'This is from podman container' > /var/www/html/index.html bash-4.4$ exit $ curl http://localhost:8080 This is from podman container
很簡單的取得了我們需要的網頁資料囉!
- 觀察一個 httpd 的服務方式:
- 設定本機 host 的目錄,讓 container 去掛載:
- 讓 container 掛載本機目錄的需求:
以上面的案例來分析,如果我們需要修改網頁還得要連線到該容器內修改!總覺得困惑。 沒關係,我們可以透過本機的目錄分享給 container 使用,這樣,在本機目錄內修改完畢, container 就可以立刻生效了!這個需求是很重要的。有時候我們在加上 --rm 的方式啟動容器, 所以容器關閉就刪除!問題是,我們希望將某些資料保留下來時,就可以透過這個功能了! - 實做目錄掛載
假設要將本機的 ~/server/www 掛載到網頁根目錄,亦即是 /var/www/html 底下時,除了 podman 本身的 -v 設定之外, 你應該要先解決 SELinux 的 container 類型問題!通常使用的 SELinux type 為『 container_file_t 』。所以,你可以嘗試這樣做:$ mkdir server; mkdir server/www $ chcon -Rv -t container_file_t ~/server $ podman stop myhttpd $ podman rm myhttpd $ podman run -d --name myhttpd -p 8080:8080 -v ~/server/www:/var/www/html \ registry.access.redhat.com/ubi8/httpd-24 $ echo 'This is from host webpage' > ~/server/www/index.html $ curl http://localhost:8080/index.html This is from host webpage $ podman exec -it myhttpd cat /var/www/html/index.html This is from host webpage
這功能倒是挺重要的!當有任何需要保留的資料,都可以使用這個方法來處理。只是要特別注意 SELinux 的類型就是了! 通常使用者都會卡在這個地方... - 查看某容器的詳細資料:
容器的詳細資料可以透過『 podman inspect containername 』來處理。如果只想要看 volume 的資訊,可以這樣做:$ podman inspect myhttpd | grep -A 10 Mounts "Mounts": [ { "Type": "bind", "Source": "/home/student/server/www", "Destination": "/var/www/html", "Driver": "", "Mode": "", "Options": [ "rbind" ], "RW": true,
- 讓 container 掛載本機目錄的需求:
- 網路查詢功能:
- 預設的 podman 容器的網路處理方式:
podman 的網路是透過一個 slirp4netns 機制處理的,在預設的情境下 (v0.4.0版本之後),podman 容器根據這個機制的設定, 會主動帶起一個名為 tap0 的裝置,固定的 IP 都會是 10.0.2.100,gatway 則是 10.0.2.2 而 DNS 則是 10.0.2.3。 當然可以透過 slirp4netns 指令去修改,詳細的資訊可以『 man slirp4netns 』去查閱一下。
不過,由於 slirp4netns 在 podman 預設的情況下 (沒有額外帶入參數時),所有的 container 都會是相同的 IP, 所以,雖然容器很容易喚醒網路,且網路效能真的還不賴~但是,容器與容器彼此之間,幾乎是沒有辦法互相溝通的! 這就是個大的困擾!
# 建立一個名為 rocky3 的容器,使用 rockylinux 映像檔,且關閉後刪除 $ podman run -it --rm --name rocky3 docker.io/rockylinux/rockylinux [root@e9c2b63ba122 /]# yum -y install net-tools [root@e9c2b63ba122 /]# ifconfig .... tap0: flags=67<UP,BROADCAST,RUNNING> mtu 65520 inet 10.0.2.100 netmask 255.255.255.0 broadcast 10.0.2.255 .... [root@e9c2b63ba122 /]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.0.2.2 0.0.0.0 UG 0 0 0 tap0 10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 tap0 [root@e9c2b63ba122 /]# [ctrl]-p [ctrl]-q # 查詢 rocky3 的網路模式: $ podman inspect rocky3 | less .... "NetworkSettings": { "EndpointID": "", "Gateway": "", "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "", "Bridge": "", "SandboxID": "", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": {}, "SandboxKey": "/run/user/1000/netns/cni-7fbda5e1-409c-6a0a-e03a-7fb566b08685" }, .... "NetworkMode": "slirp4netns", ....
不管你開幾個 container,每一個 container 的網路參數卻是一模一樣的!這是透過上述的 slirp4netns 協助的功能! 此外,其實網路堆疊是放置在 /run/user/使用者ID/netns/ 裡面啦!也因為如此,所以,所有的 container 其實是被鎖在自己的網路環境下, 彼此是無法溝通的!我們也只能透過上一小節談到的 port mapping 功能,從 host 連線到 container 而已。 - podman 的私有網路:
前幾章的 docker 網路環境中,所有的 docker 容器都是透過 docker0 的公開橋接界面來連結,所以在 host 上面, 可以簡單的透過 ip addr show 等,就可以看到各個界面了。但是 podman 並不相同,由於可以讓一般用戶自行設定網路, 所以使用 ip addr show 時,基本上,看不到網路的相關狀態的。$ ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 ... 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default.. ... # 基本上,你看不到 container 所連接的網路情境。
如果要觀察相關的設定,例如上面提到的類似 docker0 的界面,只能夠過『 podman network 』相關功能! 例如找到 podman 網路設定,並且觀察的方式如下:$ podman network ls NETWORK ID NAME VERSION PLUGINS 2f259bab93aa podman 0.4.0 bridge,portmap,firewall,tuning $ podman network inspect podman .... "ranges": [ [ { "gateway": "10.88.0.1", "subnet": "10.88.0.0/16" } ....
如上所示,基本上,我們有一個內建的網路名稱為 podman 喔!該 podman 的 router 為 10.88.0.1,並且提供一個 class B 的網段給大家使用。 - 開始使用 podman 提供的網段來分配 container 的網路:
事實上,啟動 container 時,可以呼叫上述的網路界面來提供相關的網路參數給 container 喔!就等於是讓 podman 成為一個大家共有的橋接器 (bridge) 就是了!整個方法很簡單~使用『 --network podman 』處理即可!# 分別啟動 rocky4, rocky5 兩個 container,使用網路界面 podman, # 啟動後直接離開 [crtl]-p [ctrl]-q 即可 $ podman run -it --rm --name rocky4 --network podman docker.io/rockylinux/rockylinux $ podman run -it --rm --name rocky4 --network podman docker.io/rockylinux/rockylinux $ podman inspect rocky4 | grep IPAddress "IPAddress": "", "IPAddress": "10.88.0.2", $ podman inspect rocky5 | grep IPAddress "IPAddress": "", "IPAddress": "10.88.0.3",
這樣就可以知道目前 rocky4, rocky5 的網路設定值!雖然從本機還是無法連線過去~不過,container 應該是可以互連的!$ podman attach rocky4 [root@b4f608786dc1 /]# ping -c 2 10.88.0.2 [root@b4f608786dc1 /]# ping -c 2 10.88.0.3 [root@b4f608786dc1 /]# exit $ podman rm rocky5 -f
做完測試後,就關閉兩個容器吧!
- 預設的 podman 容器的網路處理方式:
透過 pod 管理容器類群
- pod 的功能-將整個網路統一分享:
pod 的概念是由 K8S 這套軟體引進的,基本概念就是,許多的容器共用一組網路參數,簡單的說,你可以想成, 一部主機上面有多個共同的服務在跑的感覺~而且全部都綁在同一個網路參數上面!因次,容器彼此之間, 可以透過共有的 port number 來進行交流~例如某個 rockylinux 的 bash 可以透過 mysql 指令連線到資料庫的 port 3306 資料庫軟體,而無須增加任何設定!
這樣的設計方式,有點像是將某個沙箱 (sandbox) 設定好,並且給予適當的網路參數與權限後,將一堆程序 (容器) 丟進去! 所以,全部的容器通通可以混在一起玩!簡直就跟本機 (http://127.0.0.1) 一樣!大概就是這樣的想法!
- 將整個網路功能統一分享
- 一個範例:
現在,假設我們要建立一個放置在容器當中的 Web 網頁伺服器系統,他需要一個 httpd 服務 (port 8080),一個資料庫系統 (mysql, port 3306), 一個具有 bash 的管理容器 (使用 rockylinux 好了)。當然啦,這是個簡單到爆炸的範本,所以沒有其他額外的服務 (例如還得要 FTP 或 SSH 等, 才能提供用戶傳輸資料上來的特性等,先放過~)。 - 先建立一個具有網路結構的 pod 提供:
再次強調,這個 pod 需要提供網路,需要提供埠口對應,假設 pod 名稱為 web_service,那麼你可以這樣做:$ podman pod create --name web_service --network podman -p 8080:8080/tcp \ -p 2222:22/tcp -p 3306:3306/tcp $ podman pod ps POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS 5f6950eeddcb web_service Created 5 seconds ago 6c48399ddbfa 1 $ podman pod inspect web_service { "Id": "5f6950eeddcbd072ddb844c2164a894737db25e5cae6c8744c51ec2814f9bd1d", "Name": "web_service", "Created": "2021-12-22T00:18:28.868221389+08:00", "CreateCommand": [ "podman", "pod", "create", "--name", "web_service", "--network", "podman", "-p", "8080:8080/tcp", "-p", "2222:22/tcp", "-p", "3306:3306/tcp" .... $ podman ps -a CONTAINER ID ... PORTS .. NAMES 6c48399ddbfa ... 0.0.0.0:2222->22/tcp, 0.0.0.0:3306->3306/tcp,.. 5f6950eeddcb-infra
如上,你會發現有個 pod 名稱為 web_service,其中還有個很特別的『 registry.access.redhat.com/ubi8/pause 』映像檔存在! 這個是執行 pod 時,系統主動幫你暫時加上來的一個很特別的容器喔! - 加入容器們:
現在,讓我們加入一個 httpd,一個 mysql,一個 rockylinux。先加入網頁伺服器~方式如下:$ podman run -d --rm --name web_apache --pod web_service \ registry.access.redhat.com/ubi8/httpd-24 $ podman inspect web_apache | grep IPAddr "IPAddress": "", "IPAddress": "10.88.0.2", $ podman ps CONTAINER ID IMAGE COMMAND \ CREATED STATUS PORTS \ NAMES 6c48399ddbfa registry.access.redhat.com/ubi8/pause:latest \ 11 minutes ago Up About a minute ago 0.0.0.0:2222->22/tcp, 0.0.0.0:3\ 306->3306/tcp, 0.0.0.0:8080->8080/tcp 5f6950eeddcb-infra 5db082790cc4 registry.access.redhat.com/ubi8/httpd-24:latest /usr/bin/run-http\ ... About a minute ago Up About a minute ago 0.0.0.0:2222->22/tcp, 0.0.0.0:3\ 306->3306/tcp, 0.0.0.0:8080->8080/tcp web_apache
幾個重點注意一下:(1) 預設取得的 IP 在 pod 裡面都是相同的! (2) 所有的埠口對應在 pod 裡面都是相同的!所以最後執行 podman ps 時,有 3 行的輸出當中,兩個 container 的埠口對應完全相同喔!然後,再來處理一下 mysql 喔!$ podman search mysql INDEX NAME DESCRIPTION STARS ..... docker.io docker.io/library/mariadb MariaDB Server is a high performing 4512 ..... $ podman pull docker.io/library/mariadb $ podman run -d --rm --name web_mariadb --pod web_service \ -e MARIADB_ROOT_PASSWORD=mypassword docker.io/library/mariadb $ podman ps ONTAINER ID IMAGE PORTS .. NAMES 554720b853fe docker.io/library/mariadb:latest 0.0.0.0:2222->22/tcp, .. web_mariadb
這樣就多一個 mysql 囉!因為我們的 pod 有指定 3306 會指向該資料庫,因此,你可以這樣測試看看:$ mysql -h 127.0.0.1 -P 3306 -u root -p # 不能用 localhost 喔! Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 3 Server version: 10.6.5-MariaDB-1:10.6.5+maria~focal mariadb.org binary distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> quit
最後,加上管理用的 rockylinux 系統如下:$ podman run --rm -it --name web_opt --pod web_service \ docker.io/rockylinux/rockylinux [root@web_service /]#
非常有趣的是,你會發現到,主機名稱變成 pod 的名稱喔!而且我們已經進入了 rockylinux 啦! 讓我們在這個 container 裡面工作一下:[root@web_service /]# yum -y install net-tools mysql [root@web_service /]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.88.0.2 netmask 255.255.0.0 broadcast 10.88.255.255 inet6 fe80::8e7:f6ff:fe7f:6b5a prefixlen 64 scopeid 0x20 ether 0a:e7:f6:7f:6b:5a txqueuelen 0 (Ethernet) RX packets 19057 bytes 26778990 (25.5 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10111 bytes 556728 (543.6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 .... # 你會發現到,即使是第三個 container 了!IP 還是跟最初的 httpd server 相同! [root@web_service /]# netstat -tlunp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN - tcp6 0 0 :::3306 :::* LISTEN -
這就更有趣了!竟然還有看到其他的服務啟動的埠口哩!那麼,在這個地方,能不能用 rockylinux 登入 mysql 呢? 測試就對了![root@web_service /]# mysql -h 127.0.0.1 -u root -p Enter password: <==輸入密碼 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.5.5-10.6.5-MariaDB-1:10.6.5+maria~focal mariadb.org binary distribution Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> exit Bye
很快的,確定可以登入 pod 內的其他容器!且注意看 IP 喔!就感覺是自己的 IP!這就是 pod 的功能!
- 一個範例:
一般帳號的 systemd 管理容器的開機啟動行為
- podman 管理的容器何時啟動:
- 因為 podman 支援一般帳號來管理容器 (rootless 的環境),過去我們的認知裡面,一般帳號似乎沒有辦法在開機的時候, 就自動喚醒某些一般帳號自己的服務功能!所以就很麻煩!
- 現在,新的 systemd 管理機制,可以讓用戶透過 systemctl 搭配 --user 的方式,來處理自己的服務!並且跟 root 的角色一樣, 可以透過 enable 來讓系統主動喚醒這些一般帳號的服務喔!
- 只是,一般來說,這種一般用戶主動喚醒的服務,只有『使用者第一次登入時』才會喚醒!而不像 root, 在系統啟動之後就主動喚醒。這個是與系統最大的不同!但是也能夠改變啦~透過 loginctl 來修改~
- 讓一般用戶可以具有 systemctl enable 的權限:
- 啟動 enable-linger:
如前所述,如果要讓一般用戶設定 systemd 為 enable,且可以讓這個服務在開機時啟動,而不是登入後才啟動,
那用戶自己得要啟動名為 loginctl enable-linger 的功能才行!
$ loginctl --help $ loginctl show-user student UID=1000 GID=1000 Name=student Timestamp=Fri 2021-12-17 06:37:20 CST TimestampMonotonic=1840394036 RuntimePath=/run/user/1000 Service=user@1000.service Slice=user-1000.slice Display=2 State=active Sessions=2 IdleHint=no IdleSinceHint=1639775337931783 IdleSinceHintMonotonic=82937616851 Linger=no $ loginctl enable-linger student $ loginctl show-user student ..... Linger=yes
- 啟動 enable-linger:
如前所述,如果要讓一般用戶設定 systemd 為 enable,且可以讓這個服務在開機時啟動,而不是登入後才啟動,
那用戶自己得要啟動名為 loginctl enable-linger 的功能才行!
- 開始使用 systemctl --user 管理用戶的服務:
- 了解用戶的 systemd 設定檔位置:
基本上,用戶的 systemd 設定檔位置放置於:- ~/.config/systemd/user/
- 使用 systemctl 管理,需要登入的模式:
比較特別的是,你不能使用 su 或 sudo 來處理用戶的 systemd 服務,得要完整的登入系統! 例如透過本機的 tty 登入,或者是透過本機的圖形界面,或者是直接以 ssh 登入,否則不能操作 systemctl 喔!# 使用 su - student 的情境下: $ systemctl --user daemon-reload Failed to connect to bus: No such file or directory # 正常使用 tty 或者是 ssh student@localhost 的情境下,不會有錯誤訊息發生的! $ systemctl --user daemon-reload
- 使用 podman 的 generate 功能產生 systemd 範本檔:
你當然可以從頭撰寫一隻 systemd 的設定檔,不過總是覺得有點麻煩~所以,我們可以透過 podman 的 generate 指令來達成一個簡易的設定檔,然後再根據自己的需求來修改該檔案即可。 不過需要注意,設定檔一定要放置到 ~/.config/systemd/user 目錄下喔!$ mkdir -p ~/.config/systemd/user $ cd ~/.config/systemd/user # 先使用 httpd 映像檔建立名為 myweb 的容器,且掛載 ~/server/www 到 /var/www/html $ podman run -d --rm --name myweb -v /home/student/server/www:/var/www/html \ registry.access.redhat.com/ubi8/httpd-24 # 以上面的 myweb 為範本,來建立 systemd 的設定檔: $ podman generate systemd --name myweb --new --files /home/student/.config/systemd/user/container-myweb.service --name container: 以 container 這個容器名稱為範本 (容器要先存在) --new : 類似 --rm 的功能,關閉容器就刪除 --files : 在當前目錄建立設定檔 $ cat container-myweb.service [Unit] Description=Podman container-myweb.service Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=%t/containers [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/%n.ctr-id ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --sdnotify=conmon \ --cgroups=no-conmon --rm --replace -d --name myweb -v \ /home/student/server/www:/var/www/html registry.access.redhat.com/ubi8/httpd-24 ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all [Install] WantedBy=multi-user.target default.target
詳細的 systemd 設定檔請自行參考 systemd 的說明文件,上述設定當中,最重要的大概就是 ExecStart 以及 ExecStop 兩個啟動與關閉的腳本了!仔細檢查看看有沒有錯誤!有時候你可能需要做點修改就是了。 - 開始之前:一定要關閉原有的參考容器!
systemd 比較特別的地方,在於 systemd 只會管理『自己啟動』的服務,所以你自己手動啟動的程序, systemd 是沒有辦法管理的。也因為這樣,剛剛我們自己建立的樣本容器: myweb 必須要先關閉才行啊!$ podman stop myweb $ podman ps -a # 這裡要確認不存在 myweb 的容器名稱才行喔!
- 啟動、開機啟動 container-myweb 服務:
因為剛剛那個設定檔才建立而已,我們得要告知 systemctl 有多一個設定擋了! 因此得要先 daemon-reload 之後,才有辦法進行啟動與開機啟動喔!$ systemctl --user daemon-reload $ systemctl --user start container-myweb.service $ systemctl --user enable container-myweb.service $ podman ps # 這裡應該就可以看到 myweb 這個 container 的存在!
- 修改設定檔以及重新處理的方法:
設定完畢之後才想到,我們沒有辦法可以測試這個 myweb 有沒有順利的載入各項資料! 因為沒有設計 port mapping 啊!該如何是好?沒關係,我們可以手動修改設定檔, 假設目前我們使用 8085 連接到 myweb:8080 時,可以這樣做:$ vim ~/.config/systemd/user/container-myweb.service ..... ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --sdnotify=conmon \ --cgroups=no-conmon --rm -p 8085:8080 --replace -d --name myweb -v \ /home/student/server/www:/var/www/html registry.access.redhat.com/ubi8/httpd-24 ..... $ systemctl --user daemon-reload $ systemctl --user restart container-myweb.service $ podman ps # 這裡你應該就可以看到 8085 埠口會指向 8080 埠口了! $ echo 'check volume and url' > ~/server/www/myweb.html $ curl http://localhost:8085/myweb.html check volume and url
- 了解用戶的 systemd 設定檔位置:
- 實做練習:
- 目的:想要讓 student 這個用戶,可以透過 systemd 的方式來管理自己的 container,且管理的角度為 pod 類群。為了避免干擾,請將目前系統上面的所有的 container 全部刪除。不過要注意,由 systemd 管理的, 請使用 systemd 的方式關閉。由 podman 手動處理的,請手動關閉與刪除。
- 依據上述要求,使用 student 身份,建立名為 wwws 的 pod 類群,且分別對應 8080 與 3306 兩個埠口, 同時使用 podman 的網路 (--network podman)
- 透過 registry.access.redhat.com/ubi8/httpd-24 這個映像檔,建立名為 www_httpd 的容器,且: (1) 加入 wwws 當中,(2)執行在背景環境,(3)不要停止後刪除 (取消 --rm 的意思)
- 透過 docker.io/library/mariadb 這個映像檔,建立名為 www_database 的容器,且: (1) 加入 wwws 當中,(2)執行在背景環境,(3)不要停止後刪除,(4)傳遞 MARIADB_ROOT_PASSWORD 變數內容,密碼請自取。
- 進入 ~/.config/systemd/user 目錄後,以 wwws 為名,建立 systemd 的設定檔。 你應該會發現共有 3 個檔案分別被產生出來!非常重要喔!
- 透過 podman 的方式來『 stop 』容器與 pod。要注意,容器不能刪除喔!否則 pod 會啟動不了! 重要!重要!
- 使用 systemctl --user 來管理,先 daemon-reload 之後,才可以啟動 pod-wwws 喔!
- 最終請觀察 podman ps 看看有沒有順利啟動所有的容器!