github地址:https://github.com/operator-framework/operator-sdk
安装说明:https://github.com/operator-framework/operator-sdk/blob/master/doc/user/install-operator-sdk.md

operator开发过程演变

  • 手动编写所有yaml,go文件,手动部署,发布镜像,运行成镜像,手动发布在集群中
  • 编写模板文件(yaml,type.go)等文件,依赖code-generator生成剩余文件,在controller中 添加自定义逻辑,编译发布
  • 使用operator-sdk工具的CLI命令,生成 项目,在controller添加自定义逻辑,使用operator-sdk自动制作镜像发布。

operator-sdk使用

环境

  • centos7
  • go 1.13.1
  • 配置环境变量,在 /root/.bash_profile 末尾添加
  1. export GO111MODULE=on
  2. export GOPROXY=https://goproxy.cn

生成项目结构

新建项目,生成指定目录

operator-sdk new --repo network network
# --repo network ./network 不在gopath目录下时,用以指定目录生成位置,network为自定义的项目名,即文件夹名称

执行结果:生成了network目录及network下的各个文件
operator-sdk初试 - 图1

添加新的api(NetWork为operator名称)

operator-sdk add api --api-version=app.example.com/v1 --kind=NetWork

执行结果:在deploy下增加了crds文件夹,在pkg/apis下生成了如下文件
operator-sdk初试 - 图2

添加控制器

operator-sdk add controller --api-version=app.example.com/v1 --kind=NetWork

执行结果:在controller下生成了add_network.go和network文件夹
operator-sdk初试 - 图3

开发自定义功能

修改pkg/apis/app/v1下的network_types.go文件,增加自定义属性

type NetWorkSpec struct {
   Size      *int32                      `json:"size"`
   Image     string                      `json:"image"`
   Resources corev1.ResourceRequirements `json:"resources,omitempty"`
   Envs      []corev1.EnvVar             `json:"envs,omitempty"`
   Ports     []corev1.ServicePort        `json:"ports,omitempty"`
}

type NetWorkStatus struct {
   appsv1.DeploymentStatus `json:",inline"`

}

根据上一步中增加的自定义属性生成代码

operator-sdk generate k8s

执行结果:会生成新的zz_文件覆盖老的版本
operator-sdk初试 - 图4

在controller中添加自定义逻辑代码,在pkg/controller/network/network_controller.go中修改Reconcile方法

func (r *ReconcileNetWork) Reconcile(request reconcile.Request) (reconcile.Result, error) {
   reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
   reqLogger.Info("Reconciling NetWork")
   // 新建一个NetWork对象用于提交给clinet查找 Fetch the NetWork instance
   instance := &appv1.NetWork{}
   err := r.client.Get(context.TODO(), request.NamespacedName, instance)
   if err != nil {
      if errors.IsNotFound(err) {
         return reconcile.Result{}, nil
      }
      return reconcile.Result{}, err
   }
   reqLogger.Info("dddx0 Custom Work Start ! =======================================")
   deployment := &appsv1.Deployment{}
   reqLogger.Info("&appsv1.Deployment{} :" + deployment.String())
   if err = r.client.Get(context.TODO(), request.NamespacedName, deployment); err != nil && errors.IsNotFound(err) {
      //新建一个deployment
      newDeployment := resources.NewDeployment(instance)
      if err := r.client.Create(context.TODO(), newDeployment); err != nil {
         return reconcile.Result{}, err
      }
      // 2. 创建 Service
      service := resources.NewService(instance)
      if err := r.client.Create(context.TODO(), service); err != nil {
         return reconcile.Result{}, err
      }

      // 3. 关联 Annotations
      data, _ := json.Marshal(instance.Spec)
      if instance.Annotations != nil {
         instance.Annotations["spec"] = string(data)
      } else {
         instance.Annotations = map[string]string{"spec": string(data)}
      }

      if err := r.client.Update(context.TODO(), instance); err != nil {
         return reconcile.Result{}, nil
      }
      return reconcile.Result{}, nil
   }

   oldspec := appv1.NetWork{}
   reqLogger.Info("instance.Annotations[spec] : " + instance.Annotations["spec"])
   if err := json.Unmarshal([]byte(instance.Annotations["spec"]), oldspec); err != nil {
      return reconcile.Result{}, err
   }

   if !reflect.DeepEqual(instance.Spec, oldspec) {
      // 更新关联资源
      newDeploy := resources.NewDeployment(instance)
      oldDeploy := &appsv1.Deployment{}
      if err := r.client.Get(context.TODO(), request.NamespacedName, oldDeploy); err != nil {
         return reconcile.Result{}, err
      }
      oldDeploy.Spec = newDeploy.Spec
      if err := r.client.Update(context.TODO(), oldDeploy); err != nil {
         return reconcile.Result{}, err
      }

      newService := resources.NewService(instance)
      oldService := &corev1.Service{}
      if err := r.client.Get(context.TODO(), request.NamespacedName, oldService); err != nil {
         return reconcile.Result{}, err
      }
      oldService.Spec = newService.Spec
      if err := r.client.Update(context.TODO(), oldService); err != nil {
         return reconcile.Result{}, err
      }

      return reconcile.Result{}, nil
   }
   return reconcile.Result{}, nil
}

使用operator-sdk编译启动operator

// 部署deploy下的yamls
kubectl apply -f deploy/crds/app.example.com/v1_network_crd.yaml
// 本地启动operator,用以调试
operator-sdk up local