docker
文章目錄
- docker
- 1 Linux核心實作名稱空間的建立
- 1.1 ip netns指令
- 1.2 建立Network Namespace
- 1.3 操作Network Namespace
- 1.4 轉移裝置
- 1.5 veth pair
- 1.6 建立veth pair
- 1.7 實作Network Namespace間通信
- 1.8 veth裝置重命名
- 2 四種網絡模式配置
- 2.1 bridge模式配置
- 2.2 none模式配置
- 2.3 container模式配置
- 2.4 host模式配置
- 3 容器的常用操作
- 3.1 檢視容器的主機名
- 3.2 在容器啟動時注入主機名
- 3.3 手動指定容器要使用的DNS
- 3.4 手動往/etc/hosts檔案中注入主機名到IP位址的映射
- 3.5 開放容器端口
- 3.6 自定義docker0橋的網絡屬性資訊
- 3.7 docker遠端連接配接
- 3.8 docker建立自定義橋
1 Linux核心實作名稱空間的建立
1.1 ip netns指令
可以借助ip netns指令來完成對 Network Namespace 的各種操作。ip netns指令來自于iproute安裝包,一般系統會預設安裝,如果沒有的話,請自行安裝。
注意:ip netns指令修改網絡配置時需要 sudo 權限。
可以通過ip netns指令完成對Network Namespace 的相關操作,可以通過ip netns help檢視指令幫助資訊:
[root@localhost ~]# ip netns help
Usage: ip netns list
ip netns add NAME
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]
ip netns identify [PID]
ip netns pids NAME
ip [-all] netns exec [NAME] cmd ...
ip netns monitor
ip netns list-id
預設情況下,Linux系統中是沒有任何 Network Namespace的,是以ip netns list指令不會傳回任何資訊。
1.2 建立Network Namespace
通過指令建立一個名為ns0的命名空間:
[root@localhost ~]# ip netns list
[root@localhost ~]# ip netns add ns0
[root@localhost ~]# ip netns list
ns0
新建立的 Network Namespace 會出現在/var/run/netns/目錄下。如果相同名字的 namespace 已經存在,指令會報Cannot create namespace file “/var/run/netns/ns0”: File exists的錯誤。
[root@localhost ~]# ls /var/run/netns/
ns0
[root@localhost ~]# ip netns add ns0
Cannot create namespace file "/var/run/netns/ns0": File exists
對于每個 Network Namespace 來說,它會有自己獨立的網卡、路由表、ARP 表、iptables 等和網絡相關的資源。
1.3 操作Network Namespace
ip指令提供了ip netns exec子指令可以在對應的 Network Namespace 中執行指令。
檢視新建立 Network Namespace 的網卡資訊
[root@localhost ~]# ip netns exec ns0 ip addr
1: lo: 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
可以看到,新建立的Network Namespace中會預設建立一個lo回環網卡,此時網卡處于關閉狀态。此時,嘗試去 ping 該lo回環網卡,會提示Network is unreachable
[root@localhost ~]# ip netns exec ns0 ping 127.0.0.1
connect: Network is unreachable
通過下面的指令啟用lo回環網卡:
[root@localhost ~]# ip netns exec ns0 ip link set lo up
[root@localhost ~]# ip netns exec ns0 ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.073 ms
1.4 轉移裝置
我們可以在不同的 Network Namespace 之間轉移裝置(如veth)。由于一個裝置隻能屬于一個 Network Namespace ,是以轉移後在這個 Network Namespace 内就看不到這個裝置了。
其中,veth裝置屬于可轉移裝置,而很多其它裝置(如lo、vxlan、ppp、bridge等)是不可以轉移的。
1.5 veth pair
veth pair 全稱是 Virtual Ethernet Pair,是一個成對的端口,所有從這對端口一 端進入的資料包都将從另一端出來,反之也是一樣。
引入veth pair是為了在不同的 Network Namespace 直接進行通信,利用它可以直接将兩個 Network Namespace 連接配接起來。
1.6 建立veth pair
[root@localhost ~]# ip link add type veth
[root@localhost ~]# ip a
10: veth0@veth1:
mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 32:c4:23:dd:a7:1c brd ff:ff:ff:ff:ff:ff
11: veth1@veth0:
mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether a2:52:52:cd:54:62 brd ff:ff:ff:ff:ff:ff
可以看到,此時系統中新增了一對veth pair,将veth0和veth1兩個虛拟網卡連接配接了起來,此時這對 veth pair 處于”未啟用“狀态。
1.7 實作Network Namespace間通信
下面我們利用veth pair實作兩個不同的 Network Namespace 之間的通信。剛才我們已經建立了一個名為ns0的 Network Namespace,下面再建立一個資訊Network Namespace,命名為ns1
[root@localhost ~]# ip netns add ns1
[root@localhost ~]# ip netns list
ns1
ns0
然後我們将veth0加入到ns0,将veth1加入到ns1
[root@localhost ~]# ip link set veth0 netns ns0
[root@localhost ~]# ip link set veth1 netns ns1
然後我們分别為這對veth pair配置上ip位址,并啟用它們
[root@localhost ~]# ip netns exec ns0 ip link set veth0 up
[root@localhost ~]# ip netns exec ns0 ip addr add 10.0.0.1/24 dev veth0
[root@localhost ~]# ip netns exec ns1 ip link set lo up
[root@localhost ~]# ip netns exec ns1 ip link set veth1 up
[root@localhost ~]# ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth1
檢視這對veth pair的狀态
[root@localhost ~]# ip netns exec ns0 ip a
10: veth0@if11:
mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 32:c4:23:dd:a7:1c brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.0.0.1/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::30c4:23ff:fedd:a71c/64 scope link
valid_lft forever preferred_lft forever
[root@localhost ~]# ip netns exec ns1 ip a
11: veth1@if10:
mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether a2:52:52:cd:54:62 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.0.2/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::a052:52ff:fecd:5462/64 scope link
valid_lft forever preferred_lft forever
從上面可以看出,我們已經成功啟用了這個veth pair,并為每個veth裝置配置設定了對應的ip位址。我們嘗試在ns1中通路ns0中的ip位址:
[root@localhost ~]# ip netns exec ns1 ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.082 ms
可以看到,veth pair成功實作了兩個不同Network Namespace之間的網絡互動。
1.8 veth裝置重命名
[root@localhost ~]# ip netns exec ns0 ip link set veth0 down
[root@localhost ~]# ip netns exec ns0 ip link set dev veth0 name eth0
[root@localhost ~]# ip netns exec ns0 ifconfig -a
eth0: flags=4163 mtu 1500
inet 10.0.0.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::30c4:23ff:fedd:a71c prefixlen 64 scopeid 0x20
ether 32:c4:23:dd:a7:1c txqueuelen 1000 (Ethernet)
RX packets 12 bytes 928 (928.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 20 bytes 1576 (1.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@localhost ~]# ip netns exec ns0 ip link set eth0 up
2 四種網絡模式配置
2.1 bridge模式配置
[root@localhost ~]# docker run -it --name t1 --rm busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 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:508 (508.0 B) 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)
/ # exit
[root@localhost ~]# docker container ls -a
# 在建立容器時添加--network bridge與不加--network選項效果是一緻的
[root@localhost ~]# docker run -it --name t1 --network bridge --rm busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 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:508 (508.0 B) 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)
/ # exit
2.2 none模式配置
[root@localhost ~]# docker run -it --name t1 --network none --rm busybox
/ # ifconfig -a
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)
/ # exit
2.3 container模式配置
啟動第一個容器
[root@localhost ~]# docker run -it --name b1 --rm busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 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:508 (508.0 B) TX bytes:0 (0.0 B)
啟動第二個容器
[root@localhost ~]# docker run -it --name b2 --rm busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:03
inet addr:10.0.0.3 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 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:508 (508.0 B) TX bytes:0 (0.0 B)
可以看到名為b2的容器IP位址是10.0.0.3,與第一個容器的IP位址不是一樣的,也就是說并沒有共享網絡,此時如果我們将第二個容器的啟動方式改變一下,就可以使名為b2的容器IP與B1容器IP一緻,也即共享IP,但不共享檔案系統。
[root@localhost ~]# docker run -it --name b2 --rm --network container:b1 busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 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:648 (648.0 B) TX bytes:0 (0.0 B)
此時我們在b1容器上建立一個目錄
/ # mkdir /tmp/data
/ # ls /tmp
data
到b2容器上檢查/tmp目錄會發現并沒有這個目錄,因為檔案系統是處于隔離狀态,僅僅是共享了網絡而已。
在b2容器上部署一個站點
/ # echo 'hello world' > /tmp/index.html
/ # ls /tmp
index.html
/ # httpd -h /tmp
/ # netstat -antl
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 :::80 :::* LISTEN
在b1容器上用本地位址去通路此站點
/ # wget -O - -q 127.0.0.1:80
hello world
由此可見,container模式下的容器間關系就相當于一台主機上的兩個不同程序
2.4 host模式配置
啟動容器時直接指明模式為host
[root@localhost ~]# docker run -it --name b2 --rm --network host busybox
/ # ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:06:25:98:91
inet addr:10.0.0.1 Bcast:10.0.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:6ff:fe25:9891/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:55 errors:0 dropped:0 overruns:0 frame:0
TX packets:82 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:8339 (8.1 KiB) TX bytes:7577 (7.3 KiB)
ens33 Link encap:Ethernet HWaddr 00:0C:29:01:78:90
inet addr:192.168.10.144 Bcast:192.168.10.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe01:7890/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:55301 errors:0 dropped:0 overruns:0 frame:0
TX packets:26269 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:63769938 (60.8 MiB) TX bytes:2672449 (2.5 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:42 errors:0 dropped:0 overruns:0 frame:0
TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:4249 (4.1 KiB) TX bytes:4249 (4.1 KiB)
vethffa4d46 Link encap:Ethernet HWaddr 06:4F:68:16:6E:B0
inet6 addr: fe80::44f:68ff:fe16:6eb0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:648 (648.0 B)
此時如果我們在這個容器中啟動一個http站點,我們就可以直接用主控端的IP直接在浏覽器中通路這個容器中的站點了。
3 容器的常用操作
3.1 檢視容器的主機名
[root@localhost ~]# docker run -it --name t1 --network bridge --rm busybox
/ # hostname
7769d784c6da
3.2 在容器啟動時注入主機名
[root@localhost ~]# docker run -it --name t1 --network bridge --hostname wangqing --rm busybox
/ # hostname
wangqing
/ # 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
10.0.0.2 wangqing # 注入主機名時會自動建立主機名到IP的映射關系
/ # cat /etc/resolv.conf
# Generated by NetworkManager
search localdomain
nameserver 192.168.10.2 # DNS也會自動配置為主控端的DNS
/ # ping www.baidu.com
PING www.baidu.com (182.61.200.7): 56 data bytes
64 bytes from 182.61.200.7: seq=0 ttl=127 time=26.073 ms
64 bytes from 182.61.200.7: seq=1 ttl=127 time=26.378 ms
3.3 手動指定容器要使用的DNS
[root@localhost ~]# docker run -it --name t1 --network bridge --hostname wangqing --dns 114.114.114.114 --rm busybox
/ # cat /etc/resolv.conf
search localdomain
nameserver 114.114.114.114
/ # nslookup -type=a www.baidu.com
Server: 114.114.114.114
Address: 114.114.114.114:53
Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com
Name: www.a.shifen.com
Address: 182.61.200.6
Name: www.a.shifen.com
Address: 182.61.200.7
3.4 手動往/etc/hosts檔案中注入主機名到IP位址的映射
[root@localhost ~]# docker run -it --name t1 --network bridge --hostname wangqing --add-host www.a.com:1.1.1.1 --rm busybox
/ # 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.a.com
10.0.0.2 wangqing
3.5 開放容器端口
執行docker run的時候有個-p選項,可以将容器中的應用端口映射到主控端中,進而實作讓外部主機可以通過通路主控端的某端口來通路容器内應用的目的。
-p選項能夠使用多次,其所能夠暴露的端口必須是容器确實在監聽的端口。
-p選項的使用格式:
動态端口指的是随機端口,具體的映射結果可使用docker port指令檢視。
root@localhost ~]# docker run --name web --rm -p 80 nginx
以上指令執行後會一直占用着前端,我們新開一個終端連接配接來看一下容器的80端口被映射到了主控端的什麼端口上
[root@localhost ~]# docker port web
80/tcp -> 0.0.0.0:32769
由此可見,容器的80端口被暴露到了主控端的32769端口上,此時我們在主控端上通路一下這個端口看是否能通路到容器内的站點
[root@localhost ~]# curl http://127.0.0.1:32769
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
iptables防火牆規則将随容器的建立自動生成,随容器的删除自動删除規則。
将容器端口映射到指定IP的随機端口
[root@localhost ~]# docker run --name web --rm -p 192.168.10.144::80 nginx
在另一個終端上檢視端口映射情況
[root@localhost ~]# docker port web
80/tcp -> 192.168.10.144:32768
将容器端口映射到主控端的指定端口
[root@localhost ~]# docker run --name web --rm -p 80:80 nginx
在另一個終端上檢視端口映射情況
[root@localhost ~]# docker port web
80/tcp -> 0.0.0.0:80
3.6 自定義docker0橋的網絡屬性資訊
官方文檔相關配置 自定義docker0橋的網絡屬性資訊需要修改/etc/docker/daemon.json配置檔案
{
"bip": "192.168.1.5/24",
"fixed-cidr": "192.168.1.5/25",
"fixed-cidr-v6": "2001:db8::/64",
"mtu": 1500,
"default-gateway": "10.20.1.1",
"default-gateway-v6": "2001:db8:abcd::89",
"dns": ["10.20.1.2","10.20.1.3"]
}
核心選項為bip,即bridge ip之意,用于指定docker0橋自身的IP位址;其它選項可通過此位址計算得出。
3.7 docker遠端連接配接
dockerd守護程序的C/S,其預設僅監聽Unix Socket格式的位址(/var/run/docker.sock),如果要使用TCP套接字,則需要修改/etc/docker/daemon.json配置檔案,添加如下内容,然後重新開機docker服務:
"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]
在用戶端上向dockerd直接傳遞“-H|–host”選項指定要控制哪台主機上的docker容器
docker -H 192.168.10.145:2375 ps
3.8 docker建立自定義橋
建立一個額外的自定義橋,差別于docker0
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
413997d70707 bridge bridge local
0a04824fc9b6 host host local
4dcb8fbdb599 none null local
[root@localhost ~]# docker network create -d bridge --subnet "192.168.2.0/24" --gateway "192.168.2.1" br0
b340ce91fb7c569935ca495f1dc30b8c37204b2a8296c56a29253a067f5dedc9
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
b340ce91fb7c br0 bridge local
413997d70707 bridge bridge local
0a04824fc9b6 host host local
4dcb8fbdb599 none null local
使用新建立的自定義橋來建立容器:
[root@localhost ~]# docker run -it --name b1 --network br0 busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:02:02
inet addr:192.168.2.2 Bcast:192.168.2.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:11 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:926 (926.0 B) TX bytes:0 (0.0 B)
[root@localhost ~]# docker run --name b2 -it busybox
/ # ls
bin dev etc home proc root sys tmp usr var
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:00:00:02
inet addr:10.0.0.2 Bcast:10.0.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 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:508 (508.0 B) TX bytes:0 (0.0 B)