本文已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已失效。
使用 Jenkins 在 Kubernetes 中实现零停机部署
自从我们将 Kubernetes 持续部署和 Azure Container Service 插件添加到 Jenkins 更新中心以来,“如何创建零停机部署”是我们最常被问到的问题之一。我们在 Azure 上创建了一个快速入门模板,以演示零停机部署的样子。虽然我们的示例使用 Azure,但这个概念很容易应用于所有 Kubernetes 安装。
滚动更新
Kubernetes 支持滚动更新 (RollingUpdate) 策略,以逐步用新 Pod 替换旧 Pod,同时持续为客户端提供服务而不会导致停机。要执行滚动更新部署,请执行以下操作:
- 将
.spec.strategy.type
设置为RollingUpdate
(默认值)。 - 将
.spec.strategy.rollingUpdate.maxUnavailable
和.spec.strategy.rollingUpdate.maxSurge
设置为合理的数值。maxUnavailable
:更新过程中最多不可用的 Pod 数量。可以是绝对数量,也可以是副本数的百分比;默认为 25%。maxSurge
:在期望的 Pod 数量之上最多可以创建的 Pod 数量。这也可以是绝对数量或副本数的百分比;默认为 25%。
- 为您的服务容器配置
readinessProbe
,以帮助 Kubernetes 确定 Pod 的状态。Kubernetes 只会将客户端流量路由到就绪探针 (readiness probe) 健康的 Pod。
我们将使用官方 Tomcat 镜像的部署来演示这一点
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tomcat-deployment-rolling-update
spec:
replicas: 2
template:
metadata:
labels:
app: tomcat
role: rolling-update
spec:
containers:
- name: tomcat-container
image: tomcat:${TOMCAT_VERSION}
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
strategy:
type: RollingUpdate
rollingUp maxSurge: 50%
如果当前部署中运行的 Tomcat 是版本 7,我们可以将 ${TOMCAT_VERSION}
替换为 8 并将其应用于 Kubernetes 集群。使用 Kubernetes 持续部署或 Azure Container Service 插件,该值可以从环境变量中获取,从而简化部署过程。
在后台,Kubernetes 按如下方式管理更新
- 最初,所有 Pod 都运行 Tomcat 7,并且前端 Service 将流量路由到这些 Pod。
- 在滚动更新期间,Kubernetes 会关闭一些 Tomcat 7 Pod 并创建相应的新 Tomcat 8 Pod。它确保
- 期望的 Pod 中最多
maxUnavailable
个 Pod 可以不可用,也就是说,至少 (replicas
-maxUnavailable
) 个 Pod 应该为客户端提供服务,在我们的例子中是 2-1=1。 - 更新过程中最多可以创建 maxSurge 个额外的 Pod,在我们的例子中是 2*50%=1。
- 期望的 Pod 中最多
- 一个 Tomcat 7 Pod 被关闭,并创建一个 Tomcat 8 Pod。Kubernetes 不会将流量路由到其中任何一个,因为它们的就绪探针尚未成功。
- 当新的 Tomcat 8 Pod 经就绪探针检测为就绪时,Kubernetes 将开始把流量路由到它。这意味着在更新过程中,用户可能会看到旧服务和新服务。
- 滚动更新继续进行,关闭 Tomcat 7 Pod 并创建 Tomcat 8 Pod,然后将流量路由到就绪的 Pod。
- 最后,所有 Pod 都运行 Tomcat 8。
滚动更新策略确保我们始终有一些就绪的后端 Pod 为客户端请求提供服务,因此不会发生服务停机。但是,需要额外注意一些事项:
- 在更新期间,旧 Pod 和新 Pod 都可能处理请求。如果在 Service 层没有定义良好的会话亲和性,用户可能会被路由到新 Pod,然后又路由回旧 Pod。
- 这也要求您维护良好定义的数据和 API 前后兼容性,这可能具有挑战性。
- Pod 启动后可能需要很长时间才能准备好处理流量。可能会有一段较长的时间窗口,在此期间服务由比平时少的后端 Pod 提供。通常,这不应该是一个问题,因为我们倾向于在服务不繁忙时进行生产升级。但这也会延长问题 1 出现的时间窗口。
- 我们无法对正在创建的新 Pod 进行全面测试。将应用更改从开发/质量保证 (QA) 环境迁移到生产环境可能带来持续的风险,即破坏现有功能。就绪探针可以进行一些工作来检查就绪状态,但是,它应该是一个可以定期运行的轻量级任务,不适合用作启动完整测试的入口点。
蓝/绿部署
蓝/绿部署,引自 TechTarget
蓝/绿部署是一种用于发布软件代码的变更管理策略。蓝/绿部署,也可能被称为 A/B 部署,需要两个配置完全相同的硬件环境。当一个环境处于活动状态并为终端用户提供服务时,另一个环境保持空闲。
容器技术提供了独立的环境来运行所需的服务,这使得像蓝/绿部署所需的那样创建完全相同的环境变得非常容易。Kubernetes 中松耦合的 Service - ReplicaSet 以及基于标签/选择器的服务路由使得在不同后端环境之间切换变得简单。使用这些技术,可以在 Kubernetes 中按如下方式执行蓝/绿部署
- 在部署之前,基础设施按如下方式准备
- 准备蓝色部署和绿色部署,其中
TOMCAT_VERSION=7
,并且TARGET_ROLE
分别设置为 blue 或 green。
- 准备蓝色部署和绿色部署,其中
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tomcat-deployment-${TARGET_ROLE}
spec:
replicas: 2
template:
metadata:
labels:
app: tomcat
role: ${TARGET_ROLE}
spec:
containers:
- name: tomcat-container
image: tomcat:${TOMCAT_VERSION}
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
- 准备公共服务终端,它最初路由到一个后端环境,例如
TARGET_ROLE=blue
。
kind: Service
apiVersion: v1
metadata:
name: tomcat-service
labels:
app: tomcat
role: ${TARGET_ROLE}
env: prod
spec:
type: LoadBalancer
selector:
app: tomcat
role: ${TARGET_ROLE}
ports:
- port: 80
targetPort: 8080
- 可选地,准备一个测试终端,以便我们可以访问后端环境进行测试。它们类似于公共服务终端,但仅供开发/运维团队内部访问。
kind: Service
apiVersion: v1
metadata:
name: tomcat-test-${TARGET_ROLE}
labels:
app: tomcat
role: test-${TARGET_ROLE}
spec:
type: LoadBalancer
selector:
app: tomcat
role: ${TARGET_ROLE}
ports:
- port: 80
targetPort: 8080
- 更新非活动环境(例如绿色环境)中的应用程序。在部署配置中设置
TARGET_ROLE=green
和TOMCAT_VERSION=8
来更新绿色环境。 - 通过
tomcat-test-green
测试终端测试部署,确保绿色环境已准备好为客户端流量提供服务。 - 通过更新 Service 配置中的
TARGET_ROLE=green
将前端 Service 路由切换到绿色环境。 - 在公共终端上运行额外测试,确保其正常工作。
- 现在蓝色环境处于空闲状态,我们可以
- 保留旧应用,以便在新应用出现问题时可以回滚
- 更新它,使其成为活动环境的热备份
- 减少其副本数以节省占用的资源
与滚动更新相比,蓝/绿部署有以下优点:公共服务要么路由到旧应用,要么路由到新应用,但绝不同时路由到两者。
- 新 Pod 准备就绪所需的时间不影响公共服务质量,因为只有当所有新 Pod 都测试为就绪后,流量才会被路由到它们。
- 在新环境为任何公共流量提供服务之前,我们可以对其进行全面测试。请记住,这是在生产环境中进行的,测试不应污染实时应用数据。
Jenkins 自动化
Jenkins 提供了易于设置的工作流来自动化您的部署。通过 Pipeline 支持,可以灵活构建零停机部署工作流,并可视化部署步骤。为了方便 Kubernetes 资源的部署过程,我们发布了基于 kubernetes-client 构建的 Kubernetes 持续部署和 Azure Container Service 插件。您可以将资源部署到 Azure Kubernetes Service (AKS) 或通用 Kubernetes 集群,无需使用 kubectl,并且它支持资源配置中的变量替换,因此您无需更新资源配置即可将特定于环境的资源部署到集群。我们创建了一个 Jenkins Pipeline 来演示蓝/绿部署到 AKS。流程如下:
- 预清理:清理工作空间。
- SCM:从源代码管理系统拉取代码。
- 准备镜像:准备应用程序 Docker 镜像并将其上传到某个 Docker 仓库。
- 检查环境:确定活动环境和非活动环境,这将指导后续的部署。
- 部署:将新应用程序资源配置部署到非活动环境。使用 Azure Container Service 插件,这可以通过以下方式完成:
acsDeploy azureCredentialsId: 'stored-azure-credentials-id',
configFilePaths: "glob/path/to/*/resource-config-*.yml",
containerService: "aks-name | AKS",
resourceGroupName: "resource-group-name",
enableConfigSubstitution: true
- 验证预部署:验证部署到非活动环境,确保其正常工作。再次注意,这是在生产环境中进行的,因此在测试过程中请小心,不要污染实时应用数据。
- 确认:可选地,发送电子邮件通知以供用户手动批准,然后进行实际的环境切换。
- 切换:将前端服务终端路由切换到非活动环境。这只是对 AKS Kubernetes 集群进行的另一个服务部署。
- 验证生产环境:验证前端服务终端在新环境下正常工作。
- 后清理:对临时文件进行一些清理。
对于滚动更新策略,只需将部署配置部署到 Kubernetes 集群,这是一个简单的单一步骤。
整合所有内容
我们在 Azure 上构建了一个快速入门模板,以演示如何使用 Jenkins 对 AKS (Kubernetes) 进行零停机部署。前往 Jenkins 在 Kubernetes 上的蓝/绿部署,然后点击“部署到 Azure”按钮即可获取可工作的演示。此模板将预置:
- 一个 AKS 集群,包含以下资源:
- 两个相似的部署,分别代表“蓝色”和“绿色”环境。两者最初都使用
tomcat
:7 镜像进行设置。 - 两个测试终端服务(
tomcat-test-blue
和tomcat-test-green
),它们连接到相应的部署,可用于测试部署是否已准备好用于生产环境。 - 一个生产服务终端(
tomcat-service
),代表用户将访问的公共终端。最初它路由到“蓝色”环境。
- 两个相似的部署,分别代表“蓝色”和“绿色”环境。两者最初都使用
- 一个运行在 Ubuntu 16.04 虚拟机上的 Jenkins 主节点,并配置了 Azure 服务主体凭据。此 Jenkins 实例包含两个示例作业:
- AKS Kubernetes 滚动更新部署 Pipeline,用于演示向 AKS 进行滚动更新部署。
- AKS Kubernetes 蓝/绿部署 Pipeline,用于演示向 AKS 进行蓝/绿部署。
- 我们没有在快速入门模板中包含电子邮件确认步骤。要添加该步骤,您需要在 Jenkins 系统配置中配置电子邮件 SMTP 服务器详细信息,然后在“切换”步骤之前添加一个 Pipeline 阶段。
stage('Confirm') {
mail (to: 'to@example.com',
subject: "Job '${env.JOB_NAME}' (${env.BUILD_NUMBER}) is waiting for input",
body: "Please go to ${env.BUILD_URL}.")
input 'Ready to go?'
}
按照步骤设置资源,然后启动 Jenkins 构建作业即可试用。