美文网首页
podman 容器内无法访问网络

podman 容器内无法访问网络

作者: chnmagnus | 来源:发表于2020-02-19 11:36 被阅读0次

    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 
    

    问题具体表现是:

    • 直接ping外网域名,返回bad address
    • ping 域名对应的ip正常
    • curl 域名对应的ip返回no route to host
    • 容器内的网络配置文件,一切正常
    (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: 
    

    简单逐行做下解释:

    • public (active) 表示public区域是active的
    • target: default 当包与public区域匹配,而没有被下述规则所处理,的包的处理方式,除default,还有其他方式
      ACCEPT:通过这个包。
      %%REJECT%%:拒绝这个包,并返回一个拒绝的回复。
      DROP:丢弃这个包,不回复任何信息。
      default:不做任何事情。该区域不再管它,返回上一层处理
    • icmp-block-inversion: no
    • interfaces: enp0s3 enp0s8 列出这个zone上关联的interface
    • sources: 列出这个区域上的源,现在是空,如果有的话,是类似33.233.233.0/24这种格式
    • services: cockpit dhcpv6-client ssh 列出允许通过防火墙的服务,可以通过firewall-cmd --get-services列出所有服务
    • ports: 列出允许通过防火墙的target port,当要放通一个没有定义的服务时,可以使用端口
    • protocols:
    • masquerade: no 表示这个区域不支持ip伪装。允许的话,将允许ip转发,计算机作为路由器需要开启这个功能
    • icmp-blocks: 要阻塞的icmp流量名单
    • 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>
    
    

    相关文章

      网友评论

          本文标题:podman 容器内无法访问网络

          本文链接:https://www.haomeiwen.com/subject/cnbofhtx.html