1. 網路基礎知識回顧 作者Wassim Chegham
Will保哥 初學者都該了解的HTTP通訊協定基礎
2. 網路常用命令 2.1. IP地址查詢 Windows
Linux
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 link/ether 00:0c:29:ac:f4:ef brd ff:ff:ff:ff:ff:ff altname enp2s1 inet 192.168.152.137/24 brd 192.168.152.255 scope global dynamic noprefixroute ens33 valid_lft 1200sec preferred_lft 1200sec inet6 fe80::c252:7221:a2a5:42d9/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:80:62:9c:23 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:80ff:fe62:9c23/64 scope link valid_lft forever preferred_lft forever 5: veth58b174d@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether b6:1c:f6:19:ec:c8 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::b41c:f6ff:fe19:ecc8/64 scope link valid_lft forever preferred_lft forever
lo:localhost
ens33:對外網路
docker0:docker內部網路
veth:docker內部容器
2.2. 測試網路連通性 2.2.1. ping命令 測試IP連通性
1 2 3 4 root@host04:/home/kite/Desktop# ping 192.168.152.139 PING 192.168.152.139 (192.168.152.139) 56(84) bytes of data. 64 bytes from 192.168.152.139: icmp_seq=1 ttl=64 time=0.614 ms 64 bytes from 192.168.152.139: icmp_seq=2 ttl=64 time=0.521 ms
2.3. 測試端口連通性 2.3.1. telnet命令 測試(port)連通性
1 2 3 root@host04:/home/kite/Desktop# telnet www.google.com.tw 80 Trying 142.251.43.3... Connected to www.google.com.tw.
2.4. 測試路徑 2.4.1. traceroute命令 路徑探測跟蹤
2.5. 測試web服務 2.5.1. curl命令 請求web服務的工具。它的名字就是客戶端(client)的URL工具的意思。
它的功能非常強大,如果熟練的話,完全可以取代Postman這一類的圖形界面工具。
可以參考 阮一峰 curl 的用法指南
3. 容器網路需要解決哪些問題? 3.1. 第一個問題實驗 首先先創建一個容器
1 root@host04:/home/kite/Desktop# docker container run -d --rm --name box01 busybox /bin/sh -c "while true; do sleep 3600; done"
進入容器後,使用ip a,看到etho0開頭有一個ip為172.17.0.2:
1 2 3 4 5 6 7 8 9 10 11 root@host04:/home/kite/Desktop# docker exec -it box01 sh / # ip a 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 8: eth0@if9: <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 / # exit
3.2. 第二個問題實驗 退出容器後,在host使用ping觀查:
1 2 3 4 5 6 root@host04:/home/kite/Desktop# ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.184 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.040 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.032 ms
3.3. 第三個問題實驗 實驗待補
3.4. 第四個問題實驗 再創第二個容器box02
1 root@host04:/home/kite/Desktop# docker container run -d --rm --name box02 busybox /bin/sh -c "while true; do sleep 3600; done"
查看容器IP,IP為172.17.0.3:
1 2 3 4 5 6 7 8 9 root@host04:/home/kite/Desktop# docker container exec -it box02 ip a 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 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
在box02容器內去ping box01:
1 2 3 4 root@host04:/home/kite/Desktop# docker container exec -it box02 ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2): 56 data bytes 64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.060 ms 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.066 ms
3.5. 第五個問題實驗 創建一個web01:
1 root@host04:/home/kite/Desktop# docker container run -d -p 8080:80 --name web01 nginx
查看一下host04IP:
192.168.152.137
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 root@host04:/home/kite/Desktop# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000 link/ether 00:0c:29:ac:f4:ef brd ff:ff:ff:ff:ff:ff altname enp2s1 inet 192.168.152.137/24 brd 192.168.152.255 scope global dynamic noprefixroute ens33 valid_lft 1637sec preferred_lft 1637sec inet6 fe80::c252:7221:a2a5:42d9/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:80:62:9c:23 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:80ff:fe62:9c23/64 scope link valid_lft forever preferred_lft forever 9: veth2024750@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether f6:0a:2b:a1:86:df brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::f40a:2bff:fea1:86df/64 scope link valid_lft forever preferred_lft forever 11: veth3872608@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 8e:84:3d:c0:57:7c brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::8c84:3dff:fec0:577c/64 scope link valid_lft forever preferred_lft forever 19: vetha5bf5e2@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 7a:26:38:e8:03:8f brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::7826:38ff:fee8:38f/64 scope link valid_lft forever preferred_lft forever
我們利用host05使用curl來 對 host04 IP 192.168.152.137:8080:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 kite@host5:~/Desktop$ curl 192.168.152.137:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
3.6. 第一個問題:為什麼容器內會獲取到172.17.0.2 的IP地址 3.7. 第二個問題:為什麼HOST可以PING成功容器? 3.8. 第三個問題:為什麼容器可以PING外部網路成功? 3.9. 第四個問題:為什麼容器與容器之間可以互通? 3.10. 第五個問題:PORT轉發是怎麼回事? 4. 容器間通信之bridge網路 來教一個新指令docker network ls:
1 2 3 4 5 root@host04:/home/kite/Desktop# docker network ls NETWORK ID NAME DRIVER SCOPE 118c43b92cc6 bridge bridge local 60cb3d796e6b host host local c5b7b2fbb219 none null local
這是目前的container網路相關配置,我們再使用docker network inspect 118c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 root@host04:/home/kite/Desktop# docker network inspect 118 [ { "Name": "bridge", "Id": "118c43b92cc662ecc4e86dc17d63fc599bcc3d0d39547715f92ab77c4fcc2b6c", "Created": "2022-02-24T11:01:57.933731418+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, #Here "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, #Here "Containers": { "794e99f196278ea048b963bbac0845ad228a768ca9f787450d557b317f098f7e": { "Name": "web01", "EndpointID": "df84f183d1fcd096c04e9f902000176b2bb25e7eb46e1c3a9e1c16b9d42c9642", "MacAddress": "02:42:ac:11:00:04", "IPv4Address": "172.17.0.4/16", "IPv6Address": "" }, "cb69532a01df76e063aeaf5addaf921ff5e8f7fd273e6fa685b5e976c26e5355": { "Name": "box01", "EndpointID": "1d6f5ea9838a274bed7bbb4df5ed66a4071119aec190414de7ca0d12f089dd14", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" }, "ef42984b47681f9f0d923dc85902e952a6a53adcc5d7f37cb2c4290711945e62": { "Name": "box02", "EndpointID": "3153006bfa4e61c83a9edf44cdf4d666557603c74ef1bcacd8fcac01c43eef10", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ]
整理後,抽出幾個部分說明
Config
gateway為172.17.0.1
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
Containers
這個bridge就扮演了switch的角色,container都接這個bridge上面,可以再參考下面的圖片。
因此有了這個docker0,就可以讓container彼此之間進行交互溝通。
並且container在預設建立時,沒有指令要使用哪個bridge,那默認就會使用docker0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 "Containers": { "794e99f196278ea048b963bbac0845ad228a768ca9f787450d557b317f098f7e": { "Name": "web01", "EndpointID": "df84f183d1fcd096c04e9f902000176b2bb25e7eb46e1c3a9e1c16b9d42c9642", "MacAddress": "02:42:ac:11:00:04", "IPv4Address": "172.17.0.4/16", "IPv6Address": "" }, "cb69532a01df76e063aeaf5addaf921ff5e8f7fd273e6fa685b5e976c26e5355": { "Name": "box01", "EndpointID": "1d6f5ea9838a274bed7bbb4df5ed66a4071119aec190414de7ca0d12f089dd14", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" }, "ef42984b47681f9f0d923dc85902e952a6a53adcc5d7f37cb2c4290711945e62": { "Name": "box02", "EndpointID": "3153006bfa4e61c83a9edf44cdf4d666557603c74ef1bcacd8fcac01c43eef10", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" }
5. 容器對外通訊之bridge網路
可以先安裝 sudo apt-get install -y bridge-utils,接著我們下指令,我們會看到docker0有3個interfaces以veth開頭,就如我們上面圖片一樣的示意圖。因為我們有3個container所以就會有3個interface。
假設我們再刪掉一個container,再重新下brctl show指令,也就會發現少一個喔。
1 2 3 4 5 6 root@host04:/home/kite/Desktop# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024280629c23 no veth2024750 veth3872608 vetha5bf5e2
我們複習一下
如果box01 ping web01能成功,路線會經過docker0過去。
我們box01往外網ping能成功,路線一樣會經過docker0往外丟。(前提是host主機本身也能連外網,這樣才能ping成功)
那docker0怎麼走出去的,實際還是要看路由,可以使用ip route來看一下,其中 default via 192.168.152.55 就是eth0:
1 2 3 4 5 root@host04:/home/kite/Desktop# ip route default via 192.168.152.55 dev ens33 proto dhcp metric 100 169.254.0.0/16 dev ens33 scope link metric 1000 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 192.168.152.0/24 dev ens33 proto kernel scope link src 192.168.152.137 metric 100
如果box01要出去到外網時,因為是pirvate ip,因此到外網前會再透過NAT轉為成eth0的IP地址,而實際怎麼轉換的規則可以使用iptables --list -t nat來查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 root@host04:/home/kite/Desktop# iptables --list -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination DOCKER all -- anywhere !localhost/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 172.17.0.0/16 anywhere MASQUERADE tcp -- 172.17.0.4 172.17.0.4 tcp dpt:http Chain DOCKER (2 references) target prot opt source destination RETURN all -- anywhere anywhere DNAT tcp -- anywhere anywhere tcp dpt:http-alt to:172.17.0.4:80
參考資料
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Security_Guide/s1-firewall-ipt-fwd.html
6. 網路知識補充NAT NAT的技術在解決什麼問題?
IP不足的問題
網路上進行通信必須要一個獨一無二的IP才能進行傳輸。
舉例來說,在家裡的筆電、桌機、平板都給一個私有IP,透過路由器後,會再轉換成公有IP。回程的時候再透過路由器將公有IP轉換為對應內部的IP。
NAT translates:
Private to public
Public to private
7. 創建和使用自定義bridge(上)
docker network create -d [類型] [名稱]
1 docker network create -d bridge mybridge
其中-d 指的就是driver,因為network類型有很多種,以此為例,我們創建一個bridge類型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # 創建bridge名為mybridge root@host04:/home/kite/Desktop# docker network create -d bridge mybridge 020d342bf4a11304f4509d44ff64df2555918b2ff4d6664878254faba753d489 root@host04:/home/kite/Desktop# docker network ls NETWORK ID NAME DRIVER SCOPE 118c43b92cc6 bridge bridge local 60cb3d796e6b host host local 020d342bf4a1 mybridge bridge local c5b7b2fbb219 none null local # 查看mybridge 172.18.0.0 ,還記得嗎?剛剛默認的bridge 是172.17.0.0 root@host04:/home/kite/Desktop# docker network inspect mybridge [ { "Name": "mybridge", "Id": "020d342bf4a11304f4509d44ff64df2555918b2ff4d6664878254faba753d489", "Created": "2022-03-01T15:19:52.641431288+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
7.1. 如何添加指定的bridge在container上? 補上--network mybridge,完成!
1 2 root@host04:/home/kite/Desktop# docker container run -d --rm --name box03 --network mybridge busybox /bin/sh -c "while true; do sleep 3600; done"
來看一下container設定內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 root@host04:/home/kite/Desktop# docker container inspect box03 [ { "Id": "408dbc15598a60ec6f57dbfd7ae080cc9bceb318bf307a19303b757d8d9e3265", "Created": "2022-03-01T07:21:15.326172232Z", "Path": "/bin/sh", ... ... "NetworkSettings": { "Bridge": "", ... ... "Networks": { "mybridge": { "IPAMConfig": null, "Links": null, "Aliases": [ "408dbc15598a" ], "NetworkID": "020d342bf4a11304f4509d44ff64df2555918b2ff4d6664878254faba753d489", "EndpointID": "c3acfbc43272a5c1c9c01a1ff3c05faa7925e8f3b86617d24cac3d620f74be48", "Gateway": "172.18.0.1", "IPAddress": "172.18.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:12:00:02", "DriverOpts": null } } } } ]
7.2. 在一個container可否有2個bridge? 可以喔。
1 docker network connect bridge box03
查看一下container:
這個box03拿到了兩個ip,分別是172.17.0.5、172.18.0.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 root@host04:/home/kite/Desktop# docker network connect bridge box03 root@host04:/home/kite/Desktop# docker container inspect box03 [ ... ... "NetworkSettings": { ... "Networks": { "bridge": { "IPAMConfig": {}, "Links": null, "Aliases": [], "NetworkID": "118c43b92cc662ecc4e86dc17d63fc599bcc3d0d39547715f92ab77c4fcc2b6c", "EndpointID": "d15ea2680f6b23826c99995932b2ad178366ee3008cb1025fc7385133bd63b8e", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.5", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:05", "DriverOpts": {} }, "mybridge": { "IPAMConfig": null, "Links": null, "Aliases": [ "408dbc15598a" ], "NetworkID": "020d342bf4a11304f4509d44ff64df2555918b2ff4d6664878254faba753d489", "EndpointID": "c3acfbc43272a5c1c9c01a1ff3c05faa7925e8f3b86617d24cac3d620f74be48", "Gateway": "172.18.0.1", "IPAddress": "172.18.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:12:00:02", "DriverOpts": null } } } } ]
進入到container裡面驗證,的確有兩個IP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@host04:/home/kite/Desktop# docker container exec -it box03 sh / # ip a 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 21: eth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever 23: eth1@if24: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:05 brd ff:ff:ff:ff:ff:ff inet 172.17.0.5/16 brd 172.17.255.255 scope global eth1 valid_lft forever preferred_lft forever
7.3. 在bridge中移除掉container 可以透過docker network disconnect [bridge name] [container name] :
1 root@host04:/home/kite/Desktop# docker network disconnect bridge box03
再重查一次,的確沒有bridge那一項:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@host04:/home/kite/Desktop# docker container inspect box03 [ ... ... "NetworkSettings": { ... "Networks": { "mybridge": { "IPAMConfig": null, "Links": null, "Aliases": [ "408dbc15598a" ], "NetworkID": "020d342bf4a11304f4509d44ff64df2555918b2ff4d6664878254faba753d489", "EndpointID": "c3acfbc43272a5c1c9c01a1ff3c05faa7925e8f3b86617d24cac3d620f74be48", "Gateway": "172.18.0.1", "IPAddress": "172.18.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:12:00:02", "DriverOpts": null } } } } ]
8. 創建和使用自定義bridge(下) 8.1. 自定義bridge好處 container 使用自定義的bridge時,容器之間的互ping可以多一種選擇,就是直接使用container 名稱
1 2 3 4 5 6 7 root@host04:/home/kite/Desktop# docker container exec -ti box04 ping 172.18.0.4 PING 172.18.0.4 (172.18.0.4): 56 data bytes 64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.262 ms 64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.095 ms #這個方法我目前沒試成功,不過的確在教學中看老師是有成功的,目前懷疑是docker的版本問題 root@host04:/home/kite/Desktop# docker container exec -ti box04 ping box05
記錄問題:
目前在使用container 名稱互ping是失敗的,環境資訊先memo,後續找出確切問題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 root@host04:/home/kite/Desktop# docker version Client: Version: 20.10.7 API version: 1.41 Go version: go1.13.8 Git commit: 20.10.7-0ubuntu5~20.04.2 Built: Mon Nov 1 00:34:17 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Engine: Version: 20.10.7 API version: 1.41 (minimum version 1.12) Go version: go1.13.8 Git commit: 20.10.7-0ubuntu5~20.04.2 Built: Fri Oct 22 00:45:53 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.5.5-0ubuntu3~20.04.1 GitCommit: runc: Version: 1.0.1-0ubuntu2~20.04.1 GitCommit: docker-init: Version: 0.19.0 GitCommit: root@host04:/home/kite/Desktop# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal
8.2. 自己決定區段 1 docker network create -d bridge --gateway 172.200.0.1 --subnet 172.200.0.0/16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # 建立一個bridge 並自行設定gateway、subnet root@host04:/home/kite/Desktop# docker network create -d bridge --gateway 172.200.0.1 --subnet 172.200.0.0/16 mydemo b9020c5774943e03e7ddfbf9506d16434a0058330e2e4798ddfa9ef37403e598 # 檢查network清單 root@host04:/home/kite/Desktop# docker network ls NETWORK ID NAME DRIVER SCOPE 118c43b92cc6 bridge bridge local 60cb3d796e6b host host local 020d342bf4a1 mybridge bridge local b9020c577494 mydemo bridge local c5b7b2fbb219 none null local # 查看mydemo詳細 root@host04:/home/kite/Desktop# docker network inspect mydemo [ { "Name": "mydemo", "Id": "b9020c5774943e03e7ddfbf9506d16434a0058330e2e4798ddfa9ef37403e598", "Created": "2022-03-01T17:06:02.813440335+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.200.0.0/16", "Gateway": "172.200.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ]
9. 容器的端口轉發Port Forwarding 試著建立一個容器:
1 docker container run -d -p 8080:80 --name web01 nginx
我們使用sudo iptables -t nat -nvxL
裡面有一條很重要的訊息 tcp dpt:8080 to:172.17.0.4:80 也就是指host 8080 port 映射到 容器172.17.0.4:80 port。顯然很多docker network 的底層實際會用到iptable進行一些設定,好讓容器與host之間能有很好的互通效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 root@host04:/home/kite/Desktop# sudo iptables -t nat -nvxL Chain PREROUTING (policy ACCEPT 13 packets, 1629 bytes) pkts bytes target prot opt in out source destination 4 312 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT 12 packets, 1545 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 140 packets, 13111 bytes) pkts bytes target prot opt in out source destination 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT 141 packets, 13195 bytes) pkts bytes target prot opt in out source destination 0 0 MASQUERADE all -- * !br-b9020c577494 172.200.0.0/16 0.0.0.0/0 0 0 MASQUERADE all -- * !br-020d342bf4a1 172.18.0.0/16 0.0.0.0/0 27 1767 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 0 0 MASQUERADE tcp -- * * 172.17.0.4 172.17.0.4 tcp dpt:80 Chain DOCKER (2 references) pkts bytes target prot opt in out source destination 0 0 RETURN all -- br-b9020c577494 * 0.0.0.0/0 0.0.0.0/0 0 0 RETURN all -- br-020d342bf4a1 * 0.0.0.0/0 0.0.0.0/0 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0 1 60 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
10. 端口轉發和dockerfile https://github.com/nginxinc/docker-nginx/blob/master/Dockerfile-alpine.template
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FROM alpine:%%ALPINE_VERSION%%... ... COPY docker-entrypoint.sh / COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d COPY 20-envsubst-on-templates.sh /docker-entrypoint.d COPY 30-tune-worker-processes.sh /docker-entrypoint.d ENTRYPOINT ["/docker-entrypoint.sh" ] EXPOSE 80 STOPSIGNAL SIGQUITCMD ["nginx" , "-g" , "daemon off;" ]
我們可以看到在nginx的dockerfile中有一行 EXPOSE 80 ,可以做到對外PORT的映射。
提問,那如果dockerfile 沒有這行EXPOSE 80,我們還能使用docker container run -p 這樣的方式設定port嗎?答案是可以的。
dockefile這邊如果有明確使用EXPOSE 80,主要的精神在表達讓使用者知道有一個默認的對外port。
The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container,about which ports are intended to be published.
EXPOSE 默認為TCP ,也可以具體明確定義:
當然,也可以同使支持兩種協議,寫二行即可:
1 2 EXPOSE 80 /tcpEXPOSE 80 /udp
11. HOST與None 11.1. HOST 針對HOST詳細說明
1 2 3 4 root@host04:/home/kite/Desktop# docker network ls NETWORK ID NAME DRIVER SCOPE b22fdb790b7f bridge bridge local 60cb3d796e6b host host local
建立一個container 並使用hostnetwork:
1 2 root@host04:/home/kite/Desktop# docker container run -d --name web03 --network host nginx e1b6b611df2e79a4622359385b98e5a0c824af8a740d71a9ad2ecfcf14fac886
列出container ps ,我們會發現web03port 是空白的。是因為我們剛剛使用的的是host 那組netwowrk,所以目前這個container是與host共用同一組network設定的
1 2 3 4 root@host04:/home/kite/Desktop# docker container ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e1b6b611df2e nginx "/docker-entrypoint.…" 7 minutes ago Up 7 minutes web03 f1a1469eac7f nginx "/docker-entrypoint.…" 2 hours ago Up 7 minutes 80/tcp web02
使用curl查看一下,的確在host上的80 port 是映射到web03 container :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 root@host04:/home/kite/Desktop# curl 127.0.0.1:80 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
我們可以思考一下,那這樣的使用host netowrk與使用container -p 80:80(沒填寫network 預設會選擇docker0,也就是bridge network)會有什麼差別?
其實用圖片來說,多了一個docker0,當中間層的轉發,效率上是有差一些的喔。(因為突然斷電…圖的設計稿不見了,懶得重畫…)想像一下,多了一個web03 然後不經過docker0,直接一條線對接192.168.152.55 eth0。
11.2. NONE 1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@host04:/home/kite/Desktop# docker network ls NETWORK ID NAME DRIVER SCOPE b22fdb790b7f bridge bridge local 60cb3d796e6b host host local c5b7b2fbb219 none null local # 建立一個box01 使用network none root@host04:/home/kite/Desktop# docker container run -it --name box01 --network none busybox / # ip a 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
有一些第三方開發特殊需求,只需要container容器,相關的網路配置由自己決定與設計。就會使用到 none network
12. 網路命名空間
add-ns-to-br.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # !/bin/bash bridge=$1 namespace=$2 addr=$3 # link 的兩頭vethA=veth-$namespace vethB=eth00 # 建立一個namespace空間 sudo ip netns add $namespace # 建立一個link ,兩頭一個是$vethA ,另一個是$vethB sudo ip link add $vethA type veth peer name $vethB # 將$vethB 放到這個namespace裡 sudo ip link set $vethB netns $namespace # 這個namespace空間中,針對$vethB 頭賦與一個IP地址給它 sudo ip netns exec $namespace ip addr add $addr dev $vethB # 將$vethB 給up起來 sudo ip netns exec $namespace ip link set $vethB up # 將$vethA 給up起來 sudo ip link set $vethA up # 將$vethA 加到這個bridge上 sudo brctl addif $bridge $vethA
12.1. 腳本執行 1 2 kite@host04:~/Desktop/namespace$ sh add-ns-to-br.sh mydocker0 ns1 172.16.1.1/16 kite@host04:~/Desktop/namespace$ sh add-ns-to-br.sh mydocker0 ns1 172.16.1.2/16
12.2. 驗證 我使用的是ubuntu 環境,去ping 另一個ip 沒有成功。有空再回來試試看,以下是視頻的實驗結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [vagrant@docker-host1 ~]$ sudo ip netns exec ns1 bash [root@docker-host1 vagrant]# ip a 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 5: eth00@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether f2:59:19:34:73:70 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.16.1.1/16 scope global eth00 valid_lft forever preferred_lft forever inet6 fe80::f059:19ff:fe34:7370/64 scope link valid_lft forever preferred_lft forever [root@docker-host1 vagrant]# ping 172.16.1.2 PING 172.16.1.2 (172.16.1.2) 56(84) bytes of data. 64 bytes from 172.16.1.2: icmp_seq=1 ttl=64 time=0.029 ms 64 bytes from 172.16.1.2: icmp_seq=2 ttl=64 time=0.080 ms ^C --- 172.16.1.2 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.029/0.054/0.080/0.026 ms [root@docker-host1 vagrant]#
13. 多容器應用部署練習 常見需求會將各個服務進行拆分各自容器之中。後續我們會再講docker compose ,透過配置文件定義容器之間的交互溝通與設定,搭建一個完整的環境。
13.1. 鏡像準備 13.1.1. REDIS 1 root@host04:/home/kite/Desktop/namespace# docker image pull redis
13.1.2. python flask web 13.1.2.1. 資料夾結構 1 2 3 4 5 root@host04:/home/kite/Desktop/flask_redis_practice# tree . ├── app.py └── Dockerfile
13.1.2.2. 程式準備 app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 from flask import Flaskfrom redis import Redisimport osimport socketapp = Flask(__name__) redis = Redis(host=os.environ.get('REDIS_HOST' , '127.0.0.1' ), port=6379 ) @app.route('/' ) def hello (): redis.incr('hits' ) return f"Hello Container World! I have been seen {redis.get('hits' ).decode('utf-8' )} times and my hostname is {socket.gethostname()} .\n"
13.1.2.3. dockerfile準備 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FROM python:3.9 .5 -slimRUN pip install flask redis && \ groupadd -r flask && useradd -r -g flask flask && \ mkdir /src && \ chown -R flask:flask /src USER flaskCOPY app.py /src/app.py WORKDIR /src ENV FLASK_APP=app.py REDIS_HOST=redisEXPOSE 5000 CMD ["flask" , "run" , "-h" , "0.0.0.0" ]
13.1.2.4. 產生鏡像檔 1 docker image build -t flask-demo .
13.2. 建立bridge 建立名為demo-network的bridge
1 root@host04:/home/kite/Desktop/flask_redis_practice# docerk network create -d bridge demo-network
13.3. 建立container 將redis、python flask web 創建時,加入至 demo-network中。
13.3.1. REDIS 1 root@host04:/home/kite/Desktop/flask_redis_practice# docker container run -d --name redis-server --network demo-network redis
13.3.2. python flask web 1 root@host04:/home/kite/Desktop/flask_redis_practice# docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo
13.3.3. 測試 應該就會看到網頁刷新時,會一直+1