Compile outputs fun

AWS CloudFormation: 07 - Build and deploy pipeline for Docker instance

Published a year agoAWS, CloudFormation

This is continued from AWS CloudFormation: 06 - Collect logs from Docker instance .

Background

I want to build my software and deploy it to the Docker instance.

What we need?

  1. A Docker registry to store the Docker images.
  2. A CodeBuild setup to build the software.
  3. A CodeDeploy setup to deploy the software.
  4. A CodePipeline setup to build and deploy the software.

How to do it?

This is what we are going to do:

Open 06-container-cloudwatch-template.yaml from previous post and save as 07-container-codepipeline-template.yaml .

Container

We need to add the policy to allow the container service to grab the images from our new Docker registry. Change the WebServerContainerExecutionServerRole like this:

1   WebServerContainerExecutionServerRole:
    Type: AWS::IAM::Role
    Properties:
...
      Policies:
        - PolicyName: WebServerContainerExecutionServiceRole
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
...
              - Resource: !Sub ${CodePipelineContainerRegistry.Arn}
                Action: ecr:*
                Effect: Allow
Add the policy to access the Docker registry.
              - Resource: "*"
                Action:
                  - ecr:GetAuthorizationToken
                  - ecs:DeregisterTaskDefinition
                  - ecs:RegisterTaskDefinition
                Effect: Allow
Add the policy for other required actions.

Build and Deploy Pipeline

We will need to setup a pipeline to build and deploy our software. Add these resources:

1   CodePipelineContainerRegistry:
    Type: AWS::ECR::Repository
The Docker registry to store the Docker images.
2   CodePipelineBucket:
    Type: AWS::S3::Bucket
The S3 bucket to store the source artifacts.
3   CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: CodeBuildServicePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Resource:
                  - !Sub ${CodePipelineBucket.Arn}
                  - !Sub ${CodePipelineBucket.Arn}/*
                Action: s3:*
                Effect: Allo
The IAM Role to allow the CodeBuild project to access the S3 bucket.
              - Resource: !Sub ${CodePipelineContainerRegistry.Arn}
                Action: ecr:*
                Effect: Allow
And the Docker registry.
              - Resource: "*"
                Action: ecr:GetAuthorizationToken
                Effect: Allow
And some other required actions.
3   CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      ServiceRole: !Ref CodeBuildServiceRole
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        ComputeType: BUILD_GENERAL1_SMALL
The CodeBuild project to build the software.
        PrivilegedMode: true This is required to build Docker image.
      Source:
        Type: CODEPIPELINE
        BuildSpec: !Sub |
          version: 0.2
          phases:
            install:
              runtime-versions:
                docker: 19
            pre_build:
              commands:
                - echo Logging in to Amazon ECR...
                - aws --version
                - $(aws ecr get-login --region ${AWS::Region} --no-include-email)
                - REPOSITORY_URI=${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${CodePipelineContainerRegistry}
                - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
                - IMAGE_TAG=${!COMMIT_HASH:=latest}
The required steps to enable publishing Docker image to the Docker registry.
            build:
              commands:
                - echo Build started on `date`
                - echo Building the Docker image...
                - cd write-it-down-web
                - npm install
                - npm run build
The steps to build the software.
                - |
                    cat > Dockerfile <<EOF
                    FROM nginx
                    COPY build /usr/share/nginx/html/
                    EOF
                - docker build -t $REPOSITORY_URI:latest .
                - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
Add the Dockerfile dynamically to the project.
            post_build:
              commands:
                - echo Build completed on `date`
                - echo Pushing the Docker images...
                - docker push $REPOSITORY_URI:latest
                - docker push $REPOSITORY_URI:$IMAGE_TAG
Push the Docker image to the Docker registry.
                - echo Writing image definitions file...
                - printf '[{"name":"WebServer","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
Generate the image definition file so AWS knows how to deploy the Docker image.
          artifacts:
            files: imagedefinitions.json
            base-directory: write-it-down-web
Tell the CodeBuild project how to pack the build artifact.
      LogsConfig:
        CloudWatchLogs:
          Status: DISABLED
        S3Logs:
          Status: ENABLED
          Location: !Sub ${CodePipelineBucket.Arn}/BuildLogs
Store the build log in S3 bucket.
7   CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action:
              - sts:AssumeRole
The IAM Role to allow the CodePipeline service to access the required resources.
      Policies:
        - PolicyName: CodePipelineServicePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Resource: !Sub ${SourceCodeConnectionArn}
                Action: codestar-connections:UseConnection
                Effect: Allow
It needs to use the source code connection string to pull the source code from the repository.
              - Resource: !Sub ${CodePipelineBucket.Arn}/*
                Action: s3:*
                Effect: Allow
It needs to access to the S3 bucket.
              - Resource: !Sub ${CodeBuildProject.Arn}
                Action: codebuild:*
                Effect: Allow
It needs to access to the CodeBuild project.
              - Resource: !Sub ${WebServerContainerExecutionServerRole.Arn}
                Action: iam:PassRole
                Effect: Allow
It needs to pass the role to another service.
              - Resource:
                  - !Sub ${WebServerContainerCluster.Arn}
                  - !Ref WebServerContainerService
                Action: ecs:*
                Effect: Allow
It needs to access the container cluster.
              - Resource: "*"
                Action:
                  - ecs:DescribeServices
                  - ecs:DescribeTaskDefinition
                  - ecs:RegisterTaskDefinition
                Effect: Allow
And some other required actions.
8   CodePipeline:
    Type: AWS::CodePipeline::Pipeline
      RoleArn: !Sub ${CodePipelineServiceRole.Arn}
      ArtifactStore:
        Type: S3
        Location: !Ref CodePipelineBucket
      RestartExecutionOnUpdate: true
This is the pipeline to buid and deploy the software. It will use the S3 bucket created earlier to store all the artifacts.
    Properties:
      Stages:
        - Name: Source
          Actions:
            - Name: Default
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeStarSourceConnection
                Version: "1"
              OutputArtifacts:
                - Name: SourceArtifact
              Configuration:
                ConnectionArn: !Sub ${SourceCodeConnectionArn}
                FullRepositoryId: !Sub ${SourceCodeRepository}
                BranchName: master
                OutputArtifactFormat: CODE_ZIP
Define where is the source code.
        - Name: Build
          Actions:
            - Name: Default
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: "1"
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact
              Configuration:
                ProjectName: !Ref CodeBuildProject
Define the build stage.
        - Name: Deploy
          Actions:
            - Name: Default
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: "1"
              InputArtifacts:
                - Name: BuildArtifact
              Configuration:
                ClusterName: !Ref WebServerContainerCluster
                ServiceName: !Ref WebServerContainerService
Define the deploy stage.

Update the stack then you should be able to see the CodePipeline. Execute the pipeline and you should get the software built and deployed to the Docker instances.