AWS CICD之CodeCommit/Build/Deploy 下篇_apache




简介


在上一篇文章中,我们介绍了CodeCommit和CodeBuild的一般用法。

本文中我们先建一个ECR Repository,然后利用CloudWatch rule把CodeCommit和CodeBuild连接起来。

实现当有代码push到CodeCommit后,自动启动CodeBuild,拉取代码,生成镜像,并推入ECR。

接下来,我们会利用之前讲到的Lamdba函数,把CodeBuild和CodeDeploy连接起来,形成一个完整的CICD流水线,如下图所示。

图52AWS CICD之CodeCommit/Build/Deploy 下篇_docker_02

目录

环境(配置)

ECR 简介CloudWatch 简介实战步骤1.新建 ECR Repository2.修改CodeBuil项目增加使用ECR权限增加环境变量修改BuildSpec方式及内容3.设置CloudWatch创建Rule测试Push代码后自动运行CodeBuild4.利用Lambda函数连接CodeBuild和CodeDeploy创建Lambda函数创建Rule创建新Fargate Task版本修改CodeDeploy中用的appspec.json上传appspec.json到S35.测试CICD工具链总结引申后记


环境(配置)


  • AWS 中国或 Global 帐号,可在官网申请,一年内使用指定资源免费
  • AWS cli,Win10 + terminal
  • Git

ECR 简介

Amazon ECR(Amazon Elastic Container Registry)是AWS提供的镜像注册服务,具有安全,可扩展,高可靠等特点。

我们可以在ECR中保存管理Docker镜像,OCI(Open Container Initiative)镜像或者和OCI兼容的制品。

我们通过IAM控制访问ECR的权限。

下面测试中生成的镜像,会保存在ECR Repository中,最后Fargate Task中定义中的镜像会从ECR Repo中拉取。

CloudWatch 简介

CloudWatch 可以实时监控AWS服务,还可以搜集信息,日志,设置告警等。

本文在CloudWatch中设置Rule,用来触发CodeBuild服务和Lambda函数。

实战步骤

1. 新建 ECR Repository

在AWS中控台,选择“ECR”,点击进入“Amazon Container Services”界面,点击左边“Repositories”后,点击“Create repository”

图3AWS CICD之CodeCommit/Build/Deploy 下篇_json_03

添加Repo名称“tstest”,点击“Create repository”

图4AWS CICD之CodeCommit/Build/Deploy 下篇_docker_04

创建完成

图5AWS CICD之CodeCommit/Build/Deploy 下篇_docker_05

2. 修改CodeBuild项目

我们利用上一篇文章建好的CodeBuild项目“tstestCodeCommit”,在项目Role中增加使用ECR的权限。

增加使用ECR权限

在AWS中控台,选择“CodeBuild”,展开“Build”,点击“Build project”,然后点击项目“tstestCodeCommit”,点击“Build detail”后,向下滚到“Environment”部分,点击Service role,新打开IAM role页面

图7AWS CICD之CodeCommit/Build/Deploy 下篇_apache_06

我们给Role增加一个inline policy,点击“Add inline policy”

图8AWS CICD之CodeCommit/Build/Deploy 下篇_apache_07

选择JSON,粘贴下列使用ECR的权限,注意把“XXXX”换成你的Account

图9AWS CICD之CodeCommit/Build/Deploy 下篇_json_08

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            "Resource": "*"
        }
    ]
}

添加policy名称后,点击“Create policy”

图10AWS CICD之CodeCommit/Build/Deploy 下篇_apache_09

增加权限完成

图11AWS CICD之CodeCommit/Build/Deploy 下篇_json_10

增加环境变量

在Build项目“tstestCodeCommit”的“Build details”的“Environment”部分,点击“Edit”

图12AWS CICD之CodeCommit/Build/Deploy 下篇_apache_11

展开“Additional configuration”

图13

AWS CICD之CodeCommit/Build/Deploy 下篇_docker_12

在“Enviroment variables Name”部分,增加下列四个环境变量,然后点击“Update enviroment”,这些环境变量会在BuildSpec中使用。

图14AWS CICD之CodeCommit/Build/Deploy 下篇_docker_13

添加完成

图15AWS CICD之CodeCommit/Build/Deploy 下篇_json_14

修改Buildspec方式及内容

上次我们是在界面上直接粘贴Buildspec的内容,这次我们把Buildspec的内容保存成yaml文件,和Dockerfile等一起放在CodeCommit中。

在Build项目“tstestCodeCommit”的“Build details”的“Buildspec”部分,点击“Edit”

图17AWS CICD之CodeCommit/Build/Deploy 下篇_docker_15

选择“Use a buildspec file”,点击“Update buildspec”

图18AWS CICD之CodeCommit/Build/Deploy 下篇_json_16说明:

这样Codebuild会在和源代码同一层的目录中查找buildspec.yml

更新完成

图19AWS CICD之CodeCommit/Build/Deploy 下篇_docker_17

我们利用上篇文章中在本地clone的代码仓库 “tstestrep”。在“tstestrep”增加下面几个文件。(HelloWorld.java是上次测试的文件,可以删除)

tstestrep/
├── Dockerfile
├── HelloWorld.java
├── buildspec.yml
├── entrypoint.sh
└── test.html

  1. buildspec.yml
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com.cn
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...          
      - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com.cn/$IMAGE_REPO_NAME:$IMAGE_TAG      
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com.cn/$IMAGE_REPO_NAME:$IMAGE_TAG

说明:


  • pre_build 通过“aws ecr get-login-password...” 登录ECR
  • build 利用Dockerfile生成镜像并打Tag
  • post_build 把生成的镜像推入ECR “tstest”

注意:CodeBuild在中国区上面的后缀是amazonaws.com.cn,在Global区是amazonaws.com

  1. Dockerfile
FROM httpd:2.4
COPY ./test.html/ /usr/local/apache2/htdocs/test.html
COPY entrypoint.sh /entrypoint.sh
RUN chmod 755 /entrypoint.sh
 
WORKDIR /usr/local/apache2

EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]

说明:

这里把我们的网页“test.html”复制到httpd的部署目录,生成新的镜像

  1. entrypoint.sh
#!/bin/sh

#when using apachectl it take environment varianles in /usr/local/apache2/bin/envvars but when using httpd start directly it will not use env in envvars
HOST=`hostname`
echo "export HOSTNAME=$HOST" >>/usr/local/apache2/bin/envvars
/usr/local/apache2/bin/apachectl -k start
tail -f /dev/null

说明:

entrypoint.sh是容器启动时运行的脚本,最后一行tail命令是前台命令,运行后不会退出,保持容器一直运行的状态。

注意:docker容器运行的最后一条命令必须是前台命令,即运行后不会自己退出的命令,否则容器运行完所有命令后会自动退出

  1. test.html
<p>this is codebuild test for realCrapForAWS</p>

说明:

测试页面,来代替实际环境中编译好的文件

3. 设置CloudWatch

创建Rule

在AWS中控台,先择CloudWatch,在左边点击“Rules”,然后点击“Create rule”

图24AWS CICD之CodeCommit/Build/Deploy 下篇_docker_18

进行以下配置


  • 选择“Event Pattern”
  • Service Name 选择“CodeCommit”
  • Event Type 选择“All Events”
  • 点击“Edit”,粘入以下Event Pattern

{
  "source": [
    "aws.codecommit"
  ],
  "detail-type": [
    "CodeCommit Repository State Change"
  ],
  "resources": [
    "arn:aws-cn:codecommit:cn-north-1:XXXX:tstestrep"
  ],
  "detail": {
    "referenceType": [
      "branch"
    ],
    "referenceName": [
      "master"
    ]
  }
}

说明:Event Pattern用来对AWS Event进行过滤,满足Pattern则触发Targets,这里当CodeCommit Repo“tstestrep”中有变动时,触发Targets

图25AWS CICD之CodeCommit/Build/Deploy 下篇_apache_19

然后在页面的右半部分Targets中,下拉列表选择“CodeBuild project”,然后粘贴我们的Build project信息“arn:aws-cn:codebuild:cn-north-1:XXXX:project/tstestCodeCommit”(可以在Build detail中找到),然后点击“Configure details”

图20AWS CICD之CodeCommit/Build/Deploy 下篇_docker_20

添加Rule名称和描述后,点击“Create rule”

图21AWS CICD之CodeCommit/Build/Deploy 下篇_json_21

创建完成

图22AWS CICD之CodeCommit/Build/Deploy 下篇_json_22

测试Push代码后自动运行CodeBuild

现在我们在本地git环境把新文件push到CodeCommit中

tstestrep/
├── Dockerfile
├── HelloWorld.java
├── buildspec.yml
├── entrypoint.sh
└── test.html

图26AWS CICD之CodeCommit/Build/Deploy 下篇_apache_23

然后我们在CodeBuild “tstestCodeCommit”的“Build history”中,就可以看到新的构建自动开始运行了

图23AWS CICD之CodeCommit/Build/Deploy 下篇_apache_24

点进去可以看到Build日志,过一会儿Build完成

图27AWS CICD之CodeCommit/Build/Deploy 下篇_apache_25

现在可以在ECR Repo “tstest”中看到我们新推入的镜像

图28AWS CICD之CodeCommit/Build/Deploy 下篇_docker_26

4. 利用Lambda函数连接CodeBuild和CodeDeploy

我们在CloudWatch中,直接利用Rule的Targets,选择到CodeBuild的项目,把CodeCommit和Build连接起来。

但是Targets中没有CodeDeploy选项,所以下面我们利用Lambda函数把Build和Deploy连接起来。

创建Lambda函数

创建Lambda函数的详细过程请参考“AWS Lambda之CodeDeploy部署测试”一文,本文只列出不同的地方

在Lambda界面新建函数“tsCodeBuildTriggerDeploy”,“Execution role”可以选择我们上次Lambda函数中用过的“tstestCodeDeploy-role-tlh9dusl”。

在lambda_function.py文件中粘贴以下内容,然后点击“Deploy”

import json
import boto3

client = boto3.client('codedeploy')

def lambda_handler(event, context):
    # TODO implement
    print(event)
    
    response = client.create_deployment(
        applicationName='tstestFargate',
        deploymentGroupName='tstestDGrp',
        revision={
            'revisionType': 'S3',
            's3Location': {
                'bucket': 'ts-test',
                'key': 'appspec.json',
                'bundleType': 'JSON'
            }
        },
        description='testforCodeBuildTriggerDeploy',
        autoRollbackConfiguration={
            'enabled': True,
            'events': [
                'DEPLOYMENT_FAILURE',
                'DEPLOYMENT_STOP_ON_REQUEST'
            ]
        }
    )    
    print(response)

说明:

Lambda调用Boto3中CodeDeploy,创建一个CodeDeploy应用“tstestFargate”的部署,具体部署内容文件从S3中获取。

可以看到这个Lambda函数没有使用event中的数据,所以建好后可以直接点测试,然后在CodeDeploy界面查看新建的Deployment。

图31AWS CICD之CodeCommit/Build/Deploy 下篇_apache_27

创建Rule

在CouldWatch中,我们新建一个Rule,使得当CodeBuild中“tstestCodeCommit”项目构建成功,并且把镜像推入ECR后,触发上述Lambda函数。

建Rule时,按下述内容配置


  • Service Name选择“CodeBuild”
  • Event Type选择“CodeBuild Build Phase Change”
  • Edit中贴粘以下Event Pattern

{
  "source": [ 
    "aws.codebuild"
  ], 
  "detail-type": [
    "CodeBuild Build Phase Change" 
  ],
  "detail": {
    "completed-phase": [
      "POST_BUILD"
    ],
    "completed-phase-status": [
      "SUCCEEDED"
    ],
    "project-name": [
      "tstestCodeCommit"
    ]
  }  
}

说明:当CodeDeploy项目“tstestCodeCommit”进行到“POST_BUILD”阶段,状态为“SUCCEEDED”的时候,满足Event Pattern就会触发Targets

图32AWS CICD之CodeCommit/Build/Deploy 下篇_json_28


  • Targets选择Lambda function
  • Function选择我们上面建好的“tsCodeBuildTriggerDeploy”

图29AWS CICD之CodeCommit/Build/Deploy 下篇_apache_29

添加名称后,点击“Create rule”

图30AWS CICD之CodeCommit/Build/Deploy 下篇_docker_30

结果如下图

图33AWS CICD之CodeCommit/Build/Deploy 下篇_json_31

创建新Fargate Task版本

接下来我们利用之前的Fargate环境,有关Fargate Task内容,请参见“创建ECS Fargate”一文。

编辑文后附的tstest_task.json文件,把image换成我们在CodeBuild里新生成的Image信息,XXXX换成你的AWS account

{
...
    "family": "tstest-fargate-task",
    "containerDefinitions": [
        {
            "name": "fargate-app1",
            "image": "XXXX.dkr.ecr.cn-north-1.amazonaws.com.cn/tstest:latest",
 ...
}

然后利用命令建新Task版本

aws ecs register-task-definition --cli-input-json file://tstest_task.json

结果如下图,记录task版本号“12”

图34AWS CICD之CodeCommit/Build/Deploy 下篇_json_32

修改CodeDeploy中用的appspec.json

编辑文后附的appspec.json文件,把image换成我们在CodeBuild里新生成的Image信息,XXXX换成你的AWS account

 ...
   "TaskDefinition": "arn:aws-cn:ecs:cn-north-1:XXXX:task-definition/tstest-fargate-task:12",
 ...

然后上传到上面Lamdba函中指定的s3Location的bucket “ts-test”中

上传appspec.json到S3

在AWS中控台选择S3,点击“Buckets”,然后点击“ts-test”

图35AWS CICD之CodeCommit/Build/Deploy 下篇_docker_33

进入我们的S3桶,点击“Upload”

图36AWS CICD之CodeCommit/Build/Deploy 下篇_apache_34

点击“Add files”后,选择刚才编辑的本地文件“appspec.json”

图37AWS CICD之CodeCommit/Build/Deploy 下篇_json_35

点击“Upload”

图38AWS CICD之CodeCommit/Build/Deploy 下篇_docker_36

上传完成,点击“Close”

图39AWS CICD之CodeCommit/Build/Deploy 下篇_apache_37

5. 测试CICD工具链

准备工作完成,下面我们测试CICD工具链。

整个流程如下图所示

图52AWS CICD之CodeCommit/Build/Deploy 下篇_docker_02

在本地Git Repo中做一些改动,(或者touch a),然后push代码,启动CICD流水线

Push代码到CodeCommit

图46AWS CICD之CodeCommit/Build/Deploy 下篇_json_39

CloudWatch中配置的Rule “tsCodeBuildtrigger”会触发CodeBuild

图48AWS CICD之CodeCommit/Build/Deploy 下篇_docker_40

CodeBuild启动(图截的晚了,已经跑完了。正常这里先是“In progress”)

图47AWS CICD之CodeCommit/Build/Deploy 下篇_docker_41

过一会儿POST_BUILD阶段完成

图41AWS CICD之CodeCommit/Build/Deploy 下篇_apache_42

CloudWatch Rule“tsCodeDeploytrigger”触发Lambda函数,由Lambda函数启动CodeDeploy

图49AWS CICD之CodeCommit/Build/Deploy 下篇_docker_43

CodeDeploy中部署启动

图42AWS CICD之CodeCommit/Build/Deploy 下篇_json_44

在ECS Clsuter的Task界面,可以看到新Task开始启动

图43AWS CICD之CodeCommit/Build/Deploy 下篇_docker_45

过一会儿,流量导入新Task完成,进入等待阶段

图44AWS CICD之CodeCommit/Build/Deploy 下篇_apache_46

新旧Task同时运行

图45AWS CICD之CodeCommit/Build/Deploy 下篇_docker_47

部署完成

图50AWS CICD之CodeCommit/Build/Deploy 下篇_docker_48

旧Task消灭,新Task正常运行AWS CICD之CodeCommit/Build/Deploy 下篇_json_49

整个流水线完成

总结

我们按着下面的流程图再总结一下全过程

图52AWS CICD之CodeCommit/Build/Deploy 下篇_docker_02


  1. 用户在客户端用Git命令Push代码到CodeCommit的代码仓库中
  2. CloudWatch通过设置的Rule触发CodeBuild构建项目
  3. 构建项目启动后,从代码仓库中拉到代码,生成镜像,然后把镜像推送至ECR
  4. 在构建项目POST_BUILD成功后,CloudWatch通过设置的Rule触发Lambda函数
  5. Lambda函数启动CodeDeploy部署
  6. 最终新镜像在新的ECS Fargate中生成容器并运行

引申


  1. 中国区没有Codepipeline,有的话,可以用Codepipeline实现上述CICD流水线
  2. 我们可以继续扩展上述CICD流水线,比如在每个阶段加SNS通知,在CodeBuild阶段加入编译代码的过程,在CodeDeploy过程中加入测试的过程。

资源下载

相关文件可在下列链接中下载https://github.com/tansong0091/realCrapForAWS/tree/main/CodeCommit_build2

后记

可以看到通过综合运用CloudWatch, Lambda, S3我们可以把整个CICD流水线做到尽可能的完善,实现完全的云上CICD流水线。

上面写的AWS CICD流水线也可以利用AWS的其它服务来实现。CICD没有固定的工具集,拿自己用的最熟的工具实现目的即可。


喜欢请点赞,欢迎转发

微信公众号“全是 AWS 干货”

AWS CICD之CodeCommit/Build/Deploy 下篇_apache_51