微服务交付管道构建指南
在微服务开发中,构建一个高效、稳定的交付管道至关重要。它可以帮助我们自动化代码从提交到生产的过程,确保代码质量,并提高部署的可靠性。本文将详细介绍如何使用 Jenkins 构建微服务的交付管道。
1. 构建服务镜像
首先,在 Pod 中从 Git 检出代码的最新版本。然后,启动一个名为 Build 的新管道阶段,在这个阶段中,进入 Docker 容器并运行 Docker 命令来构建服务镜像。
// 在 Jenkinsfile 中添加 Build 阶段
stage('Build') {
steps {
// 进入 Docker 容器并构建服务镜像
sh("docker build -t ${service} .")
}
}
Jenkins 还提供了 Groovy DSL 来替代 shell 命令。例如,可以使用
docker.build(imageName)
来替代
sh
调用。
将新的 Jenkinsfile 提交到 Git 仓库,并在 Jenkins 中导航到构建作业。等待重新运行或手动触发作业,在控制台输出中,应该会看到容器镜像成功构建。
2. 运行测试
接下来,需要运行一些测试。这应该像任何其他持续集成作业一样:如果测试通过,部署可以继续;如果不通过,则停止管道。在这个阶段,目标是提供关于变更集质量的快速准确反馈。快速的测试套件有助于工程师快速迭代。
在构建管道的提交阶段,构建代码和执行单元测试只是可能执行的活动中的两项。以下是可能的活动列表:
| 活动 | 描述 |
| — | — |
| 单元测试 | 代码级别的测试 |
| 编译 | 将工件编译成可执行工件 |
| 依赖解析 | 解析外部依赖,例如开源包 |
| 静态分析 | 根据指标评估代码 |
| 代码检查 | 检查代码的语法和风格原则 |
现在,应该让单元测试运行起来。在 Jenkinsfile 中,在 Build 阶段之后立即添加一个 Test 阶段。
// 在 Jenkinsfile 中添加 Test 阶段
stage('Test') {
sh("docker run --rm ${service} python setup.py test")
}
提交 Jenkinsfile 并运行构建。这将在管道中添加一个新阶段,执行在
/tests
中定义的测试用例。
为了使测试结果在构建中可见,可以在 Jenkinsfile 中添加归档 XML 结果的代码。
// 在 Jenkinsfile 中添加归档测试结果的代码
stage('Test') {
try {
sh("docker run -v `pwd`:/workspace --rm ${service} python setup.py test")
} finally {
step([$class: 'JUnitResultArchiver', testResults: 'results.xml'])
}
}
这段代码将当前工作空间作为卷挂载到 Docker 容器中。Python 测试过程将输出写入该卷作为
/workspace/result.xml
,即使 Docker 停止并移除服务容器后,也可以访问这些结果。使用
try–finally
语句确保无论测试通过还是失败都能获得结果。
提交更改后的 Jenkinsfile 并运行新的构建,测试结果将存储在 Jenkins 中,可以在构建页面上查看。
3. 发布工件
为了能够部署服务,需要发布一个工件,在这种情况下是 Docker 容器镜像。如果使用了私有 Docker 注册表,需要在 Jenkins 中配置 Docker 凭证:
1. 导航到
Credentials > System > Global Credentials > Add Credentials
。
2. 添加用户名和密码凭证,使用登录
https://hub.docker.com
的凭证。
3. 将 ID 设置为
dockerhub
,然后点击
OK
保存这些凭证。
如果使用公共注册表,可以跳过此步骤。准备好后,在 Jenkinsfile 中添加一个 Publish 阶段。
// 在 Jenkinsfile 中添加 Publish 阶段
def tagToDeploy = "[your-account]/${service}"
stage('Publish') {
withDockerRegistry(registry: [credentialsId: 'dockerhub']) {
sh("docker tag ${service} ${tagToDeploy}")
sh("docker push ${tagToDeploy}")
}
}
提交并运行构建,Jenkins 将把容器发布到公共 Docker 注册表。
4. 部署到暂存环境
在这个阶段,服务已经在内部进行了测试,但处于完全隔离状态,还没有与服务的任何上游或下游协作者进行交互。不建议直接部署到生产环境,而是部署到暂存环境,在那里可以针对真实协作者运行进一步的自动化和手动测试。
使用 Kubernetes 命名空间来逻辑隔离暂存和生产环境。为了部署服务,将使用
kubectl
,可以使用 Docker 来包装这个命令行工具。
首先,创建不同的命名空间来隔离工作负载:
kubectl create namespace staging
kubectl create namespace canary
kubectl create namespace production
然后,保存以下部署和服务定义文件到
deploy/staging/market-data.yml
和
deploy/staging/market-data-service.yml
。
# deploy/staging/market-data.yml
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: market-data
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 50%
maxSurge: 50%
template:
metadata:
labels:
app: market-data
tier: backend
track: stable
spec:
containers:
- name: market-data
image: BUILD_TAG
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 8000
livenessProbe:
httpGet:
path: /ping
port: 8000
initialDelaySeconds: 10
timeoutSeconds: 15
readinessProbe:
httpGet:
path: /ping
port: 8000
initialDelaySeconds: 10
timeoutSeconds: 15
# deploy/staging/market-data-service.yml
---
apiVersion: v1
kind: Service
metadata:
name: market-data
spec:
type: NodePort
selector:
app: market-data
tier: backend
ports:
- protocol: TCP
port: 8000
nodePort: 30623
在 Jenkinsfile 中添加一个 Deploy 阶段:
// 在 Jenkinsfile 中添加 Deploy 阶段
stage('Deploy') {
sh("sed -i.bak 's#BUILD_TAG#${tagToDeploy}#' ./deploy/staging/*.yml")
container('kubectl') {
sh("kubectl --namespace=staging apply -f deploy/staging/")
}
}
提交并运行构建,Kubernetes 部署将被触发。可以使用
kubectl rollout status
检查部署状态。
kubectl rollout status –n staging deployment/market-data
部署完成后,可以访问服务:
curl `minikube service --namespace staging --url market-data`/ping
在这个阶段,可以触发进一步的自动化测试或对刚刚部署的服务和代码更改进行探索性测试。以下是可能执行的活动列表:
| 活动 | 描述 |
| — | — |
| 验收测试 | 运行自动化测试以检查期望,无论是回归测试还是验收测试 |
| 手动测试 | 某些服务可能需要手动验证或探索性测试 |
| 非功能测试 | 测试服务的安全态势 |
| 负载/容量测试 | 验证服务的容量和负载期望 |
5. 暂存环境的重要性和挑战
在开发微服务时,应该先将服务的任何新版本发布到暂存环境。微服务需要一起测试,生产环境不是首先进行测试的地方。
暂存环境的基础设施配置应该是生产环境的精确副本,尽管流量较少。不需要以相同的规模运行。用于测试服务的测试量和类型可以确定所需的规模。除了进行各种类型的自动化测试外,还可以手动验证暂存环境中的服务,以确保它们满足验收标准。
除了共享暂存环境,还可以为单个或少量密切相关的服务运行隔离的暂存环境。与完整的暂存环境不同,这些环境可能是临时的,并且可以按需启动,用于测试期间。这对于相对隔离地测试功能很有用,可以更严格地控制环境状态。
然而,暂存环境在微服务应用中可能很难管理,并且可能成为团队之间的重大争议来源。微服务可能有许多依赖项,所有这些依赖项都应该在完整的暂存环境中存在并稳定。尽管暂存环境中的服务已经通过了测试、代码审查和其他质量关卡,但仍然有可能暂存环境中的服务不如生产环境中的服务稳定,这可能会导致混乱。任何部署到共享环境的工程师都需要作为一个好邻居,确保自己拥有的服务问题不会对其他团队顺利测试(并因此交付)其他服务的能力产生重大影响。
为了进一步减少暂存环境中的摩擦,可以考虑构建部署管道,允许任何工程师轻松回滚上次部署,无论他们是否拥有该服务。
6. 部署到生产环境
如果暂存环境的部署成功,可以将服务部署到生产环境。在这个阶段,可能执行的活动包括:
| 活动 | 描述 |
| — | — |
| 代码部署 | 将代码部署到运行时环境 |
| 回滚 | 如果出现错误或意外行为,将代码回滚到上一个版本 |
| 冒烟测试 | 使用轻量级测试验证系统的行为 |
具体步骤如下:
1. 管道应该等待人工批准才能继续部署到生产环境。
2. 获得批准后,首先发布一个金丝雀实例。这有助于验证新构建在面对真实生产流量时是否稳定。
3. 如果对金丝雀实例的性能满意,管道可以继续将其余实例部署到生产环境。
4. 如果不满意,可以回滚金丝雀实例。
在 Jenkinsfile 中添加一个批准阶段:
// 在 Jenkinsfile 中添加批准阶段
stage('Approve release?') {
input message: "Release ${tagToDeploy} to production?"
}
运行这段代码,在 Jenkins 的构建管道视图中会显示一个对话框,有两个选项:Proceed 或 Abort。点击 Abort 将取消构建;点击 Proceed 目前会使构建成功完成,但还没有添加部署步骤。
首先,尝试在没有金丝雀实例的情况下进行生产部署。将之前创建的 YAML 文件复制到新的
deploy/production
目录。可以根据需要增加部署的副本数量。
然后,在 Jenkinsfile 中添加生产发布阶段:
// 在 Jenkinsfile 中添加生产发布阶段
stage('Deploy to production') {
sh("sed -i.bak 's#BUILD_TAG#${tagToDeploy}#' ./deploy/production/*.yml")
container('kubectl') {
sh("kubectl --namespace=production apply -f deploy/production/")
}
}
为了使代码更简洁,可以将与发布相关的代码移动到一个单独的文件
deploy.groovy
中:
// deploy.groovy
def toKubernetes(tagToDeploy, namespace, deploymentName) {
sh("sed -i.bak 's#BUILD_TAG#${tagToDeploy}#' ./deploy/${namespace}/*.yml")
container('kubectl') {
sh("kubectl --namespace=${namespace} apply -f deploy/${namespace}/")
}
}
def kubectl(namespace, command) {
sh("kubectl --namespace=${namespace} ${command}")
}
def rollback(deploymentName) {
kubectl("rollout undo deployment/${deploymentName}")
}
return this;
在 Jenkinsfile 中加载
deploy.groovy
:
// 在 Jenkinsfile 中加载 deploy.groovy
def deploy = load('deploy.groovy')
stage('Deploy to staging') {
deploy.toKubernetes(tagToDeploy, 'staging', 'market-data')
}
stage('Approve release?') {
input "Release ${tagToDeploy} to production?"
}
stage('Deploy to production') {
deploy.toKubernetes(tagToDeploy, 'production', 'market-data')
}
接下来,创建一个金丝雀部署文件。在
deploy/canary
中创建一个类似于生产环境的部署 YAML 文件,但有三个更改:
1. 在 Pod 规范中添加标签
track: canary
。
2. 将副本数量减少到 1。
3. 将部署名称更改为
market-data-canary
。
在 Jenkinsfile 中添加金丝雀发布阶段:
// 在 Jenkinsfile 中添加金丝雀发布阶段
stage('Deploy canary') {
deploy.toKubernetes(tagToDeploy, 'canary', 'market-data-canary')
try {
input message: "Continue releasing ${tagToDeploy} to production?"
} catch (Exception e) {
deploy.rollback('market-data-canary')
}
}
在这个例子中,假设从金丝雀到生产的移动需要人工批准。在现实世界中,这可能是一个自动化的决策,例如,可以编写代码来监控关键指标,如错误率,在金丝雀部署后的一段时间内进行监控。
提交代码后,应该能够运行整个管道。以下是代码从提交到生产的完整流程:
graph LR
A[提交代码] --> B[构建镜像]
B --> C[运行测试]
C --> D[发布工件]
D --> E[部署到暂存环境]
E --> F{批准到生产?}
F -- 是 --> G[部署金丝雀实例]
G --> H{金丝雀实例通过?}
H -- 是 --> I[部署到生产环境]
F -- 否 --> J[停止]
H -- 否 --> K[回滚金丝雀实例]
通过以上步骤,我们使用 Jenkins 构建了一个结构化的部署管道,自动化了代码从提交到生产的交付过程。构建了不同的阶段来验证代码质量,并向工程团队提供了适当的反馈。同时,了解了在开发微服务时暂存环境的重要性和挑战。这些技术为安全快速地将代码交付到生产环境提供了一致可靠的基础,有助于确保微服务应用的整体稳定性和健壮性。
7. 构建可重用的管道步骤
微服务虽然带来了独立性和技术同质性的优势,但也存在一些成本:
- 开发人员在不同团队之间切换更困难,因为技术栈可能差异很大。
- 工程师更难理解不同服务的行为。
- 需要在相同关注点的不同实现上投入更多时间,例如部署、日志记录和监控。
- 人们可能会孤立地做出技术决策,存在局部而非全局优化的风险。
为了在保持技术自由和灵活性的同时平衡这些风险,应该积极标准化服务运行的平台和工具。这样可以确保即使技术栈发生变化,不同服务之间的常见抽象也尽可能接近。
可以标准化微服务应用的许多元素,以减少复杂性、增加重用并降低持续运营成本。例如,标准化部署管道、传输/通信和可观测性等元素。
graph LR
A[服务 A] --> B[部署管道]
A --> C[传输/通信]
A --> D[可观测性]
E[服务 B] --> B
E --> C
E --> D
B --> F[部署平台]
C --> F
D --> F
通过标准化这些元素,可以提高开发效率,减少错误,并使微服务架构更加健壮和可维护。
微服务交付管道构建指南
8. 标准化平台和工具的具体实践
为了实现平台和工具的标准化,我们可以从以下几个方面入手:
8.1 统一部署脚本
将部署相关的逻辑封装到统一的脚本或函数中,如前面提到的
deploy.groovy
文件。这样,不同的服务可以复用这些脚本,减少代码重复。例如,对于不同的微服务,只需要修改
toKubernetes
函数中的参数,就可以实现不同命名空间和部署名称的部署。
// deploy.groovy
def toKubernetes(tagToDeploy, namespace, deploymentName) {
sh("sed -i.bak 's#BUILD_TAG#${tagToDeploy}#' ./deploy/${namespace}/*.yml")
container('kubectl') {
sh("kubectl --namespace=${namespace} apply -f deploy/${namespace}/")
}
}
def kubectl(namespace, command) {
sh("kubectl --namespace=${namespace} ${command}")
}
def rollback(deploymentName) {
kubectl("rollout undo deployment/${deploymentName}")
}
return this;
8.2 统一配置管理
使用统一的配置管理工具,如 Ansible、Chef 或 Puppet,来管理不同服务的配置。这样可以确保所有服务的配置遵循相同的标准和规则。例如,使用 Ansible 可以编写 playbook 来自动化配置不同服务的环境变量、依赖项等。
8.3 统一监控和日志系统
采用统一的监控和日志系统,如 Prometheus 和 Grafana 用于监控,ELK Stack(Elasticsearch、Logstash、Kibana)用于日志管理。这样可以方便地对所有微服务进行监控和分析,及时发现问题并进行处理。
9. 持续改进交付管道
构建好交付管道后,还需要不断地进行持续改进,以提高交付效率和质量。以下是一些持续改进的建议:
9.1 收集和分析指标
收集交付管道中的各种指标,如构建时间、测试通过率、部署成功率等。通过分析这些指标,可以发现瓶颈和问题,并采取相应的措施进行优化。例如,如果发现某个测试阶段的执行时间过长,可以对测试用例进行优化或并行化执行。
9.2 自动化反馈机制
建立自动化的反馈机制,当管道中的某个阶段失败时,及时通知相关的团队成员。可以通过 Slack、Email 等方式进行通知。例如,在 Jenkins 中可以配置当某个阶段失败时,发送邮件给提交代码的作者。
// 在 Jenkinsfile 中配置失败通知
pipeline {
agent any
stages {
stage('Build') {
steps {
sh("docker build -t ${service} .")
}
}
stage('Test') {
steps {
sh("docker run --rm ${service} python setup.py test")
}
}
}
post {
failure {
emailext(
subject: "Pipeline failed: ${env.JOB_NAME}",
body: "The pipeline for ${env.JOB_NAME} has failed. Please check the build logs at ${env.BUILD_URL}.",
to: "${env.COMMIT_AUTHOR_EMAIL}"
)
}
}
}
9.3 定期回顾和优化
定期对交付管道进行回顾和优化。可以组织团队成员进行会议,讨论管道中存在的问题和改进的方向。根据讨论结果,对管道进行相应的调整和优化。
10. 总结
通过构建微服务的交付管道,我们可以实现代码从提交到生产的自动化交付,提高交付效率和质量。在构建过程中,需要注意以下几点:
- 构建不同的阶段来验证代码质量,如构建、测试、发布和部署等阶段。
- 重视暂存环境的作用,先在暂存环境中进行充分的测试,再部署到生产环境。
- 积极标准化服务运行的平台和工具,构建可重用的管道步骤,减少复杂性和重复工作。
- 持续改进交付管道,通过收集和分析指标、建立自动化反馈机制和定期回顾优化等方式,不断提高交付效率和质量。
以下是一个总结表格,列出了构建微服务交付管道的关键步骤和要点:
| 步骤 | 要点 |
| — | — |
| 构建镜像 | 从 Git 检出代码,使用 Docker 构建镜像,可使用 Jenkins 的 Groovy DSL |
| 运行测试 | 包括单元测试、编译、依赖解析等活动,归档测试结果 |
| 发布工件 | 配置 Docker 凭证,将镜像发布到 Docker 注册表 |
| 部署到暂存环境 | 使用 Kubernetes 命名空间隔离环境,替换 YAML 文件中的占位符,部署服务并进行测试 |
| 部署到生产环境 | 等待人工批准,先部署金丝雀实例,根据结果再部署到生产环境 |
| 构建可重用步骤 | 标准化平台和工具,如统一部署脚本、配置管理和监控日志系统 |
| 持续改进 | 收集和分析指标,建立自动化反馈机制,定期回顾和优化 |
通过遵循这些步骤和要点,可以构建一个高效、稳定的微服务交付管道,确保微服务应用的顺利开发和部署。
超级会员免费看
11

被折叠的 条评论
为什么被折叠?



