Kubernetes 官方示例:使用 Redis 部署 PHP 留言板應用程式」Operator 化。

原始碼倉庫:https://github.com/jxlwqq/guestbook-operator

前置條件

  • 安裝 Docker Desktop,並啟動內建的 Kubernetes 叢集
  • 註冊一個 hub.docker.com 賬戶,需要將本地構建好的映象推送至公開倉庫中
  • 安裝 operator SDK CLI: brew install operator-sdk
  • 安裝 Go: brew install go

本示例推薦的依賴版本:

  • Docker Desktop: >= 4.0.0
  • Kubernetes: >= 1.21.4
  • Operator-SDK: >= 1.11.0
  • Go: >= 1.17

jxlwqq 為筆者的 ID,命令列和程式碼中涉及的個人 ID,均需要替換為讀者自己的,包括

  • --domain=
  • --repo=
  • //+kubebuilder:rbac:groups=
  • IMAGE_TAG_BASE ?=

建立專案

使用 Operator SDK CLI 建立名為 guestbook-operator 的專案。

  1. mkdir -p $HOME/projects/guestbook-operator
  2. cd $HOME/projects/guestbook-operator
  3. go env -w GOPROXY=https://goproxy.cn,direct
  4. ```shell
  5. operator-sdk init \
  6. --domain=jxlwqq.github.io \
  7. --repo=github.com/jxlwqq/guestbook-operator \
  8. --skip-go-version-check

建立 API 和控制器

使用 Operator SDK CLI 建立自定義資源定義(CRD)API 和控制器。

執行以下命令建立帶有組 app、版本 v1alpha1 和種類 Guestbook 的 API:

  1. operator-sdk create api \
  2. --resource=true \
  3. --controller=true \
  4. --group=app \
  5. --version=v1alpha1 \
  6. --kind=Guestbook

定義 Guestbook 自定義資源(CR)的 API。

修改 api/v1alpha1/guestbook_types.go 中的 Go 型別定義,使其具有以下 spec 和 status

  1. type GuestbookSpec struct {
  2. FrontendSize int32 `json:"frontendSize"`
  3. RedisFollowerSize int32 `json:"redisFollowerSize"`
  4. }

為資源型別更新生成的程式碼:

  1. make generate

執行以下命令以生成和更新 CRD 清單:

  1. make manifests

實現控制器

由於邏輯較為複雜,程式碼較為龐大,所以無法在此全部展示,完整的操作器程式碼請參見 controllers 目錄。

在本例中,將生成的控制器檔案 controllers/guestbook_controller.go 替換為以下示例實現:

  1. /*
  2. Copyright 2021.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package controllers
  14. import (
  15. "context"
  16. appsv1 "k8s.io/api/apps/v1"
  17. corev1 "k8s.io/api/core/v1"
  18. "k8s.io/apimachinery/pkg/api/errors"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. ctrl "sigs.k8s.io/controller-runtime"
  21. "sigs.k8s.io/controller-runtime/pkg/client"
  22. "sigs.k8s.io/controller-runtime/pkg/log"
  23. appv1alpha1 "github.com/jxlwqq/guestbook-operator/api/v1alpha1"
  24. )
  25. // GuestbookReconciler reconciles a Guestbook object
  26. type GuestbookReconciler struct {
  27. client.Client
  28. Scheme *runtime.Scheme
  29. }
  30. //+kubebuilder:rbac:groups=app.jxlwqq.github.io,resources=guestbooks,verbs=get;list;watch;create;update;patch;delete
  31. //+kubebuilder:rbac:groups=app.jxlwqq.github.io,resources=guestbooks/status,verbs=get;update;patch
  32. //+kubebuilder:rbac:groups=app.jxlwqq.github.io,resources=guestbooks/finalizers,verbs=update
  33. //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
  34. //+kubebuilder:rbac:groups=core,resources=service,verbs=get;list;watch;create;update;patch;delete
  35. //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
  36. // Reconcile is part of the main kubernetes reconciliation loop which aims to
  37. // move the current state of the cluster closer to the desired state.
  38. // TODO(user): Modify the Reconcile function to compare the state specified by
  39. // the Guestbook object against the actual cluster state, and then
  40. // perform operations to make the cluster state reflect the state specified by
  41. // the user.
  42. //
  43. // For more details, check Reconcile and its Result here:
  44. // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile
  45. func (r *GuestbookReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
  46. reqLogger := log.FromContext(ctx)
  47. reqLogger.Info("Reconciling Guestbook")
  48. guestbook := &appv1alpha1.Guestbook{}
  49. err := r.Client.Get(context.TODO(), req.NamespacedName, guestbook)
  50. if err != nil {
  51. if errors.IsNotFound(err) {
  52. return ctrl.Result{}, nil
  53. }
  54. return ctrl.Result{}, err
  55. }
  56. var result = &ctrl.Result{}
  57. result, err = r.ensureDeployment(r.redisLeaderDeployment(guestbook))
  58. if result != nil {
  59. return *result, err
  60. }
  61. result, err = r.ensureService(r.redisLeaderService(guestbook))
  62. if result != nil {
  63. return *result, err
  64. }
  65. result, err = r.ensureDeployment(r.redisFollowerDeployment(guestbook))
  66. if result != nil {
  67. return *result, err
  68. }
  69. result, err = r.ensureService(r.redisFollowerService(guestbook))
  70. if result != nil {
  71. return *result, err
  72. }
  73. result, err = r.handleRedisFollowerChanges(guestbook)
  74. if result != nil {
  75. return *result, err
  76. }
  77. result, err = r.ensureDeployment(r.frontendDeployment(guestbook))
  78. if result != nil {
  79. return *result, err
  80. }
  81. result, err = r.ensureService(r.frontendService(guestbook))
  82. if result != nil {
  83. return *result, err
  84. }
  85. result, err = r.handleFrontendChanges(guestbook)
  86. if result != nil {
  87. return *result, err
  88. }
  89. return ctrl.Result{}, nil
  90. }
  91. // SetupWithManager sets up the controller with the Manager.
  92. func (r *GuestbookReconciler) SetupWithManager(mgr ctrl.Manager) error {
  93. return ctrl.NewControllerManagedBy(mgr).
  94. For(&appv1alpha1.Guestbook{}).
  95. Owns(&appsv1.Deployment{}).
  96. Owns(&corev1.Service{}).
  97. Complete(r)
  98. }

執行以下命令以生成和更新 CRD 清單:

  1. make manifests

執行 Operator

捆綁 Operator,並使用 Operator Lifecycle Manager(OLM)在叢集中部署。

修改 Makefile 中 IMAGE_TAG_BASE 和 IMG:

  1. IMAGE_TAG_BASE ?= docker.io/jxlwqq/guestbook-operator
  2. IMG ?= $(IMAGE_TAG_BASE):latest

構建映象:

  1. make docker-build

將映象推送到映象倉庫:

  1. make docker-push

成功後訪問:https://hub.docker.com/r/jxlwqq/guestbook-operator

執行 make bundle 命令建立 Operator 捆綁包清單,並依次填入名稱、作者等必要資訊:

  1. make bundle

構建捆綁包映象:

  1. make bundle-build

推送捆綁包映象:

  1. make bundle-push

成功後訪問:https://hub.docker.com/r/jxlwqq/guestbook-operator-bundle

使用 Operator Lifecycle Manager 部署 Operator:

  1. # 切換至本地叢集
  2. kubectl config use-context docker-desktop
  3. # 安裝 olm
  4. operator-sdk olm install
  5. # 使用 Operator SDK 中的 OLM 整合在叢集中執行 Operator
  6. operator-sdk run bundle docker.io/jxlwqq/guestbook-operator-bundle:v0.0.1

建立自定義資源

編輯 config/samples/app_v1alpha1_guestbook.yaml 上的 Guestbook CR 清單示例,使其包含以下規格:

  1. apiVersion: app.jxlwqq.github.io/v1alpha1
  2. kind: Guestbook
  3. metadata:
  4. name: guestbook-sample
  5. spec:
  6. # Add fields here
  7. frontendSize: 2
  8. redisFollowerSize: 2

建立 CR:

  1. kubectl apply -f config/samples/app_v1alpha1_guestbook.yaml

檢視 Pod:

  1. NAME READY STATUS RESTARTS AGE
  2. frontend-85595f5bf9-jrcp4 1/1 Running 0 9s
  3. frontend-85595f5bf9-q8fkl 1/1 Running 0 9s
  4. redis-follower-76c5cc5b79-fxxlq 1/1 Running 0 9s
  5. redis-follower-76c5cc5b79-g8vnf 1/1 Running 0 9s
  6. redis-leader-6666df964-vjhp2 1/1 Running 0 9s

檢視 Service:

  1. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  2. frontend NodePort 10.106.145.169 <none> 80:30693/TCP 24s
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4m58s
  4. redis-follower ClusterIP 10.108.30.112 <none> 6379/TCP 24s
  5. redis-leader ClusterIP 10.106.255.152 <none> 6379/TCP 24s

瀏覽器訪問:http://localhost:30693

網頁上會顯示出 Guestbook 的表單頁面。

更新 CR:

  1. # 修改frontend 和 redis 副本數
  2. kubectl patch guestbook guestbook-sample -p '{"spec":{"frontendSize": 3, "redisFollowerSize": 3}}' --type=merge

檢視 Pod:

  1. NAME READY STATUS RESTARTS AGE
  2. frontend-85595f5bf9-4pmfj 1/1 Running 0 4s
  3. frontend-85595f5bf9-jrcp4 1/1 Running 0 50s
  4. frontend-85595f5bf9-q8fkl 1/1 Running 0 50s
  5. redis-follower-76c5cc5b79-bxbb4 1/1 Running 0 4s
  6. redis-follower-76c5cc5b79-fxxlq 1/1 Running 0 50s
  7. redis-follower-76c5cc5b79-g8vnf 1/1 Running 0 50s
  8. redis-leader-6666df964-vjhp2 1/1 Running 0 50s

做好清理

  1. operator-sdk cleanup guestbook-operator
  2. operator-sdk olm uninstall

更多

更多經典示例請參考:https://github.com/jxlwqq/kubernetes-examples