Docker 大勢已去,Podman 萬歲
前言
鄭重宣告:本文不是 Podman 的入門篇,入門請閱讀這篇文章:再見 Docker,是時候擁抱下一代容器工具了
Podman
原來是 CRI-O 專案的一部分,後來被分離成一個單獨的專案叫 libpod。Podman 的使用體驗和 Docker
類似,不同的是 Podman 沒有 daemon。以前使用 Docker CLI 的時候,Docker CLI 會通過 gRPC API 去跟 Docker Engine 說「我要啟動一個容器」,然後 Docker Engine 才會通過 OCI Container runtime(預設是 runc
)來啟動一個容器。這就意味著容器的程式不可能是 Docker CLI 的子程式,而是 Docker Engine 的子程式。
Podman 比較簡單粗暴,它不使用 Daemon,而是直接通過 OCI runtime(預設也是 runc
)來啟動容器,所以容器的程式是 podman 的子程式。這比較像 Linux 的 fork/exec
模型,而 Docker 採用的是 C/S
(客戶端/伺服器)模型。與 C/S 模型相比,fork/exec
模型有很多優勢,比如:
系統管理員可以知道某個容器程式到底是誰啟動的。
如果利用
cgroup
對 podman 做一些限制,那麼所有建立的容器都會被限制。SD_NOTIFY : 如果將 podman 命令放入
systemd
單元檔案中,容器程式可以通過 podman 返回通知,表明服務已準備好接收任務。socket 啟用 : 可以將連線的
socket
從 systemd 傳遞到 podman,並傳遞到容器程式以便使用它們。
廢話不多說,下面我們直接進入實戰環節,本文將手把手教你如何用 podman 來部署靜態部落格,並通過 Sidecar 模式將部落格所在的容器加入到 Envoy
mesh 之中。
方案架構
我的部署方案涉及到兩層 Envoy:
首先會有一個前端代理單獨跑一個容器。前端代理的工作是給訪問者提供一個入口,將來自外部的訪問請求轉發到具體的後端服務。
其次,部落格靜態頁面由 nginx 提供,同時以 Sidecar 模式執行一個
Envoy
容器,它與 nginx 共享network nemspace
。所有的 Envoy 形成一個 mesh,然後在他們之間共享路由資訊。
我之前寫過一篇用 Docker
部署 hugo 靜態部落格並配置 HTTPS
證照的文章,本文采用的是相同的方案,只是將 docker 換成了 podman,具體參考為 Envoy 開啟 TLS 驗證實戰。
部署 hugo 和 sidecar proxy
我的部落格是通過 hugo 生成的靜態頁面,可以將其放到 nginx
中,其他靜態網站工具類似(比如 hexo 等),都可以這麼做。現在我要做的是讓 nginx 容器和 envoy 容器共享同一個 network namespace,同時還要讓前端代理能夠通過域名來進行服務發現。以前用 docker 很簡單,直接用 docker-compose 就搞定了,podman 就比較麻煩了,它又不能用 docker-compose
,服務發現看來是搞不定了。
好不容易在 Github 上發現了一個專案叫 podman-compose,以為有救了,試用了一下發現還是不行,podman-compose 建立容器時會將欄位 network_mode: "service:hugo"
轉化為 podman CLI 的引數 --network service:hugo
(真腦殘),導致容器建立失敗,報錯資訊為 CNI network "service:hugo" not found
。將該欄位值改為 network_mode: "container:hugo_hugo_1"
可以啟動成功,然而又引來了另一個問題:podman-compose 的做法是為每一個 service
建立一個 pod
(pod 的名字為 docker-compose.yml 所在目錄名稱),然後往這個 pod 中新增容器。我總不能將前端代理和後端服務塞進同一個 pod 中吧?只能分別為前端代理和 hugo 建立兩個目錄,然後分別建立 docker-compose.yml。這個問題解決了,下個問題又來了,podman-compose 不支援通過 service name 進行服務發現,扒了一圈發現支援 links
(其實就是加個引數 --add-host
),然而 links 只在同一個 pod 下才生效,我都拆分成兩個 pod 了,links 鞭長莫及啊,還是沒什麼卵用。我能怎麼辦,現在唯一的辦法就是手擼命令列了。
上面我提到了一個新名詞叫 pod
,這裡花 30 秒的時間給大家簡單介紹一下,如果你是 Kubernetes
的重度使用者,對這個詞應該不陌生,但這裡確實說的是 podman 的 pod,意思還是一樣的,先建立一個 pause
容器,然後再建立業務容器,業務容器共享 pause
容器的各種 linux namespace,因此同一個 pod 中的容器之間可以通過 localhost 輕鬆地相互通訊。不僅如此,podman 還可以將 pod 匯出為 Kubernetes 的宣告式資源定義,舉個栗子:
先建立一個 pod:
$ podman pod create --name hugo
檢視 pod:
$ podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
88226423c4d2 hugo Running 2 minutes ago 2 7e030ef2e7ca
在這個 pod 中啟動一個 hugo 容器:
$ podman run -d --pod hugo nginx:alpine
檢視容器:
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c91cab1e99d docker.io/library/nginx:alpine nginx -g daemon o... 3 minutes ago Up 3 minutes ago reverent_kirch
檢視所有容器,包括 pause 容器:
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c91cab1e99d docker.io/library/nginx:alpine nginx -g daemon o... 4 minutes ago Up 4 minutes ago reverent_kirch
7e030ef2e7ca k8s.gcr.io/pause:3.1 6 minutes ago Up 6 minutes ago 88226423c4d2-infra
檢視所有容器,包括 pause 容器,並顯示容器所屬的 pod id:
$ podman ps -ap
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD
3c91cab1e99d docker.io/library/nginx:alpine nginx -g daemon o... 4 minutes ago Up 4 minutes ago reverent_kirch 88226423c4d2
7e030ef2e7ca k8s.gcr.io/pause:3.1 6 minutes ago Up 6 minutes ago 88226423c4d2-infra 88226423c4d2
檢視 pod 中程式的資源使用情況:
$ podman pod top hugo
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 0.000 8m5.045493912s ? 0s nginx: master process nginx -g daemon off;
nginx 6 1 0.000 8m5.045600833s ? 0s nginx: worker process
nginx 7 1 0.000 8m5.045638877s ? 0s nginx: worker process
0 1 0 0.000 9m41.051039367s ? 0s /pause
將 pod 匯出為宣告式部署清單:
$ podman generate kube hugo > hugo.yaml
檢視部署清單內容:
$ cat hugo.yaml
# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-1.0.2-dev
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: 2019-10-17T04:17:40Z
labels:
app: hugo
name: hugo
spec:
containers:
- command:
- nginx
- -g
- daemon off;
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
- name: container
value: podman
- name: NGINX_VERSION
value: 1.17.4
- name: NJS_VERSION
value: 0.3.5
- name: PKG_RELEASE
value: "1"
image: docker.io/library/nginx:alpine
name: reverentkirch
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
workingDir: /
status: {}
怎麼樣,是不是有種熟悉的味道?這是一個相容 kubernetes 的 pod 定義,你可以直接通過 kubectl apply -f hugo.yaml
將其部署在 Kubernetes 叢集中,也可以直接通過 podman 部署,步驟大致是這樣的:
先刪除之前建立的 pod:
$ podman pod rm -f hugo
然後通過部署清單建立 pod:
$ podman play kube hugo.yaml
回到之前的問題,如果通過宣告式定義來建立 pod,還是無法解決服務發現的問題,除非換個支援靜態 IP 的 CNI
外掛,而支援靜態 IP 的這些 CNI 外掛又需要 etcd 作為資料庫,我就這麼點資源,可不想再加個 etcd,還是手擼命令列吧。
首先我要建立一個 hugo 容器,並指定容器的 IP:
$ podman run -d --name hugo \
--ip=10.88.0.10 \
-v /opt/hugo/public:/usr/share/nginx/html \
-v /etc/localtime:/etc/localtime \
nginx:alpine
再建立一個 envoy 容器,與 hugo 容器共享 network namespace:
$ podman run -d --name hugo-envoy \
-v /opt/hugo/service-envoy.yaml:/etc/envoy/envoy.yaml \
-v /etc/localtime:/etc/localtime \
--net=container:hugo envoyproxy/envoy-alpine:latest
service-envoy.yaml 的內容如下:
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
access_log:
- name: envoy.file_access_log
config:
path: "/dev/stdout"
route_config:
name: local_route
virtual_hosts:
- name: service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: local_service
http_filters:
- name: envoy.router
config: {}
clusters:
- name: local_service
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: 127.0.0.1
port_value: 80
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8081
具體的含義請參考為 Envoy 開啟 TLS 驗證實戰。
本文開頭提到 podman 建立的容器是 podman 的子程式,這個表述可能比較模糊,實際上 podman 由兩部分組成,一個是 podman CLI,還有一個是 container runtime,container runtime 由 conmon
來負責,主要包括監控、日誌、TTY 分配以及類似 out-of-memory
情況的雜事。也就是說,conmon 是所有容器的父程式。
conmon 需要去做所有 systemd
不做或者不想做的事情。即使 CRI-O 不直接使用 systemd 來管理容器,它也將容器分配到 sytemd 相容的 cgroup
中,這樣常規的 systemd 工具比如 systemctl
就可以看見容器資源使用情況了。
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42762bf7d37a docker.io/envoyproxy/envoy-alpine:latest /docker-entrypoin... About a minute ago Up About a minute ago hugo-envoy
f0204fdc9524 docker.io/library/nginx:alpine nginx -g daemon o... 2 minutes ago Up 2 minutes ago hugo
對 cgroup 不熟的同學,可以參考下面這個系列:
零基礎的同學建議按照上面的目錄從上到下打怪升級,祝你好運!
部署前端代理
這個很簡單,直接建立容器就好了:
$ podman run -d --name front-envoy \
--add-host=hugo:10.88.0.10 \
-v /opt/hugo/front-envoy.yaml:/etc/envoy/envoy.yaml \
-v /etc/localtime:/etc/localtime \
-v /root/.acme.sh/yangcs.net:/root/.acme.sh/yangcs.net \
--net host envoyproxy/envoy
由於沒辦法自動服務發現,需要通過引數 --add-host
手動新增 hosts 到容器中。envoy 的配置檔案中是通過域名來新增 cluster 的,front-envoy.yaml 內容如下:
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
access_log:
- name: envoy.file_access_log
config:
path: "/dev/stdout"
route_config:
virtual_hosts:
- name: backend
domains:
- "*"
routes:
- match:
prefix: "/"
redirect:
https_redirect: true
response_code: "FOUND"
http_filters:
- name: envoy.router
config: {}
- address:
socket_address:
address: 0.0.0.0
port_value: 443
filter_chains:
- filter_chain_match:
server_names: ["yangcs.net", "www.yangcs.net"]
tls_context:
common_tls_context:
alpn_protocols: h2
tls_params:
tls_maximum_protocol_version: TLSv1_3
tls_certificates:
- certificate_chain:
filename: "/root/.acme.sh/yangcs.net/fullchain.cer"
private_key:
filename: "/root/.acme.sh/yangcs.net/yangcs.net.key"
filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "yangcs.net"
- "www.yangcs.net"
routes:
- match:
prefix: "/admin"
route:
prefix_rewrite: "/"
cluster: envoy-ui
- match:
prefix: "/"
route:
cluster: hugo
response_headers_to_add:
- header:
key: "Strict-Transport-Security"
value: "max-age=63072000; includeSubDomains; preload"
http_filters:
- name: envoy.router
config: {}
clusters:
- name: hugo
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
http2_protocol_options: {}
hosts:
- socket_address:
address: hugo
port_value: 8080
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
具體的含義請參考為 Envoy 開啟 TLS 驗證實戰。
現在就可以通過公網域名訪問部落格網站了,如果後續還有其他應用,都可以參考第二節的步驟,然後重新建立前端代理,新增 --add-host
引數。以我的網站 https://www.yangcs.net 為例:
我好像透露了一些什麼不得了的東西,就此打住,你也不要說,你也不要問。
開機自啟
由於 podman 不再使用 daemon 管理服務,--restart
引數被廢棄了,要想實現開機自動啟動容器,只能通過 systemd 來管理了。先建立 systemd 服務配置檔案:
$ vim /etc/systemd/system/hugo_container.service
[Unit]
Description=Podman Hugo Service
After=network.target
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a hugo
ExecStop=/usr/bin/podman stop -t 10 hugo
Restart=always
[Install]
WantedBy=multi-user.target
$ vim /etc/systemd/system/hugo-envoy_container.service
[Unit]
Description=Podman Hugo Sidecar Service
After=network.target
After=network-online.target
After=hugo_container.service
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a hugo-envoy
ExecStop=/usr/bin/podman stop -t 10 hugo-envoy
Restart=always
[Install]
WantedBy=multi-user.target
$ vim /etc/systemd/system/front-envoy_container.service
[Unit]
Description=Podman Front Envoy Service
After=network.target
After=network-online.target
After=hugo_container.service hugo-envoy_container.service
[Service]
Type=simple
ExecStart=/usr/bin/podman start -a front-envoy
ExecStop=/usr/bin/podman stop -t 10 front-envoy
Restart=always
[Install]
WantedBy=multi-user.target
然後將之前停止之前建立的容器,注意:是停止,不是刪除!
$ podman stop $(podman ps -aq)
最後通過 systemd 服務啟動這些容器。
$ systemctl start hugo_container
$ systemctl start hugo-envoy_container
$ systemctl start front-envoy_container
設定開機自啟。
之後每次系統重啟後 systemd 都會自動啟動這個服務所對應的容器。
總結
以上就是將部落格從 Docker 遷移到 Podman 的所有變更操作,總體看下來還是比較曲折,因為 Podman 是為 Kubernetes 而設計的,而我要求太高了,就一個資源緊張的 vps,即不想上 Kubernetes
,也不想上 etcd
,既想搞 sidecar,又想搞自動服務發現,我能怎麼辦,我也很絕望啊,這個事怨不得 podman,為了防止在大家心裡留下 “podman 不好用” 的印象,特此宣告一下。啥都不想要,只能自己想辦法了~~
●
●
●
雲原生是一種信仰 ?
掃碼關注公眾號
後臺回覆◉圖譜◉領取史上最強 Kubernetes 知識圖譜
點選 "閱讀原文" 獲取更好的閱讀體驗!
相關文章
- Docker 大勢已去,Podman 即將崛起!Docker
- 《Apex 英雄》大勢已去?
- Docker Vs PodmanDocker
- docker(podman)容器設定中文環境Docker
- Docker 與 Podman 容器管理的比較Docker
- 如何在 Windows 使用 Podman Desktop 取代 Docker DesktopWindowsDocker
- [轉帖]Podman與docker相容性問題Docker
- Podman 已成 Linux 官方標配!Docker 沒戲了?LinuxDocker
- Podman
- 在WSL2的Ubuntu中安裝和使用Docker/PodmanUbuntuDocker
- podman安裝和普通使用者使用podman的方式及podman常用命令
- RHCE(podman容器)
- 【轉】Lisp 已死,Lisp 萬歲!Lisp
- .NET已死,.NET萬歲 - Richard Reedy
- 在阿里,40歲的奮鬥姿勢阿里
- podman 入門實戰
- podman常用命令
- 在 macOS 中使用 PodmanMac
- 什麼是docker?docker有什麼優勢?Docker
- Podman修改 image儲存位置
- 用 Podman Compose 管理容器
- 谷歌大神Jeff Dean領銜,萬字展望5大AI趨勢谷歌AI
- 國家統計局:中國20歲至40歲男性比女性多1752萬人
- 《植物大戰殭屍》10歲了
- 冒險遊戲已逝?冒險遊戲萬歲!遊戲
- Docker 萬字入門教程Docker
- 幽默:研究表明每日冥想可延緩大腦衰老,41歲的大腦年齡為33歲 - medicalxpress
- 30歲,沒有月入過萬算失敗嗎?用視覺化分析30歲的人收入真相視覺化
- 國內郵電大學的歲月往事
- 為什麼Podman執行容器更安全?
- 使用 Bitwarden 和 Podman 管理你的密碼密碼
- Podman Desktop安裝與使用-Windows10Windows
- 在 Fedora 中結合權能使用 Podman
- 19 歲程式設計師找 bug 居然賺到 100 萬美元程式設計師
- 午睡睡得好,大腦小不了!近40萬人研究:習慣性午睡,大腦年輕2.6-6.5歲!最佳午睡時間是?
- 網際網路大潮已去,疫情後資料分析公司怎麼走?
- 近期大模型趨勢九大摘要大模型
- ECM 11大優勢