容器在启动时能够经过 -P 或 -p 参数来指定端口映射。docker
当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。json
-p
则指定要映射的端口,而且,在一个指定端口上只能够绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
。ubuntu
使用 hostPort:containerPort
格式本地的 5000 端口映射到容器的 5000 端口,能够执行安全
$ docker run -d -p 127.0.0.1:5000:5000 ubuntu
使用 ip::containerPort
绑定 localhost
的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。bash
$ docker run -d -p 127.0.0.1::5000 ubuntu
使用 udp 标记来指定 udp 端口服务器
docker run -d -p 127.0.0.1:5000:5000/udp ubuntu
随着 Docker
网络的完善,强烈建议将容器加入自定义的 Docker
网络来链接多个容器。网络
新建 docker 网络tcp
$ docker network create -d bridge my-net
-d 参数指定 Docker 网络类型,有 bridge overlay
。其中 overlay
网络类型用于 Swarm mode
。oop
运行一个容器并链接到新建的 my-net
网络code
$ docker run -it --rm --name busybox1 --network my-net busybox sh
打开新的终端,再运行一个容器并加入到 my-net
网络
$ docker run -it --rm --name busybox2 --network my-net busybox sh
容器的访问机制,主要经过 Linux
上的 Iptables
防火墙来实现。
容器想要访问外部网络,就须要本地系统转发支持,肩擦好转发是否打开。
$sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
若是为0,shaming没有开启转发,须要手动打开。
$sysctl -w net.ipv4.ip_forward=1
若是启动 Docker 服务的时候设定--ip-forward=true
,Docker就会自动设定系统的 ip_forward
参数替换为1。
容器之间相互访问,须要两方面的支持。
启动 Docker 服务(即 dockerd
)的时候,默认会添加一条转发策略到本地主机 iptables
的 FORWARD 链上。策略为经过(ACCEPT)仍是禁止(DROP)取决于配置--icc=true
(缺省值)仍是 --icc=false
。固然,若是手动指定 --iptables=false
则不会添加 iptables
规则。
可见,默认状况下,不一样容器之间是容许网络互通的。若是为了安全考虑,能够在 /etc/docker/daemon.json
文件中配置 {"icc": false} 来禁止它。
在经过 -icc=false
关闭网络访问后,还能够经过 --link=CONTAINER_NAME:ALIAS
选项来访问开放端口。
能够同时使用 icc=false --iptables=true
参数来关闭容许相互的网络访问,并让 Docker 能够修改系统中的 iptables
规则。
使用 --link=CONTAINER_NAME:ALIAS
选项。Docker 会在 iptable
中为 两个容器分别添加一条 ACCEPT 规则,容许相互访问开放的端口(取决于 Dockerfile 中的 EXPOSE 指令)。
注意:--link=CONTAINER_NAME:ALIAS
中的 CONTAINER_NAME
目前必须是 Docker 分配的名字,或使用 --name 参数指定的名字。主机名则不会被识别。
其中有些命令选项只有在 Docker 服务启动的时候才能配置,并且不能立刻生效。
--mtu=BYTES 容器网络中的 MTU
下面2个命令选项既能够在启动服务时指定,也能够在启动容器时指定。在 Docker 服务启动的时候指定则会成为默认值,后面执行 docker run 时能够覆盖设置的默认值。
--dns-search=DOMAIN... 指定DNS搜索域
最后这些选项只有在 docker run 执行时使用,由于它是针对容器的特性内容。
-P or --publish-all=true|false 映射容器全部端口到宿主主机
容器的访问控制,主要经过 Linux
上的 iptables
防火墙来进行管理和实现。 iptables 是 Linux 上默认的防火墙软件,在大部分发行版中都自带。
容器要想访问外部网络,须要本地系统的转发支持。在Linux 系统中,检查转发是否打开。
$sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
若是为 0,说明没有开启转发,则须要手动打开。
$sysctl -w net.ipv4.ip_forward=1
若是在启动 Docker 服务的时候设定 --ip-forward=true, Docker 就会自动设定系统的 ip_forward 参数为 1。
容器之间相互访问,须要两方面的支持。
当启动 Docker 服务(即 dockerd)的时候,默认会添加一条转发策略到本地主机 iptables 的 FORWARD 链上。策略为经过(ACCEPT)仍是禁止(DROP)取决于配置--icc=true(缺省值)仍是 --icc=false。固然,若是手动指定 --iptables=false 则不会添加 iptables 规则。
默认状况下,不一样容器之间是容许网络互通的。若是为了安全考虑,能够在 /etc/docker/daemon.json 文件中配置 {"icc": false} 来禁止它。
在经过 -icc=false 关闭网络访问后,还能够经过 --link=CONTAINER_NAME:ALIAS 选项来访问容器的开放端口。
能够同时使用 icc=false --iptables=true 参数来关闭容许相互的网络访问,并让 Docker 能够修改系统中的 iptables 规则。
使用 --link=CONTAINER_NAME:ALIAS 选项。Docker 会在 iptable 中为 两个容器分别添加一条 ACCEPT 规则,容许相互访问开放的端口(取决于 Dockerfile 中的 EXPOSE 指令)。
注意:--link=CONTAINER_NAME:ALIAS 中的 CONTAINER_NAME 目前必须是 Docker 分配的名字,或使用 --name 参数指定的名字。主机名则不会被识别。
默认状况下,容器能够主动访问到外部网络链接,可是外部链接没法访问到容器。
容器全部到外部网络的链接,源地址都会被 NAT 成本地系统的 IP 地址。这是使用 iptables 的源地址假装操做实现的。
查看主机的 NAT 规则:
$ sudo iptables -t nat -nL ... Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 ...
其中,上述规则将全部源地址在 172.17.0.0/16 网段,目标地址为其余网段(外部网络)的流量动态假装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。
容器容许外部访问,能够在 docker run
时候经过 -p 或 -P 参数来启用。
无论用那种办法,其实也是在本地的 iptable 的 nat 表中添加相应的规则。
使用 -P 时:
$ iptables -t nat -nL ... Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80
使用 -p 80:80 时:
$ iptables -t nat -nL Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
注意:
这里的规则映射了 0.0.0.0,意味着将接受主机来自全部接口的流量。用户能够经过 -p IP:host_port:container_port 或 -p IP::port 来指定容许访问容器的主机上的 IP、接口等,以制定更严格的规则。
若是但愿永久绑定到某个固定的 IP 地址,能够在 Docker 配置文件 /etc/docker/daemon.json 中添加以下内容。
{ "ip": "0.0.0.0" }
Docker 服务默认会建立一个 docker0
网桥(其上有一个 docker0 内部接口),它在内核层连通了其余的物理或虚拟网卡,这就将全部容器和本地主机都放到同一个物理网络。
Docker 默认指定了 docker0
接口 的 IP 地址和子网掩码,让主机和容器之间能够经过网桥相互通讯,它还给出了 MTU(接口容许接收的最大传输单元),一般是 1500 Bytes,或宿主主机网络路由上支持的默认值。这些值均可以在服务启动的时候进行配置。
-bip=CIDR IP
地址加掩码格式,例如 192.168.1.5/24--mtu=BYTES
覆盖默认的 Docker mtu 配置也能够在配置文件中配置 DOCKER_OPTS,而后重启服务。
因为目前 Docker 网桥是 Linux 网桥,用户可使用 brctl show 来查看网桥和端口链接信息。
$ sudo brctl show bridge name bridge id STP enabled interfaces docker0 8000.3a1d7362b4ee no veth65f9 vethdda6
注:brctl
命令在 Debian、Ubuntu
中可使用 sudo apt-get install bridge-utils
来安装。
每次建立一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0 端口。使用本地主机上 docker0
接口的 IP 做为全部容器的默认网关。
$ sudo docker run -i -t --rm base /bin/bash $ ip addr show eth0 24: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff inet 172.17.0.3/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::306f:e0ff:fe35:5791/64 scope link valid_lft forever preferred_lft forever $ ip route default via 172.17.42.1 dev eth0 172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3
除了默认的 docker0 网桥,用户也能够指定网桥来链接各个容器。
在启动 Docker 服务的时候,使用 -b BRIDGE或--bridge=BRIDGE 来指定使用的网桥。
若是服务已经运行,那须要先中止服务,并删除旧的网桥。
$ sudo systemctl stop docker $ sudo ip link set dev docker0 down $ sudo brctl delbr docker0
而后建立一个网桥 bridge0
。
$ sudo brctl addbr bridge0 $ sudo ip addr add 192.168.5.1/24 dev bridge0 $ sudo ip link set dev bridge0 up
查看确认网桥建立并启动。
$ ip addr show bridge0 4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff inet 192.168.5.1/24 scope global bridge0 valid_lft forever preferred_lft forever
在 Docker 配置文件 /etc/docker/daemon.json 中添加以下内容,便可将 Docker 默认桥接到建立的网桥上。
{ "bridge": "bridge0", }
启动 Docker 服务。
新建一个容器,能够看到它已经桥接到了 bridge0 上。
能够继续用 brctl show
命令查看桥接的信息。另外,在容器中可使用 ip addr
和 ip route
命令来查看 IP 地址配置和路由信息。
Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname
和 /etc/resolv.conf
文件。
可是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 docker commit
提交。
默认状况下,Docker 会将全部容器链接到由 docker0 提供的虚拟子网中。
用户有时候须要两个容器之间能够直连通讯,而不用经过主机网桥进行桥接。
解决办法很简单:建立一对 peer 接口,分别放到两个容器中,配置成点到点链路类型便可。
首先启动 2 个容器:
$ docker run -i -t --rm --net=none base /bin/bash root@1f1f4c1f931a:/# $ docker run -i -t --rm --net=none base /bin/bash root@12e343489d2f:/#
找到进程号,而后建立网络命名空间的跟踪文件。
$ docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a 2989 $ docker inspect -f '{{.State.Pid}}' 12e343489d2f 3004 $ sudo mkdir -p /var/run/netns $ sudo ln -s /proc/2989/ns/net /var/run/netns/2989 $ sudo ln -s /proc/3004/ns/net /var/run/netns/3004
建立一对 peer 接口,而后配置路由
$ sudo ip link add A type veth peer name B $ sudo ip link set A netns 2989 $ sudo ip netns exec 2989 ip addr add 10.1.1.1/32 dev A $ sudo ip netns exec 2989 ip link set A up $ sudo ip netns exec 2989 ip route add 10.1.1.2/32 dev A $ sudo ip link set B netns 3004 $ sudo ip netns exec 3004 ip addr add 10.1.1.2/32 dev B $ sudo ip netns exec 3004 ip link set B up $ sudo ip netns exec 3004 ip route add 10.1.1.1/32 dev B
如今这 2 个容器就能够相互 ping 通,并成功创建链接。点到点链路不须要子网和子网掩码。
此外,也能够不指定 --net=none 来建立点到点链路。这样容器还能够经过原先的网络来通讯。
利用相似的办法,能够建立一个只跟主机通讯的容器。可是通常状况下,更推荐使用 --icc=false 来关闭容器之间的通讯。