越來越多的客戶要求我們提供對Kubernetes 叢集的訪問,以便能夠存取叢集內的服務:以便他們可以直接連接到某些資料庫或服務,將本機應用程式與叢集內的應用程式連接起來... ……
例如,需要從本機電腦連線到服務 memcached.staging.svc.cluster.local
。 我們使用客戶端連接的叢集內的 VPN 來提供此功能。 為此,我們宣布 Pod、服務的子網,並將叢集 DNS 推送到客戶端。 因此,當客戶端嘗試連接到服務時 memcached.staging.svc.cluster.local
,請求發送到叢集 DNS,並作為回應從叢集服務網路或 pod 位址接收該服務的位址。
我們使用kubeadm配置K8s集群,其中預設服務子網路為 192.168.0.0/16
,而 Pod 的網路是 10.244.0.0/16
。 通常一切都很順利,但有幾點:
- 子網
192.168.*.*
通常用於客戶端辦公室網絡,甚至更常用於開發人員家庭網絡。 然後我們就會遇到衝突:家庭路由器在該子網路上工作,而 VPN 將這些子網路從叢集推送到客戶端。 - 我們有幾個集群(生產集群、階段集群和/或幾個開發集群)。 然後,預設情況下,所有 Pod 和服務都將具有相同的子網,這給多個叢集中的服務同時工作帶來了很大的困難。
我們很久以前就採用了在同一專案中為服務和 Pod 使用不同子網路的做法 - 一般來說,以便所有叢集都有不同的網路。 然而,有大量正在運行的叢集我不想從頭開始滾動,因為它們運行許多服務、有狀態應用程式等。
然後我們問自己:如何更改現有叢集中的子網路?
搜尋決策
最常見的做法是重新創建 所有 ClusterIP 類型的服務。 作為一種選擇,
以下過程有一個問題:配置所有內容後,Pod 在 /etc/resolv.conf 中使用舊 IP 作為 DNS 名稱伺服器。
由於我仍然沒有找到解決方案,我不得不使用 kubeadm reset 重置整個叢集並再次初始化。
但這並不適合所有人...以下是我們案例的更詳細介紹:
- 採用法蘭絨;
- 雲端和硬體上都有叢集;
- 我想避免重新部署叢集中的所有服務;
- 一般來說,需要以最少的問題來完成所有事情;
- Kubernetes 版本是 1.16.6(但是,其他版本的進一步步驟類似);
- 主要任務是確保在使用 kubeadm 部署且具有服務子網路的叢集中
192.168.0.0/16
,將其替換為172.24.0.0/16
.
碰巧的是,我們長期以來一直有興趣了解 Kubernetes 中 etcd 中存儲了什麼以及如何存儲,可以用它做什麼......所以我們想:“為什麼不直接更新 etcd 中的數據,用新的 IP 位址(子網路)取代舊的 IP 位址(子網路)“?
在搜尋了用於處理 etcd 中資料的現成工具後,我們沒有找到任何可以完全解決問題的工具。 (順便說一句,如果您了解直接在 etcd 中處理資料的任何實用程序,我們將不勝感激這些連結。) 然而,一個好的起點是
該實用程式可以使用證書連接到 etcd 並使用命令從那裡讀取數據 ls
, get
, dump
.
添加etcdhelper
下一個想法是合乎邏輯的:“是什麼阻止您通過添加將資料寫入 etcd 的功能來添加此實用程式?”
它成為etcdhelper的修改版本,有兩個新功能 changeServiceCIDR
и changePodCIDR
。 在她的 你可以看到程式碼
新功能有什麼作用? 演算法 changeServiceCIDR
:
- 創建一個解串器;
- 編譯正規表示式來取代CIDR;
- 我們檢查叢集中具有 ClusterIP 類型的所有服務:
- 將 etcd 中的值解碼為 Go 物件;
- 使用正規表示式,我們替換位址的前兩個位元組;
- 為該服務指派新子網路的 IP 位址;
- 我們建立一個序列化器,將 Go 物件轉換為 protobuf,將新資料寫入 etcd。
功能 changePodCIDR
本質上相似 changeServiceCIDR
- 只是我們不編輯服務規範,而是為節點進行編輯並更改 .spec.PodCIDR
到一個新的子網路。
實踐
更改服務CIDR
執行該任務的計劃非常簡單,但它涉及重新建立叢集中所有 Pod 時的停機時間。 在描述主要步驟之後,我們還將分享理論上如何最大限度地減少停機時間的想法。
準備步驟:
- 安裝必要的軟體並組裝打過補丁的 etcdhelper;
- 備份 etcd 和
/etc/kubernetes
.
更改服務CIDR的簡要行動計劃:
- 更改 apiserver 和controller-manager 清單;
- 重新頒發證書;
- 更改etcd中的ClusterIP服務;
- 重新啟動叢集中的所有 Pod。
以下是詳細的完整操作序列。
1.安裝etcd-client進行資料轉儲:
apt install etcd-client
2. 建置etcdhelper:
- 安裝golang:
GOPATH=/root/golang mkdir -p $GOPATH/local curl -sSL https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz | tar -xzvC $GOPATH/local echo "export GOPATH="$GOPATH"" >> ~/.bashrc echo 'export GOROOT="$GOPATH/local/go"' >> ~/.bashrc echo 'export PATH="$PATH:$GOPATH/local/go/bin"' >> ~/.bashrc
- 我們為自己存錢
etcdhelper.go
,下載依賴,收集:wget https://raw.githubusercontent.com/flant/examples/master/2020/04-etcdhelper/etcdhelper.go go get go.etcd.io/etcd/clientv3 k8s.io/kubectl/pkg/scheme k8s.io/apimachinery/pkg/runtime go build -o etcdhelper etcdhelper.go
3. 備份etcd:
backup_dir=/root/backup
mkdir ${backup_dir}
cp -rL /etc/kubernetes ${backup_dir}
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --endpoints https://192.168.199.100:2379 snapshot save ${backup_dir}/etcd.snapshot
4. 變更 Kubernetes 控制平面清單中的服務子網路。 在文件中 /etc/kubernetes/manifests/kube-apiserver.yaml
и /etc/kubernetes/manifests/kube-controller-manager.yaml
改變參數 --service-cluster-ip-range
到一個新的子網路: 172.24.0.0/16
而不是 192.168.0.0/16
.
5. 由於我們正在更改 kubeadm 為 apiserver(含)頒發憑證的服務子網,因此需要重新頒發憑證:
- 讓我們看看目前憑證已頒發給哪些網域和 IP 位址:
openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt X509v3 Subject Alternative Name: DNS:dev-1-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver, IP Address:192.168.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
- 讓我們為 kubeadm 準備一個最小的配置:
cat kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta1 kind: ClusterConfiguration networking: podSubnet: "10.244.0.0/16" serviceSubnet: "172.24.0.0/16" apiServer: certSANs: - "192.168.199.100" # IP-адрес мастер узла
- 讓我們刪除舊的 crt 和金鑰,因為沒有這個,將不會頒發新憑證:
rm /etc/kubernetes/pki/apiserver.{key,crt}
- 讓我們為 API 伺服器重新頒發憑證:
kubeadm init phase certs apiserver --config=kubeadm-config.yaml
- 讓我們檢查是否為新子網路頒發了憑證:
openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt X509v3 Subject Alternative Name: DNS:kube-2-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:172.24.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
- 重新頒發API伺服器憑證後,重新啟動其容器:
docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
- 讓我們重新產生配置
admin.conf
:kubeadm alpha certs renew admin.conf
- 讓我們編輯etcd中的資料:
./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-service-cidr 172.24.0.0/16
警告! 此時,域解析在叢集中停止工作,因為在現有的 pod 中
/etc/resolv.conf
舊的 CoreDNS 位址 (kube-dns) 已註冊,並且 kube-proxy 將 iptables 規則從舊子網路更改為新子網路。 本文進一步介紹了最大限度減少停機時間的可能選項。 - 讓我們修復命名空間中的 ConfigMap
kube-system
:kubectl -n kube-system edit cm kubelet-config-1.16
- 替換此處
clusterDNS
到 kube-dns 服務的新 IP 位址:kubectl -n kube-system get svc kube-dns
.kubectl -n kube-system edit cm kubeadm-config
- 我們會解決它
data.ClusterConfiguration.networking.serviceSubnet
到一個新的子網路。 - 由於 kube-dns 位址已更改,因此需要更新所有節點上的 kubelet 配置:
kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
- 剩下的就是重新啟動叢集中的所有 pod:
kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'
最大限度地減少停機時間
關於如何最大限度地減少停機時間的想法:
- 變更控制平面清單後,建立一個新的 kube-dns 服務,例如,名稱為
kube-dns-tmp
和新地址172.24.0.10
. - 使
if
在etcdhelper中,它不會修改kube-dns服務。 - 替換所有kubelet中的地址
ClusterDNS
到新服務,而舊服務將繼續與新服務同時工作。 - 等待有應用程式的 Pod 會因自然原因自行滾動或在約定的時間滾動。
- 刪除服務
kube-dns-tmp
並改變serviceSubnetCIDR
對於 kube-dns 服務。
該計劃將使您能夠將服務刪除期間的停機時間降至約一分鐘 kube-dns-tmp
並更改服務的子網 kube-dns
.
修改pod網絡
同時,我們決定研究如何使用產生的 etcdhelper 修改 podNetwork。 操作順序如下:
- 修復配置
kube-system
; - 修復 kube-controller-manager 清單;
- 直接在etcd中更改podCIDR;
- 重新啟動所有叢集節點。
現在詳細了解這些操作:
1.修改命名空間中的ConfigMap kube-system
:
kubectl -n kube-system edit cm kubeadm-config
- 糾正 data.ClusterConfiguration.networking.podSubnet
到一個新的子網 10.55.0.0/16
.
kubectl -n kube-system edit cm kube-proxy
- 糾正 data.config.conf.clusterCIDR: 10.55.0.0/16
.
2. 修改控制器管理器清單:
vim /etc/kubernetes/manifests/kube-controller-manager.yaml
- 糾正 --cluster-cidr=10.55.0.0/16
.
3.查看目前值 .spec.podCIDR
, .spec.podCIDRs
, .InternalIP
, .status.addresses
對於所有叢集節點:
kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'
[
{
"name": "kube-2-master",
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"InternalIP": "192.168.199.2"
},
{
"name": "kube-2-master",
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"InternalIP": "10.0.1.239"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.244.1.0/24",
"podCIDRs": [
"10.244.1.0/24"
],
"InternalIP": "192.168.199.222"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.244.1.0/24",
"podCIDRs": [
"10.244.1.0/24"
],
"InternalIP": "10.0.4.73"
}
]
4. 透過直接更改 etcd 來替換 podCIDR:
./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-pod-cidr 10.55.0.0/16
5. 讓我們檢查一下 podCIDR 是否真的改變了:
kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'
[
{
"name": "kube-2-master",
"podCIDR": "10.55.0.0/24",
"podCIDRs": [
"10.55.0.0/24"
],
"InternalIP": "192.168.199.2"
},
{
"name": "kube-2-master",
"podCIDR": "10.55.0.0/24",
"podCIDRs": [
"10.55.0.0/24"
],
"InternalIP": "10.0.1.239"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.55.1.0/24",
"podCIDRs": [
"10.55.1.0/24"
],
"InternalIP": "192.168.199.222"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.55.1.0/24",
"podCIDRs": [
"10.55.1.0/24"
],
"InternalIP": "10.0.4.73"
}
]
6. 讓我們一一重啟所有叢集節點。
7. 如果至少留下一個節點 舊 PodCIDR,那麼 kube-controller-manager 將無法啟動,叢集中的 pod 將無法被調度。
事實上,更改 podCIDR 可以更簡單(例如, spec.clusterIP
.)
總
本文討論了直接使用 etcd 中的數據的可能性,即繞過 Kubernetes API。 有時這種方法可以讓你做「棘手的事情」。 我們在真實的 K8s 叢集上測試了文中給出的操作。 然而,它們廣泛使用的準備狀態是 PoC(概念證明)。 因此,如果您想在叢集上使用 etcdhelper 實用程式的修改版本,請自行承擔風險。
聚苯乙烯
另請閱讀我們的博客:
- «
etcd 3.4.3:存儲可靠性和安全性研究 “; - «
Calico 用於 Kubernetes 中的網路:介紹和一點經驗 “; - «
Kubernetes 運行中的 6 個有趣的系統錯誤 [及其解決方案] “; - «
Kubernetes 故障排除視覺化指南 “。
來源: www.habr.com