相比于一开始作为Web前端工程师入职公司,现在我的工作内容已经远远超出了当时的范畴,JAVA的后端开发到Linux单机、K8S云原生的运维,最近是go开发一些项目需要的Agent应用。
而在将公司项目开发的Agent应用通过Helm安装到客户正式环境集群时,遇到了要求标明资源限制和镜像拉取凭证的问题,我将在这篇文章里讲讲我对这两个问题的理解。
集群要求标明资源限制
相比于普通的Web应用,由于项目Agent应用的特殊性:
- 部分时候需要在集群Node上执行一些资源消耗性的破坏性任务
- 以及各类任务Pod的动态创建
基于以上原因,未配置部分工作负载的limit和requests。
而使用helm将Agent应用安装到客户提供的集群后,查询却没有发现相应的Pod被创建,而Deployment是存在的。
在排除helm包内容缺失的问题后,需要使用kubectl describe replicaset查询具体Pod未被创建的原因,发现存在如下内容:
Error creating: pods "xxx" is forbidden: failed quota: xxx: must specify limits.cpu, limits.memory, requests.cpu, requests.memory
也就是需要给Pod配置requests和limit,才允许被创建。了解到命名空间配置了ResourceQuota来限制整个命名空间可以使用的CPU和内存资源。在Pod未配置limit和requests时,由于无法计算资源是否会超出限制,因此会被拒绝创建。
后续采用LimitRange的default和defaultRequest,来自动设置Pod资源在未填写limit和requests时的默认值,能够保证在不移除ResourceQuota资源限制的情况下,设置一个资源可使用的默认值,这样Pod就能够创建成功了:
apiVersion: v1
kind: LimitRange
metadata:
name: my-limitrange
namespace: my-namespace
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
但是这样也存在弊端,原本设计的消耗资源的破坏性任务可能无法达到需要的效果,后续还需要和客户进一步的沟通,不过好在大部分功能还是能够符合需求的。
进一步的,可以在对应应用的values.yaml将其配置为开关,在对应集群有资源限制需求时开启--set limitRange.enabled=true
,并可以再helm install时传入--set limitRange.defaltLimit.cpu='500m'
调整默认的资源配额:
limitRange:
enabled: false
defaultLimit:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
{{- if .Values.limitRange.enabled }}
apiVersion: v1
kind: LimitRange
metadata:
name: xxx-limitrange
namespace: {{ .Release.Namespace | quote }}
spec:
limits:
- type: Container
default:
cpu: {{ .Values.limitRange.defaultLimit.cpu | quote }}
memory: {{ .Values.limitRange.defaultLimit.memory | quote }}
defaultRequest:
cpu: {{ .Values.limitRange.defaultRequest.cpu | quote }}
memory: {{ .Values.limitRange.defaultRequest.memory | quote }}
{{- end }}
这里的quota
是一个内置的函数表示将内容使用引号包裹成字符串。
镜像拉取凭证的设计
由于需要将应用镜像提前上传到客户环境的镜像仓库,但有些客户的环境又不便于将仓库项目设置为公共项目,应用在创建Pod时需要使用镜像拉取凭证才能从私有仓库拉取镜像,因此需要针对镜像拉取凭证处理。
拉取凭证的创建可以通过kubectl
执行命令创建
kubectl create secret docker-registry my-registry-secret \
--docker-server=<DOCKER_REGISTRY_URL> \
--docker-username=<DOCKER_USERNAME> \
--docker-password=<DOCKER_PASSWORD> \
--docker-email=<DOCKER_EMAIL>
也可以将配置合并到应用helm包,先在values.yaml下创建关于镜像仓库的配置:
image:
repository: harbor.xxx.com
appProject: project-name
secret:
# 1.拉取镜像不需要secret,create和enable都改为false;
# 2.拉取镜像需要我们自己创建secret,create和enable都改为true;
# 3.拉取镜像使用客户提供的secret,create改为false,enable改为true,name改成客户提供的secret名称;
imagePull:
create: true
enable: true
username: natuki
password: mypassword
email: [email protected]
# 镜像拉取secret名称
name: image-pull-secret
创建如下的secret.yaml文件用于根据values.yaml配置生成镜像拉取凭证
{{- if .Values.image.secret.imagePull.create }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.image.secret.imagePull.name }}
namespace: {{ .Release.Namespace }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
{
"auths": {
"{{ .Values.image.repository }}": {
"username": "{{ .Values.image.secret.imagePull.username }}",
"password": "{{ .Values.image.secret.imagePull.password }}",
"email": "{{ .Values.image.secret.imagePull.email }}",
"auth": "{{ .Values.image.secret.imagePull.username | cat \":\" .Values.image.secret.imagePull.password | b64enc }}"
}
}
}
{{- end }}
在应用相关的服务账号rbac.yaml配置里添加如下配置引用镜像拉取凭证,这样和服务账号关联的Pod就会使用该凭证拉取镜像:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
{{- if .Values.global.secret.imagePull.enable }}
imagePullSecrets:
- name: {{ .Values.global.secret.imagePull.name }}
{{- end }}