本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
将工作负载从 Azure DevOps 管道部署到私有的 Amazon EKS
Mahendra Revanasiddappa,Amazon Web Services
Summary
此模式演示了如何实现从 Azure DevOps 管道到私有亚马逊 Elastic Kubernetes Service (Amazon EKS) 集群的持续集成和持续交付 (CI/CD)。此模式解决了组织在通过过渡至 Amazon EKS 集群的私有 API 服务器端点来增强其安全态势时所面临的一项关键挑战。
公有端点会直接向互联网公开 Kubernetes API 服务器,从而可能会扩大恶意行为者的攻击面。切换到私有端点后,仅限在客户的虚拟私有云(VPC)内访问集群的控制面板。
尽管将 Amazon EKS 集群过渡到私有 API 终端节点可以显著增强安全性,但它会给 Azure DevOps 等外部 CI/CD 平台带来连接挑战。只能从集群的 VPC 或对等网络中访问私有端点。因此,在 AWS 私有网络之外运行的标准微软托管的 Azure DevOps 代理无法直接访问 Kubernetes API 服务器。这就破坏了依赖于在这些代理上运行的工具(例如 kubectl 或 Helm)的典型部署工作流,因为它们无法与集群建立连接。
为了克服这个问题,这种模式展示了一种有效的方法,即在私有 Amazon EKS 集群中使用自托管 Azure DevOps 代理。该解决方案提供了卓越的成本优化、运营效率和可扩展性,同时保留了安全要求。这种方法特别有利于寻求在不影响性能或安全性的前提下简化多云 DevOps 流程的企业。
先决条件和限制
先决条件
活跃 AWS 账户的.
AWS Command Line Interface (AWS CLI) 版本 2.13.17 或更高版本,已安装。
kubectl 版本 1.25.1 或更高版本,已安装。
已创建私有 Amazon EKS 集群版本 1.24 或更高版本,具有创建命名空间、密钥和部署的权限。
Amazon EKS 集群中的工作节点具有互联网的出站连接,因此在其上运行的 Azure DevOps 代理可以连接到 Azure DevOps 代理池。
GitHub 账户已创建。
创建了一个有权配置服务连接的 Azure DevOps 项目,这些连接是 Azure Pipelines 与外部或远程服务之间经过身份验证的连接。
为上一点中描述的 Azure DevOps 项目安装的 1.15 或更高 AWS Toolkit for Azure DevOps 版本。有关安装说明,请参阅 Visual Studio Marketplace 中的 AWS Toolkit for Azure DevOps。
限制
架构
此模式会创建以下内容:
亚马逊 ECR 存储库 ——亚马逊弹性容器注册表 (Amazon ECR) Registry 存储库将 Docker 映像与 Azure DevOps 代理和部署的示例应用程序一起存储。
Azure DevOps 代理池-Azure DevOps 自托管代理池注册在私有 Amazon EKS 集群上运行的代理。
IAM 角色-Azure 服务连接的 AWS Identity and Access Management (IAM) 角色,用于为在私有 Amazon EKS 集群上运行的代理提供所需的访问权限。
Azure DevOps 服务连接-Azure DevOps 账户中的服务连接,用于使用 IAM 角色,为管道作业提供访问所需的访问权限 AWS 服务。
下图显示了在私有 Amazon EKS 集群上部署自托管 Azure DevOps 代理并在同一集群上部署示例应用程序的架构。
下图显示了如下工作流:
将自托管的 Azure DevOps 代理部署为在 Amazon EKS 集群中的部署。
Azure DevOps 代理使用个人访问令牌 (PAT) 进行身份验证,连接到 Azure DevOps 帐户上的代理池。
Azure Pipelines 使用 GitHub 存储库中的代码配置要部署的管道。
该管道可在代理池中的代理上运行,其中该代理池可管道配置中进行配置。Azure DevOps 代理通过不断轮询到 Azure DevOps 帐户来获取管道的作业信息。
Azure DevOps 代理在管道作业中构建 Docker 映像,并将该映像推送到 Amazon ECR 存储库。
Azure DevOps 代理将示例应用程序部署到名为的命名webapp空间中的私有 Amazon EKS 集群上。
工具
其他工具
代码存储库
最佳实践
操作说明
| Task | 说明 | 所需技能 |
|---|
查找 Azure DevOps 组织指南。 | 登录你的 Azure DevOps 帐户,然后使用以下 URL 查找组织 GUID:https://dev.azure.com/{DevOps_Org_ID}/_apis/projectCollections?api-version=6.0在网址中,{DevOps_org_ID}替换为你的 Azure DevOps 组织 ID。 | AWS DevOps |
在 AWS 账户中配置 IdP。 | 要在中为 Azure 服务连接配置身份提供者 (IdP),请使用以下步骤: AWS 账户 登录并打开 IAM 控制台,网址为https://console.aws.amazon.com/iam/。 AWS 管理控制台 在左侧窗格中,选择身份提供者。 选择 Add Provider(添加提供程序)。 选择 OpenID Connect 作为提供者类型。 在提供者网址中,输入 Azure DevOps 发行者网址。Azure DevOps 的每个租户都有唯一的OrganizationGUID,通常使用以下格式:https://vstoken.dev.azure.com/{OrganizationGUID}{OrganizationGUID}替换为你的 Azure DevOps 组织 ID。 对于受众,请输入 api: //Azure ADToken 交易所。这是 Azure 的固定值 DevOps。 选择 Add Provider(添加提供程序)。 记下新创建的提供商的 ARN,以便您在下一个任务中使用。
有关更多详细信息,请参阅如何 DevOps 使用 OpenID Connect AWS 从 Azure 联合到。 | AWS DevOps |
在 AWS 账户中创建 IAM 策略。 | 要创建 IAM 策略以向 Azure DevOps 管道使用的 IAM 角色提供所需权限,请使用以下步骤: 在 IAM 控制台的左侧窗格中,选择策略。 选择创建策略。 对于指定权限,在策略编辑器中,选择 JSON。将默认 JSON 策略替换为以下 JSON:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Action": [
"ecr:*",
"eks:DescribeCluster",
"eks:ListClusters"
],
"Resource": "*"
}
]
}
选择下一步。 对于策略名称,输入 IAM 策略的名称。此模式使用名称 ADO-policy。 选择创建策略。
| AWS DevOps |
在 AWS 账户中创建 IAM 角色。 | 要在中为 Azure 服务连接配置 IAM 角色,请使用以下步骤: AWS 账户 在 IAM 控制台的左侧窗格中,选择角色。 选择创建角色。 对于可信实体类型,选择 Web 身份。 从下拉列表中选择正确的 IdP。IdP 名称以 vstoken.dev.azure.com/{OrganizationGUID} 开头。 在 “受众” 下拉列表中,选择 api: //Azure ADToken Exchange。 要将此角色限制为仅一个服务连接,请添加一个条件。在条件下,选择添加条件,而对于密钥,选择 vstoken.dev.azure.com/{OrganizationGUID}:sub。在 “状况” 中,选择StringEquals。对于值,使用以下格式:sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}。对于 ServiceConnectionName,使用 aws-sc。您将在下一个任务中创建此服务连接。 选择下一步。 对于添加权限,选择 ADO-policy,这是您在上一个任务中创建的策略。 选择下一步,对于角色名称,输入 ado-role。对于选择可信实体,使用以下信任策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{account_id}:oidc-provider/vstoken.dev.azure.com/{OrganizationGUID}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"vstoken.dev.azure.com/{OrganizationGUID}:aud": "api://AzureADTokenExchange",
"vstoken.dev.azure.com/{OrganizationGUID}:sub": "sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}"
}
}
}
]
}
在策略中,针对以下占位符提供您的信息: {account_id}- AWS 账户 身份证
{OrganizationGUID}-Azure DevOps 组织指南
{OrganizationName}-Azure DevOps 组织名称
{ProjectName}-Azure DevOps 项目名称
{ServiceConnectionName}-Azure DevOps 服务连接名称。使用 aws-sc。您将在下一个任务中创建此服务连接。
| AWS DevOps |
在 Azure DevOps 帐户中创建服务连接。 | 要配置 Azure 服务连接,请使用以下步骤: 在你的 Azure DevOps 项目中,选择项目设置、服务连接。 选择新建服务连接,选择服务连接的类型为 aws,然后选择下一步。 对于要代入的角色,请输入 IAM 角色 ado-role。在上一个任务在 AWS 账户中创建 IAM 角色中,您已创建了ado-role 。 选中使用 OIDC复选框。 对于服务连接名称,在任务属性中输入 aws-sc。 选择保存。
有关更多详细信息,请参阅 Microsoft 文档中的创建服务连接。 | AWS DevOps |
将 IAM 角色添加到 Amazon EKS 配置文件。 | IAM 角色必须具有在 Amazon EKS 集群上执行所需操作的必要权限。由于 IAM 角色是一个管道角色,因此该角色必须能够管理集群上几乎所有类型的资源。因此,system:masters 组权限适用于此角色。 要将所需的配置添加到 Kubernetes 内的 aws-auth ConfigMap 中,请使用以下代码: - groups:
- system:masters
rolearn: arn:aws:iam::{account_id}:role/ADO-role
username: ADO-role
{account_id}用您的 AWS 账户 身份证替换。
有关更多详细,请参阅 Amazon EKS 文档中的Amazon EKS 如何与 IAM 配合使用。 | AWS DevOps |
| Task | 说明 | 所需技能 |
|---|
创建自托管代理池。 | 要在 Azure DevOps 帐户中配置自托管代理池,请使用以下步骤: 登录到你的 Azure DevOps 帐户组织。 选择 Azure DevOps 组织。 选择你的 Azure DevOps 项目。 选择 Project settings (项目设置)。 选择代理池。 选择添加池。 选择自托管。 对于名称,输入 eks-agent。 选中向所有管道授予访问权限复选框。 选择创建。
有关更多详细信息,请参阅 Microsoft 文档中的创建和管理代理池。 | |
| Task | 说明 | 所需技能 |
|---|
创建 Amazon ECR 存储库。 | 用于在私有 Amazon EKS 集群上部署 Azure DevOps 代理和示例应用程序 (webapp) 的 Docker 映像必须存储在 Amazon ECR 存储库中。要创建 Amazon ECR 存储库,请使用以下步骤: 有关更多详细信息,请参阅 Amazon ECR 文档中的创建 Amazon ECR 私有存储库来存储映像。 | AWS DevOps |
创建一个 Dockerfile 来构建 Azure 代理 DevOps 。 | 创建一个 Dockerfile 来生成安装了 Azure DevOps 代理的 Docker 镜像。将以下内容存储在名为 Dockerfile 的文件中:
FROM ubuntu:22.04
ENV TARGETARCH="linux-x64"
RUN apt update && apt upgrade -y && apt install -y curl git jq libicu70 unzip wget
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN ./aws/install
RUN rm -rf aws awscliv2.zip
RUN curl -sSL https://get.docker.com/ | sh
RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash
RUN mkdir -p azp
WORKDIR /azp/
COPY ./start.sh ./
RUN chmod +x ./start.sh
RUN useradd -m -d /home/agent agent
RUN chown -R agent:agent /azp /home/agent
RUN groupadd -f docker
RUN usermod -aG docker agent
USER agent
ENTRYPOINT [ "./start.sh" ]
| AWS DevOps |
为 Azure DevOps 代理创建脚本。 | 要创建 start.sh 脚本,请使用以下步骤: 转到 Microsoft 文档中的创建和构建 Dockerfile 程序,然后滚动到步骤 5。将以下内容保存到 ~/azp-agent-in-docker/start.sh,确认使用 Unix 样式(LF)行尾。 复制脚本的内容,并将其保存在名为 start.sh 的文件中,其中该文件与 Dockerfile 位于同一个目录下。
| AWS DevOps |
使用 Azure DevOps 代理构建 Docker 镜像。 | 要创建 Docker 镜像来安装 Azure DevOps 代理,请使用你之前创建的 Dockerfile 来构建镜像。在存储 Dockerfile 的同一个目录中,运行以下命令: aws ecr get-login-password --region region | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.region.amazonaws.com
docker build --platform linux/amd64 -t ado-agent:latest .
docker tag ado-agent:latest aws_account_id.dkr.ecr.region.amazonaws.com/webapp:latest
docker push aws_account_id.dkr.ecr.region.amazonaws.com/webapp:latest
用您的 AWS 账户 ID 替换aws_account_id和region,然后 AWS 区域。 | AWS DevOps |
| Task | 说明 | 所需技能 |
|---|
生成 Azure 个人访问令牌。 | 在私有 Amazon EKS 集群上运行的代理需要个人访问令牌 (PAT),这样它才能使用 Azure DevOps 账户进行身份验证。要生成 PAT,请使用以下步骤: 使用你计划在 Azure DevOps 组织中使用的用户帐户登录 (https://dev.azure.com/{Your_Organization})。
从您的主页,打开您的用户设置,然后选择个人访问令牌。 选择新令牌。 输入令牌的名称。 选择显示所有范围。 对于代理池,选中读取并管理复选框。 选择创建。 要在私有 Amazon EKS 集群上创建密钥,请使用以下配置:
apiVersion: v1
kind: Secret
metadata:
name: azdevops-pat
namespace: default
type: Opaque
stringData:
AZP_TOKEN: <PAT Token>
将配置存储在名为 ado-secret.yaml 的文件中。将 <PAT Token> 替换为您刚刚创建的个人访问令牌。要创建密钥,请运行以下命令:
kubectl create -f ado-secret.yaml
有关更多详细信息,请参阅 Microsoft 文档中的使用个人访问令牌(PAT)注册代理。 | AWS DevOps |
使用 Kubernetes 清单文件以进行代理部署。 | 要在私有 Amazon EKS 集群上部署 Azure DevOps 代理,请复制以下清单文件并将该文件存储为agent-deployment.yaml: apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-pipelines-agent-eks
labels:
app: azure-pipelines-agent
spec:
replicas: 1
selector:
matchLabels:
app: azure-pipelines-agent
template:
metadata:
labels:
app: azure-pipelines-agent
spec:
containers:
- name: docker
image: docker:dind
securityContext:
privileged: true
volumeMounts:
- name: shared-workspace
mountPath: /workspace
- name: dind-storage
mountPath: /var/lib/docker
env:
- name: DOCKER_TLS_CERTDIR
value: ""
- name: azure-pipelines-agent
image: aws_account_id.dkr.ecr.region.amazonaws.com/webapp:latest
env:
- name: AZP_URL
value: "<Azure account URL>"
- name: AZP_POOL
value: "eks-agent"
- name: AZP_TOKEN
valueFrom:
secretKeyRef:
name: azdevops-pat
key: AZP_TOKEN
- name: AZP_AGENT_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: DOCKER_HOST
value: tcp://localhost:2375
volumeMounts:
- mountPath: /workspace
name: shared-workspace
volumes:
- name: dind-storage
emptyDir: {}
- name: shared-workspace
emptyDir: {}
将aws_account_id和<Azure account URL>替换为你的 AWS 账户 ID 和 Azure DevOps 帐户网址。 | AWS DevOps |
在私有 Amazon EKS 集群上部署代理。 | 要在私有 Amazon EKS 集群上部署 Azure DevOps 代理,请使用以下命令: kubectl create -f agent-deployment.tf
| AWS DevOps |
验证代理是否正在运行。 | 要验证 Azure DevOps 代理是否正在运行,请使用以下命令: kubectl get deploy azure-pipelines-agent-eks
该预期输出应该类似于以下所示:
NAME READY UP-TO-DATE AVAILABLE AGE
azure-pipelines-agent-eks 1/1 1 1 58s
确认 READY 列已显示 1/1。 | AWS DevOps |
验证代理是否已在 Azure DevOps 代理池中注册。 | 要验证代理是否已部署在私有 Amazon EKS 集群上并已向代理池 eks-agent 中注册,请使用以下步骤: 登录到你的 Azure DevOps 组织 (https://dev.azure.com/{Your_Organization})。 选择 Project settings (项目设置)。 选择代理池。 选择 eks-agent 池,然后检查代理选项卡。
您应该看到列出的代理的状态为 “在线”,并且该代理的名称应以 azure-pipelines-agent-eks-* 开头。 | AWS DevOps |
| Task | 说明 | 所需技能 |
|---|
将示例应用程序存储库分支到您的 GitHub 账户。 | 将以下 AWS 示例存储库分支到您的 GitHub 账户: https://github.com/aws-samples/deploy-kubernetes-resources-to-amazon-eks-using-azure-devops | AWS DevOps |
创建管道。 | 要在你的 Azure DevOps 帐户中创建管道,请使用以下步骤: 使用你计划在 Azure DevOps 组织中使用的用户帐户登录 (https://dev.azure.com/{Your_Organization})。 导航到您的项目和管道控制台。 选择新管道。 对于 “您的代码在哪里”,选择GitHub。 提供管道连接到您的 GitHub 账户所需的凭证 选择存储库 deploy-kubernetes-resources-to-amazon-eks-using-azure-devops。 对于配置管道,选择现有 Azure Pipelines YAML 文件。 对于选择现有 YAML 文件,为分支选择主,并为路径选择 azure_pipelines.yaml。 选择继续。 要审查您的管道 YAML,请将 awsRegion 和 awsEKSClusterName 的输入参数值替换为您的信息:
pool:
name: eks-agent
#pool: self-hosted # If you are running self-hosted Azure DevOps Agents
stages:
# Refering the pipeline template, input parameter that are not specified will be added with defaults
- template: ./pipeline_templates/main_template.yaml
parameters:
serviceConnectionName: aws-sc
awsRegion: <your region>
awsEKSClusterName: <name of your EKS cluster>
projectName: webapp
| AWS DevOps |
验证示例应用程序是否已部署。 | 管道完成后,检查 Amazon ECR 存储库和 Amazon EKS 集群,进而验证示例应用程序是否已成功部署。 要验证 Amazon ECR 存储库中的构件,请使用以下步骤: 导航到 webapp Amazon ECR 存储库。 确认是否存在以下新构件:
例如,20250501.1-image 和 20250501.1-helm。 要在命名空间 webapp 中验证私有 Amazon EKS 集群上的部署,请使用以下命令: kubectl get deploy -n webapp
预期的输出如下所示:
NAME READY UP-TO-DATE AVAILABLE
webapp 1/1 1 1
注意:如果这是您首次运行管道,则可能需要对服务连接和代理池执行授权。在 Azure DevOps 管道界面中查找权限请求,然后批准它们以继续。 | AWS DevOps |
问题排查
| 问题 | 解决方案 |
|---|
当 Amazon ECR 存储库名称与 webapp 不匹配时,管道失败 | 示例应用程序希望 Amazon ECR 存储库名称与 azure_pipeline.yml 中的 projectName: webapp 参数相匹配。 要解决此问题,请将您的 Amazon ECR 存储库重命名为 webapp,或更新以下内容: |
错误:Kubernetes 集群无法访问:服务器已要求客户端提供凭证 | 如果您在 Azure Pipelines 的“提取并部署 Helm 图表”步骤中遇到此错误,则根本原因通常源于 Amazon EKS 集群 aws-auth ConfigMap 中的 IAM 角色配置不正确。 要解决此问题,请检查以下操作: |
相关资源
AWS 博客
AWS 服务 文档
微软文档