【问题标题】:(@aws-cdk/pipelines) Build stage for application source code(@aws-cdk/pipelines) 应用程序源代码的构建阶段
【发布时间】:2021-11-29 19:59:39
【问题描述】:

我完成了 CDK Pipelines: Continuous delivery for AWS CDK applications 教程,其中概述了使用新的 CodePipeline API 创建自变异 CDK 管道。

本教程创建一个 CodePipeline,每次将更改推送到主分支时,都会自动从 GitHub 存储库检索 CDK 源代码。 CDK 代码定义了一个带有与 CDK 一起定义的 typescript 处理程序的 lambda。

对于我的用例,我想定义一个自变异 CodePipeline,每当我推送到包含我的应用程序源代码的第二个存储库时也会触发它。第二个存储库还将包含一个构建规范,它使用我的应用程序生成一个 Docker 映像并将该映像上传到 ECR。然后,新映像将在我的管道的应用程序阶段部署到 Fargate 集群。

我在 PublishAssets 阶段之后创建了一个 ApplicationBuild 阶段,其中包括一个 CodeBuild 项目。 CodeBuild 项目从我的存储库中读取并构建/上传图像到 ECR;但是,我需要一种方法将此 CodeBuild 链接到管道的部署。我不清楚如何使用新的 cdk CodePipeline API 做到这一点。

【问题讨论】:

  • 在阅读了stackoverflow.com/q/69371375/17122558 之后,我现在不确定这个功能目前是否可以通过新的 CodePipeline API 实现。
  • 它...可能是但可能不是。您可以将某些东西组合在一起并创建两个工件。基本上,你可能能够做一个源(你的主仓库)-> 代码构建,git 克隆你的管道仓库并将两者作为单独的工件输出。然后您的 SynthPipeline Step 使用第二个工件,而您的部署/构建/其他步骤使用原始工件
  • 感谢您对 lynkfox 发表评论。我不太确定我是否完全遵循了这一点,但我也有点担心变得过于 hacky。我是否认为这是一个非常常见的功能,应该由新 API 原生支持?我很惊讶新 API 是推荐的解决方案。
  • 更新:想出了如何让它与旧的 CdkPipeline API 一起工作(见下面的答案)。谢谢你的灵感,lynkfox!

标签: aws-cdk aws-codepipeline


【解决方案1】:

如果有人遇到同样的问题,我可以按照我在问题中提到的教程的archived version 使用legacy CdkPipeline API 破解一个解决方案。

这是一个最小可行的管道堆栈,包括...

  1. CDK 管道源操作(在“源”阶段)
  2. 应用程序源操作(在“源”阶段)
  3. CDK 构建操作(在“构建”阶段)+ 自变异管道(“更新管道”阶段)
  4. 应用程序构建操作(在“构建”阶段)

lib/cdkpipelines-demo-pipeline-stack.ts

import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions';
import * as core from '@aws-cdk/core';
import {Construct, SecretValue, Stack, StackProps} from '@aws-cdk/core';
import {CdkPipeline, SimpleSynthAction} from "@aws-cdk/pipelines";
import * as iam from "@aws-cdk/aws-iam";
import * as ecr from "@aws-cdk/aws-ecr";
import * as codebuild from "@aws-cdk/aws-codebuild";

/**
 * The stack that defines the application pipeline
 */
export class CdkpipelinesDemoPipelineStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);

        const sourceArtifact = new codepipeline.Artifact();
        const cloudAssemblyArtifact = new codepipeline.Artifact();

        const pipeline = new CdkPipeline(this, 'Pipeline', {
            // The pipeline name
            pipelineName: 'MyServicePipeline',
            cloudAssemblyArtifact,

            // Where the source can be found
            sourceAction: new codepipeline_actions.GitHubSourceAction({
                actionName: 'GitHub',
                output: sourceArtifact,
                oauthToken: SecretValue.secretsManager('github-token'),
                owner: 'OWNER',
                repo: 'REPO',
            }),

            // How it will be built and synthesized
            synthAction: SimpleSynthAction.standardNpmSynth({
                sourceArtifact,
                cloudAssemblyArtifact,

                // We need a build step to compile the TypeScript Lambda
                buildCommand: 'npm run build'
            }),
        });
        const pipelineRole = pipeline.codePipeline.role;

        // Add application source action
        const appSourceArtifact = new codepipeline.Artifact();
        const appSourceAction = this.createAppSourceAction(appSourceArtifact);
        const sourceStage = pipeline.stage("Source");
        sourceStage.addAction(appSourceAction);

        // Add application build action
        const codeBuildServiceRole = this.createCodeBuildServiceRole(this, pipelineRole);
        const repository = this.createApplicationRepository(this, codeBuildServiceRole);
        const pipelineProject = this.createCodeBuildPipelineProject(
            this, codeBuildServiceRole, repository, 'REGION', 'ACCOUNT_ID');
        const appBuildOutput = new codepipeline.Artifact();
        const appBuildAction = this.createAppCodeBuildAction(
            this, appSourceArtifact, appBuildOutput, pipelineProject, codeBuildServiceRole);
        const buildStage = pipeline.stage("Build");
        buildStage.addAction(appBuildAction);

        // This is where we add the application stages...
    }

    createAppSourceAction(appSourceArtifact: codepipeline.Artifact): codepipeline_actions.GitHubSourceAction {
        return new codepipeline_actions.GitHubSourceAction({
            actionName: 'GitHub-App-Source',
            output: appSourceArtifact,
            oauthToken: SecretValue.secretsManager('github-token'),
            owner: 'SOURCE-OWNER',
            repo: 'SOURCE-REPO',
        });
    }

    createCodeBuildServiceRole(scope: core.Construct, pipelineRole: iam.IRole): iam.Role {
        const role = new iam.Role(scope, 'CodeBuildServiceRole', {
            assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
        });
        role.assumeRolePolicy?.addStatements(new iam.PolicyStatement({
            sid: "PipelineAssumeCodeBuildServiceRole",
            effect: iam.Effect.ALLOW,
            actions: ["sts:AssumeRole"],
            principals: [pipelineRole]
        }));

        // Required policies to create an AWS CodeBuild service role
        role.addToPolicy(new iam.PolicyStatement({
            sid: "CloudWatchLogsPolicy",
            effect: iam.Effect.ALLOW,
            actions: [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            resources: ["*"]
        }));
        role.addToPolicy(new iam.PolicyStatement({
            sid: "CodeCommitPolicy",
            effect: iam.Effect.ALLOW,
            actions: ["codecommit:GitPull"],
            resources: ["*"]
        }));
        role.addToPolicy(new iam.PolicyStatement({
            sid: "S3GetObjectPolicy",
            effect: iam.Effect.ALLOW,
            actions: [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            resources: ["*"]
        }));
        role.addToPolicy(new iam.PolicyStatement({
            sid: "S3PutObjectPolicy",
            effect: iam.Effect.ALLOW,
            actions: [
                "s3:PutObject"
            ],
            resources: ["*"]
        }));
        role.addToPolicy(new iam.PolicyStatement({
            sid: "S3BucketIdentity",
            effect: iam.Effect.ALLOW,
            actions: [
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ],
            resources: ["*"]
        }));

        // This statement allows CodeBuild to upload Docker images to Amazon ECR repositories.
        // source: https://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker.html#sample-docker-running
        role.addToPolicy(new iam.PolicyStatement({
            sid: "ECRUploadPolicy",
            effect: iam.Effect.ALLOW,
            actions: [
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            resources: ["*"]
        }));

        return role;
    }

    createApplicationRepository(scope: core.Construct, codeBuildServiceRole: iam.Role): ecr.Repository {
        const repository = new ecr.Repository(scope, 'Repository', {
            repositoryName: 'cdkpipelines-demo-image-repository'
        });
        repository.grantPullPush(codeBuildServiceRole);
        return repository;
    }

    createCodeBuildPipelineProject(scope: core.Construct,
                                   codeBuildServiceRole: iam.Role,
                                   repository: ecr.Repository,
                                   region: string,
                                   accountId: string): codebuild.PipelineProject {
        return new codebuild.PipelineProject(scope, 'BuildProject', {
            buildSpec: codebuild.BuildSpec.fromSourceFilename("buildspec.yml"),
            environment: {
                buildImage: codebuild.LinuxBuildImage.fromCodeBuildImageId("aws/codebuild/standard:4.0"),
                privileged: true,
                computeType: codebuild.ComputeType.SMALL,
                environmentVariables: {
                    AWS_DEFAULT_REGION: {value: region},
                    AWS_ACCOUNT_ID: {value: accountId},
                    IMAGE_REPO_NAME: {value: repository.repositoryName},
                    IMAGE_TAG: {value: "latest"},
                }
            },
            role: codeBuildServiceRole
        });
    }

    createAppCodeBuildAction(scope: core.Construct,
                             input: codepipeline.Artifact,
                             output: codepipeline.Artifact,
                             pipelineProject: codebuild.PipelineProject,
                             serviceRole: iam.Role) {
        return new codepipeline_actions.CodeBuildAction({
            actionName: "App-Build",
            checkSecretsInPlainTextEnvVariables: false,
            input: input,
            outputs: [output],
            project: pipelineProject,
            role: serviceRole,
            type: codepipeline_actions.CodeBuildActionType.BUILD,
        })
    }
}

【讨论】:

    猜你喜欢
    • 2021-09-30
    • 2021-03-10
    • 1970-01-01
    • 2023-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多