AWS CodePipeline 사용하기
AWS CodePipeline을 이용하여 Lambda를 배포하기
- 요구사항
- GitHub Enterprise(이하 GHE)에 있는 AWS Lambda용 소스코드를 CICD를 적용하여 자동으로 배포
- 문제점
- CodePipeline의 Source 단계(stage)는 GHE를 아직 지원하지 않음
- 해결책과 설계
- CodeBuild는 다행히도 GHE를 지원
- CodeBuild에서 GHE의 코드를 shallow clone하여 build하고, artifact를 S3에 업로드
- S3에 파일이 업로드 되는 것을 trigger로 하여, CodePipeline을 시작
AWS Code Series
- AWS CodeBuild
- AWS CodeBuild란?
- 빌드 작업은 간단한 경우도 있지만, 엄청 무겁고 오래걸리는 경우도 있음
- 이런 경우 CodeBuild를 이용하면 이용한 시간만큼만 비용 발생 - 빌드 용 클러스터 구축 비용 절감
- GHE로 부터 코드를 가져와 빌드: Announcing AWS CodeBuild Support for GitHub Enterprise as a Source Type and Shallow Cloning
- 빌드된 artifact는 S3에 업로드
- CodePipeline 내부에서 Build 단계로 존재 할 수 있음
- GHE가 아니라면(일반 GitHub, CodeCommit 등) CodePipeline의 Source 단계를 통해 소스코드를 불러와 Build 단계에서 BuildSpec에 정의된 대로 빌드
- 빌드된 artifact는 Output artifact로 S3에 업로드되어 다음 단계의 Input artifact로 전달 가능
- AWS CodeBuild란?
- AWS CodePipeline
- AWS CodePipeline이란?
- Source 단계에서 S3의 CloudWatch로 부터 update를 기다림
- S3 외에 GitHub, CodeCommit도 가능
- 그러나 GHE에서 가져와야 하므로, CodeBuild에서 S3로 artifact 업로드
- S3 변경 시 해당 패키지 파일을 가져와 Output artifact로 등록
- 이미 CodeBuild에서 build 되었기 때문에 Build 단계는 건너뜀(No Build)
- 보통은 여기서 CodeBuild를 수행하게 되어있음(Jenkins 등)
- 이 artifact를 CodeDeploy나 CloudFormation으로 배포해야 함
- AWS CodeDeploy
- AWS CodeDeploy란 무엇입니까?
- 사견: EC2 instance의 경우 artifact를 서버에 배포하고, 배포된 서버를 기존 서버와 치환하는 작업을 수행한다. 그러나 Lambda의 경우, 이미 수정되어 versioning이 된 함수들을 배포 모델(BlueGreen, Canary 등)에 맞춰 치환하는 작업만 수행한다.
- Application, Deployment Group, Deployment Configurations와 Revision으로 구성됨
- Application은 하는 역할이 없음, 그냥 Project같은 개념
- Application 아래 Deployment Group은 Deployment Configurations중 한 가지 배포 모델을 선택하고, Trigger나 Rollback, Alert을 설정하지만 마찬가지로 특별히 하는 역할은 없음
- Revision이라고 하고 Deployment(=Deploy new revision)라고도 하는게 CodeDeploy에서 가장 큰 역할
- Revision 생성
- Application을 선택하여 대상 플랫폼(EC2/Lambda등)을 결정
- Deployment Group을 선택하여 배포 모델(Canary, AllAtOnce 등)과 기타 옵션을 결정
- AppSpec을 수기입력 받거나 S3로 지정하여 실질적인 배포를 수행
- 사실상, AppSpec이란 것이 CodeDeploy의 전부
- 앞서 언급했듯이, AppSpec으로 (EC2에 배포할 경우에는 artifact를 지정할 수 있는 것 같음(files)) Lambda에 배포할 경우 버전 치환만 가능함
- 정리: CodeDeploy로 Lambda를 배포하는 것은 불가능
- 개발자가 console이나 CLI를 이용하든 CloudFormation(이하 CF)든 Lambda 함수의 artifact(소스코드)를 수정하고, 함수 버전을 publish 해두면, Deployment Group에 지정된 배포 모델을 적용하여 alias를 seamless하게 변경해주는 것이 전부
- 만약, 제가 잘못 이해했다면 댓글 부탁드립니다.
- AWS CodeDeploy란 무엇입니까?
작업 기록
CodeDeploy 상태를 봐선, 수동 또는 CF를 이용해서 Lambda코드를 수정하는 것은 CodePipeline의 역할인 것으로 보임: 관련 문서
- SAM(Serverless Application Model)
- AWS SAM 사용
- Spec
- CloudFormation형식을 이용한, Serverless 앱 구축을 정의한 모델(템플릿)으로, CF에 stack으로 등록 시 “Transform”하여 일반 CloudFormation template으로 변환됨
- CodePipeline 구축
- 위 “관련 문서”에서 안내하는대로 작업
- Lambda 함수는 CodePipeline의 Deploy단계에서 Deployment provider로 CF를 선택하여 배포
- Template은 SAM 형식으로 Input artifact 내부에 위치: MyApp::sam.template
- 만약, Action mode를 “Create or update a stack”으로 하게되면 Create(Update)Stack cannot be used with templates containing Transforms. 이라는 에러가 발생
- CF에서 stack으로 배포할 때는 그냥 Create Stack을 하면 됨
- 단, console이 아닌 CLI로 배포시, create-stack이나 update-stack이 아닌 deploy명령으로 실행 해야 함
- 참고: Update cloudformation stack from aws cli with SAM transform
- 헷갈리지 말고 Action mode를 “Create or replace a change set”으로 선택 할 것
- ChangeSet
- SAM은 CF template 형식을 따르지만, 변환 과정을 거쳐야 진정한 CF template이 됨
- SAM 내용을 CF 형식으로 바꾼 것이 ChangeSet
- SAM을 ChangeSet으로 transform하고, ChangeSet을 실행(Execute)하여 stack을 update
- “관련 문서”의 2단계 참조
- 잊지말고 CodePipeline에 “Execute a change set”이란 step을 하나 더 만들어야 함
- 위 “관련 문서”에서 안내하는대로 작업
- ChangeSet의 함정
- SAM을 ChangeSet화 하고, 이를 이용해 stack을 update 할 때 주의사항
- CF의 Update stack은 특성상 바뀐 부분만 적용 됨
- SAM으로 Lambda stack을 update 할 때, CodeUri라는 property를 지정해야 함
- CodeBuild로 S3에 업로드된 artifact의 파일명이 바뀌지 않으면, CodeUri부분이 변경되지 않기 때문에, stack이 update되지 않음
- 즉, S3에 올려둔 artifact가 변했더라도, CF는 인식 불가(ChangeSet의 생성 결과: FAILED - No updates are to be performed.)
- 해결책
- S3에 업로드 할 artifcat이름을 매번 바꿈? 불가 - artifact이름이 바뀌면, CodePipeline의 Source 단계를 trigger할 수 없음
- CodePipeline의 Source 단계에서 다음 단계로 전달할 Output artifact를 이용
- Output artifact는 다시 임의의 S3로 전달되는데, 이 bucket과 key는 매번 임의의 이름으로 생성되므로 이를 SAM parameter로 전달 - Parameter overrides
- CodeUri가 변경되므로 ChangeSet에서 “Modify”로 반영 됨
- SAM을 ChangeSet화 하고, 이를 이용해 stack을 update 할 때 주의사항
- CodeUri 바꾸기
- Parameter overrides 적용
- Deploy 단계에서 Deploy Action의 Advanced 항목을 펼쳐보면, parameter값을 override 할 수 있음
- 참고 1: AWS CodePipeline 파이프라인에서 파라미터 재정의 함수 사용
- 참고 2: Using Intrinsic Functions
- 참고 3: Not possible to have CodeUri as a parameter
- 실수 사례들
- Parameter overrides를 적용했는데, SAM template에서 parameter를 받을 준비(Parameters 정의)가 되어 있지 않으면 에러 발생: Parameter values specified for a template which does not require them.
- 반대로 paramters를 받을 준비는 됐지만, parameter overrides 설정이 되지 않아도 에러 발생: Parameters: [BucketName, ObjectKey] must have values.
- Parameter overrides 적용
- 기타: AutoPublishAlias
- 이 property를 지정하면
- 자동으로 version이 추가됨
- 해당 version으로 alias를 지정함
- SAM에서 별도로 Lambda 함수의 version을 생성하는 것은 불가능
- 이 property를 지정하면
- 정리
- GHE -> CodeBuild -> CodePipeline(Source: S3 -> No Build -> Deploy: CF(Create a ChangeSet) -> Deploy: CF(Execute a ChangeSet)) -> Lambda function
해야 할 것들
- CodeDeploy로 production 배포
- 위 작업들로 Lambda 함수를 새 버전으로 update하고 alias까지 부여함
- 운영에 배포해야 할 Lambda 함수는 안전을 위해 배포 모델을 적용하는 것이 필요
- CodeDeploy의 Application/Deployment Group을 이용하여 배포 모델 적용
- Production에 mapping된 alias를 CodeDeploy로 치환
- 이를 위한 AppSpec을 어디에 둘 것인가?
- AppSpec도 S3에 위치해야 하며, yaml/json 형식이어야 함
- CodePipeline상에 위치하기는 어려울 듯 - 고민 필요
Sample codes
- 특이 사항
- SAM의 CodeUri는 spec상 무조건 zip/jar로 압축되어 S3에 있어야 함
- CodePipeline의 Source 단계에서 가져오는 소스파일 역시 zip으로 압축되어 있어야 함
- Artifact가 일종의 package이기 때문
- 그러므로, 소스파일 내부에 SAM template를 넣어두는 것도 가능
- CodeBuild에서 artifact를 만들 때, SAM template도 같이 묶어 활용
- SAM의 CodeUri로 자기 자신을 참조(단, 위에 설명했듯이 Parameters로 동적으로 참조할 것)
- 이것이 best practice는 아니겠지만, SAM을 repository에 관리하는 것도 번거로움: 의견 주세요.
- Input artifact를 두 개로 하여, 하나에는 SAM을 다른 하나에는 artifact와 template configuration file이 구성되는 방안도 고려: AWS CloudFormation 아티팩트
- Artifact file structure
- zip파일
- sam.template
- test.py
- zip파일
- SAM template
# sam.template AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Parameters: BucketName: Type: String ObjectKey: Type: String Resources: PipelineTest: Type: AWS::Serverless::Function Properties: # 예제이므로 python을 사용 FunctionName: pipeline-test Handler: test.lambda_handler Runtime: python3.6 CodeUri: Bucket: !Ref BucketName Key: !Ref ObjectKey AutoPublishAlias: dev Tags: Owner: MYS Purpose: test Description: Test function
- Python code
# test.py # 예제이므로 python을 사용 def lambda_handler(event, context): # Sample print("Hello World!") return 'Hello from Lambda'
- Parameter overrides
{ "BucketName" : { "Fn::GetArtifactAtt" : ["MyApp", "BucketName"]}, "ObjectKey" : { "Fn::GetArtifactAtt" : ["MyApp", "ObjectKey"]} }