Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Đã có bài viết trên blog của chúng tôi nói về khả năng của người vận hành trong Kubernetes và làm thế nào tự viết một toán tử đơn giản. Lần này chúng tôi muốn giới thiệu với các bạn giải pháp Nguồn mở của chúng tôi, giải pháp này đưa việc tạo các toán tử lên một mức độ siêu dễ dàng - hãy xem thử người điều hành shell!

Tại sao?

Ý tưởng về shell-operator khá đơn giản: đăng ký các sự kiện từ các đối tượng Kubernetes và khi nhận được những sự kiện này, hãy khởi chạy một chương trình bên ngoài, cung cấp cho nó thông tin về sự kiện:

Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Nhu cầu về nó nảy sinh khi trong quá trình vận hành các cụm, các nhiệm vụ nhỏ bắt đầu xuất hiện mà chúng tôi thực sự muốn tự động hóa theo cách phù hợp. Tất cả những nhiệm vụ nhỏ này đã được giải quyết bằng cách sử dụng các tập lệnh bash đơn giản, mặc dù, như bạn biết, tốt hơn là nên viết các toán tử trong Golang. Rõ ràng, việc đầu tư phát triển toàn diện người vận hành cho từng nhiệm vụ nhỏ như vậy sẽ không hiệu quả.

Nhà điều hành trong 15 phút

Hãy xem một ví dụ về những gì có thể được tự động hóa trong cụm Kubernetes và cách shell-operator có thể trợ giúp. Một ví dụ sẽ như sau: sao chép một bí mật để truy cập vào sổ đăng ký docker.

Các nhóm sử dụng hình ảnh từ sổ đăng ký riêng tư phải chứa trong bảng kê khai của chúng một liên kết đến bí mật có dữ liệu để truy cập vào sổ đăng ký. Bí mật này phải được tạo trong mỗi không gian tên trước khi tạo nhóm. Việc này có thể được thực hiện thủ công, nhưng nếu chúng ta thiết lập môi trường động thì không gian tên cho một ứng dụng sẽ trở nên rất nhiều. Và nếu không có 2-3 ứng dụng... số lượng bí mật sẽ trở nên rất lớn. Và một điều nữa về bí mật: Thỉnh thoảng tôi muốn thay đổi khóa để truy cập vào sổ đăng ký. Sau cùng, thao tác thủ công như một giải pháp hoàn toàn không hiệu quả — chúng ta cần tự động hóa việc tạo và cập nhật các bí mật.

Tự động hóa đơn giản

Hãy viết một tập lệnh shell chạy N giây một lần và kiểm tra các không gian tên để tìm sự hiện diện của bí mật và nếu không có bí mật nào thì nó sẽ được tạo. Ưu điểm của giải pháp này là nó trông giống như một tập lệnh shell trong cron - một cách tiếp cận cổ điển và dễ hiểu đối với mọi người. Nhược điểm là trong khoảng thời gian giữa các lần khởi chạy, một không gian tên mới có thể được tạo và trong một thời gian, nó sẽ không có bí mật, điều này sẽ dẫn đến sai sót khi khởi chạy nhóm.

Tự động hóa với shell-operator

Để tập lệnh của chúng tôi hoạt động chính xác, lệnh khởi chạy cron cổ điển cần được thay thế bằng lệnh khởi chạy khi thêm không gian tên: trong trường hợp này, bạn có thể tạo bí mật trước khi sử dụng nó. Hãy xem cách thực hiện điều này bằng cách sử dụng toán tử shell.

Đầu tiên, chúng ta hãy nhìn vào kịch bản. Các tập lệnh theo thuật ngữ toán tử shell được gọi là hook. Mỗi hook khi chạy với một lá cờ --config thông báo cho người vận hành shell về các ràng buộc của nó, tức là về những sự kiện nào nó sẽ được tung ra. Trong trường hợp của chúng tôi, chúng tôi sẽ sử dụng onKubernetesEvent:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
  { "kind": "namespace",
    "event":["add"]
  }
]}
EOF
fi

Ở đây được mô tả rằng chúng tôi quan tâm đến việc thêm các sự kiện (add) các đối tượng thuộc loại namespace.

Bây giờ bạn cần thêm mã sẽ được thực thi khi sự kiện xảy ra:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
  # конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
  "event":["add"]
}
]}
EOF
else
  # реакция:
  # узнать, какой namespace появился
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  # создать в нём нужный секрет
  kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
EOF
fi

Tuyệt vời! Kết quả là một kịch bản nhỏ và đẹp. Để “hồi sinh” nó, chỉ còn hai bước: chuẩn bị hình ảnh và khởi chạy nó trong cụm.

Chuẩn bị một hình ảnh với một cái móc

Nếu nhìn vào script, bạn có thể thấy các lệnh được sử dụng kubectl и jq. Điều này có nghĩa là hình ảnh phải có những thứ sau: hook của chúng tôi, một toán tử shell sẽ giám sát các sự kiện và chạy hook cũng như các lệnh được hook sử dụng (kubectl và jq). Hub.docker.com đã có sẵn một image trong đó shell-operator, kubectl và jq được đóng gói. Tất cả những gì còn lại là thêm một cái móc đơn giản Dockerfile:

$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks

$ docker build -t registry.example.com/my-operator:v1 . 
$ docker push registry.example.com/my-operator:v1

Chạy trong một cụm

Chúng ta hãy nhìn lại hook và lần này viết ra những hành động và đối tượng nào nó thực hiện trong cụm:

  1. đăng ký các sự kiện tạo không gian tên;
  2. tạo ra một bí mật trong các không gian tên khác với không gian tên nơi nó được khởi chạy.

Hóa ra nhóm mà hình ảnh của chúng tôi sẽ được khởi chạy phải có quyền thực hiện những hành động này. Điều này có thể được thực hiện bằng cách tạo ServiceAccount của riêng bạn. Sự cho phép phải được thực hiện dưới dạng ClusterRole và ClusterRoleBinding, bởi vì chúng tôi quan tâm đến các đối tượng từ toàn bộ cụm.

Mô tả cuối cùng trong YAML sẽ trông giống như thế này:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: monitor-namespaces-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: monitor-namespaces
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "create", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: monitor-namespaces
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: monitor-namespaces
subjects:
  - kind: ServiceAccount
    name: monitor-namespaces-acc
    namespace: example-monitor-namespaces

Bạn có thể khởi chạy hình ảnh đã được lắp ráp dưới dạng Triển khai đơn giản:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1
      serviceAccountName: monitor-namespaces-acc

Để thuận tiện, một không gian tên riêng được tạo trong đó toán tử shell sẽ được khởi chạy và các tệp kê khai đã tạo sẽ được áp dụng:

$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

Chỉ vậy thôi: shell-operator sẽ bắt đầu, đăng ký các sự kiện tạo vùng tên và chạy hook khi cần.

Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Như vậy, một tập lệnh shell đơn giản đã biến thành một toán tử thực sự cho Kubernetes và hoạt động như một phần của cụm. Và tất cả điều này mà không cần quá trình phát triển toán tử phức tạp ở Golang:

Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Có một minh họa khác về vấn đề này...Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Chúng tôi sẽ tiết lộ ý nghĩa của nó chi tiết hơn trong một trong những ấn phẩm sau.

lọc

Theo dõi đối tượng là tốt nhưng thường phải phản ứng lại thay đổi một số thuộc tính đối tượng, ví dụ: để thay đổi số lượng bản sao trong Triển khai hoặc thay đổi nhãn đối tượng.

Khi một sự kiện đến, toán tử shell sẽ nhận được bảng kê khai JSON của đối tượng. Chúng ta có thể chọn các thuộc tính mà chúng ta quan tâm trong JSON này và chạy hook chỉ khi họ thay đổi. Có một lĩnh vực cho việc này jqFilter, trong đó bạn cần chỉ định biểu thức jq sẽ được áp dụng cho tệp kê khai JSON.

Ví dụ: để phản hồi các thay đổi về nhãn cho đối tượng Triển khai, bạn cần lọc trường labels ngoài cánh đồng metadata. Cấu hình sẽ như thế này:

cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
  "event":["update"],
  "jqFilter": ".metadata.labels"
}
]}
EOF

Biểu thức jqFilter này biến tệp kê khai JSON dài của Triển khai thành JSON ngắn có nhãn:

Giới thiệu shell-operator: việc tạo toán tử cho Kubernetes trở nên dễ dàng hơn

Toán tử shell sẽ chỉ chạy hook khi JSON ngắn này thay đổi và các thay đổi đối với các thuộc tính khác sẽ bị bỏ qua.

Bối cảnh khởi chạy hook

Cấu hình hook cho phép bạn chỉ định một số tùy chọn cho các sự kiện - ví dụ: 2 tùy chọn cho các sự kiện từ Kubernetes và 2 lịch trình:

{"onKubernetesEvent":[
  {"name":"OnCreatePod",
  "kind": "pod",
  "event":["add"]
  },
  {"name":"OnModifiedNamespace",
  "kind": "namespace",
  "event":["update"],
  "jqFilter": ".metadata.labels"
  }
],
"schedule": [
{ "name":"every 10 min",
  "crontab":"* */10 * * * *"
}, {"name":"on Mondays at 12:10",
"crontab": "* 10 12 * * 1"
]}

Một sự lạc đề nhỏ: có, shell-operator hỗ trợ chạy tập lệnh kiểu crontab. Thông tin chi tiết có thể được tìm thấy trong tài liệu.

Để phân biệt lý do tại sao hook được khởi chạy, toán tử shell tạo một tệp tạm thời và chuyển đường dẫn đến nó trong một biến cho hook BINDING_CONTEXT_TYPE. Tệp chứa mô tả JSON về lý do chạy hook. Ví dụ cứ 10 phút hook sẽ chạy với nội dung sau:

[{ "binding": "every 10 min"}]

... và vào thứ Hai, nó sẽ bắt đầu với điều này:

[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]

onKubernetesEvent Sẽ có nhiều trình kích hoạt JSON hơn, bởi vì nó chứa một mô tả của đối tượng:

[
 {
 "binding": "onCreatePod",
 "resourceEvent": "add",
 "resourceKind": "pod",
 "resourceName": "foo",
 "resourceNamespace": "bar"
 }
]

Nội dung của các trường có thể được hiểu từ tên của chúng và có thể đọc thêm chi tiết trong tài liệu. Ví dụ về lấy tên tài nguyên từ một trường resourceName việc sử dụng jq đã được hiển thị trong một hook sao chép các bí mật:

jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

Bạn có thể lấy các trường khác theo cách tương tự.

Cái gì tiếp theo?

Trong kho dự án, trong /thư mục ví dụ, có những ví dụ về hook đã sẵn sàng chạy trên một cụm. Khi viết hook của riêng mình, bạn có thể sử dụng chúng làm cơ sở.

Có hỗ trợ thu thập số liệu bằng Prometheus - các số liệu có sẵn được mô tả trong phần KIM LOẠI.

Như bạn có thể đoán, toán tử shell được viết bằng Go và được phân phối theo giấy phép Nguồn mở (Apache 2.0). Chúng tôi sẽ biết ơn bất kỳ sự hỗ trợ phát triển nào dự án trên GitHub: và các ngôi sao, các vấn đề và các yêu cầu kéo.

Vén bức màn bí mật lên, chúng tôi cũng sẽ thông báo cho bạn rằng nhà điều hành shell đang небольшая một phần trong hệ thống của chúng tôi có thể cập nhật các tiện ích bổ sung được cài đặt trong cụm Kubernetes và thực hiện nhiều hành động tự động khác nhau. Đọc thêm về hệ thống này kể lại theo đúng nghĩa đen vào thứ Hai tại HighLoad++ 2019 ở St. Petersburg - chúng tôi sẽ sớm xuất bản video và bản ghi của báo cáo này.

Chúng tôi có kế hoạch mở rộng phần còn lại của hệ thống này: toán tử bổ trợ và bộ sưu tập các hook và mô-đun của chúng tôi. Nhân tiện, toán tử addon đã có rồi có sẵn trên github, nhưng tài liệu cho nó vẫn đang được hoàn thiện. Việc phát hành bộ sưu tập các mô-đun được lên kế hoạch cho mùa hè.

Hãy theo dõi!

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