podman是什么
前幾天將自己的開發(fā)環(huán)境切到了centos 8样勃,為了兼容之前一些業(yè)務程序的編譯欺抗,所以我需要一個centos 7容器离例。在yum install docker之后發(fā)現(xiàn)礼预,centos 8的docker被一個叫podman的程序給替代了第步。去簡單了解了下脑融,podman是redhat一直主推的本地容器解決方案喻频,能夠提供和k8s的pod相同的容器組織方式,多個容器可以放在同一個pod中肘迎,同一個pod中的容器共用網(wǎng)絡甥温。
podman系列主要包含三個命令podman、buildah妓布、skopeo姻蚓,其中podman本身負責運行、停止匣沼、管理容器狰挡,buildah負責構(gòu)建容器鏡像、skopeo負責與remote repo交互释涛,拉取或推送鏡像加叁。但我們使用時不必這么麻煩,redhat為了方便用戶從docker遷移到podman唇撬,在podman上幾乎實現(xiàn)了大多數(shù)docker的常用命令它匕,podman會替你轉(zhuǎn)調(diào)buildah和skopeo,你甚至可以直接 alias docker=podman窖认,然后像使用docker一樣使用podman豫柬。podman系列的的安裝方法如下:
yum module list | grep container
# yum install podman buildah skopeo
yum install @container-tools
相比于docker,podman系列拋棄了server端扑浸,不再有類似dockerd的后臺進程烧给,執(zhí)行命令時不再需要將命令和包拷貝到server端,在build image時首装,能夠節(jié)省一筆拷貝的開銷创夜。podman不再要求root權(quán)限,可以創(chuàng)建rootfull和rootless兩種類型的容器仙逻,雖然兩者的容器運行時都是基于runc驰吓。podman引入了k8s中pod的概念,可以將多個容器放到同一個pod中系奉,共享網(wǎng)絡檬贰;默認的pod基礎(chǔ)容器和k8s一樣執(zhí)行/pause命令,并支持用戶自定義基礎(chǔ)容器缺亮。
如何簡單的使用podman可以參考: https://thenewstack.io/deploy-a-pod-on-centos-with-podman/
問題
現(xiàn)在回到我的問題翁涤。安裝好podman之后,按照如下步驟,開始搭建編譯容器葵礼。在yum install時号阿,發(fā)現(xiàn)容器不能訪問外網(wǎng)
podman pull centos:7
cat > Dockerfile <<EOF
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
EOF
podman build -t c7-systemd .
podman run -it -d --name mybuild -v /data/share:/data/share c7-systemd
podman exec -it mybuild bash
yum install gcc-c++ libstdc++-static openssl-devel
問題具體表現(xiàn)是:
- 直接ping外網(wǎng)域名,返回bad address
- ping 域名對應的ip正常
- curl 域名對應的ip返回no route to host
- 容器內(nèi)的網(wǎng)絡配置文件鸳粉,一切正常
(venv) ? root@new1 ~ $podman run --rm alpine ping www.baidu.com
ping: bad address 'www.baidu.com'
(venv) ? root@new1 ~ $nslookup www.baidu.com
Server: 10.11.56.22
Address: 10.11.56.22#53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 14.215.177.39
Name: www.a.shifen.com
Address: 14.215.177.38
(venv) ? root@new1 ~ $podman run --rm alpine ping 14.215.177.39
PING 14.215.177.39 (14.215.177.39): 56 data bytes
64 bytes from 14.215.177.39: seq=0 ttl=47 time=12.559 ms
64 bytes from 14.215.177.39: seq=1 ttl=47 time=10.266 ms
(venv) ? root@new1 ~ $podman run --rm alpine curl 14.215.177.39
curl: (7) Failed connect to 10.28.36.104:80; No route to host
容器的網(wǎng)絡, CNI
podman容器有三種網(wǎng)絡模式:bridge扔涧、host和none。啟動容器時届谈,不顯式指定的話枯夜,使用的是bridge網(wǎng)絡;可以通過--net=
參數(shù)執(zhí)行為host或none模式艰山。host模式下湖雹,容器共享宿主機的網(wǎng)絡;none模式表示容器不存在網(wǎng)絡棧曙搬,僅有本地回環(huán)摔吏,無法與外界或其他pod通信。
對于我們上面的問題织鲸,一個最簡單的解決方法就是使用host網(wǎng)絡模式重新啟動容器舔腾。當時我也是這樣做的,但正常情況下bridge模式為什么不行呢搂擦?這問題也得有個答案稳诚。
(venv) ? root@new1 ~ $podman run --rm --net=host alpine ping www.baidu.com
PING www.baidu.com (14.215.177.38): 56 data bytes
64 bytes from 14.215.177.38: seq=0 ttl=48 time=9.428 ms
64 bytes from 14.215.177.38: seq=1 ttl=48 time=9.328 ms
眾所周知,容器的網(wǎng)絡實現(xiàn)一般基于CNI(Container Networking Interface)瀑踢。podman也不例外扳还,基于CNI來實現(xiàn)其bridged network stack。CNI是容器的網(wǎng)絡標準橱夭,類似的還有CRI(Container runtime interface)氨距、CSI(Container storage interface),是k8s為了規(guī)范底層pod的實現(xiàn)方式棘劣,制定的幾種標準接口俏让。
podman使用的CNI項目是CNI的一個使用最廣泛的實現(xiàn),該項目基于iptables來實現(xiàn)bridged network stack茬暇。CNI通過podman提供的信息首昔,以及描述podman所需網(wǎng)橋信息的默認配置/etc/cni/net.d/87-podman-bridge.conflist來創(chuàng)建虛擬網(wǎng)橋。
CNI項目使用iptables來實現(xiàn)各種網(wǎng)絡棧糙俗,這就存在一個問題勒奇,當其他程序?qū)θ萜魉拗鳈C的netfilter規(guī)則進行修改,可能會影響到podman容器的網(wǎng)絡訪問巧骚。比如iptables規(guī)則的修改赊颠、firewalld規(guī)則的修改都會影響容器的網(wǎng)絡訪問格二。所以我們需要去分析當前的iptables配置和firewalld配置。
問題解決
檢查iptables規(guī)則
iptables采取黑名單策略竣蹦,INPUT顶猜、FORWARD、OUTPUT chain的默認規(guī)則都是ACCEPT痘括,對于沒有顯式配置DROP規(guī)則的網(wǎng)絡報驶兜,是采取放通的處理方式。
并且CNI創(chuàng)建了自己的chain CNI-FORWARD远寸,并且,針對容器ip 10.88.0.46(這是一個運行中的容器)放通了所有的進出流量屠凶。iptables的規(guī)則沒有問題驰后。
(venv) ? root@new1 ~ $iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
CNI-FORWARD all -- anywhere anywhere /* CNI firewall plugin rules */
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain CNI-FORWARD (1 references)
target prot opt source destination
CNI-ADMIN all -- anywhere anywhere /* CNI firewall plugin rules */
ACCEPT all -- anywhere 10.88.0.46 ctstate RELATED,ESTABLISHED
ACCEPT all -- 10.88.0.46 anywhere
Chain CNI-ADMIN (1 references)
target prot opt source destination
檢查firewalld的配置
firewalld和iptables一樣,都是基于netfilter實現(xiàn)的矗愧,但firewalld比較新灶芝,優(yōu)化了很多iptables的問題。其中一個就是唉韭,firewalld采取白名單策略夜涕,僅讓符合配置中規(guī)則的流量通過。
firewalld以zone作為配置管理的單位属愤,可以為不同的區(qū)域配置不同的放通規(guī)則女器。可以通過配置入站ip地址范圍或者配置網(wǎng)絡接口來將網(wǎng)絡包住诸,分配到不同的zone進行處理驾胆。
# 列出所有的zone
? root@new1 ~ $firewall-cmd --get-zones
block dmz drop external home internal public trusted work
默認情況下,所有的interface都會被放入public zone贱呐。配置了interface或者ip范圍的zone丧诺,被稱為active zone。默認情況下奄薇,只有public zone是活躍的驳阎。
# 列出active zone
? root@new1 ~ $firewall-cmd --get-active-zones
public
interfaces: enp0s3 enp0s8
以這個zone的配置為例,簡單解釋下每行的含義
? root@new1 ~ $firewall-cmd --zone=public --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: enp0s3 enp0s8
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
簡單逐行做下解釋:
- public (active) 表示public區(qū)域是active的
- target: default 當包與public區(qū)域匹配馁蒂,而沒有被下述規(guī)則所處理呵晚,的包的處理方式,除default远搪,還有其他方式
ACCEPT:通過這個包劣纲。
%%REJECT%%:拒絕這個包,并返回一個拒絕的回復谁鳍。
DROP:丟棄這個包癞季,不回復任何信息劫瞳。
default:不做任何事情。該區(qū)域不再管它绷柒,返回上一層處理 - icmp-block-inversion: no
- interfaces: enp0s3 enp0s8 列出這個zone上關(guān)聯(lián)的interface
- sources: 列出這個區(qū)域上的源志于,現(xiàn)在是空,如果有的話废睦,是類似33.233.233.0/24這種格式
- services: cockpit dhcpv6-client ssh 列出允許通過防火墻的服務伺绽,可以通過firewall-cmd --get-services列出所有服務
- ports: 列出允許通過防火墻的target port,當要放通一個沒有定義的服務時嗜湃,可以使用端口
- protocols:
- masquerade: no 表示這個區(qū)域不支持ip偽裝奈应。允許的話,將允許ip轉(zhuǎn)發(fā)购披,計算機作為路由器需要開啟這個功能
- icmp-blocks: 要阻塞的icmp流量名單
- rich rules: 高級規(guī)則
通過ip addr看到podman創(chuàng)建的接口是cni-podman0
4: cni-podman0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 5e:a4:d6:86:c8:b6 brd ff:ff:ff:ff:ff:ff
inet 10.88.0.1/16 brd 10.88.255.255 scope global cni-podman0
valid_lft forever preferred_lft forever
inet6 fe80::5ca4:d6ff:fe86:c8b6/64 scope link
valid_lft forever preferred_lft forever
現(xiàn)在杖挣,我們找到了問題所在,podman所創(chuàng)建的接口cni-podman0刚陡,沒有任何firewall zone與之匹配惩妇,默認的行為是拒絕來自cni-podman0的包,所以解決方法是將cni-podman0加入到trusted zone中筐乳。
? root@new1 ~ $firewall-cmd --zone=trusted --add-interface=cni-podman0
success
? root@new1 ~ $firewall-cmd --get-active-zones
public
interfaces: enp0s3 enp0s8
trusted
interfaces: cni-podman0
trusted zone的配置如下歌殃,其target是ACCEPT,只要將interface 或者source加入該zone蝙云,來自該interface或source的包就會被接受氓皱。
? root@new1 ~ $firewall-cmd --zone=trusted --list-all
trusted (active)
target: ACCEPT
icmp-block-inversion: no
interfaces: cni-podman0
sources:
services:
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
在容器中curl一下外網(wǎng)域名,做下驗證贮懈,一切正常了
? root@new1 ~ $podman run --rm c7-systemd curl www.baidu.com
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下匀泊,你就知道</title></head> <body link=#0000cc>........... </body> </html>