ลูกค้าขอให้เราให้การเข้าถึงคลัสเตอร์ Kubernetes มากขึ้นเพื่อให้สามารถเข้าถึงบริการภายในคลัสเตอร์ได้: เพื่อให้สามารถเชื่อมต่อกับฐานข้อมูลหรือบริการบางอย่างได้โดยตรง เพื่อเชื่อมต่อแอปพลิเคชันในเครื่องกับแอปพลิเคชันภายในคลัสเตอร์...
ตัวอย่างเช่น จำเป็นต้องเชื่อมต่อจากเครื่องท้องถิ่นของคุณกับบริการ memcached.staging.svc.cluster.local
. เรามอบความสามารถนี้โดยใช้ VPN ภายในคลัสเตอร์ที่ไคลเอนต์เชื่อมต่อ ในการดำเนินการนี้ เราจะประกาศซับเน็ตของพ็อด บริการ และพุช DNS ของคลัสเตอร์ไปยังไคลเอ็นต์ ดังนั้นเมื่อไคลเอนต์พยายามเชื่อมต่อกับบริการ memcached.staging.svc.cluster.local
คำขอจะส่งไปที่ DNS ของคลัสเตอร์ และในการตอบสนองจะได้รับที่อยู่ของบริการนี้จากเครือข่ายบริการคลัสเตอร์หรือที่อยู่พ็อด
เรากำหนดค่าคลัสเตอร์ K8 โดยใช้ kubeadm โดยที่ซับเน็ตบริการเริ่มต้นคือ 192.168.0.0/16
และเครือข่ายของพ็อดก็คือ 10.244.0.0/16
. โดยปกติแล้วทุกอย่างทำงานได้ดี แต่มีสองประเด็น:
- ซับเน็ต
192.168.*.*
มักใช้ในเครือข่ายสำนักงานไคลเอนต์ และบ่อยกว่านั้นในเครือข่ายในบ้านของนักพัฒนา จากนั้นเราก็พบข้อขัดแย้ง: เราเตอร์ที่บ้านทำงานบนเครือข่ายย่อยนี้ และ VPN จะผลักเครือข่ายย่อยเหล่านี้จากคลัสเตอร์ไปยังไคลเอนต์ - เรามีหลายคลัสเตอร์ (การผลิต ระยะ และ/หรือหลายคลัสเตอร์การพัฒนา) จากนั้นตามค่าเริ่มต้น ทั้งหมดจะมีซับเน็ตเดียวกันสำหรับพ็อดและบริการ ซึ่งสร้างปัญหาอย่างมากในการทำงานพร้อมกันกับบริการในหลายคลัสเตอร์
เราได้นำแนวทางปฏิบัติในการใช้ซับเน็ตที่แตกต่างกันสำหรับบริการและพ็อดภายในโปรเจ็กต์เดียวกันมาเป็นเวลานานแล้ว โดยทั่วไปเพื่อให้คลัสเตอร์ทั้งหมดมีเครือข่ายที่แตกต่างกัน อย่างไรก็ตาม มีคลัสเตอร์จำนวนมากที่ทำงานอยู่ซึ่งฉันไม่ต้องการยกยอดไปตั้งแต่ต้น เนื่องจากคลัสเตอร์เหล่านี้ใช้บริการต่างๆ มากมาย แอปพลิเคชันเก็บสถานะ ฯลฯ
แล้วเราก็ถามตัวเองว่า: จะเปลี่ยนซับเน็ตในคลัสเตอร์ที่มีอยู่ได้อย่างไร?
ค้นหาการตัดสินใจ
แนวทางปฏิบัติที่พบบ่อยที่สุดคือการสร้างใหม่ ทั้งหมด บริการประเภท ClusterIP เพื่อเป็นทางเลือก
กระบวนการต่อไปนี้มีปัญหา: หลังจากกำหนดค่าทุกอย่างแล้ว พ็อดจะแสดง IP เก่าเป็นเนมเซิร์ฟเวอร์ DNS ใน /etc/resolv.conf
เนื่องจากฉันยังไม่พบวิธีแก้ปัญหา ฉันจึงต้องรีเซ็ตคลัสเตอร์ทั้งหมดด้วยการรีเซ็ต kubeadm และเริ่มต้นใหม่อีกครั้ง
แต่นี่ไม่เหมาะสำหรับทุกคน... ต่อไปนี้เป็นการแนะนำโดยละเอียดเพิ่มเติมสำหรับกรณีของเรา:
- ใช้ผ้าสักหลาด
- มีคลัสเตอร์ทั้งบนคลาวด์และบนฮาร์ดแวร์
- ฉันต้องการหลีกเลี่ยงการปรับใช้บริการทั้งหมดในคลัสเตอร์อีกครั้ง
- โดยทั่วไปมีความจำเป็นต้องทำทุกอย่างโดยมีจำนวนปัญหาน้อยที่สุด
- เวอร์ชัน Kubernetes คือ 1.16.6 (อย่างไรก็ตาม ขั้นตอนเพิ่มเติมจะคล้ายกับเวอร์ชันอื่นๆ)
- ภารกิจหลักคือเพื่อให้แน่ใจว่าในคลัสเตอร์ปรับใช้โดยใช้ kubeadm กับเครือข่ายย่อยบริการ
192.168.0.0/16
ให้แทนที่ด้วย172.24.0.0/16
.
และมันก็เกิดขึ้นจนเราสนใจมานานแล้วว่ามีอะไรเก็บไว้ใน Kubernetes บ้างและอย่างไร ฯลฯ จะทำอะไรได้บ้าง... ดังนั้นเราจึงคิดว่า:“ทำไมไม่เพียงแค่อัปเดตข้อมูลใน etcd โดยแทนที่ที่อยู่ IP เก่า (ซับเน็ต) ด้วยอันใหม่? "
หลังจากค้นหาเครื่องมือสำเร็จรูปสำหรับการทำงานกับข้อมูลใน ฯลฯ เราไม่พบสิ่งใดที่สามารถแก้ไขปัญหาได้อย่างสมบูรณ์ (อย่างไรก็ตาม หากคุณทราบเกี่ยวกับยูทิลิตี้ใด ๆ สำหรับการทำงานกับข้อมูลโดยตรงใน ฯลฯ เราจะขอบคุณลิงก์นี้) อย่างไรก็ตาม จุดเริ่มต้นที่ดีก็คือ
ยูทิลิตี้นี้สามารถเชื่อมต่อกับ ฯลฯ โดยใช้ใบรับรองและอ่านข้อมูลจากที่นั่นโดยใช้คำสั่ง ls
, get
, dump
.
เพิ่ม etcdhelper
ความคิดต่อไปก็มีเหตุผล: “อะไรขัดขวางไม่ให้คุณเพิ่มยูทิลิตี้นี้ด้วยการเพิ่มความสามารถในการเขียนข้อมูลลงใน etcd”
มันกลายเป็น etcdhelper เวอร์ชันดัดแปลงพร้อมฟังก์ชันใหม่สองฟังก์ชัน changeServiceCIDR
и changePodCIDR
. กับเธอ คุณสามารถดูรหัสได้
คุณสมบัติใหม่มีไว้ทำอะไร? อัลกอริทึม changeServiceCIDR
:
- สร้างดีซีเรียลไลเซอร์
- รวบรวมนิพจน์ทั่วไปเพื่อแทนที่ CIDR
- เราผ่านบริการทั้งหมดด้วยประเภท ClusterIP ในคลัสเตอร์:
- ถอดรหัสค่าจาก etcd ลงในวัตถุ Go
- การใช้นิพจน์ทั่วไปจะแทนที่สองไบต์แรกของที่อยู่
- กำหนดที่อยู่ IP ให้กับบริการจากเครือข่ายย่อยใหม่
- สร้างซีเรียลไลเซอร์, แปลงวัตถุ Go เป็น protobuf, เขียนข้อมูลใหม่ไปที่ etcd
ฟังก์ชัน changePodCIDR
คล้ายกันโดยพื้นฐานแล้ว changeServiceCIDR
- แทนที่จะแก้ไขข้อกำหนดการบริการ เราทำเพื่อโหนดและเปลี่ยนแปลง .spec.PodCIDR
ไปยังซับเน็ตใหม่
การปฏิบัติ
เปลี่ยนบริการ CIDR
แผนการดำเนินงานนั้นง่ายมาก แต่เกี่ยวข้องกับการหยุดทำงานในขณะที่สร้างพ็อดทั้งหมดในคลัสเตอร์ขึ้นมาใหม่ หลังจากอธิบายขั้นตอนหลักแล้ว เราจะแบ่งปันความคิดเกี่ยวกับวิธีการลดการหยุดทำงานนี้ในทางทฤษฎีด้วย
ขั้นตอนการเตรียมการ:
- ติดตั้งซอฟต์แวร์ที่จำเป็นและประกอบ etcdhelper ที่ได้รับการติดตั้งแล้ว
- สำรองข้อมูล ฯลฯ และ
/etc/kubernetes
.
แผนปฏิบัติการโดยย่อสำหรับการเปลี่ยนแปลงบริการCIDR:
- การเปลี่ยนรายการ apiserver และตัวควบคุม - ผู้จัดการ
- การออกใบรับรองใหม่
- การเปลี่ยนบริการ ClusterIP ใน etcd;
- รีสตาร์ทพ็อดทั้งหมดในคลัสเตอร์
ต่อไปนี้เป็นลำดับการดำเนินการโดยละเอียด
1. ติดตั้ง etcd-client สำหรับการถ่ายโอนข้อมูล:
apt install etcd-client
2. สร้าง etcdhelper:
- ติดตั้งโกลัง:
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. ทำการสำรองข้อมูล ฯลฯ :
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
คำเตือน! ในขณะนี้ การแก้ไขโดเมนหยุดทำงานในคลัสเตอร์ เนื่องจากอยู่ในพ็อดที่มีอยู่
/etc/resolv.conf
มีการลงทะเบียนที่อยู่ CoreDNS เก่า (kube-dns) แล้ว และ kube-proxy เปลี่ยนกฎ iptables จากเครือข่ายย่อยเก่าไปเป็นเครือข่ายใหม่ นอกจากนี้ในบทความยังได้เขียนเกี่ยวกับตัวเลือกที่เป็นไปได้เพื่อลดเวลาหยุดทำงานให้เหลือน้อยที่สุด - มาแก้ไข ConfigMap ในเนมสเปซกันดีกว่า
kube-system
:kubectl -n kube-system edit cm kubelet-config-1.16
- แทนที่ที่นี่
clusterDNS
ไปยังที่อยู่ IP ใหม่ของบริการ kube-dns: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
- สิ่งที่เหลืออยู่คือการรีสตาร์ทพ็อดทั้งหมดในคลัสเตอร์:
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 - แทนที่ที่อยู่ใน kubelets ทั้งหมด
ClusterDNS
ไปเป็นบริการใหม่ในขณะที่บริการเก่าจะยังคงทำงานไปพร้อมกับบริการใหม่ - รอจนกว่าพ็อดที่มีแอปพลิเคชันจะพลิกคว่ำด้วยตัวเองด้วยเหตุผลตามธรรมชาติหรือตามเวลาที่ตกลงกัน
- ลบบริการ
kube-dns-tmp
และเปลี่ยนแปลงserviceSubnetCIDR
สำหรับบริการ kube-dns
แผนนี้จะช่วยให้คุณสามารถลดการหยุดทำงานให้เหลือ ~ นาที - ตลอดระยะเวลาของการลบบริการ kube-dns-tmp
และเปลี่ยนซับเน็ตเพื่อใช้บริการ kube-dns
.
การปรับเปลี่ยนพ็อดเครือข่าย
ในเวลาเดียวกัน เราตัดสินใจที่จะดูวิธีแก้ไข podNetwork โดยใช้ผลลัพธ์ etcdhelper ลำดับของการกระทำมีดังนี้:
- แก้ไขการกำหนดค่าใน
kube-system
; - แก้ไขรายการ kube-controller-manager;
- เปลี่ยน podCIDR โดยตรงใน etcd;
- รีบูตโหนดคลัสเตอร์ทั้งหมด
เพิ่มเติมเกี่ยวกับการดำเนินการเหล่านี้:
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. แทนที่ podCIDR โดยทำการเปลี่ยนแปลงโดยตรงไปที่ 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-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 จะไม่สามารถเริ่มต้นได้ และพ็อดในคลัสเตอร์จะไม่ได้รับการกำหนดเวลา
ในความเป็นจริง การเปลี่ยน podCIDR สามารถทำได้ง่ายยิ่งขึ้นไปอีก (ตัวอย่างเช่น spec.clusterIP
.)
ทั้งหมด
บทความนี้กล่าวถึงความเป็นไปได้ในการทำงานกับข้อมูลใน etcd โดยตรง เช่น ข้าม Kubernetes API บางครั้งวิธีนี้อาจทำให้คุณทำ "สิ่งที่ยุ่งยากได้" เราทดสอบการดำเนินการที่ให้ไว้ในข้อความบนคลัสเตอร์ K8 จริง อย่างไรก็ตามสถานะความพร้อมในการใช้งานอย่างแพร่หลายคือ PoC (พิสูจน์แนวคิด). ดังนั้น หากคุณต้องการใช้ยูทิลิตี้ etcdhelper เวอร์ชันแก้ไขบนคลัสเตอร์ของคุณ ให้ดำเนินการดังกล่าวด้วยความเสี่ยงของคุณเอง
PS
อ่านเพิ่มเติมในบล็อกของเรา:
- «
etcd 3.4.3: การศึกษาความน่าเชื่อถือของสตอเรจและความปลอดภัย "; - «
Calico สำหรับการสร้างเครือข่ายใน Kubernetes: บทนำและประสบการณ์เล็กน้อย "; - «
6 ข้อบกพร่องของระบบความบันเทิงในการทำงานของ Kubernetes [และวิธีแก้ปัญหา] "; - «
คู่มือแบบภาพเพื่อการแก้ไขปัญหา Kubernetes '
ที่มา: will.com