Docker技術入門與實戰 第二版-學習筆記-8-網路功能network-3-容器訪問控制和自定義網橋
1)容器訪問控制
容器的訪問控制,主要通過 Linux 上的 iptables防火牆來進行管理和實現。 iptables是 Linux 上預設的防火牆軟體,在大部分發行版中都自帶。
容器訪問外部網路
容器要想訪問外部網路,需要本地系統的轉發支援。在Linux 系統中,檢查轉發是否開啟:
[email protected]:/opt/webapp# sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
為1,說明打開了;如果為0,說明沒有開啟轉發,則需要手動開啟,在容器內執行:
sysctl -w net.ipv4.ip_forward=1
如果在啟動 Docker 服務的時候設定 --ip-forward = true, Docker 就會自動設定系統的 ip_forward引數為 1
容器之間訪問
容器之間相互訪問,需要兩方面的支援:
- 容器的網路拓撲是否已經互聯--icc。預設情況下,所有容器都會被連線到 docker0網橋上,預設--icc=true,能夠互相ping通。
- 本地系統的防火牆軟體 -- iptables是否允許通過
在本部落格的Docker技術入門與實戰 第二版-學習筆記-8-網路功能network-1-單個host上的容器網路中有如何讓兩個網段的容器互聯的例子
1》訪問所有埠
當啟動 Docker 服務時候,預設會新增一條轉發策略到 iptables 的 FORWARD 鏈 上。策略為通過(ACCEPT )還是禁止(DROP )取決於配置 --icc=true(預設值,支援容器之間進行通訊)還是 --icc=false(不支援),即能否ping通
當然,如果手動指定--iptables=false則不會新增iptables規則
可見,預設情況下,不同容器之間是允許網路互通的。如果為了安全考慮,可以在/etc/default/docker檔案中配置DOCKER_OPTS=--icc=false來禁止它(具體實現在下一章的網橋部分介紹)
2》訪問指定埠
在通過--icc=false關閉網路訪問後,還可以通過--link=CONTAINER_NAME:ALIAS選項來訪問容器的開放埠
例如,在啟動 Docker 服務時,可以同時使用--icc=false --iptables=true引數來關閉允許互相的網路訪問,並讓Docker可以修改系統中的iptables規則。
此時,檢視iptables規則,在上一篇部落格生成的ubuntu5容器中執行:
[email protected]:/# iptables -t nat -nL bash: iptables: command not found [email protected]:/# apt-get install iptables Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package iptables [email protected]:/# apt-get update Ign http://archive.ubuntu.com trusty InRelease Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB] ... Get:21 http://archive.ubuntu.com trusty/multiverse amd64 Packages [169 kB] Fetched 13.3 MB in 45s (292 kB/s) Reading package lists... Done [email protected]:/# apt-get install iptables Reading package lists... Done ... Setting up libnfnetlink0:amd64 (1.0.1-2) ... Setting up libxtables10 (1.4.21-1ubuntu1) ... Setting up iptables (1.4.21-1ubuntu1) ... Processing triggers for libc-bin (2.19-0ubuntu6.14) ... [email protected]:/# iptables -t nat -nL iptables v1.4.21: can't initialize iptables table `filter': Permission denied (you must be root)
解決辦法是docker run時使用引數 --privileged,然後再跑一遍:
[email protected]:/# exit exit userdeMacBook-Pro:~ user$ docker exec -it --privileged ubuntu5 /bin/bash Error response from daemon: Container 9e11c1b9e90dc7fe3cbdff59580e849517b254cc20081d0026cd57c4b2407f88 is not running userdeMacBook-Pro:~ user$ docker start ubuntu5 ubuntu5 userdeMacBook-Pro:~ user$ docker exec -it --privileged ubuntu5 /bin/bash [email protected]:/# iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination DOCKER_OUTPUT all -- 0.0.0.0/0 127.0.0.11 Chain POSTROUTING (policy ACCEPT) target prot opt source destination DOCKER_POSTROUTING all -- 0.0.0.0/0 127.0.0.11 Chain DOCKER_OUTPUT (1 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 127.0.0.11 tcp dpt:53 to:127.0.0.11:36705 DNAT udp -- 0.0.0.0/0 127.0.0.11 udp dpt:53 to:127.0.0.11:49094 Chain DOCKER_POSTROUTING (1 references) target prot opt source destination SNAT tcp -- 127.0.0.11 0.0.0.0/0 tcp spt:36705 to::53 SNAT udp -- 127.0.0.11 0.0.0.0/0 udp spt:49094 to::53 [email protected]:/#
之後,啟動容器(docker run)時使用--link=CONTAINER_NAME:ALIAS選項。Docker會在iptables中為兩個容器分別新增一條ACCEPT規則,允許相互訪問互相開放的埠(取決於Dockerfile的EXPOSRE行)
當添加了--link=CONTAINER_NAME:ALIAS之後,iptables規則就變了(但是上面沒有能夠看見規則,但是使用cat /etc/hosts檢視的確是連線到了db???)
注意: --link=CONTAINER_NAME:ALIAS中的 CONTAINER_NAME目前必須是 Docker 分配的名字,或使用 --name引數指定的名字。主機名則不會被識別。
2)對映容器埠到宿主主機的實現
預設情況下,容器可以主動訪問到外部網路的連線,但是外部網路無法訪問到容器。
容器訪問外部實現
容器所有到外部網路的連線,源地址都會被NAT成本地系統的IP地址。這是使用iptables的源地址偽裝操作實現的。
Linux系統檢視主機的 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 的好處是它能動態從網絡卡獲取地址。
外部訪問容器實現(-p 或 -P)
3)配置 docker0 網橋
Docker 預設指定了 docker0介面 的 IP 地址和子網掩碼,讓主機和容器之間可以通過網橋相互通訊,它還給出了 MTU(介面允許接收的最大傳輸單元),通常 是 1500 Bytes,或宿主主機網路路由上支援的預設值。這些值都可以在服務啟動的 時候進行配置。
也可以在配置檔案中配置 DOCKER_OPTS,然後重啟服務。 由於目前 Docker 網橋是 Linux 網橋,使用者可以使用 brctl show 來檢視網橋和埠連線資訊,當主機是Linux系統時。
在連線網路為host的容器中檢視
[email protected]025000000001:/# brctl show bash: brctl: command not found [email protected]-025000000001:/# apt-get install bridge-utils Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: bridge-utils 0 upgraded, 1 newly installed, 0 to remove and 6 not upgraded. Need to get 29.2 kB of archives. After this operation, 146 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu/ trusty/main bridge-utils amd64 1.5-6ubuntu2 [29.2 kB] Fetched 29.2 kB in 3s (9716 B/s) Selecting previously unselected package bridge-utils. (Reading database ... 11765 files and directories currently installed.) Preparing to unpack .../bridge-utils_1.5-6ubuntu2_amd64.deb ... Unpacking bridge-utils (1.5-6ubuntu2) ... Setting up bridge-utils (1.5-6ubuntu2) ... [email protected]-025000000001:/# brctl show bridge name bridge id STP enabled interfaces br-9dc30b4c7e39 8000.0242a6b606aa no veth2339518 //自定義的bridge網橋myNetwork1,有兩個容器連線 veth3ee80da br-f16ae5c303c6 8000.02427bc35a0e no //自定義的bridge網橋myNetwork1,無容器連線 docker0 8000.0242333af167 no veth0fcbc17 //預設bridge網橋,有四個容器連線 veth229300e veth611835f veth87e64f4 [email protected]-025000000001:/#
注: brctl命令在 Debian、Ubuntu 中可以使用 apt-get install bridge-utils來安裝。
每次建立一個新容器的時候,Docker 從可用的地址段中選擇一個空閒的 IP 地址分配給容器的 eth0 埠。
使用本地主機上docker0 介面的 IP 作為所有容器的預設閘道器,不指定連線的網段,就預設使用docker0。
1》使用--bip修改預設網橋docker的IP地址和子網掩碼
首先先檢視當前docker0的IP地址和子網掩碼:
[email protected]025000000001:/# ip addr show docker0 5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:c4:78:a1:05 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:c4ff:fe78:a105/64 scope link valid_lft forever preferred_lft forever
然後到daemon上去新增"bip": "192.188.0.1/16",如下圖:
然後重新啟動連線host網路的ubuntu6容器,檢視docker0的IP:
userdeMBP:~ user$ docker start ubuntu6 ubuntu6 userdeMBP:~ user$ docker exec -it --privileged ubuntu6 /bin/bash [email protected]-025000000001:/# ip addr show docker0 5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:4a:48:0f:f1 brd ff:ff:ff:ff:ff:ff inet 192.188.0.1/16 brd 192.188.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:4aff:fe48:ff1/64 scope link valid_lft forever preferred_lft forever
發現果然成功更改成了192.188.0.1/16
4) 自定義網橋
除了預設的 docker0 網橋,使用者也可以指定網橋來連線各個容器。
在啟動 Docker 服務的時候,使用 -b BRIDGE或 --bridge=BRIDGE來指定使用 的網橋
如果服務已經執行,那需要先停止服務,並刪除舊的網橋:
$ sudo service docker stop $ sudo ip link set dev docker0 down $ sudo brctl delbr docker0
新增自定義網段:
⚠️下面的方法是預設你在Linux系統下的操作方法,我是打開了一個連線了host網路的容器來給大家示範了一下步驟:
[email protected]025000000001:/# brctl show bridge name bridge id STP enabled interfaces br-9dc30b4c7e39 8000.0242a6b606aa no veth2339518 veth3ee80da br-f16ae5c303c6 8000.02427bc35a0e no docker0 8000.0242333af167 no veth0fcbc17 veth229300e veth611835f veth87e64f4 [email protected]-025000000001:/# brctl addbr bridge0 //新增一個名為bridge0的網橋 [email protected]025000000001:/# ip addr add 192.168.5.1/24 dev bridge0 //設定其IP地址和子網掩碼為192.168.5.1/24 [email protected]025000000001:/# ip link set dev bridge0 up //然後啟動 [email protected]025000000001:/# brctl show bridge name bridge id STP enabled interfaces br-9dc30b4c7e39 8000.0242a6b606aa no veth2339518 veth3ee80da br-f16ae5c303c6 8000.02427bc35a0e no bridge0 8000.000000000000 no docker0 8000.0242333af167 no veth0fcbc17 veth229300e veth611835f veth87e64f4 [email protected]-025000000001:/# ip addr show bridge0 /檢視其IP地址和子網掩碼 40: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether 7e:37:cc:ff:d1:7c brd ff:ff:ff:ff:ff:ff inet 192.168.5.1/24 scope global bridge0 valid_lft forever preferred_lft forever inet6 fe80::7c37:ccff:feff:d17c/64 scope link valid_lft forever preferred_lft forever
然後配置 Docker 服務,預設橋接到建立的網橋上
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker $ sudo service docker start
啟動 Docker 服務。 新建一個容器,可以看到它已經橋接到了bridge0 上
Mac系統上怎麼自定義一個網橋:
從上面的步驟我們可以看出來,和執行docker network命令得到的結果是一樣的:
首先檢視目前擁有的網路:
userdeMBP:~ user$ docker network ls NETWORK ID NAME DRIVER SCOPE f9b3e9b815d5 bridge bridge local 0012b4fcfb27 host host local 7612dc76f9c0 none null local
執行docker network create建立一個myNetwork1的網路,網段為172.20.11.0/24,閘道器為172.20.11.1:
userdeMBP:~ user$ docker network create --driver bridge --subnet 172.20.11.0/24 --gateway 172.20.11.1 myNetwork1 67ae783934fe80ace7e464ee876fddb351b0750ae51ae099508ce665dde828dd userdeMBP:~ user$ docker network ls NETWORK ID NAME DRIVER SCOPE f9b3e9b815d5 bridge bridge local 0012b4fcfb27 host host local 67ae783934fe myNetwork1 bridge local 7612dc76f9c0 none null local userdeMBP:~ user$ docker network inspect 67ae783934fe [ { "Name": "myNetwork1", "Id": "67ae783934fe80ace7e464ee876fddb351b0750ae51ae099508ce665dde828dd", "Created": "2018-12-21T08:11:55.621127909Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.20.11.0/24", "Gateway": "172.20.11.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
然後果然成功生成該網路
然後我們開啟一個連線--network=host的ubuntu:18.04容器來檢視此時的網橋資訊:
userdeMBP:~ user$ docker run -it --name=ubuntu1 --network=host ubuntu:18.04 /bin/bash Unable to find image 'ubuntu:18.04' locally 18.04: Pulling from library/ubuntu 32802c0cfa4d: Pull complete da1315cffa03: Pull complete fa83472a3562: Pull complete f85999a86bef: Pull complete Digest: sha256:6d0e0c26489e33f5a6f0020edface2727db9489744ecc9b4f50c7fa671f23c49 Status: Downloaded newer image for ubuntu:18.04 [email protected]-025000000001:/# brctl bash: brctl: command not found [email protected]-025000000001:/# apt-get install bridge-utils Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package bridge-utils [email protected]-025000000001:/# apt-get update //更新源 Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB] ... Fetched 15.3 MB in 44s (349 kB/s) Reading package lists... Done [email protected]-025000000001:/# apt-get install bridge-utils Reading package lists... Done ... [email protected]-025000000001:/# brctl show bridge name bridge id STP enabled interfaces br-67ae783934fe 8000.02423cfe1d7e no docker0 8000.02420f4186a9 no
從上面可見,我們自定義網路的過程其實也生成了一個自定義網橋br-67ae783934fe,如果你想要容器是連線在該網橋上的,那麼在run時就使用引數--network=myNetwork1設定即可