Kubernetes编程/Operator专题精讲——什么是 Kubernetes 编程?
什么是 Kubernetes 编程?
什么是 Kubernetes 编程?本文所探讨的 Kubernetes 编程,特指 开发原生 Kubernetes 应用 ——这类应用并非基于 Kubernetes 部署的普通服务,而是直接与 Kubernetes API 服务器交互,通过查询、更新集群资源状态实现业务逻辑,是真正贴合云原生理念的开发范式。
不同于 Controller 与 Operator 的运维视角,也不聚焦集群操作层面的细节,本文将从开发与测试维度,拆解原生 Kubernetes 应用的核心逻辑、技术组件,带你掌握纯正云原生应用的构建思路。
一、举个示例:原生 Kubernetes 应用的工作模式
我们以一个名为 cnat 的自定义应用为例,它的核心能力是按指定时间执行命令,类似 Kubernetes 原生 CronJob 的简化版,但通过自定义资源(CR)实现更灵活的调度逻辑。
若需让 cnat 在 2023 年 5 月 21 日凌晨 2 点执行 echo "Hello, Shanghai!" 命令,只需定义如下自定义资源(CR):
apiVersion: cnat.programming-kubernetes.info/v1alpha1
kind: At
metadata:
name: cnrex
spec:
schedule: "2023-05-21T02:00:00Z"
containers:
- name: shell
image: centos:7
command:
- "bin/bash"
- "-c"
- echo "Hello, Shanghai!"
这个示例的核心价值的在于:通过自定义资源定义业务意图,由原生应用监听该资源并触发逻辑。与普通应用不同,cnat 无需内置定时任务调度器,而是 调度时间、执行命令 封装为 Kubernetes 资源,通过 API 服务器感知资源变化并执行对应操作,完全融入 Kubernetes 生态。
二、Kubernetes 的可扩展特性:自定义与扩展
Kubernetes 的强大之处在于其天然的可扩展性,它并非封闭系统,而是提供了丰富的扩展点供开发者定制集群能力。常见的扩展方式可分为两类:
-
- 配置级扩展:通过修改控制平面组件(如 kube-apiserver、kube-controller-manager)的配置文件、启动参数,调整集群核心行为,无需开发代码,适合简单场景的定制。
- 开发级扩展:通过 Kubernetes 预定义的扩展点(如自定义资源 CRD、API 聚合层、准入控制器等),开发自定义组件与原生资源交互,本文聚焦的原生应用开发即属于此类。
原生 Kubernetes 应用的开发,本质是借助这些扩展点,将业务逻辑与 Kubernetes 的资源管理机制深度绑定,让应用成为集群的一部分,而非独立于集群之外的服务。
三、控制器与 Operator:原生应用的核心执行载体
在 Kubernetes 术语中,控制器 是实现 状态闭环 的核心组件,也是原生应用的主要执行载体。
它通过一个持续运行的控制循环,观测集群资源的共享状态,对比当前状态与期望状态的差异,执行必要操作以缩小差距。
控制器可操作 Kubernetes 核心资源(如 Deployment、Service),也可监听并操作用户自定义资源(CR);而 Operator 是一种特殊的控制器,它在控制循环的基础上,嵌入了特定应用的运维逻辑(如部署、升级、备份、故障恢复),专为复杂有状态应用的生命周期管理设计。
无论控制器还是 Operator,其进程通常以 Pod 形式运行在集群中,通过 ServiceAccount 获得与 API 服务器交互的权限,确保操作的安全性与可控性。
3.1、控制循环:原生应用的核心逻辑
控制循环是控制器的灵魂,所有原生应用的业务逻辑都围绕这个循环展开,其标准流程如下:
- 状态读取:通过事件驱动模式(基于 Kubernetes Watch 机制),持续监听目标资源的状态变化,避免轮询带来的性能损耗。
- 状态调整:根据资源的期望状态,修改集群内对象(如创建 Pod、更新 Service)或集群外部系统的状态(如调用第三方 API)。
- 状态持久化:将调整后的状态通过 API 服务器写入 etcd(Kubernetes 集群的分布式存储,用于持久化所有资源状态)。
- 循环迭代:返回第一步,持续监听资源状态变化,确保系统始终向期望状态收敛。
一个标准的控制器通常包含两个核心数据结构,以保障控制循环的稳定运行:
-
-
- Informer:负责高效、可持续地观察资源的目标状态,内置缓存机制减少对 API 服务器的请求压力,同时支持断点续传,确保状态监听不丢失。
- 工作队列:事件处理器将资源状态变化事件放入队列,控制器从队列中取出事件逐一处理,支持失败重试机制,避免因临时故障导致逻辑中断。
-
3.2、事件机制:控制器的感知神经
Kubernetes 控制器通过 API 服务器监听对象的变化事件(添加、更新、删除),一旦事件触发,控制器便执行对应的业务逻辑。这些事件通过 Watch 机制以流式方式从 API 服务器发送至 Informer,驱动控制循环运转。
我们以 通过 Deployment 创建 Pod 为例,拆解多控制器协同工作的事件流转过程,理解原生应用的底层交互逻辑:
- Deployment 控制器(位于 kube-controller-manager 中)通过 Deployment Informer 发现新创建的 Deployment,执行逻辑并创建对应的 ReplicaSet 对象。
- ReplicaSet 控制器(同样位于 kube-controller-manager 中)通过 ReplicaSet Informer 感知新 ReplicaSet,执行逻辑并创建 Pod 对象。
- 调度器(kube-scheduler,本质是特殊控制器)通过 Pod Informer 发现 新 Pod,且该 Pod 的 `spec.nodeName` 字段为空(未指定运行节点),将其加入调度队列。
- 各节点上的 kubelet(节点级控制器)通过自身的 Pod Informer 发现 新 Pod,但因 `spec.nodeName` 与自身节点名不匹配,忽略该 Pod 并等待下一次事件。
- 调度器从队列中取出 Pod,根据节点资源、亲和性、污点容忍等策略,将其调度至合适节点,更新 Pod 的 `spec.nodeName` 字段并写入 API 服务器。
- Pod 更新事件触发 kubelet 再次被唤醒,对比 `spec.nodeName` 与自身节点名一致后,启动 Pod 所需容器,并将容器启动状态写入 Pod 的 `Status` 字段,同步至 API 服务器。
- ReplicaSet 控制器收到 Pod 状态变化事件,确认 Pod 正常运行,无额外工作需执行。
- Pod 运行结束后,kubelet 感知事件,更新 Pod 状态为“已终止”并同步至 API 服务器。
- ReplicaSet 控制器发现终止的 Pod,基于期望副本数逻辑,删除该终止 Pod 并创建新 Pod,维持副本数稳定。
需注意:多个控制器之间完全通过 API 服务器上的对象状态变化实现 间接通信,无需直接交互。这些 Watch 事件通过 HTTP 流传输,对用户透明,甚至不会被 API 服务器审计日志记录(仅记录对象更新操作),控制器通常通过输出日志记录自身执行的动作,便于问题排查。
此外,Kubernetes 中还有顶层 Event对象,用于记录资源的关键操作(如创建、更新、故障),其生命周期为 1 小时,超时后自动从 etcd 中清理,避免存储冗余。
四、API 服务器:原生应用的交互中枢
Kubernetes 集群由控制平面节点(Master Node)和工作节点(Worker Node)组成,其中 API 服务器是控制平面的核心,也是原生应用与集群交互的唯一入口——所有资源的查询、创建、更新、删除操作,都必须通过 API 服务器完成。
控制平面节点除 API 服务器 外,还包含 控制器管理器(kube-controller-manager)、调度器(kube-scheduler),三者协同支撑集群的核心能力,而 API 服务器 承担着 网关 与 数据中转 的核心职责。
4.1、API 服务器的核心职责与功能
API 服务器的核心职责有两项:
-
-
- 一是 提供标准化的 Kubernetes API,供所有组件与客户端交互;
- 二是 为其他集群组件(如 kubelet、kube-proxy)提供代理服务,确保组件间通信的安全性。
-
其提供的 API 具备完整的资源操作能力,可分为两类:
-
-
- 状态读取:获取单个资源详情、列出某类资源的所有实例、监听资源变更信息(Watch)。
- 状态操作:创建、更新、删除资源,支持完整的 CURD 语义。
-
所有资源状态最终都会持久化到 etcd 中,API 服务器作为 etcd 的唯一访问入口,确保资源操作的原子性与一致性,避免并发操作导致的数据错乱。
4.2、API 服务器的 HTTP 接口与交互规范
从客户端视角,API 服务器提供 RESTful HTTP API,支持 JSON 和 Protocol Buffers(Protobuf)两种数据格式。其中,
-
-
- Protobuf 因序列化效率高、体积小,主要用于集群内部组件间的通信;
- JSON 则更适合外部客户端(如 kubectl、自定义应用)交互。
-
API 服务器通过不同的 HTTP 方法(动词)区分对资源的操作类型,遵循 REST 设计规范:
-
-
- GET:读取资源,可获取单个资源(如 `/api/v1/namespaces/default/pods/nginx`)或列出一类资源(如 `/api/v1/namespaces/default/pods`)。
- POST:创建资源,向对应资源的集合路径提交 JSON/Protobuf 格式的资源定义,如创建 Deployment 需向 `/apis/apps/v1/namespaces/default/deployments` 发送 POST 请求。
- PUT:全量更新资源,需提交完整的资源定义,若遗漏字段会导致该字段被覆盖为默认值,慎用。
- PATCH:部分更新资源,仅提交需修改的字段,支持 JSON Patch、Merge Patch 等格式,是日常更新资源的首选方式。
- DELETE:删除资源,请求对应资源的唯一路径,操作不可逆,需提前做好数据备份。
-
更多 API 细节可参考官方文档:https://kubernetes.io/docs/reference/#api-overview
4.3 核心 API 术语:理解资源与类型的映射关系
开发原生 Kubernetes 应用前,需理清几个核心 API 术语,避免混淆资源与类型的概念,确保与 API 服务器的交互符合规范。
4.3.1、Kind:资源类型的标识
Kind 是字符串类型,用于表示对象对应的 REST 资源类型,服务器可从客户端请求的端点推断 Kind,且该字段不可更新,命名需采用驼峰式(CamelCase)。Kind 主要分为三类:
-
-
- Object:表示集群中可持久化的实体对象,如 Pod、Endpoint、Deployment,这类对象需具备唯一名称,大部分属于某个命名空间(Namespace)。
- List:某类实体对象的集合,如 PodList、NodeList,包含少量公共元信息(如资源总数、继续访问的游标),执行 `kubectl get pods` 命令时返回的就是 PodList 类型。
- 特殊用途 Kind:用于特定动作或非持久化实体,如 `/binding`(绑定 Pod 到节点)、`/scale`(调整副本数)、Status(返回错误状态)等。
-
在 Kubernetes 中,每种 Kind 都对应一种 Go 语言类型,因此 Kind 名称采用单数形式且首字母大写,与 Go 语言类型命名规范保持一致。
4.3.2、API 组(API Group):资源的逻辑分类
API 组将逻辑上相关的 Kind 归类,便于管理和版本控制。例如,Job、CronJob 等批处理相关资源属于 batch 组,Deployment、ReplicaSet 等应用部署相关资源属于 apps 组。
API 组通过 HTTP 路径体现,如 /apis/batch/v1、/apis/apps/v1,而核心资源(如 Pod、Service)属于核心组(Core Group),路径为 /api/v1,无需显式指定组名。
4.3.3、版本(Version):API 的演进与兼容
每个 API 组支持多个版本共存,以适配不同阶段的功能需求与兼容性保障。常见版本标识有
-
-
-
- v1alpha1(alpha 版,功能不稳定,仅用于测试)
- v1beta1(beta 版,功能基本稳定,可能调整)
- v1(稳定版,功能成熟,兼容后续版本)
-
-
API 服务器具备跨版本转换能力:在某个版本(如 v1beta1)创建的对象,可在其他支持的版本(如 v1)中读取和操作,服务器会自动完成对象结构的无损转换,对用户透明。
4.3.4、版本(Version):API 的演进与兼容
资源通常是 Kind 的小写复数形式(如 Pod 对应 pods、Deployment 对应 deployments),用于构成 API 服务器的 HTTP 端点路径,提供对该类资源的 CURD 操作。
资源路径分为两种场景:
-
-
- 一是 集合路径(如 /api/v1/pods),用于列出或创建资源;
- 二是 实例路径(如 /api/v1/pods/nginx),用于操作单个资源实例。
-
需注意,资源与 Kind 的核心区别在于:
-
-
-
- 资源对应具体的 HTTP 路径,是 API 交互的入口。
- Kind 是资源对应的对象类型,是数据序列化与持久化的实体。
-
-
4.3.5、GVR 与 GVK:资源与类型的唯一标识
资源始终隶属于某个 API 组的某个版本,构成 GroupVersionResource(GVR),一个 GVR 唯一确定一个 HTTP 路径。例如:
-
-
-
- 命名空间级资源:batch/v1/namespaces/default/jobs(GVR)对应 jobs 资源,路径包含命名空间。
- 集群级资源(如 Node、Namespace):/api/v1/nodes(GVR),路径不包含命名空间,因这类资源不属于任何单个命名空间。
-
-
类似地,Kind 也隶属于某个 API 组的某个版本,构成 `GroupVersionKind`(GVK)。GVR 与 GVK 的映射关系称为“REST 映射”,API 服务器通过该映射,将 HTTP 请求(对应 GVR)转换为对应的对象类型(对应 GVK),实现资源操作与数据处理的衔接。
五、Kubernetes API 路径结构概览
Kubernetes API 路径采用层级结构,核心分为核心组(/api/v1)和扩展组(/apis/<group>/<version>),以下是常见 API 路径的简化结构:
# kubectl api-resources --namespaced=true -o name | sort adams.appdynamics.com alertmanagerconfigs.monitoring.coreos.com alertmanagers.monitoring.coreos.com apisixconsumers.apisix.apache.org apisixglobalrules.apisix.apache.org apisixpluginconfigs.apisix.apache.org apisixroutes.apisix.apache.org apisixtlses.apisix.apache.org apisixupstreams.apisix.apache.org apmservers.apm.k8s.elastic.co backendtlspolicies.gateway.networking.k8s.io backendtrafficpolicies.apisix.apache.org beats.beat.k8s.elastic.co bindings blockdeviceclaims.openebs.io blockdevices.openebs.io clusteragents.appdynamics.com clusterauthtokens.cluster.cattle.io clustercollectors.appdynamics.com clusteruserattributes.cluster.cattle.io configmaps consumers.apisix.apache.org controllerrevisions.apps cronjobs.batch cstorbackups.cstor.openebs.io cstorcompletedbackups.cstor.openebs.io cstorpoolclusters.cstor.openebs.io cstorpoolinstances.cstor.openebs.io cstorrestores.cstor.openebs.io cstorvolumeattachments.cstor.openebs.io cstorvolumeconfigs.cstor.openebs.io cstorvolumepolicies.cstor.openebs.io cstorvolumereplicas.cstor.openebs.io cstorvolumes.cstor.openebs.io daemonsets.apps deployments.apps elasticsearches.elasticsearch.k8s.elastic.co endpoints endpointslices.discovery.k8s.io enterprisesearches.enterprisesearch.k8s.elastic.co eventbus.argoproj.io events events.events.k8s.io eventsources.argoproj.io gatewayproxies.apisix.apache.org gateways.gateway.networking.k8s.io grafanaagents.monitoring.grafana.com grpcroutes.gateway.networking.k8s.io helmreleases.helm.fluxcd.io horizontalpodautoscalers.autoscaling httproutepolicies.apisix.apache.org httproutes.gateway.networking.k8s.io infravizs.appdynamics.com ingresses.extensions ingresses.networking.k8s.io integrations.monitoring.grafana.com jivavolumepolicies.openebs.io jivavolumes.openebs.io jobs.batch kafkabridges.kafka.strimzi.io kafkaconnectors.kafka.strimzi.io kafkaconnects2is.kafka.strimzi.io kafkaconnects.kafka.strimzi.io kafkamirrormaker2s.kafka.strimzi.io kafkamirrormakers.kafka.strimzi.io kafkarebalances.kafka.strimzi.io kafkas.kafka.strimzi.io kafkatopics.kafka.strimzi.io kafkausers.kafka.strimzi.io kibanas.kibana.k8s.elastic.co leases.coordination.k8s.io limitranges localsubjectaccessreviews.authorization.k8s.io localvolumediscoveries.local.storage.openshift.io localvolumediscoveryresults.local.storage.openshift.io localvolumesets.local.storage.openshift.io localvolumes.local.storage.openshift.io logsinstances.monitoring.grafana.com lvmnodes.local.openebs.io lvmsnapshots.local.openebs.io lvmvolumes.local.openebs.io metricsinstances.monitoring.grafana.com migrationtasks.openebs.io networkpolicies.crd.projectcalico.org networkpolicies.networking.k8s.io networksets.crd.projectcalico.org persistentvolumeclaims pluginconfigs.apisix.apache.org poddisruptionbudgets.policy podlogs.monitoring.grafana.com podmonitors.monitoring.coreos.com pods pods.metrics.k8s.io podtemplates probes.monitoring.coreos.com prometheuses.monitoring.coreos.com prometheusrules.monitoring.coreos.com referencegrants.gateway.networking.k8s.io replicasets.apps replicationcontrollers resourcequotas rolebindings.rbac.authorization.k8s.io roles.rbac.authorization.k8s.io secrets sensors.argoproj.io serviceaccounts servicemonitors.monitoring.coreos.com services statefulsets.apps tcproutes.gateway.networking.k8s.io thanosrulers.monitoring.coreos.com tlsroutes.gateway.networking.k8s.io udproutes.gateway.networking.k8s.io upgradetasks.openebs.io volumesnapshots.snapshot.storage.k8s.io xbackendtrafficpolicies.gateway.networking.x-k8s.io xlistenersets.gateway.networking.x-k8s.io zfsbackups.zfs.openebs.io zfsnodes.zfs.openebs.io zfsrestores.zfs.openebs.io zfssnapshots.zfs.openebs.io zfsvolumes.zfs.openebs.io # kubectl get --raw="/" | jq -r 'keys[]' actions data links pagination resourceType sort type
六、原生 Kubernetes 应用开发实操
理解核心概念后,我们从实操层面拆解原生应用的开发流程,以 Go 语言为例(Kubernetes 核心采用 Go 开发,官方 SDK 对 Go 支持最完善),实现一个简单的自定义控制器。
6.1、开发准备:环境与依赖
- 环境配置:安装 Go 1.19+(需与 Kubernetes 版本兼容)、kubectl(集群命令行工具)、minikube(本地测试集群)或远程 Kubernetes 集群。
- 依赖引入:核心依赖为 Kubernetes 官方 SDK(client-go),用于与 API 服务器交互、实现 Informer 与工作队列,在 go.mod 中引入:
require ( k8s.io/client-go v0.27.3 k8s.io/apimachinery v0.27.3 k8s.io/api v0.27.3 ) - 权限配置:创建 ServiceAccount、ClusterRole、ClusterRoleBinding,为控制器授予监听自定义资源、操作 Pod 的权限。
6.2、开发准备:环境与依赖
6.2.1、步骤 1:定义自定义资源(CRD)
首先通过 CRD 定义自定义资源类型(如前面的 At 资源),告知 Kubernetes 集群识别该资源:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: ats.cnat.programming-kubernetes.info
spec:
group: cnat.programming-kubernetes.info
names:
kind: At
plural: ats
singular: at
shortNames:
- at
scope: Namespaced # 命名空间级资源
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
schedule:
type: string
format: date-time # 时间格式校验
containers:
type: array
items:
type: object
properties:
name:
type: string
image:
type: string
command:
type: array
items:
type: string
应用 CRD 后,集群即可识别 At 资源,可通过 kubectl get ats 命令查看资源实例。
6.2.2、步骤 2:生成客户端代码
通过 code-generator 工具,基于 CRD 生成客户端代码(包含 Informer、Lister、ClientSet),简化与自定义资源的交互:
1 # 克隆 code-generator 工具 2 git clone https://github.com/kubernetes/code-generator.git 3 # 生成客户端代码 4 ./code-generator/generate-groups.sh all \ 5 your-project-path/client \ 6 your-project-path/api \ 7 cnat.programming-kubernetes.info:v1alpha1 \ 8 --go-header-file ./hack/boilerplate.go.txt
| 部分 | 具体内容 | 含义 |
|---|---|---|
./code-generator/generate-groups.sh |
脚本路径 | 执行 code-generator 仓库下的 generate-groups.sh 脚本,这是生成 CRD 客户端代码的核心脚本。 |
all |
第一个参数 | 指定要生成的代码类型为 all(全部),包括:
client:用于调用 CRD API 的客户端代码(Create/Update/Delete/Get 等操作);
lister:用于本地缓存查询 CRD 实例的代码(提高查询效率);
informer:用于监听 CRD 实例变化的代码(事件驱动)。
client 只生成客户端。 |
your-project-path/client |
第二个参数 | 指定生成的代码要输出到的目录(客户端代码的目标路径)。
your-project-path 需要替换成你实际的项目路径(比如 ~/my-k8s-project);
/client 是固定子目录,生成的客户端代码会放在这个目录下,结构为 client/cnat/programming-kubernetes.info/v1alpha1/...。 |
your-project-path/api |
第三个参数 | 指定你定义的 CRD API 结构体的目录(源码路径)。
api/cnat/programming-kubernetes.info/v1alpha1/xxx_types.go);
|
cnat.programming-kubernetes.info:v1alpha1 |
第四个参数 | 指定CRD 的 API 组和版本,格式是 API组:版本:
cnat.programming-kubernetes.info:自定义的 API 组名(比如官方的是 apps/v1,这里是你自己定义的);
v1alpha1:API 的版本(alpha 是测试版,后续可升级为 v1beta1、v1)。 |
--go-header-file ./hack/boilerplate.go.txt |
可选参数 | 指定代码头部的版权 / 注释模板文件:
boilerplate.go.txt 是 Kubernetes 生态的惯例文件,放在项目的 hack 目录下;
|
6.2.3、步骤 3:实现控制循环逻辑
package main
import (
"context"
"time"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
client "your-project-path/client"
api "your-project-path/api/v1alpha1"
)
type Controller struct {
client *client.Clientset
informer cache.SharedIndexInformer
workqueue workqueue.RateLimitingInterface
}
// 初始化控制器
func NewController(client *client.Clientset) *Controller {
informer := client.CnatV1alpha1().Ats("default").Informer(context.TODO())
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
// 注册事件处理器,将事件加入工作队列
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
queue.Add(obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
queue.Add(newObj)
},
})
return &Controller{
client: client,
informer: informer,
workqueue: queue,
}
}
// 处理工作队列中的事件
func (c *Controller) processItem(obj interface{}) error {
at := obj.(*api.At)
// 对比当前状态与期望状态(检查是否到调度时间)
now := time.Now().UTC()
scheduleTime, _ := time.Parse(time.RFC3339, at.Spec.Schedule)
if now.After(scheduleTime) {
// 执行命令:创建 Pod 运行指定容器
if err := c.createPod(at); err != nil {
return err
}
// 更新 At 资源状态,标记已执行
at.Status.Completed = true
_, err := c.client.CnatV1alpha1().Ats(at.Namespace).UpdateStatus(context.TODO(), at, metav1.UpdateOptions{})
return err
}
return nil
}
// 启动控制循环
func (c *Controller) Run(stopCh <-chan struct{}) {
defer c.workqueue.ShutDown()
// 启动 Informer
go c.informer.Run(stopCh)
// 等待 Informer 缓存同步完成
if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
return
}
// 持续从工作队列取事件处理
wait.Until(c.runWorker, time.Second, stopCh)
}
func (c *Controller) runWorker() {
for c.processNextItem() {
}
}
func (c *Controller) processNextItem() bool {
obj, shutdown := c.workqueue.Get()
if shutdown {
return false
}
defer c.workqueue.Done(obj)
if err := c.processItem(obj); err != nil {
// 失败重试
c.workqueue.AddRateLimited(obj)
return true
}
// 处理成功,取消限流
c.workqueue.Forget(obj)
return true
}
func main() {
// 加载 kubeconfig,初始化客户端
config, _ := rest.InClusterConfig() // 集群内运行使用
// config, _ := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) // 本地测试使用
clientset, _ := client.NewForConfig(config)
controller := NewController(clientset)
stopCh := make(chan struct{})
defer close(stopCh)
controller.Run(stopCh)
}
6.2.4、步骤 4:打包部署控制器
apiVersion: apps/v1
kind: Deployment
metadata:
name: cnat-controller
spec:
replicas: 1
selector:
matchLabels:
app: cnat-controller
template:
metadata:
labels:
app: cnat-controller
spec:
serviceAccountName: cnat-controller-sa # 绑定权限账户
containers:
- name: cnat-controller
image: your-registry/cnat-controller:v1.0.0
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi

浙公网安备 33010602011771号