同步操作将从 infraboard/go-course 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
完整的文档请参考kubebuilder官方文档
之前讲过使用Traefik + etcd的 内外网关打通的方案, 我们的服务在启动的时候可以通过框架直接注册到etcd中去, 那如果是其他框架或者语言, 比如 php, python, java 也想使用 Traefik+etcd这套方案这么办?
直接能想到的就是 每个框架和语言 都实现一个套 注册到etcd的功能, 实现起来也并不难, 但是这就面临 对别人服务的侵入性, 别人不一定愿意接受
现在的微服务开发,基本都基于容器部署, 而容器管理平台k8s也是当今 容器编排工具的标准, k8s本身是知道当前系统中有哪些service服务的, 那能否通过感知service的变化,来把service信息动态写入的etcd喃?
那我们的解决方案就很简单了:
k8s service <---watch---- service operater ------> etcd
go version v1.20.0+ docker version 17.03+. kubectl version v1.11.3+. Access to a Kubernetes v1.11.3+ cluster.
直接在云商哪儿创建一个托管集群
我们下载一个kubectl命令到本地:
curl -LO "https://dl.k8s.io/release/v1.23.0/bin/windows/amd64/kubectl"
把kubectl copy到你的path路径下去就可以了
然后简单验证下:
$ kubectl get ns
NAME STATUS AGE
default Active 111d
kube-node-lease Active 111d
kube-public Active 111d
kube-system Active 111d
mpaas Active 30d
curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
chmod +x kubebuilder && mv kubebuilder /usr/local/bin/
关于kubebuilder的使用说明一定要阅读:
$ kubebuilder -h
CLI tool for building Kubernetes extensions and tools.
Usage:
kubebuilder [flags]
kubebuilder [command]
Examples:
The first step is to initialize your project:
kubebuilder init [--plugins=<PLUGIN KEYS> [--project-version=<PROJECT VERSION>]]
<PLUGIN KEYS> is a comma-separated list of plugin keys from the following table
and <PROJECT VERSION> a supported project version for these plugins.
Plugin keys | Supported project versions
-----------------------------------------+----------------------------
base.go.kubebuilder.io/v3 | 3
base.go.kubebuilder.io/v4 | 3
declarative.go.kubebuilder.io/v1 | 2, 3
deploy-image.go.kubebuilder.io/v1-alpha | 3
go.kubebuilder.io/v2 | 2, 3
go.kubebuilder.io/v3 | 3
go.kubebuilder.io/v4 | 3
grafana.kubebuilder.io/v1-alpha | 3
kustomize.common.kubebuilder.io/v1 | 3
kustomize.common.kubebuilder.io/v2 | 3
For more specific help for the init command of a certain plugins and project version
configuration please run:
kubebuilder init --help --plugins=<PLUGIN KEYS> [--project-version=<PROJECT VERSION>]
Default plugin keys: "go.kubebuilder.io/v4"
Default project version: "3"
Available Commands:
alpha Alpha-stage subcommands
completion Load completions for the specified shell
create Scaffold a Kubernetes API or webhook
edit Update the project configuration
help Help about any command
init Initialize a new project
version Print the kubebuilder version
Flags:
-h, --help help for kubebuilder
--plugins strings plugin keys to be used for this subcommand execution
--project-version string project version (default "3")
Use "kubebuilder [command] --help" for more information about a command.
我们需要使用kubebuilder来为我们生成Operator开发的框架代码
kubebuilder 提供了一个 init命令用来初始化一个新的Operator工程目录,具体用法如下:
$ kubebuilder init -h
Initialize a new project including the following files:
- a "go.mod" with project dependencies
- a "PROJECT" file that stores project configuration
- a "Makefile" with several useful make targets for the project
- several YAML files for project deployment under the "config" directory
- a "cmd/main.go" file that creates the manager that will run the project controllers
Usage:
kubebuilder init [flags]
Examples:
# Initialize a new project with your domain and name in copyright
kubebuilder init --plugins go/v4 --domain example.org --owner "Your name"
# Initialize a new project defining a specific project version
kubebuilder init --plugins go/v4 --project-version 3
Flags:
--domain string domain for groups (default "my.domain")
--fetch-deps ensure dependencies are downloaded (default true)
-h, --help help for init
--license string license to use to boilerplate, may be one of 'apache2', 'none' (default "apache2")
--owner string owner to add to the copyright
--project-name string name of this project
--project-version string project version (default "3")
--repo string name to use for go module (e.g., github.com/user/repo), defaults to the go package of the current working directory.
--skip-go-version-check if specified, skip checking the Go version
Global Flags:
--plugins strings plugin keys to be used for this subcommand execution
关键参数说明:
然后开始初始化项目:
$ kubebuilder init --domain magedu.com --repo gitee.com/go-course/go12/skills/operator
INFO Writing kustomize manifests for you to edit...
INFO Writing scaffold for you to edit...
INFO Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.16.3
go: downloading sigs.k8s.io/controller-runtime v0.16.3
go: downloading k8s.io/apimachinery v0.28.3
go: downloading k8s.io/client-go v0.28.3
go: downloading k8s.io/api v0.28.3
go: downloading k8s.io/apiextensions-apiserver v0.28.3
go: downloading k8s.io/component-base v0.28.3
INFO Update dependencies:
$ go mod tidy
go: downloading github.com/onsi/gomega v1.27.10
go: downloading github.com/onsi/ginkgo/v2 v2.11.0
go: downloading go.uber.org/zap v1.25.0
go: downloading golang.org/x/tools v0.9.3
go: downloading github.com/benbjohnson/clock v1.3.0
Next: define a resource with:
$ kubebuilder create api
通过脚手架为我们提供的create api来创建 CRD 相关的Resource 和控制器:
$ kubebuilder create api -h
Scaffold a Kubernetes API by writing a Resource definition and/or a Controller.
If information about whether the resource and controller should be scaffolded
was not explicitly provided, it will prompt the user if they should be.
After the scaffold is written, the dependencies will be updated and
make generate will be run.
Usage:
kubebuilder create api [flags]
Examples:
# Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
kubebuilder create api --group ship --version v1beta1 --kind Frigate
# Edit the API Scheme
nano api/v1beta1/frigate_types.go
# Edit the Controller
nano internal/controller/frigate/frigate_controller.go
# Edit the Controller Test
nano internal/controller/frigate/frigate_controller_test.go
# Generate the manifests
make manifests
# Install CRDs into the Kubernetes cluster using kubectl apply
make install
# Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
make run
Flags:
--controller if set, generate the controller without prompting the user (default true)
--force attempt to create resource even if it already exists
--group string resource Group
-h, --help help for api
--kind string resource Kind
--make make generate if true, run make generate after generating files (default true)
--namespaced resource is namespaced (default true)
--plural string resource irregular plural form
--resource if set, generate the resource without prompting the user (default true)
--version string resource Version
Global Flags:
--plugins strings plugin keys to be used for this subcommand execution
kubebuilder create api --group traefik --version v1 --kind TraefikService
INFO Create Resource [y/n]
y
INFO Create Controller [y/n]
y
INFO Writing kustomize manifests for you to edit...
INFO Writing scaffold for you to edit...
INFO api/v1/traefikservice_types.go
INFO api/v1/groupversion_info.go
INFO internal/controller/suite_test.go
INFO internal/controller/traefikservice_controller.go
INFO Update dependencies:
$ go mod tidy
INFO Running make:
$ make generate
mkdir -p /Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin
test -s /Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen && /Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen --version | grep -q v0.13.0 || \
GOBIN=/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.13.0
go: downloading sigs.k8s.io/controller-tools v0.13.0
go: downloading github.com/fatih/color v1.15.0
go: downloading k8s.io/api v0.28.0
go: downloading k8s.io/apimachinery v0.28.0
go: downloading github.com/gobuffalo/flect v1.0.2
go: downloading golang.org/x/net v0.14.0
go: downloading golang.org/x/text v0.12.0
/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:
$ make manifests
样例生成完了后,我们会看到我们项目新增了一些文件:
.
|____cmd
| |____main.go
|____go.mod
|____bin
| |____controller-gen
|____config
| |____rbac
| | |____auth_proxy_service.yaml
| | |____leader_election_role_binding.yaml
| | |____role.yaml
| | |____leader_election_role.yaml
| | |____kustomization.yaml
| | |____role_binding.yaml
| | |____traefikservice_editor_role.yaml
| | |____service_account.yaml
| | |____auth_proxy_role.yaml
| | |____traefikservice_viewer_role.yaml
| | |____auth_proxy_client_clusterrole.yaml
| | |____auth_proxy_role_binding.yaml
| |____crd
| | |____kustomizeconfig.yaml
| | |____kustomization.yaml
| |____default
| | |____manager_auth_proxy_patch.yaml
| | |____kustomization.yaml
| | |____manager_config_patch.yaml
| |____samples
| | |____traefik_v1_traefikservice.yaml
| | |____kustomization.yaml
| |____manager
| | |____kustomization.yaml
| | |____manager.yaml
| |____prometheus
| | |____monitor.yaml
| | |____kustomization.yaml
|____Dockerfile
|____Makefile
|____internal
| |____controller
| | |____traefikservice_controller.go
| | |____suite_test.go
|____hack
| |____boilerplate.go.txt
|____go.sum
|____.golangci.yml
|____README.md
|____PROJECT
|____.dockerignore
|____.gitignore
|____api
| |____v1
| | |____groupversion_info.go
| | |____zz_generated.deepcopy.go
| | |____traefikservice_types.go
按照之前的设计,我们其实是没有必要定义CRD的, 下面关于CRD的定义和安装 是出于教学演示目的, 如果你只想做项目,可以忽略这部分内容
我们需要定义Traefik Service, 我们来看看Traefik Service一个service 实例定义:
<etcd_prefix>/<entry_point>/services/loadBalancer/servers/<index>/url <url_value>
traefik etcd配置的前缀, provider配置时 有设置
services: 表示 web entrypoint的 services配置
loadBalancer: cmdb 服务loadBalancer配置
servers: loadBalancer 下的实例配置
0(变量): index
因此我们定义的Service需要有如下属性:
作为k8s的CRD 必须是一个runtime.Object, 也就是为我们生成的TraefikService对象, 我们需要编辑的是TraefikServiceSpec对象
由于 Name已经在 ObjectMeta 有声明了, 因此我们只需要添加 entrypoint 和 url
修改资源定义: api/v1/traefikservice_types.go
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// TraefikServiceSpec defines the desired state of TraefikService
type TraefikServiceSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of TraefikService. Edit traefikservice_types.go to remove/update
Entrypoint string `json:"entrypoint"`
URL string `json:"url"`
}
// TraefikServiceStatus defines the observed state of TraefikService
type TraefikServiceStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// 是否被激活
Active bool `json:"active,omitempty"`
// 最新更新时间
LastUpdateTime *metav1.Time `json:"lastUpdateTime,omitempty"`
// 如果更新失败, 失败的原因
Message string `json:"message,omitempty"`
}
我们通过make 提供
$ make manifests generate
/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
我们看到CRD的描述文件已经有变化了:
$ :: operator/config/crd ‹master*› » tree .
.
|____kustomizeconfig.yaml
|____kustomization.yaml
|____bases
| |____traefik.magedu.com_traefikservices.yaml
traefik.magedu.com_traefikservices.yaml的内容
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.13.0
name: traefikservices.traefik.magedu.com
spec:
group: traefik.magedu.com
names:
kind: TraefikService
listKind: TraefikServiceList
plural: traefikservices
singular: traefikservice
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: TraefikService is the Schema for the traefikservices API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: TraefikServiceSpec defines the desired state of TraefikService
properties:
entrypoint:
description: Foo is an example field of TraefikService. Edit traefikservice_types.go
to remove/update
type: string
url:
type: string
required:
- entrypoint
- url
type: object
status:
description: TraefikServiceStatus defines the observed state of TraefikService
properties:
active:
description: 是否被激活
type: boolean
lastUpdateTime:
description: 最新更新时间
format: date-time
type: string
message:
description: 如果更新失败, 失败的原因
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
到此我们的CRD的描述文件已经生成ok, 剩下的就是把该文件注册到k8s集群内, 也就是我们所的安装CRD
脚手架使用
Deployment
install Install CRDs into the K8s cluster specified in ~/.kube/config.
uninstall Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
执行如下命令安装:
$ make install
/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
test -s /Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/kustomize || GOBIN=/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@v5.2.1
go: downloading sigs.k8s.io/kustomize/kustomize/v5 v5.2.1
go: downloading sigs.k8s.io/kustomize/kyaml v0.15.0
go: downloading sigs.k8s.io/kustomize/api v0.15.0
go: downloading sigs.k8s.io/kustomize/cmd/config v0.12.0
go: downloading github.com/go-errors/errors v1.4.2
go: downloading k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961
go: downloading github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00
go: downloading github.com/xlab/treeprint v1.2.0
go: downloading gopkg.in/evanphx/json-patch.v5 v5.6.0
go: downloading github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
go: downloading go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
/Users/oldyu/Workspace/Golang/go-course-project/go12/skills/operator/bin/kustomize build config/crd | kubectl apply -f -
customresourcedefinition.apiextensions.k8s.io/traefikservices.traefik.magedu.com created
查看是否安装成功, 由于我的集群是腾讯托管的, 界面上可以直接看到,如下:
$ kubectl get crd
NAME CREATED AT
traefikservices.traefik.magedu.com 2024-01-07T00:40:57Z
接下来我们验证通过CRD来创建一个 TraefikService的资源
我们参考samples下的样例, 补充spec相关参数
apiVersion: traefik.magedu.com/v1
kind: TraefikService
metadata:
name: traefikservice-sample
spec:
# TODO(user): Add fields here
entrypoint: web
url: https://www.baidu.com
# 创建资源
$ kubectl apply -f config/samples/traefik_v1_traefikservice.yaml
traefikservice.traefik.magedu.com/traefikservice-sample created
# 查看资源
$ kubectl get TraefikService
NAME AGE
traefikservice-sample 35s
# 查看资源yaml
$ kubectl get TraefikService -o yaml
apiVersion: v1
items:
- apiVersion: traefik.magedu.com/v1
kind: TraefikService
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"traefik.magedu.com/v1","kind":"TraefikService","metadata":{"annotations":{},"labels":{"app.kubernetes.io/created-by":"operator","app.kubernetes.io/instance":"traefikservice-sample","app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"traefikservice","app.kubernetes.io/part-of":"operator"},"name":"traefikservice-sample","namespace":"default"},"spec":{"entrypoint":"web","url":"https://www.baidu.com"}}
creationTimestamp: "2024-01-07T00:42:42Z"
generation: 1
labels:
app.kubernetes.io/created-by: operator
app.kubernetes.io/instance: traefikservice-sample
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/name: traefikservice
app.kubernetes.io/part-of: operator
name: traefikservice-sample
namespace: default
resourceVersion: "3230760"
uid: 9f74998f-9e37-4f28-81ab-95a628f96319
spec:
entrypoint: web
url: https://www.baidu.com
kind: List
metadata:
resourceVersion: ""
最后我们删除我们定义的资源
$ kubectl delete -f config/samples/traefik_v1_traefikservice.yaml
traefikservice.traefik.magedu.com "traefikservice-sample" deleted
到此为止我们仅仅完成了对象的基本操作, 比如创建和删除, 但是与这个对象关联的业务逻辑代码 我们还没编写,也就是 说我们仅仅完成了对象的声明, 那如何开发对象的业务逻辑喃,着就是涉及到Crontroller了
什么是Crontroller? 他和 CRD之间又有啥纠葛, 且听我一一道来
我们可以通过API Server声明一个资源对象(CRD),实践上是声明了对象的期望状态, 是一个愿望, 比如期望副本数量为3个, Controller的核心逻辑就是让这个愿望实现, 并且实时Watch对象的变化, 一旦对象变化,我们就需要再次做出调整,让现实中的状态 变成期望的状态
因此Crontroller 是个面向期望的编程模型, 我们声明的这个期望对象,就是API Object(K8s Runtime Object)
Reconcile 函数是 Operator 的核心逻辑, 位于 controllers/traefikservice_controller.go 文件中, 我们修改他,添加我们的业务逻辑
// TraefikServiceReconciler reconciles a TraefikService object
type TraefikServiceReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// 每当traefikv1.TraefikService{}对象有变化时,我们就会收到一个请求
func (r *TraefikServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 获取日志对象
l := log.FromContext(ctx, "namespace", req.Namespace)
// TODO(user): your logic here
// 1.通过名称获取TraefikService对象, 并打印
var obj traefikv1.TraefikService
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
// 如果Not Found则表示该资源已经删除, 需要做删除处理
if apierrors.IsNotFound(err) {
l.Info("delete service ...")
err = nil
} else {
l.Error(err, "unable to fetch TraefikService")
}
return ctrl.Result{}, err
}
l.Info("get TraefikService object",
"name", obj.Name,
"url", obj.Spec.URL,
"entrypoint", obj.Spec.Entrypoint)
// 2. 更加状态 排除已经同步完成的对象
if obj.Status.Active {
l.Info("traefik service is active, skip ...")
return ctrl.Result{}, nil
}
// 3. 注册服务到Traefik service中, 比如写入到etcd provider中
l.Info("set traefik service ...")
// 4. 修改成功调整对象的状态
obj.Status.Active = true
obj.Status.LastUpdateTime = &metav1.Time{Time: time.Now()}
if err := r.Status().Update(ctx, &obj); err != nil {
l.Info("unable to update status", "reason", err.Error())
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *TraefikServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&traefikv1.TraefikService{}).
Complete(r)
}
为了确保我们的CRD描述是最新的, 我们重新安装下
make manifests && make install
为了方便起见,我们将在本地运行 controller,当然您也可以将其部署到 Kubernetes 上运行
make run
我们继续使用之前的样例, 看看Controller能否感觉对象的变化, 正常工作
# 创建资源
$ kubectl apply -f config/samples/traefik_v1_traefikservice.yaml
traefikservice.traefik.magedu.com/traefikservice-sample created
我们通过controller的日志, 来确认Controller是否正常工作
...
2024-01-07T08:46:20+08:00 INFO setup starting manager
2024-01-07T08:46:20+08:00 INFO starting server {"kind": "health probe", "addr": "[::]:8081"}
2024-01-07T08:46:20+08:00 INFO controller-runtime.metrics Starting metrics server
2024-01-07T08:46:20+08:00 INFO controller-runtime.metrics Serving metrics server {"bindAddress": ":8080", "secure": false}
2024-01-07T08:46:20+08:00 INFO Starting EventSource {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "source": "kind source: *v1.TraefikService"}
2024-01-07T08:46:20+08:00 INFO Starting Controller {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService"}
2024-01-07T08:46:20+08:00 INFO Starting workers {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "worker count": 1}
2024-01-07T08:46:52+08:00 INFO get TraefikService object {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "TraefikService": {"name":"traefikservice-sample","namespace":"default"}, "namespace": "default", "name": "traefikservice-sample", "reconcileID": "e69f4e7d-56bf-4a57-bde5-227181e0a9ce", "namespace": "default", "name": "traefikservice-sample", "url": "https://www.baidu.com", "entrypoint": "web"}
2024-01-07T08:46:52+08:00 INFO set traefik service ... {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "TraefikService": {"name":"traefikservice-sample","namespace":"default"}, "namespace": "default", "name": "traefikservice-sample", "reconcileID": "e69f4e7d-56bf-4a57-bde5-227181e0a9ce", "namespace": "default"}
2024-01-07T08:46:52+08:00 INFO get TraefikService object {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "TraefikService": {"name":"traefikservice-sample","namespace":"default"}, "namespace": "default", "name": "traefikservice-sample", "reconcileID": "0d024e51-81cf-464d-b821-80e2fbf3f3f2", "namespace": "default", "name": "traefikservice-sample", "url": "https://www.baidu.com", "entrypoint": "web"}
2024-01-07T08:46:52+08:00 INFO traefik service is active, skip ... {"controller": "traefikservice", "controllerGroup": "traefik.magedu.com", "controllerKind": "TraefikService", "TraefikService": {"name":"traefikservice-sample","namespace":"default"}, "namespace": "default", "name": "traefikservice-sample", "reconcileID": "0d024e51-81cf-464d-b821-80e2fbf3f3f2", "namespace": "default"}
并不是什么时候我们都需要 CRD的, 比如我们只想Watch Pod的变化, 因此我们可以独立开发Controller即可
// SetupWithManager sets up the controller with the Manager.
func (r *PodReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1.Pod{}).
Complete(r)
}
func (r *EndpointReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 获取日志对象
l := log.FromContext(ctx)
// TODO(user): your logic here
// 1.通过名称获取Pods对象, 并打印
var obj v1.Pod
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
// 如果Not Found则表示该资源已经删除, 需要做删除处理
if apierrors.IsNotFound(err) {
l.Info("delete Pods ...",
"namespace", req.Namespace,
"name", req.Name)
err = nil
} else {
l.Error(err, "unable to fetch Pods")
}
}
eps, _ := json.Marshal(obj)
l.Info(string(eps))
return ctrl.Result{}, nil
}
controller开发完成后,需要注册给Manager, 才能给启动, 因此我们在main.go中完成该controller的加载
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
// 默认配置
options := ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "d18673dd.magedu.com",
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err = (&controllers.TraefikServiceReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "TraefikService")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
// Pods Controller
if err = (&controllers.PodReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Pods")
os.Exit(1)
}
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
下面是创建一个Nginx 的Deployment 观察到的Pod变化
1.6479577314173741e+09 INFO controller.pod nginx-86bd55b966-hwhtz {"reconciler group": "", "reconciler kind": "Pod", "name": "nginx-86bd55b966-hwhtz", "namespace": "default", "namespace": "default", "labels": {"k8s-app":"nginx","pod-template-hash":"86bd55b966","qcloud-app":"nginx"}}
1.647957731418373e+09 INFO controller.pod 172.16.0.69 {"reconciler group": "", "reconciler kind": "Pod", "name": "nginx-86bd55b966-hwhtz", "namespace": "default"}
1.647957731419115e+09 INFO controller.pod test {"reconciler group": "", "reconciler kind": "Pod", "name": "nginx-86bd55b966-hwhtz", "namespace": "default", "pod_id": null}
基于次, 我们就可以把Pod容器里面的服务注册到注册中心去了
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。