인프라/클라우드

AWS Copilot으로 컨테이너 서비스 환경 구성하기

jwKim96 2023. 5. 21. 23:55

최근 회사에서 신규 프로젝트의 컨테이너 기반 서비스 환경을 AWS Copilot을 이용해 1~2일 만에 구축했습니다.
그래서 이번 글에서는 AWS Copilot이 무엇인지, 어떤 특징이 있고, 어떻게 사용할 수 있는지 공유드리려고 합니다.

1. AWS Copilot 이란?

많은 분들이 'Copilot' 이라고 하면, Github의 Copilot을 먼저 떠올리시는데요.
하지만 AWS Copilot은 컨테이너 기반 서비스 환경을 간편하게 빌드, 배포, 운영할 수 있게 해주는 도구입니다.

위 그림은 Copilot에서 제공하는 리소스들을 도식화한 그림입니다.

여기서 app은 하위의 env, pipeline, service들을 묶는 범주입니다.
그리고 실제 인프라로 프로비저닝되는 대상이 env, pipeline, service 입니다.

각 요소들에 대해서는 아래에서 조금 더 자세히 살펴보겠습니다.

2. AWS Copilot은 언제 사용하면 좋을까?

  • 서비스를 빌드, 배포, 운영할 수 있는 CLI 도구가 필요한 경우
  • 간단한 코드 기반 인프라 구성 도구가 필요한 경우
    • Terraform 처럼 모든 영역을 구성하는게 부담스러운 경우
    • 기타 인프라보다 서비스 인프라 구성, 유지보수에만 집중하고싶은 경우
  • 소~중규모 컨테이너 기반 서비스 환경이 필요한 경우
    • EKS를 쓸 정도로 큰 서비스는 아닌 경우
  • 아래 인프라들에 대한 배경 지식이 있는 경우
    • ECS, Load Balancer, Docker

즉, 정리하자면 아래와 같습니다.

'소규모 팀(서비스)에서 컨테이너 기반 서비스 환경을 관리할 cli, IaC 도구가 필요한 경우'

3. 제공하는 서비스 아키텍처

위 그림에서 잠깐 살펴본대로 현재 총 5가지의 서비스 종류를 제공합니다.

  • Request-Driven Web Service
    • App Runner 인프라로 프로비저닝
    • 트래픽 증가/감소에 따라 서비스 컨테이너를 자동으로 늘리거나/줄임
    • 한국 리전을 아직 지원하지 않는게 아쉬움
  • Load Balanced Web Service
    • ECS Fargate 인프라로 프로비저닝
    • 인터넷이 연결된 환경에서 로드 밸런서, 서비스 컨테이너 환경을 구성할 수 있음
    • 프론트엔드 서비스 환경 구성에 적합함
  • Backend Service
    • ECS Fargate 인프라로 프로비저닝
    • 기본적으로 인터넷과 연결되지 않은 환경으로 구성할 수 있음
    • 백엔드 서비스 환경 구성에 적합함
  • Worker Service
    • ECS Fargate 인프라로 프로비저닝
    • pub/sub 구조의 Consumer 서비스의 환경을 구성할 수 있음
    • 기본적으로 pub/sub 구조로 서비스를 하고 있는 경우여야 함
      • 특정 topic을 subscribe 하는 Consumer 서비스를 프로비저닝 하기 때문
  • Scheduled Job
    • ECS Fargate 인프라로 프로비저닝
    • 스케줄 기반으로 작업을 실행하게 함

4. 환경 구성 순서

AWS Copilot은 cli가 정말 친절한데요.
위 서비스 아키텍처 중 어떤 것을 사용할지 선택만 하기만 하면, 그 다음은 cli의 안내에 따라 진행하기만 하면 됩니다.

진행되는 순서는 다음과 같습니다.

  • copilot init
  • copilot env init
  • copilot env deploy
  • copilot svc init
  • copilot svc deploy

이 다섯 단계만 거치고 나면, ECS Fargate 기반 서비스환경이 구축됩니다.

처음에 구성할때는 관련 인프라들이 구성되어야해서 시간이 조금 걸리는데요.
이후에는 변경되는 부분만 반영하기 때문에 시간은 조금 덜 걸리는 편입니다.

4.1 환경 구성 실습

실제로 서비스 환경을 구성하는 방법을 아래에서 알아보겠습니다.
(실습은 MacOS기준이며, aws cli가 설치되어있는 환경을 가정하고 설명합니다.)

1) 설치

brew install aws/tap/copilot-cli

homebrew가 설치되어있다면 이 명령어 한번으로 간단하게 설치할 수 있습니다.
만약 다른 방법으로 설치해야하거나, MacOS가 아닌 경우에는 이 링크에서 설치 방법을 확인할 수 있습니다.

2) copilot init

copilot init 명령어를 수행하면, app의 초기 설정을 진행합니다.
Application name, Service type, Service name, Docker image, Port 를 설정하는데요.

그러면 서비스 관련 인프라를 구성하는데 필요한 Role들을 생성할 수 있는 인프라가 구성됩니다.

아래 그림처럼 CloudFormation stack과 관련 작업을 위한 IAM Role이 아래와 같이 되는걸 확인할 수 있습니다.

  • 관리자 Role을 생성하는 CloudFormation Stack이 하나 생긴것을 확인할 수 있음

  • 2개의 Role이 생성된것을 확인할 수 있음

그리고 명령을 실행한 경로에 copilot이라는 디렉토리가 생성되며 아래와 같이 서비스 manifest가 생성됩니다.

해당 manifest 내용은 다음과 같습니다.

# The manifest for the "index-service" service.
# Read the full specification for the "Load Balanced Web Service" type at:
#  https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/

# Your service name will be used in naming your resources like log groups, ECS services, etc.
name: index-service
type: Load Balanced Web Service

# Distribute traffic to your service.
http:
  # Requests to this path will be forwarded to your service.
  # To match all requests you can use the "/" path.
  path: '/'
  # You can specify a custom health check path. The default is "/".
  # healthcheck: '/'

# Configuration for your containers and service.
image:
  location: nginx
  # Port exposed through your container to route traffic to it.
  port: 80

cpu: 256       # Number of CPU units for the task.
memory: 512    # Amount of memory in MiB used by the task.
count: 1       # Number of tasks that should be running in your service.
exec: true     # Enable running commands in your container.
network:
  connect: true # Enable Service Connect for intra-environment traffic between services.

# storage:
  # readonly_fs: true       # Limit to read-only access to mounted root filesystems.

# Optional fields for more advanced use-cases.
#
#variables:                    # Pass environment variables as key value pairs.
#  LOG_LEVEL: info

#secrets:                      # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
#  GITHUB_TOKEN: GITHUB_TOKEN  # The key is the name of the environment variable, the value is the name of the SSM parameter.

# You can override any of the values defined above by environment.
#environments:
#  test:
#    count: 2               # Number of tasks to run for the "test" environment.
#    deployment:            # The deployment strategy for the "test" environment.
#       rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments.%       

주석이 상세하게 작성되어있어서 주석만 읽어도 어느정도 이해할 수 있습니다.
추가적인 다른 속성이나 설명이 필요하신 경우 manifest 문서를 참고하시기 바랍니다.

여기서 포인트만 살짝 짚고 넘어가 보겠습니다.

  • name, type : 아까 copilot init 중 입력한 이름과 서비스 타입
  • http : http 네트워크 설정인가 오해할 수 있는데, 로드밸런서와 타겟 그룹 설정임
    • healthcheck 관련 속성과, domain alias 등을 설정할 수 있음
  • image : Docker image 를 명시
    • 이미 존재하는 이미지를 참조하는 경우에는 위 예시처럼 이미지 이름을 입력
      • 별도의 저장소에서 이미지를 가져오는 경우 registry/image:tag 형태로 입력하면 됨
      • Dockerfile을 참조하는 경우에는 해당 경로를 입력해주면 됨(copilot 디렉토리 위치를 루트 경로로 인식함)
      • sample-web/docker/dev/Dockerfile, sample-web/copilot/sample-web/index-service/manifest.yml
        • 이 경우 ./docker/dev/Dockerfile 이라고 입력하면 됨
  • cpu, memory : 컨테이너가 사용할 cpu, memory를 입력해주면 됨
  • count : 해당 서비스 컨테이너 몇개를 유지할건지의 개수

3) copilot env init

env init 명령어를 수행하면 추가적으로 리소스가 더 생성됩니다.
이 리소스들은 dev환경을 구성하기 위해 필요한 리소스들을 선언한

  • CloudFormation Stack 2개
  • IAM Role 2개

  • sample-web-devStackSet-sample-web-infrastructure-~ 가 생성된것을 확인할 수 있음

  • 관련 Role이 2개 더 생성된것을 확인할 수 있음

4) copilot env deploy

env deploy 명령어를 수행하면 더 많은 리소스들이 프로비저닝됩니다.
주요 리소스들은 다음과 같습니다.

  • AWS Cloud Map 네임스페이스
    • Service Discovery
  • Route53 호스팅 영역
  • VPC 관련 리소스
    • 이 부분은 기존에 있는 VPC를 참조하도록 설정하면, 새로 생성되지는 않음
  • ECS Cluster

env 를 Deploy하면 이미 ECS Cluster는 생성됩니다.
즉, AWS Copilot에서는 기본적으로 환경별로 Cluster를 분리하도록 구성되는걸 알 수 있습니다.

5) copilot svc deploy

사실 copilot svc init을 해주는게 순서상 맞지만, copilot init 단계에서 이미 생성어서 생략합니다.

위 처럼 명령어를 실행하면 실제로 ECS Cluster에 서비스 관련 리소스들이 프로비저닝 됩니다.

  • ECS Service
    • Load Balancer
  • ECS Task Definition
  • ECS Task

  • ECS Cluster에 index-server 서비스가 생성된것을 확인할 수 있음
    • 서비스 이름 : {APP_NAME}-{ENV}-{SERVICE_NAME}-Service-...
    • 실제 : sample-web-dev-index-service-Service-...

아까 위의 cli에서 서비스에 접근할 수 있는 URL이 출력되었는데요.
접속하면 다음과 같이 Nginx의 index.html이 출력되는것을 확인하실 수 있습니다.

6) copilot app delete

공부삼아 app을 생성해서 이것저것 테스트를 해본 뒤 이 명령을 통해서 모두 삭제할 수 있습니다.

  • 관련 리소스들을 알아서 다 삭제함
    • 이때 copilot-application, copilot-service 와 같은 Tag들을 활용함

5. 총평

AWS Copilot을 사용해보면서 느낀 장단점은 다음과 같습니다

  • 장점
    • 사용법이 간단하여 빠르게 적응 가능
    • 서비스 인프라와 관련된 리소스에만 집중할 수 있음
    • cli 기반 도구이기 때문에 자동화가 가능함
    • Override 지원을 통한 커스텀 가능
      • Task Definition 이나 CloudFormation template 을 커스텀할 수 있음
  • 단점
    • 조금 답답한 배포 속도
      • 사실 이건 찾아보니 AWS Copilot의 문제라기 보다는, CloudFormation + ECS Deployment 조합이 원래 조금 느린 것 같았음
    • 자동 인프라 생성을 위해 생성되는 관련 리소스들이 많음
      • 만약 시간이나 인력이 충분하다면, Terraform을 사용하는게 나을수도..?
    • 자동 생성되는 이름이 조금 거슬림
      • copilot init시 지정한 app name, service name을 기준으로 인프라 이름을 생성함
      • 그러나 뒤에 해시값이 붙거나, 이름이 조금 잘리는 경우가 있어 불편함

그래도 서비스 관련 인프라를 손쉽게 구성하고, 변경하고 코드를 기반으로 이를 추적할 수 있는점은 아주 큰 장점이라고 생각됩니다.

6. 관련 팁

환경을 구성하면서 copilot 의 manifest 만으로는 설정할 수 없거는 경우를 해결하는 방법이나, 추가적인 팁을 공유드리려고 해요.

6.1 Task Definition & CloudFormation template 오버라이드

copilot 의 manifest 속성 지정/변경만으로는 설정할 수 없는 부분들이 있는데요.
제가 겪은 부분은 2가지였습니다.

  • Task Deployment 최적화
  • ECS TaskExecution Role 권한 수정

1) Task Deployment 최적화

ECS 배포 속도 최적화를 하기 위해서 조정해야하는 여러 요소들이 있는데요.
그 중에서 문제를 겪었던 부분은 Task Deployment 최적화 단계에서 조정해야하는 minimumHealthyPercent, maximumPercent 속성입니다.

이 속성들은 service manifest에서 deployment.rolling으로 조정할 수 있는데요.
하지만 선택지가 2가지 밖에 없습니다.
(option : minimumHealthyPercent/maximumPercent)

  • defualt : 100 / 200
  • recreate : 0 / 100

그래서 이를 오버라이드하는 방법이 없나 찾아보던 중 AWS Copilot에서 Yaml Patch Override 기능을 제공하는것을 알게 되었습니다.
이는 생성된 CloudFormation Template 파일의 특정 경로의 값을 변경하는 기능인데요.
주의해야할 점은, 이 오버라이드 설정을 잘못 하면 인프라 프로비저닝이 실패하게될 수 있으니 오버라이드 할 경로를 잘 확인해야 합니다.

저는 아래와 같이 오버라이드 했습니다.
(copilot svc override 명령어를 사용하면 오버라이드할 속성을 명시할 수 있는 파일이 생성됩니다.)

> copilot svc override --name sample-web --env dev

------
# sample-web/copilot/index-service/overrides/cnf.pathes.yml

- op: replace
  path: /Resources/Service/Properties/DeploymentConfiguration/MinimumHealthyPercent
  value: 50

기존에는 default, recreate인지에 따라 MinimumHealthyPercent는 100 또는 0 밖에 설정할 수 없는데요.
이렇게 오버라이드 하면 원하는 값으로 변경할 수 있게 됩니다.

2) ECS TaskExecution Role 권한 수정

그런데 서비스를 개발하다보면 S3, SecretsManager, SQS, SNS등의 인프라에 접근해야하는 경우가 생기는데요
이때 AWS Copilot이 생성한 Role에 권한을 주면, 컨테이너의 Credential로 사용되어 접근할 수 있게됩니다.

그런데 사실 copilot svc deploy 명령어를 수행할때마다, 기존 Task Definition을 삭제하고 재생성하는데요.
그래서 배포할때마다 권한을 부여했던 Role이 삭제되고 재생성되는 불편함이 있습니다.
이를 해결하기 위해서는 Task Definition 파일에서 사용할 Role을 설정해서 해결할 수 있습니다.

하지만 이 또한 서비스 manifest 기본 속성으로는 설정할 수 없습니다.
그렇지만 이는 서비스 manifest 파일에서 taskdef_overrides 속성을 이용하면 가능합니다.

저는 아래와 같이 속성을 추가했습니다.

# sample-web/copilot/index-service/manifest.yml

name: index-service
type: Load Balanced Web Service

... 생략

taskdef_overrides:
  - path: "taskRoleArn"
    value: "arn:aws:iam::123456789012:role/sample-web-ecsTaskRole"
  - path: "executionRoleArn"
    value: "arn:aws:iam::635325158267:role/sample-web-ecsTaskExecutionRole"
  • taskRoleArn : 서비스 컨테이너에 부여되는 Role
    • 이 Role에 접근해야하는 리소스 권한을 주면 됨
  • executionRoleArn : ECS Task를 실행하기 위한 Role
    • 이 Role은 AWS에서 제공하는 'AmazonECSTaskExecutionRolePolicy' 만 갖고있으면 됨

두가지 Role을 미리 생성해놓고 위와 같이 작성해놓으면, 더이상 Role을 재생성하지 않습니다.
그리고 제가 접근해야하는 리소스 권한이 부여된 Role이 부여된 상태로 서비스 컨테이너가 시작되게 됩니다.

6.2 로드 밸런서 HTTPS 설정

HTTPS 설정은 두 가지를 해야하는데요.

  • env manifest 에 https 인증서 ARN을 입력해줘야 합니다.
  • service manifest에 domain alias 설정
    • 예를 들어서 인증서가 *.sample-web.com 을 인증받은 경우
    • http.alias를 'dev.sample-web.com' 으로 설정할 수 있음
      • 여기에 기존 Route53 호스팅 영역이 있다면 해당 영역의 ID를 지정할 수 있음
      • 그러면 해당 영역에 A record가 생성되고, 이는 로드 밸런서를 가리키게 됨