podman 容器内无法访问网络

2020-02-19  本文已影响0人  chnmagnus

podman是什么

前几天将自己的开发环境切到了centos 8,为了兼容之前一些业务程序的编译,所以我需要一个centos 7容器。在yum install docker之后发现,centos 8的docker被一个叫podman的程序给替代了。去简单了解了下,podman是redhat一直主推的本地容器解决方案,能够提供和k8s的pod相同的容器组织方式,多个容器可以放在同一个pod中,同一个pod中的容器共用网络。

podman系列主要包含三个命令podman、buildah、skopeo,其中podman本身负责运行、停止、管理容器,buildah负责构建容器镜像、skopeo负责与remote repo交互,拉取或推送镜像。但我们使用时不必这么麻烦,redhat为了方便用户从docker迁移到podman,在podman上几乎实现了大多数docker的常用命令,podman会替你转调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的后台进程,执行命令时不再需要将命令和包拷贝到server端,在build image时,能够节省一笔拷贝的开销。podman不再要求root权限,可以创建rootfull和rootless两种类型的容器,虽然两者的容器运行时都是基于runc。podman引入了k8s中pod的概念,可以将多个容器放到同一个pod中,共享网络;默认的pod基础容器和k8s一样执行/pause命令,并支持用户自定义基础容器。

如何简单的使用podman可以参考: https://thenewstack.io/deploy-a-pod-on-centos-with-podman/

问题

现在回到我的问题。安装好podman之后,按照如下步骤,开始搭建编译容器。在yum install时,发现容器不能访问外网

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 

问题具体表现是:

(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

容器的网络, CNI

podman容器有三种网络模式:bridge、host和none。启动容器时,不显式指定的话,使用的是bridge网络;可以通过--net=参数执行为host或none模式。host模式下,容器共享宿主机的网络;none模式表示容器不存在网络栈,仅有本地回环,无法与外界或其他pod通信。

对于我们上面的问题,一个最简单的解决方法就是使用host网络模式重新启动容器。当时我也是这样做的,但正常情况下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

众所周知,容器的网络实现一般基于CNI(Container Networking Interface)。podman也不例外,基于CNI来实现其bridged network stack。CNI是容器的网络标准,类似的还有CRI(Container runtime interface)、CSI(Container storage interface),是k8s为了规范底层pod的实现方式,制定的几种标准接口。

podman使用的CNI项目是CNI的一个使用最广泛的实现,该项目基于iptables来实现bridged network stack。CNI通过podman提供的信息,以及描述podman所需网桥信息的默认配置/etc/cni/net.d/87-podman-bridge.conflist来创建虚拟网桥。

CNI项目使用iptables来实现各种网络栈,这就存在一个问题,当其他程序对容器宿主机的netfilter规则进行修改,可能会影响到podman容器的网络访问。比如iptables规则的修改、firewalld规则的修改都会影响容器的网络访问。所以我们需要去分析当前的iptables配置和firewalld配置。

问题解决

检查iptables规则

iptables采取黑名单策略,INPUT、FORWARD、OUTPUT chain的默认规则都是ACCEPT,对于没有显式配置DROP规则的网络报,是采取放通的处理方式。
并且CNI创建了自己的chain CNI-FORWARD,并且,针对容器ip 10.88.0.46(这是一个运行中的容器)放通了所有的进出流量。iptables的规则没有问题。

(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实现的,但firewalld比较新,优化了很多iptables的问题。其中一个就是,firewalld采取白名单策略,仅让符合配置中规则的流量通过。

firewalld以zone作为配置管理的单位,可以为不同的区域配置不同的放通规则。可以通过配置入站ip地址范围或者配置网络接口来将网络包,分配到不同的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: 

简单逐行做下解释:

通过ip addr看到podman创建的接口是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

现在,我们找到了问题所在,podman所创建的接口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一下外网域名,做下验证,一切正常了

▸ 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>

上一篇下一篇

猜你喜欢

热点阅读