最近接到一个需求,自动更新替换k8s集群中某些服务的镜像,并支持回滚到原先版本。
我首先想到的是使用 kubectl
命令,通过编写一个shell脚本,并传送到k8s集群某个管理节点,执行类似下面这种命令:
arduino
kubectl set image deployment/<deployment-name> <container-name>=<new-image>
其中,是服务的Deployment名称,是服务容器的名称,是新的镜像名称。
例如,如果服务的Deployment名称为my-service,容器名称为my-container,新的镜像名称为my-registry/my-image:latest,则命令如下:
kubectl set image deployment/my-service my-container=my-registry/my-image:latest
但是后续了解到在部署该k8s集群时,不会预装 kubectl 命令,所以上述方法有局限性,需要客户先手动安装 kubectl 命令。
后来选择调用 k8s 的api接口进行实现镜像版本替换。
bash
/apis/apps/v1/namespaces/{namespace}/deployments/{name}
相关接口的解释在k8s官网中有解释,但是感觉过于简单,具体的调用方法实现还是借助的chatgpt
官网文档地址: kubernetes.io/docs/refere...
示例代码如下:
python
import requests
def update_service_image(deployment_name, container_name, new_image):
api_server = "<api-server>"
token = "<your-token>"
namespace = "<namespace>"
# 构建API URL
url = f"{api_server}/apis/apps/v1/namespaces/{namespace}/deployments/{deployment_name}"
# 构建请求头,包括认证信息
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/strategic-merge-patch+json"
}
# 构建要更新的镜像的部分
patch = {
"spec": {
"template": {
"spec": {
"containers": [
{
"name": container_name,
"image": new_image
}
]
}
}
}
}
# 发送 PATCH 请求
response = requests.patch(url, headers=headers, json=patch, verify=False)
if response.status_code == 200:
print("Service image updated successfully")
else:
print("Failed to update service image. Status code:", response.status_code)
# 使用示例
update_service_image("my-deployment", "my-container", "new-image:latest")
但是该api接口返回200只会保证修改deployement相关配置成功,并不意味着后续的滚动升级也会成功。
常见的一个错误场景就是替换成一个不存在的镜像版本,调用该接口时也会成功,返回200。
但是在后续的滚动升级时,新的pod会出现异常,提示镜像不存在,更新失败。
所以我们要调用 GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}/status
接口来获取deployment相关状态来判断是否更新成功。
有一个情况注意下,调用完更新接口后立即调用该查询接口时会返回旧版本状态,不包含新版本的状态,建议先停顿一会再调用该接口。
如何根据上述接口返回的参数进行判断是否成功呢?
上述接口返回信息里有这几个信息:
json
"status": { "observedGeneration": 3, "replicas": 3, "readyReplicas": 3, "availableReplicas": 3}
参考kubernets源码中的方法判断:
位置:pkg/controller/deployment/util/deployment_util.go:708
代码:
ini
func DeploymentComplete(deployment *apps.Deployment, newStatus *apps.DeploymentStatus) bool {
return newStatus.UpdatedReplicas == *(deployment.Spec.Replicas) &&
newStatus.Replicas == *(deployment.Spec.Replicas) &&
newStatus.AvailableReplicas == *(deployment.Spec.Replicas) &&
newStatus.ObservedGeneration >= deployment.Generation
}
如何进行回滚呢?
在kunernetes官网中并没有搜到相关api介绍,咨询chatgpt给出的方法如下,但是没有生效,没有找到失效的原因:
python
import requests
import json
def rollback_deployment(deployment_name, namespace):
# 构建API请求的URL
url = f"https://your-kubernetes-api-url/apis/apps/v1/namespaces/{namespace}/deployments/{deployment_name}"
# 获取Deployment的当前修订版本
current_revision = get_current_revision(deployment_name, namespace)
# 构建回滚请求的Body
body = {
"kind": "Deployment",
"apiVersion": "apps/v1",
"metadata": {
"name": deployment_name,
"annotations": {
"deployment.kubernetes.io/revision": str(current_revision) # 回滚到当前修订版本
}
}
}
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN" # 替换为你的API Token
}
# 发送回滚请求
response = requests.patch(url, headers=headers, data=json.dumps(body), verify=False)
if response.status_code == 200:
print("Deployment回滚成功!")
else:
print(f"Deployment回滚失败:{response.text}")
def get_current_revision(deployment_name, namespace):
# 构建API请求的URL
url = f"https://your-kubernetes-api-url/apis/apps/v1/namespaces/{namespace}/deployments/{deployment_name}"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN" # 替换为你的API Token
}
# 发送请求获取Deployment的���细信息
response = requests.get(url, headers=headers, verify=False)
if response.status_code == 200:
deployment_info = json.loads(response.text)
current_revision = deployment_info["metadata"]["annotations"]["deployment.kubernetes.io/revision"]
return current_revision
else:
print(f"获取Deployment信息失败:{response.text}")
return None
# 使用示例
rollback_deployment("my-deployment", "my-namespace")
后面最终实行的方法是在更新前先保留一份deployment配置备份,然后在回滚时直接替换成这个配置即可。
但是直接替换会提示错误,需要删除之前配置metadata.creationTimestamp、
metadata.generation、
metadata.resourceVersion等信息。