필자는 지금 도커 이미지를 ecr에 올려서 lambda에 연결해 호출하는 작업을 하고 있다. 근데 코드 변경할 때마다 수시로 다시 aws 로그인 하고... 이미지 빌드 하고... ecr 레포 파고.. ecr에 푸시하고... lambda 만들고... 연결하고... 이런 작업이 굉장히 번거롭고 힘들기 때문에 개발의 꽃이라 할 수 있는 자동화 즉, 일종의 cicd 를 하고자 글을 써보자 한다.
파이프라인을 생성하는 창을 보면 이름을 설정하고 유형을 설정하라는데 어떤 파이프라인이 나에게 적합하라는 가이드라인을 참고했을 때 github를 사용할 예정이라 v2로 결정하였다 나머지는 그냥 다 디폴트로 지정!
그리고 버전1은 권장되지 않는다고 하지만 지금 iam 계정 권한이 막혀서 어쩔 수 없이 버전1으로 진행하였다. 다른 분들은 권장되는 소스 공급자 버전으로 진행하시길!
일단 단일 레포지토리와 단일 브랜치로 소스코드의 변경을 감지하는 것 같다. 자세한 것은 아래와 같다.
AWS CodePipeline에서 소스 스테이지로 GitHub를 설정하고 GitHub 웹훅을 사용하여 변경 감지를 활성화했을 때, 지정된 브랜치에 대한 코드 변경이 감지되면 파이프라인이 자동으로 시작됩니다. 여기서 "변경"은 커밋 추가, 브랜치 내 기존 파일의 수정, 또는 파일 삭제 등 브랜치의 코드에 대한 어떠한 변화도 포함됩니다. 웹훅이 이러한 이벤트를 감지하면, AWS CodePipeline은 자동으로 새로운 빌드와 배포 프로세스를 시작합니다.
이제 빌드 스테이지를 추가해야되는데 AWS Codebuild를 선택하고 프로젝트 생성을 눌러보자.
모든 것은 거의 다 디폴트로... 아무것도 건들이지 않겠다. (왜냐면 내 계정이 아니라 뭐만 하면 권한이 없대...) 그치만!!!! 제일 중요한 환경변수는 꼭 설정해줘야한다. 추가구성을 열어서 환경변수를 설정해주자
이것도 아무것도 건들이지 않고 환경변수만 추가해줬다. 근데 환경변수의 유형으로 세가지가 존재하고 아래와 같다.
- 일반 텍스트 : 가장 기본적, 비밀정보가 아닌 데이터에 적합
- 파라미터 스토어 : 암호화가 필요하지만 시크릿의 자동회전이 필요 없는 경우 사용
- 시크릿 매니저 : 높은 수준의 보안과 시크릿의 자동 회전이 필요한 경우 사용
나는 환경변수가 여러가지로 많았지만 그냥 모두 다 sm을 선택했다. 이럴 때 아니면 언제 해보겠어~~ 보안을 중요시 하게 하는 프로젝트도 진행해봐야지 ㅎㅎ
이제 Buildspec으로 넘어가보자. 나는 다음과 같은 과정이 필요했다.
- ecr 로그인
- docker image 빌드
- 도커 이미지 태그 작업
- 도커 ecr에 push
- ecr 레포를 lambda에 업로드
- lambda 환경변수 처리
위 과정을 모두 수행한 코드는 다음과 같다.
version: 0.2
phases:
pre_build:
commands:
- >
ELASTIC_API_KEY=$(aws secretsmanager get-secret-value --secret-id ELASTIC_API_KEY --query SecretString --output text)
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region your-region | docker login --username AWS --password-stdin your-account-id.dkr.ecr.your-region.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t your-ecr-repository-name .
- docker tag your-ecr-repository-name:latest your-ecr-repository-url/your-ecr-repository-name:latest
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push your-ecr-repository-url/your-ecr-repository-name:latest
- echo Updating lambda function...
- aws lambda update-function-code --function-name your-lambda-function-name --image-uri your-ecr-repository-url/your-ecr-repository-name:latest
- echo Updating lambda function environment variables...
- >
aws lambda update-function-configuration --function-name your-lambda-function-name
--environment "Variables={ELASTIC_API_KEY=$ELASTIC_API_KEY}"
위 코드는 참고용이며 여기서 변수는 적절히 알아서 변환해주어야 한다.
이제 배치구성과 로그가 남았는데 나는 배치가 필요할 정도로 무거운 레포가 아니기에 넘기고 cloud watch 는 에러난 것을 확인해야하기에 선택해줬다.
그래서 CodePipeline으로 계속을 눌러줬건만... 에러를 만났다.
맨처음에는 프로젝트 이름이 이상하다고 하고 그다음은 역할이름이 이상하다고 하고 그냥 계속 에러가 떴다... 그래서 iam 으로 들어가서 역할을 보니까 아래와 같이 역할이 발급되어 있었다.
근데?! 저 ARN 링크로도 연결해도 아래와 같은 에러가 떠서 정말 미치고 팔짝뛰고 머리 박박 긁어버렸다.
The role with name CodeBuildBasePolicy-bookbara-code-build-ap-northeast-2 cannot be found.
그래서 그냥 iam 역할을 하나 생성했다.
만들어진 역할을 참고로해서 역할을 하나 다시 파고 나서 다시 ARN을 복붙했지만 같은 에러가 발생해서 또 머리를 박박 긁었다(사실 안긁음 호들갑임) 근데 성공함!! 해결책은
codebuild 생성창에서 나갔다가 다시 들어오자. 그래야지 새로 발급한 iam 역할이 인식된다.
찾아보니까 나같은 사람이 몇몇 있었는데 걍 거의 도움안됐고 codebuild에서 역할 생성 문제로 에러를 겪고 있다면 이부분에서
📍새 역할 생성 말고 iam 계정에서 역할 하나 만들기
로 진행하시길... 🥹
난 참 초록색이 좋아... 어쨌든 다음을 눌러서 진행하자
배포스테이지를 지정하라는데 나는 람다 함수업데이트가 배포과정의 일부로 이미 목적을 달성했기에 지정해주지 않겠다.
눈물겨운 초록색 창 목도...!!!!! 후후후후 나는 행복한 감자야~~
지금 모두 다 진행중이라고 뜨는데 시간이 지났는데 행복하다고 하자마자 빨간색 감지요;; 🚨
원인을 파악하러 가보자...
AccessDeniedException
User: arn:aws:iam::27272:user/393939 is not authorized to perform: codebuild:BatchGetBuilds on resource: arn:aws:codebuild:ap-northeast-2:27272:project/bookbara-build because no identity-based policy allows the codebuild:BatchGetBuilds action
그래 역시나 권한이 없다는 거였다. 그래서 역할에서 권한을 추가해줬다.
iam에서 역할에서 정책을 아래와 같이 설정하자.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"arn:aws:logs:ap-northeast-2:여러분꺼:log-group:/aws/codebuild/bookbara-build",
"arn:aws:logs:ap-northeast-2:여러분꺼:log-group:/aws/codebuild/bookbara-build:*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"codebuild:BatchGetBuilds"
]
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codepipeline-ap-northeast-2-*"
],
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
},
{
"Effect": "Allow",
"Action": [
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
"codebuild:BatchPutCodeCoverages"
],
"Resource": [
"arn:aws:codebuild:ap-northeast-2:여러분꺼:report-group/bookbara-build-*"
]
},
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"lambda:UpdateFunctionCode",
"lambda:GetFunction",
"lambda:UpdateFunctionConfiguration"
],
"Resource": "arn:aws:lambda:ap-northeast-2:여러분꺼:function:함수이름"
}
]
}
🚨 주의 : ecr resource는 "*" 모든것을 허용한다는 와일드 카드를 설정해야한다. 특정 레포만 지정한다면 에러남...
근데 이번에 또 실패가 났다... 로그를 확인해보자
An error occurred (ResourceConflictException) when calling the UpdateFunctionConfiguration operation: The operation cannot be performed at this time. An update is in progress for resource: arn:aws:lambda:ap-northeast-2:your_id:function:recommendBook
찾아보니까 이게 람다 function을 생성 혹은 수정한 직후에 함수를 업데이트하는 workflow에 영향을 주기 때문에 function 업데이트를 할 때 fail이 일어날 수 있다고 한다.
관련 레퍼런스
그래서 buildspec.yml 파일을 아래와 같이 수정해주자.
post_build:
commands:
...
- echo Updating lambda function environment variables...
- |
MAX_ATTEMPTS=10
ATTEMPT=0
FUNCTION_STATE=$(aws lambda get-function --function-name recommendBook --query 'Configuration.State' --output text)
while [ "$FUNCTION_STATE" != "Active" ] && [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
echo "Waiting for Lambda function to become Active..."
sleep 100
ATTEMPT=$((ATTEMPT+1))
FUNCTION_STATE=$(aws lambda get-function --function-name recommendBook --query 'Configuration.State' --output text)
done
if [ "$FUNCTION_STATE" != "Active" ]; then
echo "Lambda function did not become Active in time. Exiting..."
sleep 30
fi
- aws lambda update-function-configuration --function-name recommendBook --environment "Variables={ELASTIC_API_KEY=$ELASTIC_API_KEY,ELASTIC_CLOUD_ID=$ELASTIC_CLOUD_ID,ELASTIC_INDEX=$ELASTIC_INDEX,ELASTIC_MODEL=$ELASTIC_MODEL,OPENAI_API_KEY=$OPENAI_API_KEY}"
이 코드는 post_build
단계에서 Lambda 함수가 "Active" 상태인지 확인하고, 그렇지 않은 경우 최대 10번까지 100초 간격으로 상태를 재확인한다.
함수가 "Active" 상태가 되면 환경 변수 업데이트를 진행하고, 지정된 시도 횟수 내에 "Active" 상태가 되지 않으면 에러 메시지를 출력하고 빌드를 실패로 마친다. 또 중요한 것은 active 상태가 된 후에 sleep 을 걸어주는 것이다. 이것을 안한다면... 계속 에러가 난다...
이 방법을 통해, 별도의 스크립트 파일을 사용하지 않고도 buildspec.yml 내에서 Lambda 함수의 상태를 확인하고 안전하게 환경 변수를 업데이트할 수 있다.
하 이 아름다운 초록색을 보기위해 몇시간을 시도했는지.. 성공하니까 도파민 뿜뿜 ㅎㅎㅎㅎㅎㅎ
드디어 끝났다~ 나는 이제 자동화의 귀재다~~ (아님)
🚨주의할 점
역할과 정책을 잘 지정해주자.