ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

มีบทความในบล็อกของเราพูดถึงแล้ว ความสามารถของผู้ให้บริการใน Kubernetes แล้วยังไง เขียนโอเปอเรเตอร์ง่ายๆ ด้วยตัวเอง. ในครั้งนี้ เราอยากจะนำเสนอโซลูชั่นโอเพ่นซอร์สของเราแก่คุณ ซึ่งจะนำการสร้างโอเปอเรเตอร์ไปสู่ระดับที่ง่ายมาก - ลองดู ตัวดำเนินการเชลล์!

ทำไม?

แนวคิดของตัวดำเนินการเชลล์นั้นค่อนข้างง่าย: สมัครรับกิจกรรมจากวัตถุ Kubernetes และเมื่อได้รับเหตุการณ์เหล่านี้ ให้เปิดโปรแกรมภายนอกโดยให้ข้อมูลเกี่ยวกับเหตุการณ์:

ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

ความจำเป็นนี้เกิดขึ้นเมื่อระหว่างการทำงานของคลัสเตอร์ งานเล็กๆ เริ่มปรากฏว่าเราต้องการทำให้เป็นอัตโนมัติในวิธีที่ถูกต้องจริงๆ งานเล็ก ๆ เหล่านี้ทั้งหมดได้รับการแก้ไขโดยใช้สคริปต์ทุบตีแบบง่าย ๆ แม้ว่าอย่างที่คุณทราบจะเป็นการดีกว่าถ้าเขียนตัวดำเนินการใน Golang แน่นอนว่าการลงทุนในการพัฒนาผู้ปฏิบัติงานอย่างเต็มรูปแบบสำหรับงานเล็กๆ แต่ละงานจะไม่มีประสิทธิภาพ

เจ้าหน้าที่ดำเนินการภายใน 15 นาที

มาดูตัวอย่างของสิ่งที่สามารถทำให้เป็นอัตโนมัติในคลัสเตอร์ Kubernetes และวิธีที่ผู้ดำเนินการเชลล์สามารถช่วยได้ ตัวอย่างจะเป็นดังต่อไปนี้: การจำลองข้อมูลลับเพื่อเข้าถึงรีจิสทรีนักเทียบท่า

พ็อดที่ใช้รูปภาพจากรีจิสทรีส่วนตัวจะต้องมีลิงก์ไปยังข้อมูลลับพร้อมข้อมูลสำหรับการเข้าถึงรีจิสทรีในรายการ ต้องสร้างข้อมูลลับนี้ในแต่ละเนมสเปซก่อนสร้างพ็อด ซึ่งสามารถทำได้ด้วยตนเอง แต่หากเราตั้งค่าสภาพแวดล้อมแบบไดนามิก เนมสเปซสำหรับแอปพลิเคชันหนึ่งจะมีจำนวนมาก และหากไม่มีแอปพลิเคชัน 2-3 รายการ... จำนวนข้อมูลลับจะมีขนาดใหญ่มาก และอีกอย่างเกี่ยวกับความลับ: ฉันต้องการเปลี่ยนคีย์เพื่อเข้าถึงรีจิสทรีเป็นครั้งคราว ในท้ายที่สุด, การดำเนินการด้วยตนเอง เป็นวิธีแก้ปัญหา ไม่ได้ผลอย่างสมบูรณ์ — เราจำเป็นต้องสร้างและอัปเดตความลับโดยอัตโนมัติ

ระบบอัตโนมัติที่เรียบง่าย

มาเขียนเชลล์สคริปต์ที่ทำงานทุกๆ N วินาทีและตรวจสอบเนมสเปซว่ามีความลับอยู่หรือไม่ และหากไม่มีความลับ ก็จะถูกสร้างขึ้น ข้อดีของโซลูชันนี้คือ ดูเหมือนเชลล์สคริปต์ใน cron ซึ่งเป็นแนวทางที่คลาสสิกและเข้าใจง่ายสำหรับทุกคน ข้อเสียคือในช่วงเวลาระหว่างการเปิดตัว คุณสามารถสร้างเนมสเปซใหม่ได้ และในบางครั้งมันจะคงอยู่โดยไม่มีความลับ ซึ่งจะนำไปสู่ข้อผิดพลาดในการเปิดพ็อด

ระบบอัตโนมัติพร้อมตัวดำเนินการเชลล์

เพื่อให้สคริปต์ของเราทำงานได้อย่างถูกต้อง การเปิดใช้ cron แบบคลาสสิกจะต้องแทนที่ด้วยการเปิดตัวเมื่อมีการเพิ่มเนมสเปซ: ในกรณีนี้ คุณสามารถสร้างข้อมูลลับก่อนใช้งานได้ เรามาดูวิธีการนำไปใช้โดยใช้ตัวดำเนินการเชลล์

ก่อนอื่นเรามาดูสคริปต์กันก่อน สคริปต์ในเงื่อนไขของตัวดำเนินการเชลล์เรียกว่า hooks ตะขอทุกอันเมื่อวิ่งพร้อมธง --config แจ้งให้ผู้ดำเนินการเชลล์ทราบถึงการเชื่อมโยง เช่น ว่าควรจะเปิดตัวในงานอะไร ในกรณีของเราเราจะใช้ onKubernetesEvent:

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

มีการอธิบายไว้ที่นี่ว่าเราสนใจที่จะเพิ่มกิจกรรม (add) วัตถุประเภท namespace.

ตอนนี้คุณต้องเพิ่มโค้ดที่จะดำเนินการเมื่อมีเหตุการณ์เกิดขึ้น:

#!/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

ยอดเยี่ยม! ผลลัพธ์ที่ได้คือสคริปต์เล็กๆ ที่สวยงาม หากต้องการ “ฟื้นฟู” มัน เหลืออีกสองขั้นตอน: เตรียมอิมเมจและเปิดใช้งานในคลัสเตอร์

การเตรียมภาพด้วยเบ็ด

หากดูที่สคริปต์จะเห็นว่ามีการใช้คำสั่งต่างๆ kubectl и jq. ซึ่งหมายความว่าอิมเมจจะต้องมีสิ่งต่อไปนี้: hook ของเรา ตัวดำเนินการเชลล์ที่จะตรวจสอบเหตุการณ์และรัน hook และคำสั่งที่ใช้โดย hook (kubectl และ jq) Hub.docker.com มีอิมเมจสำเร็จรูปซึ่งมีแพ็คเกจตัวดำเนินการเชลล์, kubectl และ jq อยู่แล้ว สิ่งที่เหลืออยู่คือการเพิ่มเบ็ดง่ายๆ 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

ทำงานเป็นคลัสเตอร์

มาดูที่ hook อีกครั้ง และคราวนี้จดบันทึกว่าการกระทำใดบ้างและใช้กับอ็อบเจ็กต์ใดบ้างที่ทำในคลัสเตอร์:

  1. สมัครรับกิจกรรมการสร้างเนมสเปซ
  2. สร้างความลับในเนมสเปซอื่นที่ไม่ใช่ที่ที่มันถูกเรียกใช้

ปรากฎว่าพ็อดที่อิมเมจของเราจะเปิดตัวจะต้องมีสิทธิ์ในการดำเนินการเหล่านี้ ซึ่งสามารถทำได้โดยการสร้าง ServiceAccount ของคุณเอง การอนุญาตจะต้องทำในรูปแบบของ ClusterRole และ ClusterRoleBinding เนื่องจาก เราสนใจวัตถุจากทั้งคลัสเตอร์

คำอธิบายสุดท้ายใน YAML จะมีลักษณะดังนี้:

---
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

คุณสามารถเปิดอิมเมจที่ประกอบขึ้นเป็นการปรับใช้แบบธรรมดา:

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

เพื่อความสะดวก เนมสเปซที่แยกต่างหากจะถูกสร้างขึ้นโดยที่ตัวดำเนินการเชลล์จะถูกเรียกใช้และรายการที่สร้างขึ้นจะถูกนำไปใช้:

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

นั่นคือทั้งหมด: ตัวดำเนินการเชลล์จะเริ่ม สมัครรับกิจกรรมการสร้างเนมสเปซ และรันฮุกเมื่อจำเป็น

ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

ดังนั้น เชลล์สคริปต์ธรรมดากลายเป็นโอเปอเรเตอร์จริงสำหรับ Kubernetes และทำงานเป็นส่วนหนึ่งของคลัสเตอร์ และทั้งหมดนี้ไม่มีกระบวนการที่ซับซ้อนในการพัฒนาตัวดำเนินการใน Golang:

ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

มีภาพประกอบเรื่องนี้อีกเรื่อง...ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

เราจะเปิดเผยความหมายของมันอย่างละเอียดในสิ่งพิมพ์ต่อไปนี้

กรอง

การติดตามวัตถุเป็นสิ่งที่ดี แต่มักจำเป็นต้องตอบสนอง การเปลี่ยนแปลงคุณสมบัติของวัตถุบางอย่างตัวอย่างเช่น เพื่อเปลี่ยนจำนวนเรพลิกาในการปรับใช้หรือเปลี่ยนเลเบลอ็อบเจ็กต์

เมื่อเหตุการณ์มาถึง ตัวดำเนินการเชลล์จะได้รับไฟล์ Manifest JSON ของอ็อบเจ็กต์ เราสามารถเลือกคุณสมบัติที่เราสนใจใน JSON นี้และเรียกใช้ฮุค เท่านั้น เมื่อพวกเขาเปลี่ยนไป มีสนามสำหรับสิ่งนี้ jqFilterโดยคุณจะต้องระบุนิพจน์ jq ที่จะใช้กับไฟล์ Manifest JSON

ตัวอย่างเช่น หากต้องการตอบสนองต่อการเปลี่ยนแปลงในป้ายกำกับสำหรับออบเจ็กต์การทำให้ใช้งานได้ คุณจะต้องกรองฟิลด์ labels ออกจากสนาม metadata. การกำหนดค่าจะเป็นดังนี้:

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

นิพจน์ jqFilter นี้จะเปลี่ยนรายการ JSON แบบยาวของการปรับใช้ให้เป็น JSON แบบสั้นพร้อมป้ายกำกับ:

ขอแนะนำตัวดำเนินการเชลล์: การสร้างตัวดำเนินการสำหรับ Kubernetes กลายเป็นเรื่องง่ายยิ่งขึ้น

ตัวดำเนินการเชลล์จะรันฮุกเฉพาะเมื่อ JSON แบบสั้นนี้เปลี่ยนแปลง และการเปลี่ยนแปลงคุณสมบัติอื่น ๆ จะถูกละเว้น

บริบทการเปิดตัว Hook

การกำหนดค่า hook ช่วยให้คุณระบุตัวเลือกต่างๆ สำหรับเหตุการณ์ได้ เช่น 2 ตัวเลือกสำหรับเหตุการณ์จาก Kubernetes และ 2 กำหนดการ:

{"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"
]}

การพูดนอกเรื่องเล็กน้อย: ใช่ ตัวดำเนินการเชลล์รองรับ ใช้งานสคริปต์สไตล์ crontab. รายละเอียดเพิ่มเติมสามารถพบได้ใน เอกสาร.

เพื่อแยกความแตกต่างว่าทำไม hook ถึงถูกเรียกใช้ ตัวดำเนินการเชลล์จะสร้างไฟล์ชั่วคราวและส่งพาธไปยังไฟล์นั้นในตัวแปรไปยัง hook BINDING_CONTEXT_TYPE. ไฟล์นี้มีคำอธิบาย JSON ของเหตุผลในการเรียกใช้ฮุค ตัวอย่างเช่น ทุก ๆ 10 นาที hook จะทำงานโดยมีเนื้อหาต่อไปนี้:

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

... และในวันจันทร์จะเริ่มด้วยสิ่งนี้:

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

สำหรับ onKubernetesEvent จะมีทริกเกอร์ JSON มากขึ้นเพราะว่า มันมีคำอธิบายของวัตถุ:

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

เนื้อหาของฟิลด์สามารถเข้าใจได้จากชื่อและสามารถอ่านรายละเอียดเพิ่มเติมได้ เอกสาร. ตัวอย่างการรับชื่อทรัพยากรจากฟิลด์ resourceName การใช้ jq ได้แสดงให้เห็นแล้วใน hook ที่จำลองความลับ:

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

คุณสามารถรับช่องอื่นๆ ได้ในลักษณะเดียวกัน

ทำอะไรต่อไป

ในพื้นที่เก็บข้อมูลโครงการใน / ตัวอย่างไดเร็กทอรีมีตัวอย่างของ hooks ที่พร้อมที่จะรันบนคลัสเตอร์ เมื่อเขียนตะขอของคุณเองคุณสามารถใช้เป็นพื้นฐานได้

มีการรองรับการรวบรวมตัววัดโดยใช้ Prometheus - ตัววัดที่มีอยู่ได้อธิบายไว้ในส่วนนี้ เมตริก.

ดังที่คุณอาจเดาได้ Shell-Operator นั้นเขียนด้วยภาษา Go และเผยแพร่ภายใต้ลิขสิทธิ์ Open Source (Apache 2.0) เราจะขอบคุณสำหรับความช่วยเหลือในการพัฒนา โครงการบน GitHub: และดาว และปัญหา และคำขอดึง

เมื่อปิดม่านแห่งความลับแล้ว เราจะแจ้งให้คุณทราบว่าผู้ปฏิบัติงานเชลล์นั้นคือ เล็ก ส่วนหนึ่งของระบบของเราที่สามารถทำให้ส่วนเสริมที่ติดตั้งในคลัสเตอร์ Kubernetes เป็นปัจจุบันและดำเนินการอัตโนมัติต่างๆ อ่านเพิ่มเติมเกี่ยวกับระบบนี้ บอก แท้จริงแล้วในวันจันทร์ที่งาน HighLoad++ 2019 ในเซนต์ปีเตอร์สเบิร์ก - เราจะเผยแพร่วิดีโอและบทถอดเสียงของรายงานนี้ในไม่ช้า

เรามีแผนที่จะเปิดส่วนที่เหลือของระบบนี้: ตัวดำเนินการ addon และคอลเลกชั่น hooks และโมดูลของเรา ยังไงก็ตาม addon-operator ก็มีอยู่แล้ว มีอยู่ใน githubแต่เอกสารสำหรับเรื่องนี้ยังคงอยู่ในระหว่างดำเนินการ มีการวางแผนการเปิดตัวคอลเลกชันโมดูลสำหรับฤดูร้อน

คอยติดตาม!

PS

อ่านเพิ่มเติมในบล็อกของเรา:

ที่มา: will.com

เพิ่มความคิดเห็น