Docker網路是容器化中最難理解的一點也是整個容器化中最容易出問題又難以排查的地方,加上使用Kubernets後大部分人即使是專業運維如果沒有紮實的網路知識也很難定位容器網路問題,因此這裡就容器網路單獨拿出來理一理。
先了解一下Docker的一點基礎架構知識,Docker 技術架構圖:
Docker是不能直接在 Windows 平臺上執行的,只支援 linux 系統,因為Docker 依賴 linux kernel 三項最基本的技術。
- Namespaces 充當隔離的第一級,是對 Docker 容器進行隔離,讓容器擁有獨立的 hostname,ip,pid,同時確保一個容器中執行一個程式而且不能看到或影響容器外的其它程式 。
- Cgroups 是容器對使用的宿主機資源進行核算並限制的關鍵功能,比如 CPU, 記憶體, 磁碟等。
- Union FS 主要是對映象也就是 image 這一塊作支援,採用 copy-on-write 技術,讓大家可以共用某一層,對於某些差異層的話就可以在差異的記憶體儲存,
- Libcontainer 是一個庫,是對上面這三項技術做一個封裝。
- Docker engine 用來控制容器 container 的執行,以及映象檔案的拉取。
Docker的工作原理:每個容器都在自己的名稱空間中執行,但使用與所有其他容器完全相同的核心。發生隔離是因為核心知道分配給程式的名稱空間,並且在API呼叫期間確保程式只能訪問其自己的名稱空間中的資源。
Docker部署關鍵配置
daemon.json檔案
指定私有倉庫地址insecure-registries,否則拉取映象出現問題:
1 { 2 "data-root": "/docker/data", 3 "exec-opts": ["native.cgroupdriver=cgroupfs"], 4 "registry-mirrors": [ 5 "https://docker.mirrors.ustc.edu.cn", 6 "http://hub-mirror.c.163.com" 7 ], 8 "hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"], 9 "insecure-registries": ["192.168.0.23:5000"], 10 "max-concurrent-downloads": 10, 11 "live-restore": false, 12 "log-driver": "json-file", 13 "log-level": "warn", 14 "log-opts": { 15 "max-size": "50m", 16 "max-file": "1" 17 }, 18 "storage-driver": "overlay2" 19 }
1.指定data-root 配置容器資料地址,在伺服器中單獨規劃磁碟空間,避免佔用系統空間
2.指定hosts,放開2375對外介面
3.Docker使用storage driver(儲存驅動程式)來管理image和container的資料,要使用overlayfs
,要確保系統的核心版本大於等於3.18,overlay
要比aufs和device mapper快一點,OverlayFS僅有兩層,映象中的每一層對應/var/lib/docker/overlay
中的一個資料夾,資料夾以該層的UUID命名。然後使用硬連線將下面層的檔案引用到上層。這在一定程度上節省了磁碟空間。
4.指定檔案驅動native.cgroupdriver=cgroupfs控制的資源主要包括CPU、記憶體、block I/O、網路頻寬等,也可以指定為systemd,這裡要注意的是後續佈署k8s時要與k8s設定的檔案驅動操持一致,否時會報錯:
failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"
需要修改kubelet.service Environment中新增--cgroup-driver=cgroupfs或systemd
docker.service檔案
[Unit] Description=Docker Application Container Engine Documentation=http://docs.docker.io [Service] Environment="PATH=/docker/bin:/bin:/sbin:/usr/bin:/usr/sbin" ExecStart=/docker/bin/dockerd ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT ExecReload=/bin/kill -s HUP $MAINPID Restart=always RestartSec=5 LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TimeoutStartSec=0 Delegate=yes KillMode=process [Install] WantedBy=multi-user.target
docker 從 1.13 版本開始,將`iptables` 的`filter` 表的`FORWARD` 鏈的預設策略設定為`DROP`,從而導致 ping 其它 Node 上的 Pod IP 失敗,因此必須在 `filter` 表的`FORWARD` 鏈增加一條預設允許規則 `iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT`
通過了解以上docker基礎框架後排查網路問題思路會更清晰。
Docker容器的網路模型
Docker容器網路的原始模型主要有三種:Bridge(橋接)、Host(主機)及Container(容器)
Docker預設使用Bridge+NAT的通訊模型,Bridge模型藉助於虛擬網橋裝置為容器建立網路連線,Docker守護程式首次啟動時,它會在當前節點上建立一個名為docker0的橋裝置,並預設配置其使用172.17.0.0/16網路,此主機上啟動的Docker容器會連線到這個虛擬網橋上。
容器與外部網路間的通訊
為了解決容器訪問外部網路,docker引入NAT,通過iptables規則控制,網橋 docker0 通過 iptables 中的配置與宿主機器上的網路卡相連,所有符合條件的請求都會通過 iptables 轉發到 docker0 並由網橋分發給對應的機器。建立MASQUERADE規則:
檢視nat表
# iptables -t nat -S
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
資料包流程
這條規則將所有從容器發出的、目的地址為Host外部網路的包的IP都修改成Host的IP,並由Host傳送出去。
外部網路訪問容器
Docker容器是通過dnat對映或docker-proxy服務對外提供訪問,如指定埠對映:docker run -p 9001:9000。
使用docker run -p時,docker實際是在iptables做了DNAT規則,實現埠轉發功能,為容器分配一個 IP 地址,同時向 iptables 中追加一條新的規則。
可以使用iptables -t nat -vnL檢視。
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 9001 -j DNAT --to-destination 172.17.0.2:9000
外部訪問外部伺服器訪問10.3.20.87:9001
匹配到DNAT規則,訪問到容器-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 9001 -j DNAT --to-destination 172.17.0.2:9000
本機訪問127.0.0.1:9001 沒有匹配到任何iptable,走docker-proxy
另外容器要訪問外部網路需要宿主機進行轉發,要在宿主機中開啟轉發設定:
#sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
為0說明沒有開啟,需要手動開啟。
#docker network lsNETWORK ID NAME DRIVER7fca4eb8c647 bridge bridge9f904ee27bf5 none nullcf03ee007fb4 host host
docker network create -d macvlan \--subnet=172.16.86.0/24 \--gateway=172.16.86.1 \-o parent=eth0 pub_net
Docker跨主機容器間網路通訊
具體的介紹可參考 之前的文章 Kubernetes叢集部署關鍵知識總結 地址 https://www.cnblogs.com/zhangs1986/p/10749721.html
注意:flannel 使用 vxlan 技術為各節點建立一個可以互通的 Pod 網路,使用的埠為 UDP 8472,需要開放該埠。
本文所用到Docker版本為19.03.15
Kubernetes 1.20 版本開始將棄用 Docker
kubelet目前推薦方式是直連containerd。
被去掉的部分是刪除 dockershim(Dockershim 作用
:把外部收到的請求轉化成 Docker Daemon
能聽懂的請求,讓 Docker Daemon 執行建立、刪除等容器操作。)
可以用Containerd 或 Podman 替換。