ไทย

เจาะลึก Kubernetes Operators วิธีการที่ช่วยให้การจัดการแอปพลิเคชันที่ซับซ้อนและ Custom Resource เป็นเรื่องง่ายและอัตโนมัติ พร้อมเรียนรู้วิธีสร้างและใช้งาน Operator ของคุณเอง

Kubernetes Operators: การจัดการ Custom Resource แบบอัตโนมัติ

Kubernetes ได้ปฏิวัติวิธีการที่เราปรับใช้และจัดการแอปพลิเคชัน อย่างไรก็ตาม การจัดการแอปพลิเคชันที่ซับซ้อนและมีสถานะ (stateful) ยังคงเป็นเรื่องท้าทาย นี่คือจุดที่ Kubernetes Operators เข้ามามีบทบาท โดยเป็นวิธีที่มีประสิทธิภาพในการจัดการแอปพลิเคชันโดยอัตโนมัติและขยายขีดความสามารถของ Kubernetes

Kubernetes Operators คืออะไร?

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

ซึ่งแตกต่างจากคอนโทรลเลอร์ของ Kubernetes แบบดั้งเดิมที่จัดการทรัพยากรหลัก เช่น Pods และ Services แต่ Operators จะจัดการ custom resources ที่กำหนดผ่าน Custom Resource Definitions (CRDs) ซึ่งช่วยให้คุณสามารถกำหนดทรัพยากรเฉพาะของแอปพลิเคชันของคุณเองและให้ Kubernetes จัดการโดยอัตโนมัติ

ทำไมต้องใช้ Kubernetes Operators?

Operators มีประโยชน์ที่สำคัญหลายประการสำหรับการจัดการแอปพลิเคชันที่ซับซ้อน:

ทำความเข้าใจเกี่ยวกับ Custom Resource Definitions (CRDs)

Custom Resource Definitions (CRDs) เป็นรากฐานของ Kubernetes Operators CRDs ช่วยให้คุณสามารถขยาย Kubernetes API โดยการกำหนดประเภททรัพยากรที่คุณกำหนดเอง ทรัพยากรเหล่านี้จะถูกจัดการเหมือนกับทรัพยากรอื่นๆ ของ Kubernetes เช่น Pods หรือ Services และสามารถจัดการได้โดยใช้ `kubectl` และเครื่องมือ Kubernetes อื่นๆ

นี่คือวิธีการทำงานของ CRDs:

  1. คุณกำหนด CRD ที่ระบุสคีมาและกฎการตรวจสอบสำหรับ custom resource ของคุณ
  2. คุณปรับใช้ CRD กับคลัสเตอร์ Kubernetes ของคุณ
  3. คุณสร้างอินสแตนซ์ของ custom resource ของคุณ โดยระบุการกำหนดค่าที่ต้องการ
  4. Operator จะคอยเฝ้าดูการเปลี่ยนแปลงของ custom resources เหล่านี้และดำเนินการเพื่อปรับสถานะที่ต้องการให้ตรงกับสถานะจริง

ตัวอย่างเช่น สมมติว่าคุณต้องการจัดการแอปพลิเคชันฐานข้อมูลโดยใช้ Operator คุณสามารถกำหนด CRD ที่เรียกว่า `Database` พร้อมฟิลด์ต่างๆ เช่น `name`, `version`, `storageSize` และ `replicas` จากนั้น Operator จะเฝ้าดูการเปลี่ยนแปลงของทรัพยากร `Database` และสร้างหรืออัปเดตอินสแตนซ์ฐานข้อมูลพื้นฐานตามนั้น

Kubernetes Operators ทำงานอย่างไร

Kubernetes Operators ทำงานโดยการรวม Custom Resource Definitions (CRDs) เข้ากับคอนโทรลเลอร์ที่กำหนดเอง คอนโทรลเลอร์จะคอยเฝ้าดูการเปลี่ยนแปลงของ custom resources และดำเนินการเพื่อปรับสถานะที่ต้องการให้ตรงกับสถานะจริง กระบวนการนี้โดยทั่วไปประกอบด้วยขั้นตอนต่อไปนี้:

  1. การเฝ้าดูเหตุการณ์ (Watching for Events): Operator จะเฝ้าดูเหตุการณ์ที่เกี่ยวข้องกับ custom resources เช่น การสร้าง การลบ หรือการอัปเดต
  2. การปรับสถานะ (Reconciling State): เมื่อมีเหตุการณ์เกิดขึ้น Operator จะปรับสถานะของแอปพลิเคชัน ซึ่งเกี่ยวข้องกับการเปรียบเทียบสถานะที่ต้องการ (กำหนดไว้ใน Custom Resource) กับสถานะจริง และดำเนินการเพื่อให้สอดคล้องกัน
  3. การจัดการทรัพยากร (Managing Resources): Operator จะสร้าง อัปเดต หรือลบทรัพยากรของ Kubernetes (Pods, Services, Deployments ฯลฯ) เพื่อให้ได้สถานะที่ต้องการ
  4. การจัดการข้อผิดพลาด (Handling Errors): Operator จะจัดการข้อผิดพลาดและพยายามดำเนินการที่ล้มเหลวอีกครั้งเพื่อให้แน่ใจว่าแอปพลิเคชันยังคงอยู่ในสถานะที่สอดคล้องกัน
  5. การให้ข้อมูลย้อนกลับ (Providing Feedback): Operator จะให้ข้อมูลย้อนกลับเกี่ยวกับสถานะของแอปพลิเคชัน เช่น การตรวจสอบสถานะ (health checks) และการใช้ทรัพยากร

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

การสร้าง Kubernetes Operator ของคุณเอง

มีเครื่องมือและเฟรมเวิร์กหลายอย่างที่สามารถช่วยคุณสร้าง Kubernetes Operators:

นี่คือภาพรวมอย่างง่ายของขั้นตอนที่เกี่ยวข้องในการสร้าง Operator โดยใช้ Operator Framework:

  1. กำหนด Custom Resource Definition (CRD): สร้าง CRD ที่อธิบายสถานะที่ต้องการของแอปพลิเคชันของคุณ สิ่งนี้จะกำหนดสคีมาและกฎการตรวจสอบสำหรับ custom resource ของคุณ
  2. สร้างโค้ด Operator: ใช้ Operator SDK เพื่อสร้างโค้ด Operator เริ่มต้นตาม CRD ของคุณ ซึ่งจะสร้างคอนโทรลเลอร์และคำจำกัดความของทรัพยากรที่จำเป็น
  3. นำตรรกะ Reconcile ไปใช้: นำตรรกะ reconcile ไปใช้ ซึ่งจะเปรียบเทียบสถานะที่ต้องการ (กำหนดไว้ใน Custom Resource) กับสถานะจริง และดำเนินการเพื่อให้สอดคล้องกัน นี่คือหัวใจของฟังก์ชันการทำงานของ Operator ของคุณ
  4. สร้างและปรับใช้ Operator: สร้างอิมเมจของ Operator และปรับใช้กับคลัสเตอร์ Kubernetes ของคุณ
  5. ทดสอบและปรับปรุง: ทดสอบ Operator ของคุณอย่างละเอียดและปรับปรุงโค้ดเพื่อเพิ่มฟังก์ชันการทำงานและความน่าเชื่อถือ

มาดูตัวอย่างพื้นฐานโดยใช้ Operator Framework สมมติว่าคุณต้องการสร้าง Operator ที่จัดการการปรับใช้ `Memcached` แบบง่ายๆ

1. กำหนด CRD:

สร้างไฟล์ `memcached.yaml` พร้อมคำจำกัดความ CRD ต่อไปนี้:


apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: memcacheds.cache.example.com
spec:
  group: cache.example.com
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: integer
                  description: Size is the number of Memcached instances
              required: ["size"]
  scope: Namespaced
  names:
    plural: memcacheds
    singular: memcached
    kind: Memcached
    shortNames: ["mc"]

CRD นี้กำหนด resource ชื่อ `Memcached` ที่มีฟิลด์ `size` ซึ่งระบุจำนวนอินสแตนซ์ Memcached ที่จะรัน

2. สร้างโค้ด Operator:

ใช้ Operator SDK เพื่อสร้างโค้ด Operator เริ่มต้น:


operator-sdk init --domain=example.com --repo=github.com/example/memcached-operator
operator-sdk create api --group=cache --version=v1alpha1 --kind=Memcached --resource --controller

คำสั่งนี้จะสร้างไฟล์และไดเรกทอรีที่จำเป็นสำหรับ Operator ของคุณ รวมถึงโค้ดคอนโทรลเลอร์และคำจำกัดความของทรัพยากร

3. นำตรรกะ Reconcile ไปใช้:

แก้ไขไฟล์ `controllers/memcached_controller.go` เพื่อนำตรรกะ reconcile ไปใช้ ฟังก์ชันนี้จะสร้าง อัปเดต หรือลบการปรับใช้ Memcached ตามสถานะที่ต้องการซึ่งกำหนดไว้ใน resource `Memcached`


func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := r.Log.WithValues("memcached", req.NamespacedName)

	// Fetch the Memcached instance
	memcached := &cachev1alpha1.Memcached{}
	err := r.Get(ctx, req.NamespacedName, memcached)
	if err != nil {
		if errors.IsNotFound(err) {
			// Request object not found, could have been deleted after reconcile request.
			// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
			// Return and don't requeue
			log.Info("Memcached resource not found. Ignoring since object must be deleted")
			return ctrl.Result{}, nil
		}
		// Error reading the object - requeue the request.
		log.Error(err, "Failed to get Memcached")
		return ctrl.Result{}, err
	}

	// Define a new Deployment object
	deployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      memcached.Name,
			Namespace: memcached.Namespace,
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: &memcached.Spec.Size,
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"app": memcached.Name,
				},
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": memcached.Name,
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  "memcached",
							Image: "memcached:1.6.17-alpine",
							Ports: []corev1.ContainerPort{
								{
									ContainerPort: 11211,
								},
							},
						},
					},
				},
			},
		},
	}

	// Set Memcached instance as the owner and controller
	if err := ctrl.SetControllerReference(memcached, deployment, r.Scheme);
		err != nil {
			log.Error(err, "Failed to set controller reference")
			return ctrl.Result{}, err
	}

	// Check if this Deployment already exists
	found := &appsv1.Deployment{}
	err = r.Get(ctx, types.NamespacedName{
		Name:      deployment.Name,
		Namespace: deployment.Namespace,
	}, found)
	if err != nil && errors.IsNotFound(err) {
		log.Info("Creating a new Deployment", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
		err = r.Create(ctx, deployment)
		if err != nil {
			log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
			return ctrl.Result{}, err
		}

		// Deployment created successfully - return and requeue
		return ctrl.Result{Requeue: true}, nil
	} else if err != nil {
		log.Error(err, "Failed to get Deployment")
		return ctrl.Result{}, err
	}

	// Ensure the deployment size is the same as the spec
	size := memcached.Spec.Size
	if *found.Spec.Replicas != size {
		log.Info("Updating Deployment", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
		found.Spec.Replicas = &size
		err = r.Update(ctx, found)
		if err != nil {
			log.Error(err, "Failed to update Deployment", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
			return ctrl.Result{}, err
		}
		// Spec updated - return and requeue
		return ctrl.Result{Requeue: true}, nil
	}

	// Deployment already exists - don't requeue
	log.Info("Skip reconcile: Deployment already exists", "Deployment.Namespace", deployment.Namespace, "Deployment.Name", deployment.Name)
	return ctrl.Result{}, nil
}

ตัวอย่างนี้เป็นเวอร์ชันที่ง่ายมากของตรรกะ reconcile Operator ที่พร้อมใช้งานจริงจะต้องมีการจัดการข้อผิดพลาด การบันทึก และตัวเลือกการกำหนดค่าที่แข็งแกร่งกว่านี้

4. สร้างและปรับใช้ Operator:

สร้างอิมเมจของ Operator และปรับใช้กับคลัสเตอร์ Kubernetes ของคุณโดยใช้ `make deploy`

5. สร้าง Memcached Resource:

สร้างไฟล์ `memcached-instance.yaml` พร้อมเนื้อหาต่อไปนี้:


apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
  name: memcached-sample
spec:
  size: 3

ปรับใช้ไฟล์นี้กับคลัสเตอร์ของคุณโดยใช้ `kubectl apply -f memcached-instance.yaml`

ตอนนี้ Operator จะสร้าง Deployment ที่มีอินสแตนซ์ Memcached 3 ตัว

แนวทางปฏิบัติที่ดีที่สุดสำหรับการพัฒนา Kubernetes Operators

การพัฒนา Kubernetes Operators ที่มีประสิทธิภาพต้องมีการวางแผนและการดำเนินการอย่างรอบคอบ นี่คือแนวทางปฏิบัติที่ดีที่สุดที่ควรคำนึงถึง:

ตัวอย่างการใช้งาน Kubernetes Operators ในโลกความเป็นจริง

หลายองค์กรกำลังใช้ Kubernetes Operators เพื่อจัดการแอปพลิเคชันที่ซับซ้อนในการใช้งานจริง นี่คือตัวอย่างบางส่วน:

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

ข้อควรพิจารณาด้านความปลอดภัยสำหรับ Kubernetes Operators

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

นี่คือข้อควรพิจารณาด้านความปลอดภัยที่สำคัญสำหรับ Kubernetes Operators:

ด้วยการใช้มาตรการความปลอดภัยเหล่านี้ คุณสามารถลดความเสี่ยงของการละเมิดความปลอดภัยและปกป้อง Kubernetes Operators ของคุณจากกิจกรรมที่เป็นอันตรายได้อย่างมาก

อนาคตของ Kubernetes Operators

Kubernetes Operators กำลังพัฒนาอย่างรวดเร็วและกลายเป็นส่วนสำคัญของระบบนิเวศ Kubernetes มากขึ้นเรื่อยๆ ในขณะที่การนำ Kubernetes มาใช้ยังคงเติบโตอย่างต่อเนื่อง เราคาดว่าจะได้เห็นนวัตกรรมในพื้นที่ Operator มากยิ่งขึ้น

นี่คือแนวโน้มบางส่วนที่กำลังกำหนดอนาคตของ Kubernetes Operators:

สรุป

Kubernetes Operators เป็นวิธีที่มีประสิทธิภาพในการจัดการแอปพลิเคชันที่ซับซ้อนโดยอัตโนมัติและขยายขีดความสามารถของ Kubernetes ด้วยการกำหนด custom resources และการนำ custom controllers มาใช้ Operators ช่วยให้คุณจัดการแอปพลิเคชันในรูปแบบที่เป็น declarative, อัตโนมัติ และทำซ้ำได้ ในขณะที่การนำ Kubernetes มาใช้ยังคงเติบโตอย่างต่อเนื่อง Operators จะกลายเป็นส่วนสำคัญของภูมิทัศน์ cloud-native มากขึ้นเรื่อยๆ

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

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