原创 吴就业 193 0 2023-06-27
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/e2ee4eec386342378a6b3ba81e5a2107
作者:吴就业
链接:https://wujiuye.com/article/e2ee4eec386342378a6b3ba81e5a2107
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
kubebuilder默认使用kustomize,因此生成的yaml会存放在config/crd
目录下,替换成helm后,如果不想维护多个git仓库,或者一份yaml存两份的话,我们就需要修改kubebuilder生成yaml存放的目录,放到heml chart的templates目录下。
怎么修改?其实搜索一下“config/crd”在哪个文件出现,就能知道是在哪里配置的了。
主要是修改kubebuilder项目的Makefile文件。makefile的代码其实就是kubebuilder提供的命令的实现。
假设我们的Operator项目名是my-operator,在项目下创建一个名为chart的目录,作为my-operator helm chart的根目录。
然后我们需要修改这些地方:
1.修改crd代码生成存放的目录
.PHONY: manifests
manifests: controller-gen
$(CONTROLLER_GEN) rbac:roleName=my-operator-manager-role crd webhook paths="./..." output:crd:artifacts:config=chart/crds output:rbac:artifacts:config=chart/templates output:webhook:artifacts:config=chart/templates
其实就是修改make manifests
命令,该命令用于生成 WebhookConfiguration、ClusterRole 和 CustomResourceDefinition的yaml文件。
参数说明:
rbac:roleName
是指定role.yaml的角色的名称。output:crd:artifacts:config
指定crd文件存放路径。output:rbac:artifacts:config
指定rbac文件存放路径。output:webhook:artifacts:config
指定webhook文件存放路径。2.install、unstall命令改成helm的install、unstall命令。
.PHONY: install
install: manifests ## 安装helm chart,会检查语法错误、自动打包
helm lint chart
helm package chart/
helm install test my-operator-helm-chart-0.1.0.tgz
.PHONY: uninstall
uninstall: manifests ## 卸载helm chart
helm uninstall test
3.由于不使用kustomize部署了,需要注释或删掉部署命令。
## 用helm安装部署不能用这些命令
#.PHONY: deploy
#deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
# cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
# $(KUSTOMIZE) build config/default | kubectl apply -f -
#
#.PHONY: undeploy
#undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
# $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
4.不需要kustomize了,注释或删掉kustomize的安装命令。
#KUSTOMIZE ?= $(LOCALBIN)/kustomize
#KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
#.PHONY: kustomize
#kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
#$(KUSTOMIZE): $(LOCALBIN)
# @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \
# echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \
# rm -rf $(LOCALBIN)/kustomize; \
# fi
# test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); }
了解了Makefile之后,我们也可以自己添加一些方便自己开发测试的命令。例如,我将install、unstall命令改为仅安装crd,添加install-chart和uninstall-chart安装和卸载helm chart,添加install simple用于apply demo。
##@ Deployment
ifndef ignore-not-found
ignore-not-found = false
endif
.PHONY: install
install: manifests ## 生成crd并安装crd
kubectl apply -f chart/crds
.PHONY: uninstall
uninstall: ## 卸载crd
kubectl delete --ignore-not-found=$(ignore-not-found) -f chart/crds
.PHONY: install-chart
install-chart: manifests ## 生成crd、生成helm chart,并安装helm chart
helm lint chart
helm package chart/
helm install test my-operator-helm-chart-0.1.0.tgz
.PHONY: uninstall-chart
uninstall-chart: ## 卸载helm chart
helm uninstall test
##@ Demo
.PHONY: install-simple
install-simple: manifests install ## 安装demo yaml
kubectl apply -f example
.PHONY: uninstall-simple
uninstall-simple: ## 安装demo yaml
kubectl delete --ignore-not-found=$(ignore-not-found) -f example
如果是本地kind启动的k8s集群,要使kind启动的k8s集群能够拉取到本地镜像,可以使用命令:kind load docker-image ${IMG}
,并且不管镜像的版本号是不是latest,每次镜像更新都需要执行一次这个命令。
如果是将镜像推送到远程仓库,那么遇到代码改了但似乎没生效这个问题,一般是由于镜像tag没变,或者imagePullPolicy配置为IfNotPresent,导致镜像没更新。
首先检查镜像的tag是不是latest,如果是,则检查imagePullPolicy是不是配置了,且配置为IfNotPresent了,这会导致即使是latest,容器部署的时候也不会拉新的镜像。
如果tag版本保留latest,那么建议删除imagePullPolicy的配置,因为imagePullPolicy默认值会跟据tag是不是latest设置。如果是latest,则imagePullPolicy默认值为Always,否则为IfNotPresent。
如果tag版本号是指定版本号,那么imagePullPolicy需要手动指定为Always,或者就是每次更新镜像都修改一下版本号。
如果中间件当前使用apollo作为配置输入,想要改成使用ConfigMap,首先是中间件配置这一块需要重构,将获取和监听配置改变抽象为接口,提供apollo实现和configmap实现,然后通过一个环境变量实现切换选择apollo实现,还是configmap实现。
Configmap实现其实是读本地配置文件,通过将Configmap挂盘使用,让进程直接读文件。这样的好处是中间件不需要像k8s spring cloud框架那样将k8s的client-go也依赖进项目,导致部署在虚拟机上跑不起来,以及中间件本身单元测试或测试需要依赖k8s环境问题。
containers:
- name: xxx
image: {{.Spec.Image}}
.....
volumeMounts:
- name: xxx-configmap-volume
mountPath: "/etc/config/xxx"
......
volumes:
- name: xxx-configmap-volume
configMap:
name: application
items:
- key: application
path: application.json
.......
另一个比较坑的问题。当我们一个中间件使用多个configmap的时候,每个configmap的挂盘路径是不能一样的,否则后面的会覆盖前面的,另外subPath也是不能用的,这样会导致configmap内容更新的时候,容器里面的配置文件不会更新。
正确的做法是每个configmap一个独立的挂盘路径,然后配置文件的路径通过环境变量方式传给中间件。
containers:
- name: lz-common-bfe
image: {{.Spec.Image}}
env:
- name: CONFIG_PATH_LEGACY_CM
value: "/etc/config/bfe/legacy-cm/legacy-cm.json"
- name: CONFIG_PATH_LEGACY_AS
value: "/etc/config/bfe/legacy-as/legacy-as.json"
.......
.....
volumeMounts:
- name: bfe-legacy-cm-configmap-volume
mountPath: "/etc/config/bfe/legacy-cm"
- name: bfe-legacy-as-configmap-volume
mountPath: "/etc/config/bfe/legacy-as"
......
......
volumes:
- name: bfe-legacy-cm-configmap-volume
configMap:
name: bfe-legacy-cm
items:
- key: bfe-legacy-cm
path: legacy-cm.json
- name: bfe-legacy-as-configmap-volume
configMap:
name: bfe-legacy-as
items:
- key: bfe-legacy-as
path: legacy-as.json
.......
如果我们当前项目在打包镜像的时候会注入一些环境变量,或者存在一个发布平台,在部署服务时会生成一个Deployment资源,能够通过Deployment资源注入一些环境变量。而改成通过Operator生成Deployment来部署服务的方式,由于Deployment资源是Operator生成的,在打包部署Operator阶段就没办法介入修改这个Deployment。不过可以将环境变量注入到Operator的Deployment资源,再由Operator将环境变量注入到中间件的Deployment资源。
例如,Operator在生成服务的Deployment资源时注入环境变量信息:
func FullEnv(d *appv1.Deployment) {
for index := 0; index < len(d.Spec.Template.Spec.Containers); index++ {
envArr := d.Spec.Template.Spec.Containers[index].Env
envArr = append(envArr,
v1.EnvVar{
Name: "REGION",
Value: GetRegion(),
},
v1.EnvVar{
Name: "DEPLOY_ENV",
Value: GetDeployEnv(),
},
v1.EnvVar{
Name: "AZ",
Value: GetAz(),
},
v1.EnvVar{
Name: "IDC",
Value: GetIdc(),
},
v1.EnvVar{
Name: "SERVICE_NAME",
Value: GetServiceName(),
ValueFrom: nil,
},
)
// 数组元素不是指针,所以Env是copy的,只是改copy对象的字段是不生效的
d.Spec.Template.Spec.Containers[index].Env = envArr
}
}
如果直接在idea点run执行单元测试错误:"error": "fork/exec /usr/local/kubebuilder/bin/etcd: no such file or directory"
,然后手动安装了etcd之后执行单元测试错误:fork/exec /usr/local/kubebuilder/bin/kube-apiserver: no such file or directory
。
我们使用kind或minikube安装一个集群,避免影响公共的测试集群。
然后将~/.kube/config
文件改为kind/minikube
集群的config,测完后再改回测试环境k3s的config。
然后启动单元测试时需要添加环境变量:USE_EXISTING_CLUSTER=true
,意思是使用存在的k8s集群测试,而不需要搭建一个本地集群,也就不需要安装etcd、kube-apiserver了。
参考:https://cloudnative.to/kubebuilder/reference/envtest.html
执行make test
命令会自动下载etcd、kubectl、kube-apiserver到项目bin/k8s
目录下。
make test
是make manifests
、make generate
、 make envtest
、go test
的组合,其中make envtest
命令会下载setup-envtest二进制到项目的bin目录下,最后make test
还会执行:
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
这条命令包括了设置KUBEBUILDER_ASSETS环境变量,然后执行go test
命令,而设置环境变量又隐藏了一条命令:
$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path
替换变量后就是:
./bin/setup-envtest use 1.26.0 --bin-dir ./bin -p path
这条命令就是使用setup-envtest去下载etcd、kubectl、kube-apiserver这些工具,-p path
输出的路径是“./bin/k8s/1.26.0-darwin-amd64
”。
在使用finalizer时发现一个问题,在helm uninstall的时候,operator会卸载掉,这个时候自定义的资源(如bfe)变成删除状态后,由于没有operator处理finalizer,就会一直删不掉,所以中间件部署和operator部署不应打包在一个helm chart中。
参考开源项目:https://github.com/mysql/mysql-operator
,该项目分两个helm chart打包operator和innodb mysql cluster。我们可以这样分:CRD和Operator打包到一个chart,CR则打包到另一个chart。
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
很多企业内部为了不与云厂商绑定,避免上云容易下云难的尴尬,以及企业内部可能也会做私有云,或者封装一个混合云平台,因此不能直接用云厂商提供的provider。
通常申请基础设施,我们需要向运维描述我们需要什么基础设施、什么规格,运维根据我们的描述去检查是否已经申请过这样的资源,有就会直接给我们使用基础设施的信息,没有再帮我们申请,然后告诉我们使用基础设施的信息,例如mysql的jdbc和用户名、密码。如果将描述代码化,基础设施的申请自动化,就能实现“基础设施即代码”。而terraform就是实现“将描述代码化”的工具软件。
在Job场景,如果Job达到backoffLimit还是失败,而且backoffLimit值很小,很快就重试完,没能及时的获取到容器的日记。而达到backoffLimit后,job的controller会把pod删掉,这种情况就没办法获取pod的日记了,导致无法得知job执行失败的真正原因,只能看到job给的错误:"Job has reached the specified backoff limit"。
新的云原生中间件很难短时间内覆盖到企业项目中,企业走云原生这条道路,还需要考虑传统中间件如何上云的问题。最需要解决的是如何容器化部署,以及自动化运维。这就不得不借助Operator了。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。