AWS ECS 인프라 구축(2) 편에서 스프링부트 애플리케이션을 ecs 클러스터의 private subnet에 배포했고, parameter store에서 값을 잘 받아오는것도 확인했다.
code pipeline을 사용해 깃헙의 커밋을 감지하고 ecr에 이미지를 푸시, ecs 클러스터에 자동배포하는 환경을 만들어보려고한다.
추가로, 일반적인 방법으로는 NLB에 ssl 인증서를 등록할 수 없는것같아 NLB를 ALB로 바꾸려고한다.
프로젝트 생성 누르면 아래와 같이 빌드 프로젝트를 만들 수 있다.
codepipeline과 codebuild 생성이 되었으면
buildspec.yml 파일을 작성한다.
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- REPOSITORY_URI=192219970851.dkr.ecr.ap-northeast-2.amazonaws.com/release-me-api
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- echo '[{"name":"release-me-api","imageUri":"'$REPOSITORY_URI:$IMAGE_TAG'"}]' > imagedefinitions.json
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
커밋 후 빌드가 시작되었으나 에러가 발생했다.
[Container] 2023/08/04 06:10:39 Waiting for agent ping
[Container] 2023/08/04 06:10:40 Waiting for DOWNLOAD_SOURCE
[Container] 2023/08/04 06:10:41 Phase is DOWNLOAD_SOURCE
[Container] 2023/08/04 06:10:41 CODEBUILD_SRC_DIR=/codebuild/output/src798320806/src
[Container] 2023/08/04 06:10:41 YAML location is /codebuild/output/src798320806/src/buildspec.yml
[Container] 2023/08/04 06:10:41 Setting HTTP client timeout to higher timeout for S3 source
[Container] 2023/08/04 06:10:41 Processing environment variables
[Container] 2023/08/04 06:10:41 No runtime version selected in buildspec.
[Container] 2023/08/04 06:10:41 Moving to directory /codebuild/output/src798320806/src
[Container] 2023/08/04 06:10:41 Configuring ssm agent with target id: codebuild:e7eb1d5a-b2e0-42ec-a4a2-8f1c2819ec47
[Container] 2023/08/04 06:10:41 Successfully updated ssm agent configuration
[Container] 2023/08/04 06:10:41 Registering with agent
[Container] 2023/08/04 06:10:41 Phases found in YAML: 3
[Container] 2023/08/04 06:10:41 BUILD: 4 commands
[Container] 2023/08/04 06:10:41 POST_BUILD: 5 commands
[Container] 2023/08/04 06:10:41 PRE_BUILD: 6 commands
[Container] 2023/08/04 06:10:41 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED
[Container] 2023/08/04 06:10:41 Phase context status code: Message:
[Container] 2023/08/04 06:10:41 Entering phase INSTALL
[Container] 2023/08/04 06:10:41 Phase complete: INSTALL State: SUCCEEDED
[Container] 2023/08/04 06:10:41 Phase context status code: Message:
[Container] 2023/08/04 06:10:41 Entering phase PRE_BUILD
[Container] 2023/08/04 06:10:41 Running command echo Logging in to Amazon ECR..
Logging in to Amazon ECR..[Container] 2023/08/04 06:10:41 Running command aws --version
aws-cli/2.13.2 Python/3.11.4 Linux/4.14.291-218.527.amzn2.x86_64 exec-env/AWS_ECS_EC2 exe/x86_64.amzn.2023 prompt/off[Container] 2023/08/04 06:10:52 Running command echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.AWS_DEFAULT_REGION.amazonaws.com
An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:sts::192219970851:assumed-role/codebuild-release-me-service-role/AWSCodeBuild-e7eb1d5a-b2e0-42ec-a4a2-8f1c2819ec47 is not authorized to perform: ecr:GetAuthorizationToken on resource: * because no identity-based policy allows the ecr:GetAuthorizationToken action
Error: Cannot perform an interactive login from a non TTY device[Container] 2023/08/04 06:10:53 Command did not exit successfully echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.AWS_DEFAULT_REGION.amazonaws.com exit status 1
[Container] 2023/08/04 06:10:53 Phase complete: PRE_BUILD State: FAILED
[Container] 2023/08/04 06:10:53 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.AWS_DEFAULT_REGION.amazonaws.com. Reason: exit status 1
ECR에 접근권한이 없어서 에러가 발생한 것 같다.
code build 프로젝트를 생성할 때 함께 만들어진 iam 역할이 있다. 여기에 ecr 관련 정책을 부여한다.
정책을 부여하고 다시 빌드했는데, 빌드된 jar파일을 찾지못해 에러가 발생했다.
buildspec.yml에 gradle로 빌드하는 과정을 추가한다.
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- REPOSITORY_URI=192219970851.dkr.ecr.ap-northeast-2.amazonaws.com/release-me-api
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building with Gradle...
- chmod +x gradlew
- ./gradlew build
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- echo '[{"name":"release-me-api","imageUri":"'$REPOSITORY_URI:$IMAGE_TAG'"}]' > imagedefinitions.json
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
빌드성공을 확인했다.
하지만 ecr에 새로운 이미지가 푸시되었으나 배포를 실패했다.
s3에 대한 권한이 없어서 에러가 발생했으니, code build 프로젝트의 역할에 s3 권한을 추가해준다.
하지만 여전히 s3 권한에러가 발생했고, 찾아본 결과 buildspec.yml 파일에 출력 아티펙트로 imagedefinitions.json 을 포함시켜야한다는 것을 확인했다.
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- echo $(aws ecr get-login-password --region $AWS_DEFAULT_REGION) | docker login --username AWS --password-stdin 192219970851.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- REPOSITORY_URI=192219970851.dkr.ecr.ap-northeast-2.amazonaws.com/release-me-api
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- echo Build started on `date`
- echo Building with Gradle...
- chmod +x gradlew
- ./gradlew build
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- echo '[{"name":"release-me-api","imageUri":"'$REPOSITORY_URI:$IMAGE_TAG'"}]' > imagedefinitions.json
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
artifacts:
files: imagedefinitions.json
다시 배포해보니 새로운 태스크가 생성된다.
하지만, 새로운 컨테이너가 올라가면 이전 컨테이너가 내려가며 업데이트가 되어야하는데 이 과정에서 문제가 생겼다.
새로 올라간 컨테이너에 요청이 정상적으로 가지 않는다. 로드밸런서가 새로운 컨테이너에 healthcheck 하는 중 에러가 발생해 해당 컨테이너는 종료된다.
네트워크 설정은 문제가없는것같아서, 로드밸런서와 대상그룹을 다시 정의하고 테스트했으나 여전히 헬스체크에 실패했다. 대상그룹에서 등록된 private ip와 컨테이너의 private ip가 동일한데 계속 실패하는 상황이었다.
컨테이너의 로그를 확인하니 황당한 실수를 하고있었다.
8080 포트로 프로세스가 돌아가고있었다. application-local.yaml 파일이 ignore 처리되어 static 하게 정의했던 7000 포트가 제대로 동작하지 않고있었다.
7000 포트로 실행되게 수정하고 healthcheck의 경로도 명확히 명시하니 수동배포에 성공했다.
자동배포 테스트를 위해 v1을 v2로 바꿔 커밋해봤다.
정상적으로 업데이트되었다.