log
码中赤兔

AWS CDK 使用 GitHub 集成创建 CodePipeline 示例

发布于 2025年1月15日
更新于 2025年1月17日
25 分钟阅读
AWS

介绍

在本文中,我们将探索如何使用 AWS CDK 创建一个 AWS CodePipeline。该 CodePipeline 包括与 GitHub 的源代码集成、使用 CodeBuild 构建应用程序,以及通过 CloudFormation 部署资源。此外,我们还将配置一个自定义的 S3 存储桶用于存储流水线的构建工件。


前置条件

在开始之前,请确保您已完成以下准备工作:

  1. 已安装并配置 AWS CDK。
  2. 拥有一个包含应用程序代码和 buildspec.yml 文件的 GitHub 仓库。
  3. 将 GitHub OAuth 令牌存储在 AWS Secrets Manager 中。
  4. 为 CodePipeline 和 CodeBuild 配置了适当的 IAM 角色。

关键组件

该CodePipeline包含三个阶段:

  1. 源阶段(Source Stage): 从 GitHub 仓库获取代码。
  2. 构建阶段(Build Stage): 使用 CodeBuild 构建应用程序。
  3. 部署阶段(Deploy Stage): 使用 CloudFormation 部署资源。

代码详解

以下是用于创建CodePipeline的 CDK 代码的详细说明。

1. 资源存储桶

创建一个 S3 存储桶用于存储CodePipeline运行中的asset文件。该存储桶配置了生命周期规则,在 7 天后删除文件。

const assetBucket = new s3.Bucket(this, '<construct id>', {
  bucketName: '<bucketName>',
  lifecycleRules: [
    {
      id: 'DeleteAfter7Days',
      enabled: true,
      expiration: cdk.Duration.days(7),
    },
  ],
});

2. IAM 角色

CodePipeline 使用一个通过 ARN 指定的现有 IAM 角色。

const codepipelineRole = iam.Role.fromRoleArn(this, '<construct id>', '<CODE_PIPELINE_ROLE_ARN>', {
  mutable: false,
});

3. 构建项目

配置一个 CodeBuild 项目用于构建应用程序。该项目使用 Amazon Linux 2 作为构建环境。

const buildProject = new codebuild.PipelineProject(this, '<construct id>', {
  projectName: '<projectName>',
  role: codepipelineRole,
  queuedTimeout: cdk.Duration.hours(0.5),
  timeout: cdk.Duration.hours(0.5),
  buildSpec: codebuild.BuildSpec.fromSourceFilename('buildspec.yml'), 
  environment: {
    buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_5,
    computeType: codebuild.ComputeType.SMALL,
    environmentVariables: {
      XX: {
        type: codebuild.BuildEnvironmentVariableType.PLAINTEXT,
        value: '<value>',
      },
    },
  },
});

这里指定了 buildspec.yml 文件,该文件包含构建命令和 CodeBuild 项目的配置,确保构建过程被定义且可以与应用程序代码一起进行版本控制。

4. Pipeline 定义

配置CodePipeline的三个阶段:源代码、构建和部署,并使用前面定义的 S3 存储桶和 IAM 角色。

const pipeline = new codepipeline.Pipeline(this, '<construct id>', {
  pipelineName: '<pipelineName>',
  artifactBucket: assetBucket,
  role: codepipelineRole,
  pipelineType: codepipeline.PipelineType.V1,
});

5. 源代码阶段

源代码阶段通过 Webhook 从 GitHub 仓库拉取代码。GitHub OAuth 令牌通过 AWS Secrets Manager 安全获取。

const sourceAction = new codepipeline_actions.GitHubSourceAction({
  actionName: 'GitHubSource',
  owner: '<GitHub account user>',
  repo: '<GitHub repository>',
  branch: '<branch>',
  oauthToken: cdk.SecretValue.secretsManager('github-token', {
    jsonField: 'oauthToken',
  }),
  output: sourceOutput,
  trigger: codepipeline_actions.GitHubTrigger.WEBHOOK,
  runOrder: 1,
});

说明

  • cdk.SecretValue.secretsManager: 此方法安全地获取存储在 AWS Secrets Manager 中的密钥。
  • 'github-token': Secrets Manager 中密钥的名称。此密钥必须事先存在,并包含必要的 OAuth 令牌。
  • jsonField: 'oauthToken': 在此示例中,OAuth 令牌保存在 oauthToken 字段里。
  • 此方法确保敏感凭据(如 OAuth 令牌)不会硬编码在应用程序中,从而提高安全性。

6. 构建阶段

构建阶段执行之前定义的 CodeBuild 项目。

const buildAction = new codepipeline_actions.CodeBuildAction({
  actionName: 'Build',
  project: buildProject,
  input: sourceOutput,
  outputs: [buildOutput],
  role: codepipelineRole,
  runOrder: 2,
});

7. 部署阶段

部署阶段使用 CloudFormation 动作创建或更新堆栈。

const deployAction = new codepipeline_actions.CloudFormationCreateUpdateStackAction({
  actionName: 'CloudFormation-CreateUpdateStack',
  stackName: '<stackName>',
  adminPermissions: true,
  templatePath: buildOutput.atPath('cloudformation-template.yaml'),
  deploymentRole: codepipelineRole,
  replaceOnFailure: true,
  role: codepipelineRole,
  runOrder: 3,
});

这里指定的 cloudformation-template.yaml 文件在构建阶段中通过执行cdk synth命令来获取的。

8. 添加阶段到CodePipeline

最后,将各阶段添加到CodePipeline中。

pipeline.addStage({
  stageName: 'Source',
  actions: [sourceAction],
});

pipeline.addStage({
  stageName: 'Build',
  actions: [buildAction],
});

pipeline.addStage({
  stageName: 'Deploy',
  actions: [deployAction],
});

完整代码

import * as cdk from 'aws-cdk-lib';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';
import type { Construct } from 'constructs';

export class CodePipelineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    /** asset bucket */
    const assetBucket = new s3.Bucket(this, `<construct id>`, {
      bucketName: `<bucketName>`,
      lifecycleRules: [
        {
          id: 'DeleteAfter7Days',
          enabled: true,
          expiration: cdk.Duration.days(7),
        },
      ],
    });

    const codepipelineRole = iam.Role.fromRoleArn(this, `<construct id>`, '<CODE_PIPELINE_ROLE_ARN>', {
      mutable: false,
    });

    const buildProject = new codebuild.PipelineProject(this, `<construct id>`, {
      projectName: `<projectName>`,
      role: codepipelineRole,
      queuedTimeout: cdk.Duration.hours(0.5),
      timeout: cdk.Duration.hours(0.5),
      buildSpec: codebuild.BuildSpec.fromSourceFilename('buildspec.yml'),
      environment: {
        buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_5,
        computeType: codebuild.ComputeType.SMALL,
        environmentVariables: {
          XX: {
            type: codebuild.BuildEnvironmentVariableType.PLAINTEXT,
            value: '<value>',
          },
        },
      },
    });

    const pipeline = new codepipeline.Pipeline(this, `<construct id>`, {
      pipelineName: `<pipelineName>`,
      artifactBucket: assetBucket,
      role: codepipelineRole,
      pipelineType: codepipeline.PipelineType.V1,
    });

    const sourceOutput = new codepipeline.Artifact();
    const buildOutput = new codepipeline.Artifact();

    const sourceAction = new codepipeline_actions.GitHubSourceAction({
      actionName: 'GitHubSource',
      owner: '<GitHub account user>',
      repo: '<GitHub repository>',
      branch: '<branch>',
      oauthToken: cdk.SecretValue.secretsManager('github-token', {
        jsonField: 'oauthToken',
      }),
      output: sourceOutput,
      trigger: codepipeline_actions.GitHubTrigger.WEBHOOK,
      runOrder: 1,
    });

    const buildAction = new codepipeline_actions.CodeBuildAction({
      actionName: 'Build',
      project: buildProject,
      input: sourceOutput,
      outputs: [buildOutput],
      role: codepipelineRole,
      runOrder: 2,
    });

    const deployAction = new codepipeline_actions.CloudFormationCreateUpdateStackAction({
      actionName: 'CloudFormation-CreateUpdateStack',
      stackName: `<stackName>`,
      adminPermissions: true,
      templatePath: buildOutput.atPath(`cloudformation-template.yaml`),
      deploymentRole: codepipelineRole,
      replaceOnFailure: true,
      role: codepipelineRole,
      runOrder: 3,
    });

    pipeline.addStage({
      stageName: 'Source',
      actions: [sourceAction],
    });

    pipeline.addStage({
      stageName: 'Build',
      actions: [buildAction],
    });

    pipeline.addStage({
      stageName: 'Deploy',
      actions: [deployAction],
    });
  }
}

结论

此示例展示了如何使用 AWS CDK 构建一个强大的 CI/CD CodePipeline。通过与 GitHub 集成,可以在每次提交时自动触发CodePipeline,而通过 CloudFormation 部署可以确保您的基础设施始终保持最新。

通过此CodePipeline,您可以简化部署流程,更专注于为应用程序开发新功能。

关于

分享技术见解、经验和思考的个人博客

联系方式

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 码中赤兔. 版权所有