计算巢简介
计算巢是阿里云开放给ISV与其客户的服务管理PaaS平台,旨在解决ISV云上交付、部署、运维问题,建立ISV与客户之间的通道。针对ISV的实际场景,计算巢提供了私有化部署、托管版部署、代运维服务三种模式。托管版和私有化部署的区别是针对于部署在ISV的账号下还是部署在用户账号下。
本文主要介绍如何在计算巢中,通过服务部署helm chart到ack集群中。
实现思路
利用ROS进行服务部署
计算巢服务部署主要借助于[ROS资源编排服务](https://www.aliyun.com/product/ros)来实现资源编排, 因此最初的实现思路是使用ros的ClusterHelmApplication直接进行部署,对应的模版关键部分如下:
ClusterHelmApplication: Type: ALIYUN::CS::ClusterHelmApplication DependsOn: - AddonsSleep Properties: ChartValues: Ref: ChartValues ClusterId: Fn::If: - CreateAck - Fn::GetAtt: - ManagedKubernetesCluster - ClusterId - Ref: ClusterId ChartUrl: Ref: ChartUrl Namespace: Ref: Namespace Name: Ref: Name
经过测试发现,这种方式存在两个问题:
- 存放在acr镜像仓库中的helm chart包不支持转成https链接使用
- ack线上部署依赖的helm版本为3.0.7,不支持helm一些比较高级的语法。如部署nginx服务时,提示不支持lookup函数。
job中helm命令安装
既然使用ack提供的线上部署方式走不通,就考虑直接使用helm命令进行安装,实现方式就是在ack集群中起个job,在job中进行helm chart部署的动作,模版关键部分如下:
Resources: ClusterUserKubeconfig: Type: DATASOURCE::CS::ClusterUserKubeconfig Properties: ClusterId: Ref: ClusterId ClusterApplication: Type: ALIYUN::CS::ClusterApplication Properties: YamlContent: Fn::Sub: - | apiVersion: batch/v1 kind: Job metadata: name: helm-install-job spec: template: metadata: name: helm-install-job spec: containers: - name: helm-intall image: alpine/helm:3.12.0 command: [ "/bin/sh", "-c", "--" ] args: ["cd ~; mkdir ~/.kube; echo '${KubeConfig}' | base64 -d >> ~/.kube/config; chartPackage=$({{ computenest::helmpull::test }}); helm install $chartPackage --generate-name;"] restartPolicy: Never - KubeConfig: Fn::Base64Encode: Fn::GetAtt: - ClusterUserKubeconfig - Config
在job中将KubeConfig写到config文件中,然后执行helm pull命令,最后使用helm install命令进行helm chart安装,其中的{{ computenest::helmpull::test }}标识符会被替换为helm chart下载命令。
这种方式的问题是job进行了helm chart的部署,服务实例进行删除时,helm chart并不会进行卸载。
Helm Hook方式进行安装
helm提供了hook功能,可以在post-install-hook中进行helm chart的安装操作,在pre-delete-hook中进行helm chart的卸载操作,这样就可以解决job只进行了部署,未进行卸载的问题了。
同样还是使用ros的ClusterHelmApplication资源类型,去运行helm hook模版框架,在框架中去执行helm chart的安装和卸载。
post-install-hook处理逻辑如下:
apiVersion: batch/v1 kind: Job metadata: name: post-install-job-{{ .Release.Name }} labels: app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} app.kubernetes.io/version: {{ .Chart.AppVersion }} helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" annotations: # This is what defines this resource as a hook. Without this line, the # job is considered part of the release. "helm.sh/hook": post-install "helm.sh/hook-weight": "-5" "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: name: "{{ .Release.Name }}" labels: app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: restartPolicy: Never containers: - name: post-install-job image: "alpine/helm:3.12.0" command: [ "/bin/sh", "-c", "--" ] args: [" set -x; cd ~; curl -LO \"https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\"; install -o root -g root -m 0755 kubectl /usr/bin/kubectl; mkdir ~/.kube; echo {{ .Values.Kubeconfig }} | base64 -d >> ~/.kube/config; echo {{ toYaml .Values.ChartValues | b64enc }} | base64 -d > values.yaml; export HELM_EXPERIMENTAL_OCI=1; wget {{ .Values.ChartUrl }}; data=$(helm install {{.Values.ReleaseName}} {{ .Values.ChartPackage }} -f values.yaml --namespace {{ .Values.ChartNamespace }} | base64 | tr -d '\n'); if [ -n \"{{ .Values.OutputCmd | b64enc }}\" ]; then sleep {{ .Values.InstallSeconds }}; echo {{ .Values.OutputCmd | b64enc }} | base64 -d > outputCmd.sh; data=$(source outputCmd.sh | base64 | tr -d '\n'); fi; {{ .Values.CurlCli }} -d \"{\\\"Data\\\":\\\"$data\\\",\\\"status\\\":\\\"SUCCESS\\\"}\"; "]
pre-delete-hook的处理逻辑如下:
apiVersion: batch/v1 kind: Job metadata: name: pre-delete-job-{{ .Release.Name }} labels: app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} app.kubernetes.io/version: {{ .Chart.AppVersion }} helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" annotations: # This is what defines this resource as a hook. Without this line, the # job is considered part of the release. "helm.sh/hook": pre-delete "helm.sh/hook-weight": "-5" "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: name: "{{ .Release.Name }}" labels: app.kubernetes.io/managed-by: {{ .Release.Service | quote }} app.kubernetes.io/instance: {{ .Release.Name | quote }} helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" spec: restartPolicy: Never containers: - name: pre-delete-job image: "alpine/helm:3.12.0" command: [ "/bin/sh", "-c", "--" ] args: [" cd ~; mkdir ~/.kube; echo {{ .Values.Kubeconfig }} | base64 -d >> ~/.kube/config; export HELM_EXPERIMENTAL_OCI=1; helm uninstall {{ .Values.ReleaseName }} --namespace {{ .Values.ChartNamespace }}; "]
计算巢服务模版关键部分如下,将真正要部署的chartUrl通过values的方式传到helm hook部署模版中:
总结
在计算巢中在线部署helm chart到ack集群的探索过程中,遇到了很多的问题,现有的云产品对通过api调用和在线使用支持不是很完善,估计是这种使用方式比较少,大部分用户还是通过命令来执行,比如acr服务不支持https方式拉取,ack的ack-helm-manager对应的helm版本过低,最终只能想办法通过其它路径来达到想要的效果。