1-Quick Start

2-Memcached-Operator

refer

kubebuilder init --domain my.domain --repo my.domain/memcached-operator

Create a api

kubebuilder create api --group cache --version v1alpha1 --kind Memcached --image=memcached:1.4.36-alpine --image-container-command="memcached,-m=64,-o,modern,-v" --image-container-port="11211" --run-as-user="1001" --plugins="deploy-image/v1-alpha" --make=false
 

ๅ‘ฝไปคๆœ‰็‚น้•ฟ:

  • --group cache --version v1alpha1 --kind Memcached:
    • ่ฟ™ไธช API ็š„ๅฎŒๅ…จ้™ๅฎšๅ็งฐๆ˜ฏ: cache.my.domain/v1alpha1 ?
  • --image=memcached:1.4.36-alpine: ๆŒ‡ๅฎš่ฆ้™ๅฎš็š„ๅฎนๅ™จ
  • --image-container-command="memcached,-m=64,-o,modern,-v" : ๅฎนๅ™จ็š„ๅฏๅŠจๅ‘ฝไปค.
  • --image-container-port="11211" : ็ซฏๅฃ
  • --run-as-user="1001"
  • --make=false: ็ฆๆญขไฝฟ็”จ make ๅ‘ฝไปค๏ผŒ่ฟ™ๆ ทๅฏไปฅๅœจ็”Ÿๆˆไปฃ็ ไน‹ๅŽๆ‰‹ๅŠจๆฃ€ๆŸฅๅ’Œไฟฎๆ”น

่ฟ™้‡Œ็”จไบ† ๆ’ไปถ --plugins="deploy-image/v1-alpha", ไผšๅธฎๆˆ‘ไปฌๆ นๆฎไธŠ้ข็š„้…็ฝฎ่‡ชๅŠจ็”Ÿๆˆ:

  • controller ไปฃ็ 
  • manage.yaml ไธญ่ฟ˜ไผšๅŒ…ๅซๅฏนๅบ”็š„ Deployment

2-1 API Scehma

ไธ‹้ขๅฐฑๆ˜ฏ็†่งฃ่ฟ™ไบ›ไปฃ็ .

FROM: api/v1alpha1/memcached_types.go

// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "make" to regenerate code after modifying this file
 
    // Size defines the number of Memcached instances
    // The following markers will use OpenAPI v3 schema to validate the value
    // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
    // +kubebuilder:validation:Minimum=1
    // +kubebuilder:validation:Maximum=3
    // +kubebuilder:validation:ExclusiveMaximum=false
    Size int32 `json:"size,omitempty"`
 
    // Port defines the port that will be used to init the container with the image
    ContainerPort int32 `json:"containerPort,omitempty"`
}
 
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
    // Represents the observations of a Memcached's current state.
    // Memcached.status.conditions.type are: "Available", "Progressing", and "Degraded"
    // Memcached.status.conditions.status are one of True, False, Unknown.
    // Memcached.status.conditions.reason the value should be a CamelCase string and producers of specific
    // condition types may define expected values and meanings for this field, and whether the values
    // are considered a guaranteed API.
    // Memcached.status.conditions.Message is a human readable message indicating details about the transition.
    // For further information see: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
 
    Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}
 
  1. MemacchedSpec: ๆˆ‘ไปฌ้œ€่ฆ็ฎก็š„้ƒจๅˆ†, ๅ‘Š่ฏ‰ k8sๆˆ‘ไปฌๆœŸๅพ…็š„็Šถๆ€

    • ่‡ชๅฎšไน‰่ต„ๆบ็š„ CR ้ƒจๅˆ†, Custom Resource
    • ๅฎšไน‰ไบ†็”จๆˆทๆ‰€ๆœ‰ๅฏไปฅ่ฎพ็ฝฎ็š„้€‰้กน
    • ็ฎ€ๅ•ๆฅ่ฏด๏ผŒ ่ฟ™้‡Œๆ˜ฏๆˆ‘ไปฌๅ‘Š่ฏ‰ k8s, ๆˆ‘ไปฌ็š„ memcached ้œ€่ฆ้•ฟๆˆไป€ไนˆๆ ทๅญ
  2. Status Condition :

    • ๆ ‡ๅ‡†ๅŒ–็š„ API, ่กจ่พพๅฝ“ๅ‰้›†็พคไธญ็š„ ็Šถๆ€
    • ๆฏๅฝ“่ต„ๆบ็š„ ็Šถๆ€ๅ‘็”Ÿๅ˜ๅŒ–็š„ๆ—ถๅ€™, (ๅˆ›ๅปบ๏ผŒๆ›ดๆ–ฐ๏ผŒๆˆ–่€…ๅ‡บ้”™), Controller ๅฐฑไผšๆ›ดๆ–ฐ่ฟ™ไบ›ๆกไปถ
  3. ่€Œ Memcached ๅŒๆ—ถๅŒ…ๅซ่ฟ™2ไธช ็ป“ๆž„ไฝ“๏ผŒๅฐฑๆ˜ฏ่ฎก็ฎ—ๅ‡บ ๅฝ“ๅ‰็š„็Šถๆ€ๅ’Œ ๆœŸๆœ›็Šถๆ€็š„ Delta

ๆœ‰ไธ€ไบ›ๆฏ”่พƒ trick ็š„ไธœ่ฅฟ, markers, ไธŠ้ข็š„ๆณจ้‡Š: +kubebuilder:validation:Minimum=1

  • ไธ€็ง ๅฃฐๆ˜Žๅผ็š„็บฆๆŸๆ‰‹ๆฎต, ่ฟ™ไธชๆ—ถๅ€™ไผšๅœจ apply ไน‹ๅ‰ๆ ก้ชŒ่ฟ™ไธชๅ€ผๅฟ…้กป >=1
  • ็”Ÿๆˆ็š„ไฟฎๆ”นไผšๅœจไธญ config/crd/bases ไธญ

ๆฏๆฌกไฟฎๆ”น Api-Spec ๅŽ๏ผŒ้œ€่ฆ make generate: ๆฅๅŒๆญฅๆ›ดๆ–ฐ CRD ไธญ็š„ๅ†…ๅฎน.

ไธ€่ˆฌๅนณๆ—ถ็š„demoไผšๆ”พๅœจ `config/samples ไธ‹้ข, ไพ‹ๅฆ‚

apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
  name: memcached-sample
spec:
  # TODO(user): edit the following value to ensure the number
  # of Pods/Instances your Operand must have on cluster
  size: 1
 
  # TODO(user): edit the following value to ensure the container has the right port to be initialized
  containerPort: 11211

2-2 Controller

Reconciliation : ๆ˜ฏ k8s controller-loop ๆจกๅผไธญ็š„ๆ ธๅฟƒๆฆ‚ๅฟต.

็›ฎ็š„:

  • ็กฎไฟ่ต„ๆบ็š„ๅฎž้™…็Šถๆ€ไธŽๆœŸๆœ›็Šถๆ€็š„ๅŒๆญฅ
  • ๅŸบไบŽๅตŒๅ…ฅ็š„ไธšๅŠก้€ป่พ‘ๅค„็†่ต„ๆบ็Šถๆ€

ๅพช็Žฏ็‰นๆ€ง:

  • Reconciliation ๅ‡ฝๆ•ฐไผšไธๆ–ญๆ‰ง่กŒ๏ผŒ็›ดๅˆฐๆ‰€ๆœ‰็š„ๆกไปถ้ƒฝ็ฌฆๅˆ้ข„ๆœŸ

ไธ‹้ข็œ‹ไผชไปฃ็ :

reconcile App {
 
  // Check if a Deployment for the app exists, if not, create one
  // If there's an error, then restart from the beginning of the reconcile
  if err != nil {
    return reconcile.Result{}, err
  }
 
  // Check if a Service for the app exists, if not, create one
  // If there's an error, then restart from the beginning of the reconcile
  if err != nil {
    return reconcile.Result{}, err
  }
 
  // Look for Database CR/CRD
  // Check the Database Deployment's replicas size
  // If deployment.replicas size doesn't match cr.size, then update it
  // Then, restart from the beginning of the reconcile. For example, by returning `reconcile.Result{Requeue: true}, nil`.
  if err != nil {
    return reconcile.Result{Requeue: true}, nil
  }
  ...
 
  // If at the end of the loop:
  // Everything was executed successfully, and the reconcile can stop
  return reconcile.Result{}, nil
 
}
 

ไธ€ไธชๆ ‡ๅ‡†็š„ๆต็จ‹:

  1. ๆฃ€ๆŸฅ Development ๆ˜ฏๅฆๅญ˜ๅœจ ;
  2. ๆฃ€ๆŸฅ Service ๆ˜ฏๅฆๅญ˜ๅœจ ;
  3. ... ็›ดๅˆฐๆ‰€ๆœ‰็š„ไบ‹ๆƒ…้ƒฝๆฃ€ๆŸฅๅฎŒๆฏ• ;

2-3 Return Options

return ctrl.Result{}, err
  • ็”จ้€”: ๅฝ“้‡ๅˆฐ้”™่ฏฏ็š„ๆ—ถๅ€™๏ผŒ้œ€่ฆ็ซ‹ๅˆป้‡่ฏ•๏ผŒ่ฟ”ๅ›ž่ฟ™ไธช
  • ่กŒไธบ: ๆŽงๅˆถๅ™จไผš่ฎฐๅฝ•ๅฝ“ๅ‰็š„้”™่ฏฏ, ๅœจ็Ÿญๆš‚็š„ๅปถ่ฟŸๅŽ๏ผŒ่‡ชๅŠจ้‡ๆ–ฐ่งฆๅ‘ reconciliation
  • ๅœบๆ™ฏ: ไธดๆ—ถๆ€ง็š„้”™่ฏฏ, ็ฝ‘็ปœ้—ฎ้ข˜ๆˆ–่€…่ต„ๆบๆš‚ๆ—ถไธๅฏ็”จ
return ctrl.Result{Requeue: true}, nil
  • ็”จ้€”: ๅฝ“้œ€่ฆ็ซ‹ๅณ้‡ๆ–ฐๆ‰ง่กŒ reconciliation, ไฝ†ๆ˜ฏๆฒกๆœ‰้”™่ฏฏ็ซ‹ๅณๅ‘็”Ÿๆ—ถๅ€™
  • ๅœบๆ™ฏ: ้€‚ๅˆ่ฟž็ปญๅคšๆฌก่ฐƒๆ•ดๆˆ–่€…ๆฃ€ๆŸฅ๏ผŒๆฏ”ๅฆ‚่ฏดๆˆ‘ไปฌ่ฆ็ญ‰ๅพ…ๆŸไธชๆกไปถๆปก่ถณ็š„ๆ—ถๅ€™ไฝฟ็”จ
return ctrl.Result{}, nil
  • ็”จ้€”: ๅฝ“ reconciliation ๆˆๅŠŸๅฎŒๆˆ, ่€Œไธ”ๆš‚ๆ—ถไธ้œ€่ฆ further action ็š„ๆ—ถๅ€™ไฝฟ็”จ
  • ๅœบๆ™ฏ: ่ต„ๆบ่พพๅˆฐๆœŸๆœ›็Šถๆ€๏ผŒๆ— ้œ€่ฟ›ไธ€ๆญฅๆ“ไฝœ
return ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())}, nil
  • ็”จ้€”: ๅปถ่ฟŸ้‡ๆ–ฐ Reconcile , ๅœจๆŒ‡ๅฎš็š„ๆ—ถๅ€™ไน‹ๅŽ้‡ๆ–ฐๆ‰ง่กŒ reconciliation
  • ๅœบๆ™ฏ: ้œ€่ฆๅฎšๆœŸ็š„ๆฃ€ๆŸฅๆˆ–่€…ๆ‰ง่กŒ, ไพ‹ๅฆ‚่ฝฎ่ฏขๅค–้ƒจ็š„่ต„ๆบ็Šถๆ€