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 末尾添加
export GO111MODULE=on
export GOPROXY=https://goproxy.cn
生成项目结构
新建项目,生成指定目录
operator-sdk new --repo network network
# --repo network ./network 不在gopath目录下时,用以指定目录生成位置,network为自定义的项目名,即文件夹名称
执行结果:生成了network目录及network下的各个文件
添加新的api(NetWork为operator名称)
operator-sdk add api --api-version=app.example.com/v1 --kind=NetWork
执行结果:在deploy下增加了crds文件夹,在pkg/apis下生成了如下文件
添加控制器
operator-sdk add controller --api-version=app.example.com/v1 --kind=NetWork
执行结果:在controller下生成了add_network.go和network文件夹
开发自定义功能
修改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_文件覆盖老的版本
在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