1. 程式人生 > >Docker技術入門與實戰 第二版-學習筆記-8-網路功能network-3-容器訪問控制和自定義網橋

Docker技術入門與實戰 第二版-學習筆記-8-網路功能network-3-容器訪問控制和自定義網橋

1)容器訪問控制

容器的訪問控制,主要通過 Linux 上的 iptables防火牆來進行管理和實現。 iptablesLinux 上預設的防火牆軟體,在大部分發行版中都自帶。

 

容器訪問外部網路

容器要想訪問外部網路,需要本地系統的轉發支援。在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命令在 DebianUbuntu 中可以使用 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設定即可