Trải nghiệm của chúng tôi với dữ liệu trực tiếp trong cụm etcd Kubernetes (không có API K8s)

Ngày càng có nhiều khách hàng yêu cầu chúng tôi cung cấp quyền truy cập vào cụm Kubernetes để có thể truy cập các dịch vụ trong cụm: có thể kết nối trực tiếp với một số cơ sở dữ liệu hoặc dịch vụ, kết nối ứng dụng cục bộ với các ứng dụng trong cụm...

Trải nghiệm của chúng tôi với dữ liệu trực tiếp trong cụm etcd Kubernetes (không có API K8s)

Ví dụ: cần kết nối từ máy cục bộ của bạn với một dịch vụ memcached.staging.svc.cluster.local. Chúng tôi cung cấp khả năng này bằng cách sử dụng VPN trong cụm mà máy khách kết nối. Để thực hiện việc này, chúng tôi công bố các mạng con của nhóm, dịch vụ và đẩy cụm DNS tới máy khách. Vì vậy, khi một client cố gắng kết nối với dịch vụ memcached.staging.svc.cluster.local, yêu cầu sẽ chuyển đến DNS của cụm và để phản hồi sẽ nhận được địa chỉ của dịch vụ này từ mạng dịch vụ cụm hoặc địa chỉ nhóm.

Chúng tôi định cấu hình các cụm K8 bằng kubeadm, trong đó mạng con dịch vụ mặc định là 192.168.0.0/16và mạng lưới các nhóm là 10.244.0.0/16. Thông thường mọi thứ đều hoạt động tốt, nhưng có một số điểm:

  • Mạng con 192.168.*.* thường được sử dụng trong mạng văn phòng khách hàng và thậm chí thường xuyên hơn trong mạng gia đình của nhà phát triển. Và sau đó chúng ta gặp phải xung đột: bộ định tuyến gia đình hoạt động trên mạng con này và VPN sẽ đẩy các mạng con này từ cụm đến máy khách.
  • Chúng tôi có một số cụm (sản xuất, giai đoạn và/hoặc một số cụm phát triển). Sau đó, theo mặc định, tất cả chúng sẽ có cùng mạng con cho các nhóm và dịch vụ, điều này gây khó khăn lớn cho việc làm việc đồng thời với các dịch vụ trong một số cụm.

Từ lâu, chúng tôi đã áp dụng phương pháp sử dụng các mạng con khác nhau cho các dịch vụ và nhóm trong cùng một dự án - nói chung là để tất cả các cụm có các mạng khác nhau. Tuy nhiên, có một số lượng lớn các cụm đang hoạt động mà tôi không muốn triển khai lại từ đầu, vì chúng chạy nhiều dịch vụ, ứng dụng trạng thái, v.v.

Và sau đó chúng tôi tự hỏi: làm cách nào để thay đổi mạng con trong cụm hiện có?

Tìm kiếm các quyết định

Cách thực hành phổ biến nhất là tạo lại tất cả dịch vụ với loại ClusterIP. Như là một lựa chọn, có thể tư vấn và điều này:

Quá trình sau đây có vấn đề: sau khi định cấu hình mọi thứ, các nhóm sẽ xuất hiện IP cũ làm máy chủ tên DNS trong /etc/resolv.conf.
Vì vẫn không tìm ra giải pháp nên tôi phải đặt lại toàn bộ cụm bằng kubeadm reset và khởi tạo lại.

Nhưng điều này không phù hợp với tất cả mọi người... Dưới đây là những giới thiệu chi tiết hơn về trường hợp của chúng tôi:

  • Flannel được sử dụng;
  • Có các cụm cả trên đám mây và trên phần cứng;
  • Tôi muốn tránh triển khai lại tất cả các dịch vụ trong cụm;
  • Nhìn chung cần phải làm mọi thứ với số lượng vấn đề tối thiểu;
  • Phiên bản Kubernetes là 1.16.6 (tuy nhiên, các bước tiếp theo sẽ tương tự đối với các phiên bản khác);
  • Nhiệm vụ chính là đảm bảo rằng trong cụm được triển khai bằng kubeadm có mạng con dịch vụ 192.168.0.0/16, thay thế bằng 172.24.0.0/16.

Và tình cờ là từ lâu chúng tôi đã quan tâm đến việc xem Kubernetes được lưu trữ trong etcd những gì và như thế nào, có thể làm gì với nó... Vì vậy, chúng tôi nghĩ: “Tại sao không cập nhật dữ liệu trong etcd, thay thế địa chỉ IP (mạng con) cũ bằng địa chỉ mới? "

Sau khi tìm kiếm các công cụ làm sẵn để làm việc với dữ liệu trong etcd, chúng tôi không tìm thấy bất kỳ công cụ nào có thể giải quyết triệt để vấn đề. (Nhân tiện, nếu bạn biết về bất kỳ tiện ích nào để làm việc với dữ liệu trực tiếp trong etcd, chúng tôi sẽ đánh giá cao các liên kết.) Tuy nhiên, một điểm khởi đầu tốt là người giúp đỡ vvdhelper từ OpenShift (cảm ơn các tác giả của nó!).

Tiện ích này có thể kết nối với etcd bằng chứng chỉ và đọc dữ liệu từ đó bằng lệnh ls, get, dump.

Thêm etcdhelper

Suy nghĩ tiếp theo rất hợp lý: “Điều gì ngăn cản bạn thêm tiện ích này bằng cách thêm khả năng ghi dữ liệu vào etcd?”

Nó trở thành phiên bản sửa đổi của etcdhelper với hai chức năng mới changeServiceCIDR и changePodCIDR. trên cô ấy bạn có thể xem mã đây.

Các tính năng mới làm gì? Thuật toán changeServiceCIDR:

  • tạo ra một bộ khử lưu huỳnh;
  • biên dịch một biểu thức chính quy để thay thế CIDR;
  • chúng tôi xem xét tất cả các dịch vụ có loại ClusterIP trong cụm:
    • giải mã giá trị từ etcd thành đối tượng Go;
    • bằng cách sử dụng biểu thức chính quy, chúng tôi thay thế hai byte đầu tiên của địa chỉ;
    • gán cho dịch vụ một địa chỉ IP từ mạng con mới;
    • chúng tôi tạo một bộ tuần tự hóa, chuyển đổi đối tượng Go thành protobuf, ghi dữ liệu mới vào etcd.

Chức năng changePodCIDR về cơ bản là giống nhau changeServiceCIDR - chỉ thay vì chỉnh sửa đặc tả dịch vụ, chúng tôi thực hiện việc đó cho nút và thay đổi .spec.PodCIDR sang một mạng con mới.

Tập luyện

Thay đổi dịch vụ CIDR

Kế hoạch thực hiện nhiệm vụ rất đơn giản nhưng nó liên quan đến thời gian ngừng hoạt động tại thời điểm tạo lại tất cả các nhóm trong cụm. Sau khi mô tả các bước chính, chúng tôi cũng sẽ chia sẻ suy nghĩ về cách có thể giảm thiểu thời gian ngừng hoạt động này về mặt lý thuyết.

Các bước chuẩn bị:

  • cài đặt phần mềm cần thiết và lắp ráp etcdhelper đã vá;
  • sao lưu vvd và /etc/kubernetes.

Kế hoạch hành động ngắn gọn để thay đổi dịch vụCIDR:

  • thay đổi bảng kê khai apiserver và trình quản lý bộ điều khiển;
  • cấp lại giấy chứng nhận;
  • thay đổi dịch vụ ClusterIP trong etcd;
  • khởi động lại tất cả các nhóm trong cụm.

Sau đây là một chuỗi hành động đầy đủ một cách chi tiết.

1. Cài đặt etcd-client để kết xuất dữ liệu:

apt install etcd-client

2. Xây dựng etcdhelper:

  • Cài đặt 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
  • Chúng ta tiết kiệm cho chính mình etcdhelper.go, tải xuống các phần phụ thuộc, thu thập:
    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. Tạo một bản sao lưu, v.v.:

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. Thay đổi mạng con dịch vụ trong bảng kê khai mặt phẳng điều khiển Kubernetes. Trong tập tin /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml thay đổi tham số --service-cluster-ip-range sang một mạng con mới: 172.24.0.0/16 thay vì 192.168.0.0/16.

5. Vì chúng tôi đang thay đổi mạng con dịch vụ mà kubeadm cấp chứng chỉ cho apiserver (bao gồm cả) nên chúng cần được cấp lại:

  1. Hãy xem chứng chỉ hiện tại đã được cấp cho những miền và địa chỉ IP nào:
    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. Hãy chuẩn bị cấu hình tối thiểu cho 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. Hãy xóa crt và key cũ, vì nếu không có chứng chỉ mới này sẽ không được cấp:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. Hãy cấp lại chứng chỉ cho máy chủ API:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. Hãy kiểm tra xem chứng chỉ đã được cấp cho mạng con mới chưa:
    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. Sau khi cấp lại chứng chỉ máy chủ API, hãy khởi động lại vùng chứa của nó:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. Hãy tạo lại cấu hình cho admin.conf:
    kubeadm alpha certs renew admin.conf
  8. Hãy chỉnh sửa dữ liệu trong 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 

    Cảnh báo! Tại thời điểm này, độ phân giải miền ngừng hoạt động trong cụm vì trong các nhóm hiện có /etc/resolv.conf địa chỉ CoreDNS cũ (kube-dns) đã được đăng ký và kube-proxy thay đổi quy tắc iptables từ mạng con cũ sang mạng con mới. Hơn nữa trong bài viết còn viết về các lựa chọn khả thi để giảm thiểu thời gian chết.

  9. Hãy sửa ConfigMap trong không gian tên kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - thay thế ở đây clusterDNS đến địa chỉ IP mới của dịch vụ kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - chúng ta sẽ sửa nó data.ClusterConfiguration.networking.serviceSubnet sang một mạng con mới.

  10. Vì địa chỉ kube-dns đã thay đổi nên cần phải cập nhật cấu hình kubelet trên tất cả các nút:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. Tất cả những gì còn lại là khởi động lại tất cả các nhóm trong cụm:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

Giảm thiểu thời gian ngừng hoạt động

Suy nghĩ về cách giảm thiểu thời gian chết:

  1. Sau khi thay đổi bảng kê khai mặt phẳng điều khiển, hãy tạo một dịch vụ kube-dns mới, ví dụ: với tên kube-dns-tmp và địa chỉ mới 172.24.0.10.
  2. Làm if trong etcdhelper, nó sẽ không sửa đổi dịch vụ kube-dns.
  3. Thay thế địa chỉ trong tất cả kubelets ClusterDNS sang dịch vụ mới, trong khi dịch vụ cũ sẽ tiếp tục hoạt động đồng thời với dịch vụ mới.
  4. Đợi cho đến khi các nhóm có ứng dụng tự chuyển đổi vì lý do tự nhiên hoặc vào thời điểm đã thỏa thuận.
  5. Xóa dịch vụ kube-dns-tmp và thay đổi serviceSubnetCIDR cho dịch vụ kube-dns.

Gói này sẽ cho phép bạn giảm thiểu thời gian ngừng hoạt động xuống ~ một phút - trong suốt thời gian ngừng dịch vụ kube-dns-tmp và thay đổi mạng con cho dịch vụ kube-dns.

Nhóm sửa đổiMạng

Đồng thời, chúng tôi quyết định xem xét cách sửa đổi podNetwork bằng cách sử dụng etcdhelper. Trình tự các hành động như sau:

  • sửa cấu hình trong kube-system;
  • sửa bảng kê khai trình quản lý bộ điều khiển kube;
  • thay đổi podCIDR trực tiếp trong etcd;
  • khởi động lại tất cả các nút cụm.

Bây giờ nói thêm về những hành động này:

1. Sửa đổi ConfigMap trong không gian tên kube-system:

kubectl -n kube-system edit cm kubeadm-config

- sửa chữa data.ClusterConfiguration.networking.podSubnet đến một mạng con mới 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

- sửa chữa data.config.conf.clusterCIDR: 10.55.0.0/16.

2. Sửa đổi bảng kê khai trình quản lý bộ điều khiển:

vim /etc/kubernetes/manifests/kube-controller-manager.yaml

- sửa chữa --cluster-cidr=10.55.0.0/16.

3. Nhìn vào giá trị hiện tại .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses cho tất cả các nút cụm:

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. Thay thế podCIDR bằng cách thực hiện các thay đổi trực tiếp đối với 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. Hãy kiểm tra xem podCIDR đã thực sự thay đổi chưa:

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. Hãy khởi động lại từng nút một.

7. Nếu bạn để lại ít nhất một nút nhóm cũCIDR, thì kube-controller-manager sẽ không thể khởi động và các nhóm trong cụm sẽ không được lên lịch.

Trên thực tế, việc thay đổi podCIDR có thể được thực hiện đơn giản hơn (ví dụ: để). Nhưng chúng tôi muốn tìm hiểu cách làm việc trực tiếp với etcd, vì có những trường hợp khi chỉnh sửa các đối tượng Kubernetes trong etcd - đơn biến thể có thể. (Ví dụ: bạn không thể chỉ thay đổi trường Dịch vụ mà không có thời gian ngừng hoạt động spec.clusterIP.)

Tổng

Bài viết thảo luận về khả năng làm việc trực tiếp với dữ liệu trong etcd, tức là. bỏ qua API Kubernetes. Đôi khi cách tiếp cận này cho phép bạn làm được “những điều khó khăn”. Chúng tôi đã thử nghiệm các thao tác được đưa ra trong văn bản trên các cụm K8 thực. Tuy nhiên, tình trạng sẵn sàng để sử dụng rộng rãi của chúng là PoC (bằng chứng về khái niệm). Do đó, nếu bạn muốn sử dụng phiên bản sửa đổi của tiện ích etcdhelper trên các cụm của mình, bạn phải tự chịu rủi ro khi làm như vậy.

PS

Đọc thêm trên blog của chúng tôi:

Nguồn: www.habr.com

Thêm một lời nhận xét