文档章节

Kubernetes Operator简易教程

R
 Robotcl_Blog
发布于 09/16 18:06
字数 1742
阅读 14
收藏 0

1. 安装operator-sdk

//安装 operator-sdk
$ apt-get install operator-sdk
.....
$ operator-sdk version
operator-sdk version: v0.7.0
$ go version
go version go1.11.4 darwin/amd64

2. 创建新项目

//创建新项目目录
$ mkdir -p kubernetes-operator
//设置该目录为GOPATH路径
$ cd kubernetes-operator && exprot GOPATH=$PWD
$ mkdir -p $GOPATH/src/robotcl_operator/
$ cd $GOPATH/src/robotcl_operator/
//使用 operator-sdk 创建一个名为operator项目
$ operator-sdk new opdemo
......
//该过程需要科学上网,需要花费很长时间,请耐心等待
......
$ cd opdemo && tree -L 2
.
├── Gopkg.lock
├── Gopkg.toml
├── build
│   ├── Dockerfile
│   ├── _output
│   └── bin
├── cmd
│   └── manager
├── deploy
│   ├── crds
│   ├── operator.yaml
│   ├── role.yaml
│   ├── role_binding.yaml
│   └── service_account.yaml
├── pkg
│   ├── apis
│   └── controller
├── vendor
│   ├── cloud.google.com
│   ├── contrib.go.opencensus.io
│   ├── github.com
│   ├── go.opencensus.io
│   ├── go.uber.org
│   ├── golang.org
│   ├── google.golang.org
│   ├── gopkg.in
│   ├── k8s.io
│   └── sigs.k8s.io
└── version
    └── version.go

23 directories, 8 files

3. 添加API

operator-sdk add api --api-version=app.example.com/v1 --kind=AppService
//添加API成功后,会生成源文件pkg/apis/app/v1/appservice_types.go

4. 添加Controller

operator-sdk add controller --api-version=app.example.com/v1 --kind=AppService
//添加Controller成功后,会生成源文件pkg/controller/appservice/appsrvice_controller.go

5. 自定义API

打开源文件pkg/apis/app/v1/appservice_types.go,自定义API结构体

type AppServiceSpec 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"`
}
import (
    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    appv1  "github.com/cnych/opdemo/pkg/apis/app/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type AppServiceStatus struct {
    appsv1.DeploymentStatus `json:",inline"`
}

完成自定义API结构体后,执行下面命令,自动生成pkg/apis/app/v1/zz_generated文件

$ operator-sdk generate k8s

6. 实现业务逻辑

打开源文件pkg/controller/appservice/appsrvice_controller.go,实现业务逻辑

func (r *ReconcileAppService) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
	reqLogger.Info("Reconciling AppService")

	// Fetch the AppService instance
	instance := &appv1.AppService{}
	err := r.client.Get(context.TODO(), request.NamespacedName, instance)
	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
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		return reconcile.Result{}, err
	}

	if instance.DeletionTimestamp != nil {
		return reconcile.Result{}, err
	}

	// 如果不存在,则创建关联资源
	// 如果存在,判断是否需要更新
	//   如果需要更新,则直接更新
	//   如果不需要更新,则正常返回

	deploy := &appsv1.Deployment{}
	if err := r.client.Get(context.TODO(), request.NamespacedName, deploy); err != nil && errors.IsNotFound(err) {
		// 创建关联资源
		// 1. 创建 Deploy
		deploy := resources.NewDeploy(instance)
		if err := r.client.Create(context.TODO(), deploy); 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.AppServiceSpec{}
	if err := json.Unmarshal([]byte(instance.Annotations["spec"]), oldspec); err != nil {
		return reconcile.Result{}, err
	}

	if !reflect.DeepEqual(instance.Spec, oldspec) {
		// 更新关联资源
		newDeploy := resources.NewDeploy(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

}

7. 调试

查看集群信息,确保本地有可访问的kuberbetes集群:

$ kubectl cluster-info
Kubernetes master is running at https://ydzs-master:6443
KubeDNS is running at https://ydzs-master:6443/api/v1/namespaces/kube-system/services/kube-dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

首先,在集群中安装crd对象;然后,通过kubectl get crd命令获取自定义的crd资源对象:

$ kubectl create -f deploy/crds/app_v1_appservice_crd.yaml
customresourcedefinition "appservices.app.example.com" created
$ kubectl get crd
NAME                                   AGE
appservices.app.example.com            <invalid>
......

当自定义的crd安装成功后,其具体业务逻辑还在本地,仍未部署到集群中,我们可以通过operator-sdk up local命令在本地项目中启动operator的调试:

$ operator-sdk up local                                                     
INFO[0000] Running the operator locally.                
INFO[0000] Using namespace default.                     
{"level":"info","ts":1559207203.964137,"logger":"cmd","msg":"Go Version: go1.11.4"}
{"level":"info","ts":1559207203.964192,"logger":"cmd","msg":"Go OS/Arch: darwin/amd64"}
{"level":"info","ts":1559207203.9641972,"logger":"cmd","msg":"Version of operator-sdk: v0.7.0"}
{"level":"info","ts":1559207203.965905,"logger":"leader","msg":"Trying to become the leader."}
{"level":"info","ts":1559207203.965945,"logger":"leader","msg":"Skipping leader election; not running in a cluster."}
{"level":"info","ts":1559207206.928867,"logger":"cmd","msg":"Registering Components."}
{"level":"info","ts":1559207206.929077,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"appservice-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1559207206.9292521,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"appservice-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1559207209.622659,"logger":"cmd","msg":"failed to initialize service object for metrics: OPERATOR_NAME must be set"}
{"level":"info","ts":1559207209.622693,"logger":"cmd","msg":"Starting the Cmd."}
{"level":"info","ts":1559207209.7236018,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"appservice-controller"}
{"level":"info","ts":1559207209.8284118,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"appservice-controller","worker count":1}

直接创建AppService资源对象:

$ kubectl create -f deploy/crds/app_v1_appservice_cr.yaml
appservice "nginx-app" created

当该资源对象创建成功后,可以看到operator的调试窗口会出现如下的调试信息:

......
{"level":"info","ts":1559207416.670523,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207417.004226,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207417.004331,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207418.33779,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207418.951193,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
......

最后,查看集群中是否有预期的资源对象:

$ kubectl get AppService
NAME        AGE
nginx-app   <invalid>
$ kubectl get deploy
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-app                2         2         2            2           <invalid>
$ kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        76d
nginx-app    NodePort    10.108.227.5   <none>        80:30002/TCP   <invalid>
$ kubectl get pods
NAME                                      READY     STATUS    RESTARTS   AGE
nginx-app-76b6449498-2j82j                1/1       Running   0          <invalid>
nginx-app-76b6449498-m4h58                1/1       Running   0          <invalid>

8. 部署

现在,我们的自定义资源对象测试已经通过,但如果将本地命令operator-sdk up local终止掉,就没办法处理AppService资源对象的操作了,所以我们需要将该业务逻辑的实现部署到集群中去。
执行以下命令构建operator应用打包成Docker镜像:

$ operator-sdk build robotcl/operator                         
INFO[0002] Building Docker image robotcl/operator           
Sending build context to Docker daemon  400.7MB
Step 1/7 : FROM registry.access.redhat.com/ubi7-dev-preview/ubi-minimal:7.6
......
Successfully built a8cde91be6ab
Successfully tagged robotcl/operator:latest
INFO[0053] Operator build complete. 

镜像构建成功后,直接推送到docker hub:

$ docker push robotcl/operator

镜像推送成功后,使用上面的镜像地址更新operator的资源清单:

$ sed -i 's|REPLACE_IMAGE|robotcl/operator|g' deploy/operator.yaml  
//如果你使用的是Mac系统,使用下面的命令
$ sed -i "" 's|REPLACE_IMAGE|robotcl/operator|g' deploy/operator.yaml

当operator的资源清单文件准备好后,创建对应的rbac对象:

//Setup Service Account
$ kubectl create -f deploy/service_account.yaml
//Setup RBAC
$ kubectl create -f deploy/role.yaml
$ kubectl create -f deploy/role_binding.yaml

当权限相关声明完成后,安装crd和operator:

//Setup the CRD
$ kubectl apply -f deploy/crds/app_v1_appservice_crd.yaml
$ kubectl get crd
NAME                                   CREATED AT
appservices.app.example.com            2019-05-30T17:03:32Z
......
//Deploy the Operator
$ kubectl create -f deploy/operator.yaml
deployment.apps/opdemo created
$ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
opdemo-64db96d575-9vtq6                   1/1     Running   0          2m2s

现在,crd和operator的实现都已经安装成功,最后我们再来部署AppService资源清单文件,其业务逻辑就会在上面的opdemo-64db96d575-9vtq6的pod中去处理了。

$ kubectl create -f deploy/crds/app_v1_appservice_cr.yaml
appservice.app.example.com/nginx-app created
$ kubectl get appservice
NAME        AGE
nginx-app   18s
$  kubectl get deploy
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
nginx-app                2/2     2            2           24s
opdemo                   1/1     1            1           5m51s
$  kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        76d
nginx-app    NodePort    10.106.129.82   <none>        80:30002/TCP   29s
opdemo       ClusterIP   10.100.233.51   <none>        8383/TCP       4m25s
$  kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
nginx-app-76b6449498-ffhgx                1/1     Running   0          32s
nginx-app-76b6449498-wzjq2                1/1     Running   0          32s
opdemo-64db96d575-9vtq6                   1/1     Running   0          5m59s
$ kubectl describe appservice nginx-app
Name:         nginx-app
Namespace:    default
Labels:       <none>
Annotations:  spec: {"size":2,"image":"nginx:1.7.9","resources":{},"ports":[{"protocol":"TCP","port":80,"targetPort":80,"nodePort":30002}]}
API Version:  app.example.com/v1
Kind:         AppService
Metadata:
  Creation Timestamp:  2019-05-30T17:41:28Z
  Generation:          2
  Resource Version:    19666617
  Self Link:           /apis/app.example.com/v1/namespaces/default/appservices/nginx-app
  UID:                 2756f232-8302-11e9-80ca-525400cc3c00
Spec:
  Image:  nginx:1.7.9
  Ports:
    Node Port:    30002
    Port:         80
    Protocol:     TCP
    Target Port:  80
  Resources:
  Size:  2
Events:  <none>

© 著作权归作者所有

R
粉丝 3
博文 13
码字总数 8653
作品 0
广州
私信 提问
怎样在 Kubernetes 上运行 PostgreSQL

创建统一管理的,具备灵活性的云原生生产部署来部署一个个性化的数据库即服务(DBaaS)。 通过在 Kubernetes 上运行 PostgreSQL 数据库,你能创建统一管理的,具备灵活性的云原生生产部署应用...

作者: Jonathan S. Katz
04/22
0
0
摈弃传统模式,蚂蚁金服如何实现K8S集群自动化运维?

此文章分享了蚂蚁金服进行自动化运维大规模Kubernetes集群的实践经历。 “大规模Kubernetes集群”主要体现在几十个Kubernetes集群,十万级别的Kubernetes Worker节点。蚂蚁金服使用Operator的...

陈俊
2018/08/15
0
0
etcd operator 介绍

原文:https://coreos.com/blog/introducing-the-etcd-operator.html etcd operator 介绍:简化 etcd 集群配置和管理(Introducing the etcd Operator: Simplify etcd cluster configuratio......

weixin_38975685
2018/04/23
0
0
在 Kubernetes 上运行 PostgreSQL

导读 创建统一管理的,具备灵活性的云原生生产部署来部署一个个性化的数据库即服务(DBaaS)。 通过在 Kubernetes 上运行 PostgreSQL 数据库,你能创建统一管理的,具备灵活性的云原生生产部...

问题终结者
05/04
63
0
Prometheus Operator 架构 - 每天5分钟玩转 Docker 容器技术(178)

本节讨论 Prometheus Operator 的架构。 因为 Prometheus Operator 是基于 Prometheus 的,我们需要先了解一下 Prometheus。 Prometheus 架构 Prometheus 是一个非常优秀的监控工具。准确的说...

cloudman6
2018/06/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

iOS Xcode升级包地址(感谢大神)

下载地址:DeviceSupport

_____1____
16分钟前
6
0
Qt编写自定义控件71-圆弧进度条

一、前言 现在web形式的图表框架非常流行,国产代表就是echart,本人用过几次,三个字屌爆了来形容,非常强大,而且易用性也非常棒,还是开源免费的,使用起来不要太爽,内置的各种图表和仪表...

飞扬青云
16分钟前
4
0
润乾报表与 ActiveReport JS 功能对比

简介 润乾报表是用于报表制作的大型企业级报表软件,核心特点在于开创性地提出了非线性报表数学模型,采用了革命性的多源关联分片、不规则分组、自由格间运算、行列对称等技术,使得复杂报表...

泡泡糖儿
27分钟前
5
0
【1015】LNMP架构二

【1015】LNMP架构二 三、PHP安装 PHP安装和LAMP安装PHP方法有差别,需要开启php-fpm服务 1、下载PHP7至/usr/local/src/ 切换目录:cd /usr/local/src 2、解压缩 tar -jxvf php-7.3.0.tar.gz...

飞翔的竹蜻蜓
今天
5
0
浅谈Visitor访问者模式

一、前言 什么叫访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫作访问,那么...

青衣霓裳
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部