Docker网络
- 单机网络:Bridge,Host,None
- 多机网络:Overlay
学习Docker网络务必至少清晰理解以下概念:
- 数据包通信
- 网络分层模型
- IP地址和路由
- 公有IP、私有IP与NAT(网络地址转换)
- 网络检测工具:ping,telnet,tracert
命名空间
- 网络命名空间即ip和端口
- Container的Namespace与宿主机隔离
- Container之间的Namespace也是隔离但互通的
docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done"
docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done"
docker ps
docker exec -it container_id /bin/sh # 进入Container中的操作系统
ip a
或
docker exec container_id ip a # 查看Namespace:ip地址和端口
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
35: eth0@if36: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 可发现这两个Container可以相同
创建、删除Namespaces
ip netns list # 查看、删除、创建Namespace
ipnetns delete test1
ipnetns delete test2
ip netns add test1 # 创建namespace,创建后默认为Down
ip netns add test2
ip netns exec test1 ip a # 在test1中执行“ip a”命令:查看test1的网卡信息
ipnetns exec test1 ip link set dev lo up # 启动test1的lo接口(不能单独启动,必须成对)
实例:Linux环境下创建veth-pair连通两个Namespace的步骤
- 添加一对Veth:
ip link add veth-test1 type veth peer name veth-test2
- 分别把两个Namespace接口添加到Veth:
ip link set veth-test1 netns test1
ip link set veth-test2 netns test2
- 为两个接口分配ip地址
ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2
- 启动两端接口并查看状态
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 link
ip netns exec test2 ip link
ip link
- 测试两个Namespace是否可ping通
ip netns exec test1 ping 192.168.1.2
ip netns exec test2 ping 192.168.1.1
Link
- Container创建会动态生成ip,因此无法通过固定的ip预先配置好连接(如数据库);
- 可以使用
--name
为Container命名,容器之间直接通过--link
连通,被连通的容器之间可以直接通过Link访问而不需要指定ip; - 实际使用场景中会有更方便的方法,因此Link并不常用。
# test2通过--link指定了test1,因此test2可以直接通过test1来ping通,反之不行
docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done"
docker run -d --name test2 --link test1 busybox /bin/sh -c "while true; do sleep 3600; done"
docker exec -it test2 /bin/sh
ping test1
Bridge
Container创建时默认网络模式为Bridge(也可以自己手动指定,如果没有指定--network,则默认连接docker0):
docker network ls # 查看当前机器上Docker有哪些网络
docker ps
docker network inspect container_id # 查看Container网络状态
两个容器各有一个Network Namespace,通过docker0相互通信:
# 创建一个Container test1
docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done"
# 安装brctl命令,查看接口连接状态
yum install -y brctl
ip a
brctl show
# 可发现test1与docker0通过一个Veth相连
ip a
docker ps
docker exec test1 ip a
# 创建第二个Container2
docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done"
brctl show
ip a # 可发现docker0上连上了两个接口
实例:自定义一个Bridge,并使容器通过这个Bridge互连
- 容器通过用户自己创建的Bridge(而不是默认的docker0),则连接到Bridge时已经Link好,不需要--link指定。
- 访问外网:容器可通过NAT使用外部主机的网卡访问外网。
# 创建Bridge
docker network create -d bridge my-bridge
docker network ls
brctl show
# 创建第二个Container
docker run -d --name test3 --network my-brdge busybox /bin/sh -c "while true; do sleep 3600; done"
brctl show
docker ps
docker inspect container_id
# 把test1也连接到Bridge
docker network connect my-bridge test1
# 在test3中测试ping
docker exec -it test3
ping test1
Host
Container与宿主机共享一套Network Namespace(端口可能会冲突)。
docker run --name test1 -d --network host busybox /bin/sh -c "while true; do sleep 3600; done"
docker exec -it test1 /bin/sh
ip a
None
指定--network为none,没有ip地址和mac地址,只允许通过exec本地访问(安全性要求较高时适用)。
端口映射
需要供外界访问的Container服务:端口映射
# 把Container的80端口映射到本地的8080端口
docker run --name web -d -p 8080:80 nginx
curl localhost:8080
Overlay
多机器通信
具体部署方法:https://github.com/yipwinghong/FileRepository/blob/master/docker/multi-host-network.md
Etcd
分布式存储,避免多机器环境下Container的ip不会被占用。
VxLAN
- https://blog.csdn.net/octopusflying/article/details/77609199
- https://www.evoila.de/de/blog/2015/11/06/what-is-vxlan-and-how-it-works/
多容器应用部署
app.py(接收请求,返回从Redis获取的数据)
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.\n' % (redis.get('hits'),socket.gethostname())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
Dockerfile
FROM python:2.7
LABEL maintaner="YWH yipwinghong0401@gmail.com"
COPY . /app
WORKDIR /app
RUN pip install flask redis
EXPOSE 5000
CMD [ "python", "app.py" ]
Redis Container:
docker run -d --name redis redis # 仅app访问,不需要把端口暴露给外部
Flask Container:
docker build -t ywh/flask-redis .
docker run -d -p 5000:5000 --link redis --name flask-redis -e REDIS_HOST=redis ywh/flask-redis # 动态指定环境变量
docker exec -it flask-redis
ping redis
外部访问:
curl localhost:5000
网友评论