MacOS 終于可以完美使用 Podman 了!
前言
Podman 是一個(gè)無(wú)守護(hù)程序與 Docker 命令兼容的下一代 Linux 容器工具,該項(xiàng)目由 RedHat 主導(dǎo),其他的細(xì)節(jié)可以參考 Podman 使用指南[1],本文的重點(diǎn)不是這個(gè)。
Podman 一直以來(lái)只能跑在 Linux 系統(tǒng)上,macOS 和 Windows 只能通過(guò) CLI 遠(yuǎn)程連接 Podman 的 API 來(lái)管理容器。事實(shí)上 Docker 也不支持 macOS 和 Windows,但 Docker 針對(duì) Windows 和 macOS 推出了專門的客戶端,客戶端里面集成了虛擬化相關(guān)的設(shè)置,通過(guò)嵌套一層虛擬化來(lái)支持 Docker。對(duì)于 Podman 來(lái)說(shuō),想要在 macOS 上運(yùn)行也只能通過(guò)虛擬化來(lái)實(shí)現(xiàn),網(wǎng)上也有不少方案,基本上都是通過(guò) Virtualbox 來(lái)實(shí)現(xiàn),都不太優(yōu)雅。本文將介紹一種相對(duì)更優(yōu)雅的方案,雖然不是很完美,但我已經(jīng)盡力做到接近完美了。。
HyperKit 介紹
HyperKit 是一個(gè)具有 hyperisor 能力的輕量級(jí)虛擬化工具集,包含了基于 xhyve(The BSD Hypervisor)的完整 hypervisor。HyperKit 設(shè)計(jì)成上層組件諸如 VPNKit[2] 和 DataKit[3] 的接口。xhyve 是 基于 bhyve 的 Mac OS X 移植版本,而 bhyve 又是 FreeBSD 下的虛擬化技術(shù)。。。
我們知道,Docker 在 Linux 上利用了 Linux 原生支持的容器方式實(shí)現(xiàn)資源和環(huán)境的隔離,直接利用宿主內(nèi)核,性能接近原生。然而,在 macOS 上卻仍然需要虛擬化的技術(shù)。早期的 Docker 干脆直接在開源的 VirtualBox 中構(gòu)建虛擬機(jī),性能低下。后期的 Docker 基于輕量化的虛擬化框架 HyperKit[4] 開發(fā),據(jù)說(shuō)性能得到很大提升。
本文將介紹如何通過(guò) HyperKit 來(lái)使用 Podman。方法也很簡(jiǎn)單,先通過(guò) Hyperkit 創(chuàng)建一個(gè)輕量級(jí)虛擬機(jī),然后在虛擬機(jī)中安裝 Podman,并開啟 remote API,最后在本地通過(guò) CLI 連接虛擬機(jī)中的 Podman。這和 macOS 中的 Docker 實(shí)現(xiàn)原理是一樣的,只不過(guò) Podman 是沒(méi)有 Daemon 的,與 Docker 相比可以節(jié)省不少資源。
2. 安裝 HyperKit
你可以自己下載源代碼編譯 HyperKit,但我不建議這么做,不同的 macOS 版本會(huì)遇到各種各樣的錯(cuò)誤。我這里推薦兩種超級(jí)簡(jiǎn)單的方法:
-
直接通過(guò)安裝 Docker 來(lái)獲得 HyperKit,因?yàn)?Docker Desktop on Mac 就是基于 HyperKit 實(shí)現(xiàn)的,所以安裝 Docker Desktop on Mac 就能夠獲得完整的 HyperKit 運(yùn)行環(huán)境。整個(gè)過(guò)程會(huì)非常順暢和簡(jiǎn)單。安裝完 Docker 之后可以永遠(yuǎn)不用打開 Docker,直接使用 HyperKit 就好。或者你可以直接卸載 Docker,卸載之前先把
hyperkit二進(jìn)制文件備份出來(lái),因?yàn)樾遁d Docker 也會(huì)刪掉hyperkit二進(jìn)制文件。 -
直接通過(guò)安裝
Multipass來(lái)獲得 HyperKit。Multipass 是 Canonical 公司(Ubuntu)開發(fā)的基于不同操作系統(tǒng)內(nèi)建原生 Hypervisor 實(shí)現(xiàn)的工作站。由于 Windows(Hyper-V),macOS(hyperkit)和 Linux(KVM)都原生支持 hypervisor,這樣通過(guò)multipass shell命令就能夠在一個(gè) shell 中實(shí)現(xiàn)創(chuàng)建運(yùn)行 Ubuntu 虛擬機(jī)。在 macOS 平臺(tái),默認(rèn)的后端是 hyperkit,需要 macOS Yosemite (10.10.3) 以上版本并且需要安裝在 2010 以后生產(chǎn)的 Mac 設(shè)備。安裝方法很簡(jiǎn)單:
$ brew cask install multipass
安裝好了之后可以在 /Library/Application Support/com.canonical.multipass/bin/ 目錄下找到 hyperkit 二進(jìn)制文件。
3. 創(chuàng)建虛擬機(jī)
你可以直接通過(guò) hyperkit 來(lái)創(chuàng)建虛擬機(jī),但參數(shù)比較復(fù)雜,有興趣的自己研究吧。我推薦直接通過(guò) multipass 來(lái)創(chuàng)建,命令特別簡(jiǎn)單:
$ multipass launch -c 2 -d 10G -m 2G -n podman
-
-n : 指定啟動(dòng)實(shí)例名字 -
-c : 分配 CPU 數(shù)量 -
-d : 設(shè)置磁盤容量 -
-m : 設(shè)置內(nèi)存容量
第一次啟動(dòng)虛擬機(jī)的時(shí)候會(huì)去拉去鏡像,國(guó)內(nèi)網(wǎng)速可能會(huì)很慢。
查看已經(jīng)啟動(dòng)的虛擬機(jī):
$ multipass list
Name State IPv4 Image
podman Running 192.168.64.2 Ubuntu 20.04 LTS
進(jìn)入虛擬機(jī):
$ multipass shell podman
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-52-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sun Nov 8 19:30:29 CST 2020
System load: 0.0 Processes: 119
Usage of /: 13.4% of 11.46GB Users logged in: 0
Memory usage: 11% IPv4 address for enp0s2: 192.168.64.2
Swap usage: 0%
0 updates can be installed immediately.
0 of these updates are security updates.
Last login: Sun Nov 8 17:38:31 2020 from 192.168.64.1
ubuntu@podman:~$
4. 安裝 Podman
在虛擬機(jī)中安裝 Podman:
ubuntu@podman:~$ . /etc/os-release
ubuntu@podman:~$ echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
ubuntu@podman:~$ curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -
ubuntu@podman:~$ sudo apt-get update
ubuntu@podman:~$ sudo apt-get -y upgrade
ubuntu@podman:~$ sudo apt-get -y install podman
5. 建立 Podman Socket
Podman 依賴于 systemd 的 socket activation[5] 特性。假設(shè) Daemon B 依賴于 Daemon A,那么它就必須等到 Daemon A 完成啟動(dòng)后才能啟動(dòng)。socket activation的思想就是:Daemon B 啟動(dòng)時(shí)其實(shí)并不需要 Daemon A 真正運(yùn)行起來(lái),它只需要 Daemon A 建立的 socket 處于 listen 狀態(tài)就 OK 了。而這個(gè) socket 不必由 Daemon A 建立, 而是由 systemd 在系統(tǒng)初始化時(shí)就建立。當(dāng) Daemon B 發(fā)起啟動(dòng)時(shí)發(fā)起連接,systemd 再將 Daemon A 啟動(dòng),當(dāng) Daemon A 啟動(dòng)后,再將 socket 歸還給 Daemon A。
Podman 會(huì)通過(guò) podman.socket 先創(chuàng)建一個(gè)處于監(jiān)聽狀態(tài)的 socket 文件 /run/podman/podman.sock,當(dāng)有進(jìn)程向該 socket 發(fā)起連接時(shí),systemd 會(huì)啟動(dòng)同名的 service:podman.service,以接管該 socket。先看看 podman.socket 和 podman.service 長(zhǎng)啥樣:
ubuntu@podman:~$ sudo systemctl cat podman.socket
# /lib/systemd/system/podman.socket
[Unit]
Description=Podman API Socket
Documentation=man:podman-system-service(1)
[Socket]
ListenStream=%t/podman/podman.sock
SocketMode=0660
[Install]
WantedBy=sockets.target
ubuntu@podman:~$ sudo systemctl cat podman.service
# /lib/systemd/system/podman.service
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0
[Service]
Type=notify
KillMode=process
ExecStart=/usr/bin/podman system service
設(shè)置開機(jī)自啟 podman.socket,并立即啟動(dòng):
ubuntu@podman:~$ sudo systemctl enable podman.socket --now
確認(rèn) socket 是否正處于監(jiān)聽狀態(tài):
ubuntu@podman:~$ podman --remote info
host:
arch: amd64
buildahVersion: 1.16.1
cgroupManager: systemd
cgroupVersion: v1
conmon:
package: 'conmon: /usr/libexec/podman/conmon'
path: /usr/libexec/podman/conmon
version: 'conmon version 2.0.20, commit: '
cpus: 2
...
3. 客戶端 CLI 設(shè)置
接下來(lái)所有的設(shè)置,如不作特殊說(shuō)明,都在 macOS 本地終端執(zhí)行。
Podman 遠(yuǎn)程連接依賴 SSH,所以需要設(shè)置免密登錄,先生成秘鑰文件:
$ ssh-keygen -t rsa # 一路回車到底
然后將本地的公鑰 ~/.ssh/id_rsa.pub 追加到虛擬機(jī)的 /root/.ssh/authorized_keys 文件中。
安裝 Podman CLI:
$ brew install podman
添加遠(yuǎn)程連接:
$ podman system connection add ubuntu --identity ~/.ssh/id_rsa ssh://[email protected]/run/podman/podman.sock
查看已經(jīng)建立的連接:
$ podman system connection list
Name Identity URI
podman* /Users/Ryan/.ssh/id_rsa ssh://[email protected]:22/run/podman/podman.sock
由于這是第一個(gè)連接,所以被直接設(shè)置為默認(rèn)連接(podman 后面加了 *)。
測(cè)試遠(yuǎn)程連接是否可用:
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ podman pull nginx:alpine
Trying to pull docker.io/library/nginx:alpine...
Getting image source signatures
Copying blob sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964
Copying blob sha256:9dd8e8e549988a3e2c521f27f805b7a03d909d185bb01cdb4a4029e5a6702919
Copying blob sha256:85defa007a8b33f817a5113210cca4aca6681b721d4b44dc94928c265959d7d5
Copying blob sha256:f2dc206a393cd74df3fea6d4c1d3cefe209979e8dbcceb4893ec9eadcc10bc14
Copying blob sha256:0ca72de6f95718a4bd36e45f03fffa98e53819be7e75cb8cd1bcb0705b845939
Copying config sha256:e5dcd7aa4b5e5d2df8152b9e58aba32a05edd9b269816f5d8b7ced535743d16c
Writing manifest to image destination
Storing signatures
e5dcd7aa4b5e5d2df8152b9e58aba32a05edd9b269816f5d8b7ced535743d16c
$ podman image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/nginx alpine e5dcd7aa4b5e 2 days ago 23.3 MB
現(xiàn)在我們就可以直接在本地用 podman 愉快地玩耍了!
如果你建立了多個(gè)連接,可用使用 --connection 參數(shù)指定遠(yuǎn)程連接,或者使用 podman system connection default
來(lái)設(shè)置默認(rèn)的遠(yuǎn)程連接。
最后,我們來(lái)看看 hyperkit 的內(nèi)存占用:
物理內(nèi)存只占用了 921M,如果你覺(jué)得這個(gè)內(nèi)存占用很多,不妨去對(duì)比下 Docker Desktop 的內(nèi)存占用。
總結(jié)
本文介紹了在 macOS 中使用 podman 的方法,通過(guò) HyperKit 創(chuàng)建 Ubuntu 虛擬機(jī)運(yùn)行 Podman,并建立 Podman Socket,然后客戶端通過(guò) SSH 連接服務(wù)端的 Socket,以實(shí)現(xiàn)通過(guò)遠(yuǎn)程連接來(lái)管理容器。
參考資料
Podman 使用指南: https://fuckcloudnative.io/posts/podman-sidecar/
[2]VPNKit: https://github.com/moby/vpnkit
[3]DataKit: https://github.com/moby/datakit
[4]HyperKit: https://github.com/moby/hyperkit
[5]socket activation: http://0pointer.de/blog/projects/socket-activation.html
DD自研的滬牌代拍業(yè)務(wù),點(diǎn)擊直達(dá)
往期推薦
云原生是一種信仰 ?

掃碼關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!
﹀
﹀
﹀
深度內(nèi)容
推薦加入




