docker 网络设置
docker容器启动时,相关的网络选项
1,启动container时,指定网络连接方式:--network bridge/host/none
# docker container run --name b1 --network bridge -it --rm busybox:latest
2,查看主机名字命令:hostname
,如果启动容器时,没有指定主机名称,则容器id(CONTAINER ID)和容器的主机名称是相同的。
启动容器的时候,指定主机名称:-h name
# docker container run --name b1 --network bridge -it --rm -h host1 busybox:latest
3,查看本机的hosts文件,cat /etc/hosts
# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4 host1
发现本容器的名字host1是可以翻译成ip地址的。
查看一下DNS server的ip:cat /etc/resolv.conf
cat /etc/resolv.conf
# Generated by NetworkManager
search DHCP HOST
nameserver 192.168.1.1
nameserver 192.168.0.1
发现DNS server的ip是你宿主机所在网络的网关ip,如果这个网关是联网的,则这个容器是可以连接上外网的。可以使用nslookup -type=A www.baidu.com
做实验。
# nslookup -type=a www.baidu.com
Server: 192.168.1.1
Address: 192.168.1.1:53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 39.156.66.14
Name: www.a.shifen.com
Address: 39.156.66.18
也可以ping www.baidu.com
,也是可以ping通的。
4,启动container时,指定dns server:--dns 8.8.8.8
。
# docker container run --name b1 --network bridge -it --rm -h host1 --dns 8.8.8.8 busybox:latest
# cat /etc/resolv.conf
search DHCP HOST
nameserver 8.8.8.8
5,启动container时,指定dns 搜索域:--dns-search ubuntu.org.cn
。
# docker container run --name b1 --network bridge -it --rm -h host1 --dns 8.8.8.8 --dns-search ubuntu.org.cn busybox:latest
# cat /etc/resolv.conf
search ubuntu.org.cn
nameserver 8.8.8.8
dns搜索域是什么:它的作用在于当你用主机名(比如说YourHost)去访问别的机器时,如果DNS无法将YourHost解析为IP,则DNS会使用你的这个search设定的内容(比如说ubuntu.org.cn)加上需要查询的主机名YourHost,变成MyHost.ubuntu.org.cn后,再去DNS server里查找一次ip地址。
6,启动container时,指定hosts文件内容 :---add-host list
。
# docker container run --name b1 --network bridge -it --rm -h host1 --dns 8.8.8.8 --dns-search ubuntu.org.cn --add-host www.mysite.com:1.1.1.1 busybox:latest
# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
1.1.1.1 www.mysite.com
172.17.0.4 host1
问题:启动一个nginx容器后,nginx容器使用的是私有IP,外部机器无法直接访问它,所以我们需要使用DNAT做映射,把ngxin暴露(expose)到外部。
解决办法:在启动容器时,使用-p
选项,去暴露到外部。-p
选项的使用方式有4种。
1,-p <containerPort>
-
把容器的端口containerPort,映射到主机上所有网卡地址的一个动态分配的端口上。因为是动态的所有每次都会变化,这对于使用nginx的客户端来说是无法容忍的。
-
例子:把nginx的80端口,映射到主机上所有网卡地址的一个动态分配的端口上。
# docker run --name httpd --rm -p 80 ys/busybox:local3
然后使用
docker port 容器名
,查看映射到哪个端口上了。# docker port httpd 80/tcp -> 0.0.0.0:32769
也可以使用
iptables -t nat -vnL
,查看映射到哪个端口上了。-p选项会自动映射。# 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 2 104 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.2:80
发现是映射到32769端口上了。这时访问宿主机的32769端口,就会访问到nginx容器。
当容器关闭后,再执行
iptables -t nat -vnL
,发现映射又被自动删除了。
2,-p <hostPort>:<containerPort>
-
把容器端口<containerPort>映射到主机上所有网卡地址的端口<hostPort>上。
-
例子:把nginx的80端口,映射到主机上所有网卡地址的80端口上。
# docker run --name httpd --rm -p 80:80 ys/busybox:local3
然后使用
docker port 容器名
,查看映射到哪个端口上了。]# docker port httpd 80/tcp -> 0.0.0.0:80
也可以使用
iptables -t nat -vnL
,查看映射到哪个端口上了。-p选项会自动映射。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 2 104 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
3,-p <ip>::<containerPort>
-
把容器端口<containerPort>映射主机里指定网卡<ip>的动态端口。
-
例子:把nginx的80端口,映射到主机上网卡地址为10.210.65.99的动态端口上。
# docker run --name httpd --rm -p 10.210.65.99::80 ys/busybox:local3
然后使用
docker port 容器名
,查看映射到哪个端口上了。# docker port httpd 80/tcp -> 10.210.65.99:32768
也可以使用
iptables -t nat -vnL
,查看映射到哪个端口上了。-p选项会自动映射。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 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 10.210.65.99 tcp dpt:32768 to:172.17.0.2:80
4,-p <ip>:<hostPort>:<containerPort>
-
把容器端口<containerPort>映射主机里指定网卡<ip>的指定端口<hostPort>上。
-
例子:把nginx的80端口,映射到主机上网卡地址为10.210.65.99的8080端口上。
# docker run --name httpd --rm -p 10.210.65.99:8080:80 ys/busybox:local3
然后使用
docker port 容器名
,查看映射到哪个端口上了。docker port httpd 80/tcp -> 10.210.65.99:8080
也可以使用
iptables -t nat -vnL
,查看映射到哪个端口上了。-p选项会自动映射。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 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 10.210.65.99 tcp dpt:8080 to:172.17.0.2:80
5,-P
-
不需要写端口号,docker会自动找到你要想暴露出去的端口号,比如是nginx容器的话,-P自动把80端口暴露出去。效果同1。如果image是用dockerfile做出来的话,并在dockerfile里写了EXPOSE命令,那么指定-P会让EXPOSE命令生效,默认dockerfile里写了EXPOSE命令是不生效的。
-
例子:把nginx的80端口,映射到主机上所有网卡地址的一个动态分配的端口上。
# docker run --name httpd --rm -P nginx:stable-alpine
然后使用
docker port 容器名
,查看映射到哪个端口上了。# docker port httpd 80/tcp -> 0.0.0.0:32768
也可以使用
iptables -t nat -vnL
,查看映射到哪个端口上了。-P选项会自动映射。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 2 104 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.2:80
额外:如果容器里的进程既监听80端口,又监听8080端口,则启动容器的时候可以使用-p多次。
joined containers(联盟式容器)
让(user,mount, pid)成为独立的命名空间,让另外3个(uts, net, ipc)空间,共享使用。
一,和别的容器共享net命名空间的方法:使用【--networt containner:已经启动了容器的name】
-
例子:先启动一个busybox,名字叫b1;再启动一个busybox,名字叫b2,并且共享b1的net命名空间,在b2里启动一个httpd服务,然后在b1里用lo(127.0.0.1)去访问在b2里启动的httpd服务。
-
步骤1:先启动一个busybox,名字叫b1。
# docker run --name b1 -it busybox:latest
-
步骤2:再启动一个busybox,名字叫b2,并在b2里启动一个httpd服务。
# docker run --name b2 -it --network container:b1 busybox:latest / # echo "<h1>hello!</h1>" > /tmp/index.html / # httpd -h /tmp / # netstat -tnl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 :::80 :::* LISTEN
-
步骤3:在b1里用lo(127.0.0.1)去访问在b2里启动的httpd服务。
/ # wget -O - -q 127.0.0.1 <h1>hello!</h1>
-
-
验证net命名空间是共享的。
- 使用
ifconfig
,发现b1和b2的ip地址是相同的。
- 使用
-
验证uts命名空间是共享的。
- 使用
hostname
,发现主机名称是相同的。
- 使用
-
ipc命名空间不知道用什么命令验证。
-
验证mount命名空间是不共享的。
在b2里创建了/tmp/index.html文件,但是在b1的/tmp/目录下没有index.html文件。
-
验证pid命名空间是不共享的。
在b2里执行ps命令结果如下:
PID USER TIME COMMAND 1 root 0:00 sh 10 root 0:00 httpd -h /tmp
在b1里执行ps命令结果如下:
PID USER TIME COMMAND 1 root 0:00 sh
进程httpd的pid,在b1里面是没有的。
-
不知道如何验证user命名空间是不共享的。
二,和宿主机共享net命名空间的方法:使用【--networt host】
-
例子:使用宿主机的net命名空间。
# docker run --name b1 -it --rm --network host busybox:latest
-
验证user命名空间是不共享的。
宿主机里有用户ys,在容器里无法看到用户ys
/ # su ys su: unknown user ys
bridge containers(NAT桥式容器)
一,修改docker0桥自身的ip地址的方法:
添加【bip】到,文件/etc/docker/daemon.json中。
bip:bridge ip。
{
"registry-mirrors": ["https://f61bammj.mirror.aliyuncs.com","https://registry.docker-cn.com"],
"bip": "192.0.0.1/16"
}
除了bip还可以添加一下信息.
下面的除了dns,都可以通过bip自动算出来。
{
"fixed-cidr": "10.20.0.0/16",//子网
"fixed-cidr-v6": "2001.db8::/64",//ipv6子网
"mtu": "1500",
"default-gateway": "10.20.1.1",//网关
"default-gateway-v6": "",//ipv6网关
"dns": ["x.x.x.x","x.x.x.x","x.x.x.x"]//dns
}
如果容器不想使用宿主机的dns,则可以指定dns。容器默认是使用宿主机的dns。dns最大指定3个。
二,docker客户端,可以访问远程的docker服务吗?
由于,docker使用的通信技术是unix socker,
所以,默认是不支持远程访问docker服务的。
在本地访问时,docker客户端和服务器是通过/var/run/docker.sock
文件来通信的。[s]代表本地socker文件。
srw-rw----. 1 root docker 0 Dec 4 17:39 /var/run/docker.sock
那么可不可以,让docker客户端,访问非本机的docker服务的呢?是可以的,需要修改文件/etc/docker/daemon.json,添加下面的设置,就可以让外面的客户端访问了。
{
"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]
}
[0.0.0.0:2375]:docker服务所在的机器上的所有网卡的IP地址都监听2375端口。
注意:【unix:///var/run/docker.sock】要留着,不然本地的docker客户都就无法访问本地的docker服务端了。
重新启动docker服务,用查看系统在监听哪些端口的命令ss -tnl
,去看2375端口是否是被监听的。
# ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 :::2375 :::*
然后,外面的客户端使用:-H
或者--host
选项,指定IP地址和端口号来访问docker服务端。
# docker -H 192.168.0.21:2375 images
-
在/etc/docker/daemon.json里加上hosts无法启动docker服务的有没有?
反正我是不能启动docker服务了。
折腾了老半天,终于知道为什么了。
docker服务启动时默认是加了【-H fd://】选项的,然后你又在/etc/docker/daemon.json里加上hosts,它俩就冲突了,只能留一个。
先不要在/etc/docker/daemon.json里加上hosts,然后启动docker服务,用下面的命令验证发现有【-H fd://】,如何解决呢?
# systemctl cat docker | grep Exec ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID
解决办法:
-
步骤1:创建文件:/etc/systemd/system/docker.service.d/docker.conf
-
步骤2:把下面的文件内容写入到上面的文件,保存文件。
[Service] ExecStart= ExecStart=/usr/bin/dockerd
-
步骤3:重新启动docker服务,就可以正常启动了。
再使用
systemctl cat docker | grep Exec
,发现最下面又多了2行,就说明把【-H fd://】覆盖掉了。# systemctl cat docker | grep Exec ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID ExecStart= ExecStart=/usr/bin/dockerd
-
三,创建自定义的桥
docker安装完成,默认只有下面3种network,是否还有其他种类的network呢?
# docker network ls
NETWORK ID NAME DRIVER SCOPE
283d6e0a0dc9 bridge bridge local
d4106bd6d37e host host local
bbe1571b559b none null local
答案是有的,使用docker info可以看到,还有【ipvlan macvlan overlay】3种network。
# docker info
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
我们尝试再创建一个bridge类型的network。先看一下命令帮助。
# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
发现了create命令,再看一下,create命令的使用帮助。
# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which copying the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
- -d:知道network的类型。
- --subnet:ip和子网
- --gateway:网关
有了这些选项,就可以使用下面的命令创建一个network了。
# docker network create -d bridge --subnet "172.26.0.0/16" --gateway "172.26.0.1" mybr0
8e2468865ca64f8f16a697399613314a6615be23ed2ac58bd7d45a09f80b0778
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
283d6e0a0dc9 bridge bridge local
d4106bd6d37e host host local
8e2468865ca6 mybr0 bridge local
bbe1571b559b none null local
发现上面多了一个刚刚创建的network,类型是bridge,名字是mybr0.
使用ifconfig看看,是否多了一个桥,一看果然多了一个名字为br-8e2468865ca6的桥。
# ifconfig
br-8e2468865ca6: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.26.0.1 netmask 255.255.0.0 broadcast 172.26.255.255
ether 02:42:12:85:e1:a0 txqueuelen 0 (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
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.0.0.1 netmask 255.255.0.0 broadcast 192.0.255.255
ether 02:42:a0:74:84:52 txqueuelen 0 (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
启动一个容器,并使用刚刚创建的桥,试一试。
# docker run --name b1 --network mybr0 -it --rm busybox:latest
/ # ls
bin dev etc home proc root sys tmp usr var
/ # ifconfig
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
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:20 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:2282 (2.2 KiB) 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:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ #
发现ip地址是刚刚创建的桥给分配的ip地址。
问题来了,在docker0上启动的容器,和在mybr0上启动的容器,它们之间可以通信吗?
如果内核的转发功能打开了,并且iptables里的没有阻止它们通信的规则,它们之间是可以通信的。
-
使用下面命令查看内核是否打开了转发的功能。
# cat /proc/sys/net/ipv4/ip_forward 1
值为1,说明内核的转发功能已经打开了.
-
使用下面命令查看iptables里的规则。
# iptables -vnL
额外小知识,刚刚创建的mybr0时,桥的名字是随机生成的,比如为br-8e2468865ca6,提供一个修改名字的命令。如果修改了名字,在启动容器的如果指定了刚刚做成的network mybr0的话,容器会启动失败,理由是你把桥的名字br-8e2468865ca6修改了,docker就找不到mybr0对应的桥了,所有还是不要修改桥的名字。
修改桥br-8e2468865ca6名字的方法:
-
先让网卡br-8e2468865ca6停止
ifconfig br-8e2468865ca6 down
-
修改桥的名字
ip link set dev br-8e2468865ca6 name docker1
-
查看是否修改成功
ifconfig -a
-
激活桥docker1
ifconfig docker1 up