Operator SDK 中的 Go 编程语言支持可以利用 Operator SDK 中的 Go 编程语言支持,为 Memcached 构

建基于 Go 的 Operator 示例、分布式键值存储并管理其生命周期。

github: https://github.com/jxlwqq/memcached-operator

前置条件

  • 安装 Docker Desktop,并启动内置的 Kubernetes 集群
  • 注册一个 hub.docker.com 账户,需要将本地构建好的镜像推送至公开仓库中
  • 安装 operator SDK CLI: brew install operator-sdk
  • 安装 Go: brew install go

创建项目

使用 Operator SDK CLI 创建名为 memcached-operator 的项目。

mkdir -p $HOME/projects/memcached-operator
cd $HOME/projects/memcached-operator
go env -w GOPROXY=https://goproxy.cn,direct operator-sdk init \
--domain=jxlwqq.github.io \
--repo=github.com/jxlwqq/memcached-operator \
--skip-go-version-check

创建 API 和控制器

使用 Operator SDK CLI 创建自定义资源定义(CRD)API 和控制器。

运行以下命令创建带有组 cache、版本 v1alpha1 和种类 Memcached 的 API:

operator-sdk create api \
--resource=true \
--controller=true \
--group=cache \
--version=v1alpha1 \
--kind=Memcached

定义 Memcached 自定义资源(CR)的 API。

修改 api/v1alpha1/memcached_types.go 中的 Go 类型定义,使其具有以下 spec 和 status

type MemcachedSpec struct {
// +kubebuilder:validation:Minimum=0
// Size is the size of the memcached deployment
Size int32 `json:"size"`
} type MemcachedStatus struct {
// Nodes are the names of the memcached pods
Nodes []string `json:"nodes"`
}

为资源类型更新生成的代码:

make generate

运行以下命令以生成和更新 CRD 清单:

make manifests

实现控制器

在本例中,将生成的控制器文件 controllers/memcached_controller.go 替换为以下示例实现:

/*
Copyright 2021. Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/ package controllers import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"reflect"
"time" "context" "k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log" cachev1alpha1 "github.com/jxlwqq/memcached-operator/api/v1alpha1"
) // MemcachedReconciler reconciles a Memcached object
type MemcachedReconciler struct {
client.Client
Scheme *runtime.Scheme
} //+kubebuilder:rbac:groups=cache.jxlwqq.github.io,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=cache.jxlwqq.github.io,resources=memcacheds/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=cache.jxlwqq.github.io,resources=memcacheds/finalizers,verbs=update
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch // Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Memcached object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := ctrllog.FromContext(ctx) // Fetch the Memcached instance
memcached := &cachev1alpha1.Memcached{}
err := r.Get(ctx, req.NamespacedName, memcached)
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
log.Info("Memcached resource not found. Ignoring since object must be deleted")
return ctrl.Result{}, nil
}
// Error reading the object - requeue the request.
log.Error(err, "Failed to get Memcached")
return ctrl.Result{}, err
} // Check if the deployment already exists, if not create a new one
found := &appsv1.Deployment{}
err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
// Define a new deployment
dep := r.deploymentForMemcached(memcached)
log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
err = r.Create(ctx, dep)
if err != nil {
log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
return ctrl.Result{}, err
}
// Deployment created successfully - return and requeue
return ctrl.Result{Requeue: true}, nil
} else if err != nil {
log.Error(err, "Failed to get Deployment")
return ctrl.Result{}, err
} // Ensure the deployment size is the same as the spec
size := memcached.Spec.Size
if *found.Spec.Replicas != size {
found.Spec.Replicas = &size
err = r.Update(ctx, found)
if err != nil {
log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
return ctrl.Result{}, err
}
// Ask to requeue after 1 minute in order to give enough time for the
// pods be created on the cluster side and the operand be able
// to do the next update step accurately.
return ctrl.Result{RequeueAfter: time.Minute}, nil
} // Update the Memcached status with the pod names
// List the pods for this memcached's deployment
podList := &corev1.PodList{}
listOpts := []client.ListOption{
client.InNamespace(memcached.Namespace),
client.MatchingLabels(labelsForMemcached(memcached.Name)),
}
if err = r.List(ctx, podList, listOpts...); err != nil {
log.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name)
return ctrl.Result{}, err
}
podNames := getPodNames(podList.Items) // Update status.Nodes if needed
if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
memcached.Status.Nodes = podNames
err := r.Status().Update(ctx, memcached)
if err != nil {
log.Error(err, "Failed to update Memcached status")
return ctrl.Result{}, err
}
} return ctrl.Result{}, nil
} // deploymentForMemcached returns a memcached Deployment object
func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment {
ls := labelsForMemcached(m.Name)
replicas := m.Spec.Size dep := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: m.Name,
Namespace: m.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Image: "memcached:1.4.36-alpine",
Name: "memcached",
Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
Ports: []corev1.ContainerPort{{
ContainerPort: 11211,
Name: "memcached",
}},
}},
},
},
},
}
// Set Memcached instance as the owner and controller
ctrl.SetControllerReference(m, dep, r.Scheme)
return dep
} // labelsForMemcached returns the labels for selecting the resources
// belonging to the given memcached CR name.
func labelsForMemcached(name string) map[string]string {
return map[string]string{"app": "memcached", "memcached_cr": name}
} // getPodNames returns the pod names of the array of pods passed in
func getPodNames(pods []corev1.Pod) []string {
var podNames []string
for _, pod := range pods {
podNames = append(podNames, pod.Name)
}
return podNames
} // SetupWithManager sets up the controller with the Manager.
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cachev1alpha1.Memcached{}).
Owns(&appsv1.Deployment{}).
Complete(r)
}

运行以下命令以生成和更新 CRD 清单:

make manifests

运行 Operator

捆绑 Operator,并使用 Operator Lifecycle Manager(OLM)在集群中部署。

修改 Makefile 中 IMAGE_TAG_BASE 和 IMG:

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

构建镜像:

make docker-build

将镜像推送到镜像仓库:

make docker-push

成功后访问:https://hub.docker.com/r/jxlwqq/memcached-operator

运行 make bundle 命令创建 Operator 捆绑包清单,并依次填入名称、作者等必要信息:

make bundle

构建捆绑包镜像:

make bundle-build

推送捆绑包镜像:

make bundle-push

成功后访问:https://hub.docker.com/r/jxlwqq/memcached-operator-bundle

使用 Operator Lifecycle Manager 部署 Operator:

# 切换至本地集群
kubectl config use-context docker-desktop
# 安装 olm
operator-sdk olm install
# 使用 Operator SDK 中的 OLM 集成在集群中运行 Operator
operator-sdk run bundle docker.io/jxlwqq/memcached-operator-bundle:v0.0.1

查看 Pod:

NAME                                                              READY   STATUS      RESTARTS   AGE
7bb60cfcec4a426a75d6ca01153f5e6d3061e175d035791255372bdd7ebrvmd 0/1 Completed 0 67s
docker-io-jxlwqq-memcached-operator-bundle-v0-0-1 1/1 Running 0 84s
memcached-operator-controller-manager-666cfff6c-fxhrk 2/2 Running 0 41s

创建自定义资源

编辑 config/samples/cache_v1alpha1_memcached.yaml 上的 Memcached CR 清单示例,使其包含

以下规格:

apiVersion: cache.jxlwqq.github.io/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 2

创建 CR:

kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml

查看 Pod:

NAME                                                              READY   STATUS      RESTARTS   AGE
7bb60cfcec4a426a75d6ca01153f5e6d3061e175d035791255372bdd7ebrvmd 0/1 Completed 0 4m14s
docker-io-jxlwqq-memcached-operator-bundle-v0-0-1 1/1 Running 0 4m31s
memcached-operator-controller-manager-666cfff6c-fxhrk 2/2 Running 0 3m48s
memcached-sample-6c765df685-d8ppn 1/1 Running 0 29s
memcached-sample-6c765df685-dtw9l 1/1 Running 0 29s

更新 CR:

kubectl patch memcached memcached-sample -p '{"spec":{"size": 3}}' --type=merge

查看 Pod:

NAME                                                              READY   STATUS      RESTARTS   AGE
7bb60cfcec4a426a75d6ca01153f5e6d3061e175d035791255372bdd7ebrvmd 0/1 Completed 0 6m52s
docker-io-jxlwqq-memcached-operator-bundle-v0-0-1 1/1 Running 0 7m9s
memcached-operator-controller-manager-666cfff6c-fxhrk 2/2 Running 0 6m26s
memcached-sample-6c765df685-d8ppn 1/1 Running 0 3m7s
memcached-sample-6c765df685-dtw9l 1/1 Running 0 3m7s
memcached-sample-6c765df685-n7ctq 1/1 Running 0 6s

做好清理

operator-sdk cleanup memcached-operator
operator-sdk olm uninstall

为 Memcached 构建基于 Go 的 Operator 示例的更多相关文章

  1. SSM框架整合环境构建——基于Spring4和Mybatis3

    目录 环境 配置说明 所需jar包 配置db.properties 配置log4j.properties 配置spring.xml 配置mybatis-spring.xml 配置springmvc.x ...

  2. [WCF安全3]使用wsHttpBinding构建基于SSL与UserName授权的WCF应用程序

    上一篇文章中介绍了如何使用wsHttpBinding构建UserName授权的WCF应用程序,本文将为您介绍如何使用wsHttpBinding构建基于SSL的UserName安全授权的WCF应用程序. ...

  3. 【转】nginx+memcached构建页面缓存应用

    如需转载请注明出处: http://www.ttlsa.com/html/2418.html nginx的memcached_module模块可以直接从memcached服务器中读取内容后输出,后续的 ...

  4. css013 构建基于浮动的布局

    css013 构建基于浮动的布局 基于浮动的布局时利用float属性是网页上的元素并排,并创建列 float有三个值:left .right .none 1.假设要把一张图片浮动到网页的左侧 .flo ...

  5. 构建基于WCF Restful Service的服务

    前言 传统的Asmx服务,由于遵循SOAP协议,所以返回内容以xml方式组织.并且客户端需要添加服务端引用才能使用(虽然看到网络上已经提供了这方面的Dynamic Proxy,但是没有这种方式简便), ...

  6. memcached构建集群分析之一

    memcached本身是不支持集群的,集群所关注的容灾.容错.宕机恢复机制统统都没有,实战中需要自己实现容灾机制. memcached集群相比memcached的优势: 巨量数据分布到集群的多台应用主 ...

  7. maven-bundle-plugin插件, 用maven构建基于osgi的web应用

    maven-bundle-plugin 2.4.0以下版本导出META-INF中的内容到MANIFEST.MF中 今天终于把maven-bundle-plugin不能导出META-INF中的内容到Ex ...

  8. 最简单的基于Flash的流媒体示例:网页播放器(HTTP,RTMP,HLS)

    http://blog.csdn.net/leixiaohua1020/article/details/43936415 ======================================= ...

  9. 正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——3 计算4个函数

    整个引擎代码在github上,地址为:https://github.com/sun2043430/RegularExpression_Engine.git nullable, firstpos, la ...

随机推荐

  1. Spring Cloud分区发布实践(5)--定制ServiceInstanceListSupplier

    现在我们简单地来定制二个 ServiceInstanceListSupplier, 都是zone-preference的变种. 为了方便, 我重新调整了一下项目的结构, 把一些公用的类移动到hello ...

  2. Salesforce Integration 概览(三) Remote Process Invocation—Fire and Forget(远程进程调用-发后即弃)

    本篇参考:https://resources.docs.salesforce.com/sfdc/pdf/integration_patterns_and_practices.pdf 我们在上一篇讲了远 ...

  3. .NET Core/.NET5/.NET6 开源项目汇总13:模板引擎

    系列目录     [已更新最新开发文章,点击查看详细] 开源项目是众多组织与个人分享的组件或项目,作者付出的心血我们是无法体会的,所以首先大家要心存感激.尊重.请严格遵守每个项目的开源协议后再使用.尊 ...

  4. java环境的配置——实现win10下双击直接运行jar文件

    java环境的配置--实现win10下双击直接运行jar文件 在渗透测试的过程中很多工具的安装和使用需要java环境,下面我来介绍一下java环境配置的超详细步骤(包含怎样实现win10下双击直接运行 ...

  5. NodeJS 中的事件循环,读了这篇就全懂了

    事件循环是 NodeJS 处理非阻塞 I/O 操作的和核心机制.NodeJS 的事件循环脱胎于 libuv 的事件循环,因此,要搞清楚 NodeJS 的事件循环,还需要先了解 libuv 的事件循环是 ...

  6. 漏洞复现|Dubbo反序列化漏洞CVE-2019-17564

    01漏洞描述 - Apache Dubbo支持多种协议,官方推荐使用Dubbo协议.Apache Dubbo HTTP协议中的一个反序列化漏洞(CVE-2019-17564),该漏洞的主要原因在于当A ...

  7. 零基础学Java之Java学习笔记(四):运算符

    ​ 算术运算符: 算术运算符是对数值类型的变量进行运算的,在 Java 程序中使用的非常多. 运算符 说明 例子 结果 + 加法-相加运算符两侧的值 9+9 18 - 减法-左操作数减去右操作数 10 ...

  8. 剑指 Offer 61. 扑克牌中的顺子

    剑指 Offer 61. 扑克牌中的顺子 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的.2-10为数字本身,A为1,J为11,Q为12,K为13,而大.小王为 0 ,可以看成任意 ...

  9. ConcurrentDictionary 并发字典

    线程安全 Dictionary 本身是不支持线程安全的 线程的字典--ConcurrentDictionary 线程安全实现 写安全 以往线程安全我们通过Lock实现 比如通过lock一个全局的obj ...

  10. 深入浅出Mybatis系列(八)---objectFactory、plugins、mappers

    1.objectFactory是干什么的? 需要配置吗? MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成.默认的对象工厂需要做的仅仅是实例化 ...