我們直接在 etcd Kubernetes 集群中處理數據的經驗(沒有 K8s API)

越來越多的客戶要求我們提供對Kubernetes 叢集的訪問,以便能夠存取叢集內的服務:以便他們可以直接連接到某些資料庫或服務,將本機應用程式與叢集內的應用程式連接起來... ……

我們直接在 etcd Kubernetes 集群中處理數據的經驗(沒有 K8s API)

例如,需要從本機電腦連線到服務 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 幫助程序 來自 OpenShift (感謝其作者!).

該實用程式可以使用證書連接到 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(含)頒發憑證的服務子網,因此需要重新頒發憑證:

  1. 讓我們看看目前憑證已頒發給哪些網域和 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
  2. 讓我們為 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-адрес мастер узла
  3. 讓我們刪除舊的 crt 和金鑰,因為沒有這個,將不會頒發新憑證:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. 讓我們為 API 伺服器重新頒發憑證:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. 讓我們檢查是否為新子網路頒發了憑證:
    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
  6. 重新頒發API伺服器憑證後,重新啟動其容器:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. 讓我們重新產生配置 admin.conf:
    kubeadm alpha certs renew admin.conf
  8. 讓我們編輯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 規則從舊子網路更改為新子網路。 本文進一步介紹了最大限度減少停機時間的可能選項。

  9. 讓我們修復命名空間中的 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 到一個新的子網路。

  10. 由於 kube-dns 位址已更改,因此需要更新所有節點上的 kubelet 配置:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. 剩下的就是重新啟動叢集中的所有 pod:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

最大限度地減少停機時間

關於如何最大限度地減少停機時間的想法:

  1. 變更控制平面清單後,建立一個新的 kube-dns 服務,例如,名稱為 kube-dns-tmp 和新地址 172.24.0.10.
  2. 使 if 在etcdhelper中,它不會修改kube-dns服務。
  3. 替換所有kubelet中的地址 ClusterDNS 到新服務,而舊服務將繼續與新服務同時工作。
  4. 等待有應用程式的 Pod 會因自然原因自行滾動或在約定的時間滾動。
  5. 刪除服務 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 可以更簡單(例如, 所以)。 但我們想學習如何直接使用 etcd,因為在 etcd 中編輯 Kubernetes 物件時存在一些情況 - 唯一的 可能的變體。 (例如,您不能在不停機的情況下只更改服務字段 spec.clusterIP.)

本文討論了直接使用 etcd 中的數據的可能性,即繞過 Kubernetes API。 有時這種方法可以讓你做「棘手的事情」。 我們在真實的 K8s 叢集上測試了文中給出的操作。 然而,它們廣泛使用的準備狀態是 PoC(概念證明)。 因此,如果您想在叢集上使用 etcdhelper 實用程式的修改版本,請自行承擔風險。

聚苯乙烯

另請閱讀我們的博客:

來源: www.habr.com

添加評論