容器系列之docker网络
1.网络名称空间:iproute
实质:ip命令是系统内核级的操作
[root@node1 ~]# rpm -q iproute 使用网络名称空间模拟容器间的通讯使用ip命令就可以实现 iproute-4.11.0-14.el7.x86_64 使用ip去管理时,其他名称空间都是共享的,除了网络名称空间是隔离的
2.网络名称空间管理
[root@node1 ~]# ip netns add r1 添加网络名称空间 [root@node1 ~]# ip netns add r2 [root@node1 ~]# ip netns list r2 r1 [root@node1 ~]# ip netns exec r1 ifconfig -a exec是执行命令 lo: flags=8<LOOPBACK> mtu 65536 没有网卡,只有一个lo loop txqueuelen 1 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
创建虚拟网卡对
[root@node1 ~]# ip link add name veth1.1 type veth peer name veth1.2 [root@node1 ~]# ip link sh 24: veth1.1@veth1.2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 name: 指定网卡的名称 veth1.1: 第一个设备的第一段 type: 类型 peer: 相邻的网卡
把其中一个网卡留在宿主机中,把另一个移动r1名称空间中
[root@node1 ~]# ip link set dev veth1.2 netns r1 [root@node1 ~]# ip link show 24: veth1.1@if23: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 [root@node1 ~]# ip netns exec r1 ifconfig veth1.2 在r1中有veth1.2 veth1.2: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether 5a:06:37:1f:f2:a8 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
修改虚拟网卡的名称
[root@node1 ~]# ip netns exec r1 ip link set dev veth1.2 name eth0 [root@node1 ~]# ip netns exec r1 ifconfig eth0 eth0: flags=4098<BROADCAST,MULTICAST> mtu 1500 ether 5a:06:37:1f:f2:a8 txqueuelen 1000 (Ethernet)
激活虚拟网卡对
激活宿主机上的一段虚拟网卡 [root@node1 ~]# ifconfig veth1.1 10.1.0.1/24 up [root@node1 ~]# ifconfig veth1.1 veth1.1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 10.1.0.1 netmask 255.255.255.0 broadcast 10.1.0.255 激活名称空间中的虚拟网卡 [root@node1 ~]# ip netns exec r1 ifconfig eth0 10.1.0.2/24 up [root@node1 ~]# ip netns exec r1 ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.1.0.2 netmask 255.255.255.0 broadcast 10.1.0.255 [root@node1 ~]# ping 10.1.0.2 使用宿主机Ping名称空间 PING 10.1.0.2 (10.1.0.2) 56(84) bytes of data. 64 bytes from 10.1.0.2: icmp_seq=1 ttl=64 time=0.085 ms
把宿主机虚拟网络卡veth1.1移到名称空间r2中
[root@node1 ~]# ip link set dev veth1.1 netns r2 [root@node1 ~]# ip netns exec r2 ifconfig veth1.1 10.1.0.3/24 up 激活r2中的虚拟网卡 [root@node1 ~]# ip netns exec r2 ifconfig veth1.1 veth1.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 [root@node1 ~]# ip netns exec r2 ping 10.1.0.2 r2 ping r1是可以通讯的 PING 10.1.0.2 (10.1.0.2) 56(84) bytes of data. 64 bytes from 10.1.0.2: icmp_seq=1 ttl=64 time=0.050 ms
实现原理
描述:在宿主机上创建r1,r2两个名称空间,然后创建一对网卡为veth1.1和veth1.2,创建时就是像有一根线连接起来似的,当把一半放在r1时,就实现r1与宿主机通讯,另一半放在r2就实现两个名称空间来通讯
3.容器的四种网络类型
创建封闭式容器
[root@node1 ~]# docker run --name t0 -it --network none --rm busybox:latest / # ifconfig -a lo Link encap:Local Loopback 只有一个loop inet addr:127.0.0.1 Mask:255.0.0.0
创建bridge容器
[root@node1 ~]# docker run --name t0 -it --rm busybox:latest #--rm退出会自动删除,bridge在容器之间通讯是确定的 WARNING: IPv4 forwarding is disabled. Networking will not work. / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:05 正常启动有eth0网络接口 inet addr:172.17.0.5 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:648 (648.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) [root@node1 ~]# docker run --name t0 -it --network bridge --rm busybox:latest #以不指定网络一样效果
设置主机名
[root@node1 ~]# docker run --name t0 -it --network bridge --rm busybox:latest / # hostname ba4083991e81 主机名,也就是容器的id [root@node1 ~]# docker ps |grep busybox ba4083991e81 busybox:latest "sh" About a minute ago Up About a minute t0 [root@node1 ~]# docker run --name t0 -it --network bridge -h t0.reid.com --rm busybox:latest / # hostname t0.reid.com 在容器启动时,直接注入主机名称
容器实现主机名访问其他主机
a. 容器可以通过DNS来解析
b. 通过本地的/etc/hosts
/ # cat /etc/hosts 172.17.0.5 t0.reid.com t0 自动生成的 / # cat /etc/resolv.conf ; generated by /usr/sbin/dhclient-script search localdomain reid.com nameserver 192.168.56.2 容器中使用的是宿主机的DNS,是物理网络 / # nslookup -type=A www.baidu.com 可以正常解析baidu,是通过56.2解析的 Server: 192.168.56.2 Address: 192.168.56.2:53 Non-authoritative answer: www.baidu.com canonical name = www.a.shifen.com Name: www.a.shifen.com Address: 61.135.169.125 Name: www.a.shifen.com Address: 61.135.169.121
docker启动时直接指定dns
[root@node1 ~]# docker run --name t0 -it --network bridge -h t0.reid.com --dns 114.114.114.114 --rm busybox:latest / # cat /etc/resolv.conf search localdomain reid.com nameserver 114.114.114.114 [root@node1 ~]# docker run --name t0 -it --network bridge -h t0.reid.com --dns 114.114.114.114 --dns-search reid.linux --rm busybox:latest / # cat /etc/resolv.conf search reid.linux nameserver 114.114.114.114 [root@node1 ~]# docker run --name t0 -it --network bridge -h t0.reid.com --dns 114.114.114.114 --dns-search reid.linux --add-host www.reid333.com:1.2.1.1 --rm busybox:latest / # tail -2 /etc/hosts 1.2.1.1 www.reid333.com 172.17.0.5 t0.reid.com t0 启动时直接注入解析到/etc/hosts文件中
开放式容器通讯
场景:假设容器中使用nginx服务,默认情况下它是隐藏在docker0后面的,所以默认情况下使用跨主机通讯时是不通的,如果使用静态路由来解决,可以把nginx服务expose出来
-p <containerPort> 指定的容器端口映射至主机所有地址的一个动态端口(30000-32767,如果在同一个宿主机上起了多个nginx就不会冲突) -p <hostPort>:<containerPort> 使用多个端口时,-p可以使用多次 将容器端口<containerPort>映射至指定的主机<hostPort> -p <ip>::<containerPort> 将指定的容器端口<containerPort>映射至主机指定<ip>的端口<hostPort>
expose容器中的端口
[root@node1 ~]# docker run --name myweb --rm -p 80 reid/httpd:v0.2 [root@node1 ~]# docker inspect myweb |grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "172.17.0.5", "IPAddress": "172.17.0.5", [root@node1 ~]# curl 172.17.0.5 (内部访问) dockerbusybox [root@node1 ~]# iptables -t nat -vnL Chain DOCKER (2 references) nat规则是docker使用-p选项时自动生成的 pkts bytes target prot opt in out source destination 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32769 to:172.17.0.5:80 [root@node3 ~]# curl 192.168.56.129:32769 访问宿主机 dockerbusybox [root@node1 ~]# docker kill myweb 删除时会自动删除nat规则 myweb [root@node1 ~]# iptables -t nat -vnL Chain DOCKER (2 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
指定映射的IP地址或端口
[root@node1 ~]# docker run --name myweb --rm -p 80 reid/httpd:v0.2 [root@node1 ~]# docker port myweb 查看映射关系 80/tcp -> 0.0.0.0:32770 将容器中的80端口映射到宿主机的所有地址的327790上 [root@node1 ~]# docker run --name myweb --rm -p 192.168.56.129::80 reid/httpd:v0.2 #Ip是宿主机的,::是动态,80是容器端口 [root@node1 ~]# docker port myweb 80/tcp -> 192.168.56.129:32768 [root@node3 ~]# curl 192.168.56.129:32768 dockerbusybox [root@node1 ~]# docker run --name myweb --rm -p 80:80 reid/httpd:v0.2 #不写地址是所有地址 [root@node1 ~]# docker port myweb 80/tcp -> 0.0.0.0:80 宿主机的所的IP和80端口 [root@node1 ~]# curl 192.168.56.129 dockerbusybox
同时指定IP和端口
[root@node1 ~]# docker run --name myweb --rm -p 192.168.56.129:80:80 reid/httpd:v0.2 [root@node1 ~]# docker port myweb 80/tcp -> 192.168.56.129:80
联盟式容器
使得两个容器使用共用一个名称空间,各自使用独立user ,monut ,共享pid,ipc,uts,net
[root@node1 ~]# docker run --name b0 -it --rm busybox / # ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:05 inet addr:172.17.0.5 Bcast:172.17.255.255 Mask:255.255.0.0 #### [root@node1 ~]# docker run --name b2 -it --rm busybox 正常情况 / # ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:07 inet addr:172.17.0.7 Bcast:172.17.255.255 Mask:255.255.0.0 [root@node1 ~]# docker run --name b2 -it --network container:b0 -it --rm busybox #意思是使用共享container b0的网络名称空间 / # ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:05 inet addr:172.17.0.5 Bcast:172.17.255.255 Mask:255.255.0.0 ##### 但是文件系统还是隔离的 / # mkdir /tmp/test1 / # ls /tmp/ b0 test1 / # ls /tmp/ b2为空 共享loop接口(相当于共享ipc) b2: / # echo "hello reid" > /tmp/index.html / # httpd -h /tmp/ / # netstat -ant|grep 80 tcp 0 0 :::80 :::* LISTEN b0: / # wget -O - -q 127.0.0.1 hello reid
创建时使用host(宿主机)网络
优势:在宿主机上配置httpd,要配置安装,起服务,使用容器直接docker run完事
[root@node1 ~]# docker run --name b2 --network host -it --rm busybox docker0 Link encap:Ethernet HWaddr 02:42:E5:83:52:6B inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0 eth0 Link encap:Ethernet HWaddr 00:0C:29:0F:73:2D inet addr:192.168.56.129 Bcast:192.168.56.255 Mask:255.255.255.0 / # echo "test reid" > /tmp/index.html / # httpd -h /tmp/ / # netstat -ant|grep 80 tcp 0 0 :::80 :::* LISTEN [root@node3 ~]# curl 192.168.56.129 test reid
自定义docker的网络属性
[root@node1 ~]# systemctl stop docker [root@node1 ~]# cat /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"], "bip": "10.0.0.1/16" #设定ip段 } [root@node1 ~]# systemctl start docker [root@node1 ~]# ifconfig docker0 docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 10.0.0.1 netmask 255.255.0.0 broadcast 10.0.255.255 还可以修改其他选项 [root@node1 ~]# cat /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"], "bip": "10.0.0.1/16", #核心选项为big,即bridge ip,用于指定docker0桥自身的IP地址,其他选项可以通过此计算出来,除了DNS "fixed-cidr": "10.20.0.0/16", "fixed-cidr-v6": "2001:db8::/64", "default-gateway": "10.20.1.1", "default-gateway-v6": "2001:db8:abcd::89", "dns": ["10.20.1.2","10.20.1.3"] }
设定外部主机连接docker
dockerd守护进程的C/S,其默认仅监听unix socket格式地址,/var/run/docker.sock;如果使用tcp套接字
[root@node1 ~]# cat /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"], "bip": "10.0.0.1/16", "hosts": ["tcp://0.0.0.0:2375","unix:///var/run/docker.sock"] } [root@node1 ~]# systemctl start docker [root@node1 ~]# ss -tnl|grep 2375 LISTEN 0 128 :::2375 :::* [root@node3 ~]# docker -H 192.168.56.129:2375 ps #在另一台主机上测试 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@node3 ~]# docker -H 192.168.56.129:2375 image ls REPOSITORY TAG IMAGE ID CREATED SIZE reid/httpd v0.2 4f752241c109 6 days ago 1.16 MB docker422018/httpd v0.1-1 3aa96b4f58c1 6 days ago 1.16 MB reid/httpd latest 3aa96b4f58c1 6 days ago 1.16 MB reid/httpd v0.1-1 3aa96b4f58c1 6 days ago 1.16 MB redis 4-alpine db23f46600bc 10 days ago 30 MB nginx 1.14-alpine 14d4a58e0d2e 11 days ago 17.4 MB busybox latest e1ddd7948a1c 7 weeks ago 1.16 MB quay.io/coreos/flannel v0.10.0-amd64 f0fad859c909 8 months ago 44.6 MB
自定义桥
[root@node1 ~]# docker network create -d bridge --subnet "172.26.0.0/16" --gateway "172.26.0.1" mybr0 abedd62ed28dd57e04dfb523eb963c458aea9df11cc3291554debcb0f45f1d2e [root@node1 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE d0abdc2e1fa8 bridge bridge local f5b3bb476293 host host local abedd62ed28d mybr0 bridge local d93680d000ce none null local [root@node1 ~]# ifconfig br-abedd62ed28d: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.26.0.1 netmask 255.255.0.0 broadcast 172.26.255.255 [root@node1 ~]# docker run --name t0 -it --network mybr0 busybox:latest #直接加入mybr0网络 / # ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:AC:1A:00:02 inet addr:172.26.0.2 Bcast:172.26.255.255 Mask:255.255.0.0 [root@node1 ~]# docker run --name t1 -it --network bridge busybox:latest / # ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02 inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0 / # ping 172.26.0.2 PING 172.26.0.2 (172.26.0.2): 56 data bytes
分析:t1与t2两个不同的容器相当于分析接了两个不同网段的虚拟交换机,在不同网段,两个容器要通信
解决方案:两个虚拟交换机的接口还是在宿主机上,而且两个都是nat bridge, 只要在宿主机上打开核心转发功能就可以实现
[root@node1 ~]# cat /proc/sys/net/ipv4/ip_forward 1 如果不行就是iptables规则