一、Linux network namespace
network namespace 是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自的网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己就在独立的网络中。
Usage: ip netns list ip netns add NAME ip netns set NAME NETNSID ip [-all] netns delete [NAME] ip netns identify [PID] ip netns pids NAME ip [-all] netns exec [NAME] cmd ... ip netns monitor ip netns list-id
有了不同 network namespace 之后,也就有了网络的隔离,但是如果它们之间没有办法通信,也没有实际用处。要把两个网络连接起来,linux 提供了 veth pair 。可以把 veth pair 当做是双向的 pipe(管道),从一个方向发送的网络数据,可以直接被另外一端接收到;或者也可以想象成两个 namespace 直接通过一个特殊的虚拟网卡连接起来,可以直接通信。
ip netns add test1 #添加网络名称空间; ip netns add test2 #添加网络名称空间; ip netns list #列出网络名称空间; ip netns exec test1 ip a #进入test1网络名称空间执行命令ip a; ip netns exec test1 ip link set dev lo up #进入test1网络名称空间启动lo接口; ip netns exec test2 ip link set dev lo up #进入test2网络名称空间启动lo接口; ip link add veth-test1 type veth peer name veth-test2 #添加2个端口veth-test1、veth-test2,并为端口建立信道; ip link set veth-test1 netns test1 #将veth-test1端口添加进网络名称空间test1; ip link set veth-test2 netns test2 #将veth-test2端口添加进网络名称空间test2; ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1 #为端口添加IP; ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2 #为端口添加IP; ip netns exec test1 ip link set dev veth-test1 up ip netns exec test2 ip link set dev veth-test2 up ip netns exec test1 ip a ip netns exec test2 ip a ip netns exec test1 ping 192.168.1.2 -c 3 #测试2个网络名称空间的端口是否能通信;
二、docker容器网络
在顶层设计中,Docker 网络架构由 3 个主要部分构成:CNM、Libnetwork 和驱动。
1)CNM 是设计标准。在 CNM 中,规定了 Docker 网络架构的基础组成要素。
2)Libnetwork 是 CNM 的具体实现,并且被 Docker 采用,Libnetwork 通过 Go 语言编写,并实现了 CNM 中列举的核心组件。
3)驱动通过实现特定网络拓扑的方式来拓展该模型的能力。
网络模式1:桥接网络;开启2个容器,查看容器网络不同namespace间如何通信;
docker run -d --name test1 busybox /bin/sh -c "while true;do sleep 3600;done" #启动容器; docker network ls #查看docker网络; docker network inspect bridge #查看桥接网络详细信息; docker exec -it test1 ip a #查看容器网络;veth928e759@if16与eth0@if17是2个不同的network namespace之间的一对veth,实现两者间的通信; brctl show #查看宿主机桥接网络信息;veth928e759@if16桥接在宿主机docker0桥接网络的veth928e759接口上; docker run -d --name test2 busybox /bin/sh -c "while true;do sleep 3600;done" docker exec -it test2 ip a docker exec -it test1 ping 172.17.0.3 -c 3
##网络模式2:NAT网络;
查看容器如何访问Internet;---通过iptables实现nat转换;
docker exec -it test2 ping baidu.com -c 3
##网络模式3:none;该网络模式适用于高安全性的应用场景,如:保存密码; docker run -d --name test3 --network none busybox /bin/sh -c "while true;do sleep 3600;done" #启动网络模式为none的容器test3; brctl show docker network inspect none #none网络中有一个容器test3; docker exec -it test3 ip a #容器test3只有lo接口,表示该容器只能通过exec登录;
##网络模式4:host;容器共享宿主机的network namespace,但易引起宿主机端口及容器端口冲突; docker run -d --name test4 --network host busybox /bin/sh -c "while true;do sleep 3600;done" #启动网络模式为host的容器test4; docker container ls brctl show docker network inspect host #host网络中有一个容器test4;容器没有网卡mac地址及IP; docker exec -it test4 ip a #容器test4的接口与宿主机一致; ip a #宿主机与容器的接口一致;
##docker link:使用默认docker0桥接网络,在启动容器时需添加--link参数才能实现单方向域名解析;使用自建的桥接网络,在启动容器时会默认添加--link参数,实现双方向域名解析; docker network create my-bridge #创建dockers网桥; docker network ls #显示dockers网桥; brctl show #查看Linux中网桥; docker run -d --name test1 busybox /bin/sh -c "while true;do sleep 3600;done" #启动默认桥接模式docker0的容器test1; docker run -d --name test2 --link test1 busybox /bin/sh -c "while true;do sleep 3600;done" #启动默认桥接模式docker0的容器test2,添加指向test1的link; docker exec -it test1 ip a |awk '/172/{print $2}' |cut -d/ -f 1 #查看容器test1的IP; docker exec -it test2 ip a |awk '/172/{print $2}' |cut -d/ -f 1 #查看容器test2的IP; docker exec -it test1 ping $(docker exec -it test2 ip a |awk '/172/{print $2}' |cut -d/ -f 1) -c 3 #容器test1能ping通test2的IP; docker exec -it test1 ping test2 -c 3 #容器test1不能ping通test2的name; docker exec -it test2 ping $(docker exec -it test1 ip a |awk '/172/{print $2}' |cut -d/ -f 1) -c 3 #容器test1能ping通test2的IP; docker exec -it test2 ping test1 -c 3 #容器test1能ping通test2的name;
docker run -d --name nginx-local nginx #容器默认映射端口为80;但由于未作端口映射,容易引发端口冲突; telnet 172.17.0.2 80 telnet 127.0.0.1 80 telnet 192.168.66.10 80 curl http://172.17.0.2:80 #宿主机上都能访问nginx服务; curl http://127.0.0.1:80 #宿主机上都能访问nginx服务; curl http://192.168.66.10:80 #其他主机及宿主机上都能访问nginx服务; docker network create my-bridge docker run -d --network my-bridge -p 1800:80 --name nginx-1 nginx telnet 172.20.0.2 80 #端口服务正常; telnet 127.0.0.1 1800 #端口服务正常; telnet 192.168.66.10 1800 #端口服务正常;
VMware虚拟机映射端口或端口转发:https://jingyan.baidu.com/article/b2c186c8e61a2e856ef6ff8e.html;
案例:端口映射 mkdir flask-redis cd flask-redis cat <<eof> app.py from flask import Flask from redis import Redis import os import socket app = Flask(__name__) redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379) @app.route('/') def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s. ' % (redis.get('hits'),socket.gethostname()) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) eof cat <<eof> Dockerfile FROM python:2.7 LABEL maintaner="chalon" COPY . /app WORKDIR /app RUN pip install flask redis EXPOSE 5000 CMD [ "python", "app.py" ] eof docker build -t chalon/flask-redis ./ docker image ls docker run -d --name redis redis docker run -d --link redis --name flask-redis -e REDIS_HOST=redis chalon/flask-redis #-e设置环境变量; docker exec -it flask-redis env #查看容器flask-redis中env环境变量; docker exec -it flask-redis ping redis -c 3 #测试容器flask-redis是否能ping通redis容器; docker exec -it flask-redis curl 127.0.0.1:5000 #测试容器内部的服务结果为正常; docker exec -it flask-redis curl 127.0.0.1:5000 #测试容器内部的服务结果为正常; docker exec -it flask-redis curl 127.0.0.1:5000 #测试容器内部的服务结果为正常; curl 127.0.0.1:5000 #宿主机测试服务结果为不正常; docker stop flask-redis && docker rm flask-redis #删除容器; docker run -d --link redis -p 1500:5000 --name flask-redis -e REDIS_HOST=redis chalon/flask-redis #启动容器; docker exec -it flask-redis curl 127.0.0.1:5000 #测试容器内部的服务结果为正常; docker exec -it flask-redis curl 127.0.0.1:5000 #测试容器内部的服务结果为正常; curl 127.0.0.1:1500 #宿主机能访问服务,由于redis-server未重启,则会累加访问次数; curl 127.0.0.1:1500 #宿主机能访问服务,由于redis-server未重启,则会累加访问次数;
网络模式5:overlay---多节点容器通信
详细信息详见https://github.com/docker/labs/blob/master/networking/concepts/; VXLAN包结构示意图如下:
案例:docker网络模式5---overlay ####部署etcd集群(分布式存储),用于存储容器IP,以避免IP冲突; ##在docker-node1(192.168.66.10)上执行 wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz tar zxvf etcd-v3.0.12-linux-amd64.tar.gz cd etcd-v3.0.12-linux-amd64 nohup ./etcd --name controller --initial-advertise-peer-urls http://192.168.66.10:2380 --listen-peer-urls http://192.168.66.10:2380 --listen-client-urls http://192.168.66.10:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.66.10:2379 --initial-cluster-token etcd-cluster --initial-cluster controller=http://192.168.66.10:2380,k8s-master02=http://192.168.66.11:2380 --initial-cluster-state new & ##在docker-node2(192.168.66.11)上执行 wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz tar zxvf etcd-v3.0.12-linux-amd64.tar.gz cd etcd-v3.0.12-linux-amd64 nohup ./etcd --name k8s-master02 --initial-advertise-peer-urls http://192.168.66.11:2380 --listen-peer-urls http://192.168.66.11:2380 --listen-client-urls http://192.168.66.11:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.66.11:2379 --initial-cluster-token etcd-cluster --initial-cluster controller=http://192.168.66.10:2380,k8s-master02=http://192.168.66.11:2380 --initial-cluster-state new & ./etcdctl cluster-health #检查cluster状态 ###重启docker服务 ##在docker-node1(192.168.66.10)上执行 service docker stop /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.66.10:2379 --cluster-advertise=192.168.66.10:2375 & #启动docker,并获取etcd集群信息;此时,可使用ps或docker version查看dockerd状态是运行的,并未采取systemctl方式启动服务; ##在docker-node2(192.168.66.11)上执行 service docker stop /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.66.11:2379 --cluster-advertise=192.168.66.11:2375 & #启动docker,并获取etcd集群信息; ###创建overlay network ##在docker-node1上创建一个demo的overlay network docker network create -d overlay demo docker network ls docker network inspect demo ##看到在node2上,这个demo的overlay network会被同步创建 docker network ls ./etcdctl ls docker #检查etcd数据库中的docker目录; #在docker-node1(192.168.66.10)上执行 docker run -d --name overlay_test1 --net demo busybox sh -c "while true; do sleep 3600; done" #创建容器后,将会创建1个docker_gwbridge的网络; docker exec overlay_test1 ifconfig docker network inspect demo #查看demo网络,包含1个container信息,其IP为10.0.0.1; #在docker-node2(192.168.66.11)上执行 docker run -d --name overlay_test2 --net demo busybox sh -c "while true; do sleep 3600; done" docker exec overlay_test2 ifconfig docker network inspect demo #查看demo网络,包含2个container信息,其IP为10.0.0.2; docker exec overlay_test2 ping 10.0.0.1 -c 3