Amazon ECS 서버 구축 & 자동배포 설정

Dongwoo Kim·2024년 11월 25일
0

TIL / WIL

목록 보기
125/126

사전지식 (from ChatGPT 4o)

1. ECS (Elastic Container Service)

  • Amazon ECS는 컨테이너화된 애플리케이션을 실행, 관리할 수 있도록 지원하는 완전 관리형 컨테이너 오케스트레이션 서비스입니다.
  • Kubernetes와 유사한 기능을 제공하지만, AWS에 최적화되어 있습니다.
  • 컨테이너 런타임으로 Docker를 사용하며, EC2(가상머신)와 Fargate(서버리스 방식) 두 가지 방식으로 컨테이너를 배포할 수 있습니다.

2. Cluster

  • Cluster는 ECS에서 컨테이너를 실행할 수 있는 논리적인 그룹을 의미합니다.
  • 클러스터 안에는 컨테이너를 실행하는 EC2 인스턴스Fargate 작업이 포함됩니다.
  • 클러스터는 리소스를 논리적으로 분리하는 데 사용되며, 한 애플리케이션 또는 여러 애플리케이션이 동일한 클러스터에 속할 수 있습니다.

3. Service

  • Service는 특정 애플리케이션의 Task를 실행하고 유지 관리하는 ECS의 구성 요소입니다.
  • 서비스는 스케일링로드 밸런싱을 지원하며, 지정된 수의 태스크가 항상 실행되도록 보장합니다.
    • 예: 웹 애플리케이션 서비스에서 3개의 컨테이너가 항상 실행되도록 설정 가능.
  • 서비스는 ALB(Application Load Balancer) 또는 NLB(Network Load Balancer)와 통합되어 트래픽을 컨테이너에 분산할 수 있습니다.

4. Task

  • Task는 실행 가능한 ECS 작업의 최소 단위로, 컨테이너 하나 이상으로 구성됩니다.
  • Task Definition이라는 JSON 형식의 템플릿에 따라 정의됩니다.
    • 예: 어떤 Docker 이미지를 사용할지, CPU/메모리 자원은 얼마나 할당할지 등을 지정.
  • Task는 특정 서비스에 의해 실행되거나, 독립적으로 실행될 수도 있습니다.

5. ECR (Elastic Container Registry)

  • ECR은 Amazon이 제공하는 Docker 이미지 저장소입니다.
  • Docker Hub와 유사한 기능을 제공하며, ECS와 통합되어 이미지 배포를 간소화합니다.
  • 특징:
    • 프라이빗 레지스트리를 제공하므로, 보안이 중요한 애플리케이션에 적합합니다.
    • IAM 권한을 기반으로 안전하게 접근 관리.
    • 이미지 스캐닝과 라이프사이클 정책을 지원하여 보안을 강화하고 비용을 최적화할 수 있습니다.

배포 시나리오

  1. 작업한 코드를 Github Repository에 푸시
  2. Github Actions에서 정의된 workflow 대로 작업 수행
    1. Docker image로 빌드
    2. 빌드된 이미지를 ECR에 푸시
    3. ECS 서비스를 업데이트
  3. 업데이트된 ECS 서비스는 ECR의 이미지로 정의된 Task 배포

작업 순서

  1. ECR 생성
    • URI를 이용해서 도커 이미지 푸시 가능
  2. Task 정의
    • 기존 Task에서 환경변수, 서비스명, ECR만 변경
    • json file
      {
          "family": "{family_name}",
          "containerDefinitions": [
              {
                  "name": "{task_name}",
                  "image": "{ECR_URI}",
                  "cpu": 0,
                  "portMappings": [
                      {
                          "name": "{service_name}-80-tcp",
                          "containerPort": 80,
                          "hostPort": 80,
                          "protocol": "tcp",
                          "appProtocol": "http"
                      },
                      {
                          "name": "{service_name}-443-tcp",
                          "containerPort": 443,
                          "hostPort": 443,
                          "protocol": "tcp",
                          "appProtocol": "http"
                      }
                  ],
                  "essential": true,
                  "environment": [],
                  "environmentFiles": [
                      {
                          "value": "arn:aws:s3:::{env_file_object_key_1}",
                          "type": "s3"
                      },
                      {
                          "value": "arn:aws:s3:::{env_file_object_key_2}",
                          "type": "s3"
                      }
                  ],
                  "mountPoints": [],
                  "volumesFrom": [],
                  "logConfiguration": {
                      "logDriver": "awslogs",
                      "options": {
                          "awslogs-group": "/ecs/{service_name}",
                          "awslogs-create-group": "true",
                          "awslogs-region": "ap-northeast-2",
                          "awslogs-stream-prefix": "ecs"
                      }
                  },
                  "systemControls": []
              }
          ],
          "taskRoleArn": "",
          "executionRoleArn": "",
          "networkMode": "awsvpc",
          "volumes": [],
          "placementConstraints": [],
          "requiresCompatibilities": [
              "FARGATE"
          ],
          "cpu": "2048",
          "memory": "4096",
          "runtimePlatform": {
              "cpuArchitecture": "X86_64",
              "operatingSystemFamily": "LINUX"
          }
      }
  3. Github Repository에 환경변수 설정
    • 경로: Settings > Secrets and variables > Actions
    • ECR_REPOSITORY_URI : ECR의 URI
    • AWS_REGION , AWS_ACCESS_KEY_ID , AWS_SECRET_ACCESS_KEY : AWS 인증 정보
  4. workflow 파일 정의
    • dev 브랜치 푸시될 경우 ECR에 해당 브랜치 빌드 후 푸시
    • ci-cd.yml
      name: Update, Delete Old Autoscaling Policy, and Apply New Policy for ECS Service
      
      on:
        push:
          branches:
            - dev
      
      jobs:
        update-and-autoscale:
          runs-on: ubuntu-latest
      
          environment:
            name: ${{ github.ref_name == 'main' && 'main' || 
                      github.ref_name == 'dev' && 'dev' }}
      
          steps:
          - name: Checkout code
            uses: actions/checkout@v2
      
          - name: Set up Docker Buildx
            uses: docker/setup-buildx-action@v1
      
          - name: Get Git Repository URL
            id: get_repo_url
            run: echo "REPOSITORY_URL=$(git config --get remote.origin.url)" >> $GITHUB_ENV
      
          - name: Login to Amazon ECR
            id: login-ecr
            uses: aws-actions/amazon-ecr-login@v1
            env:
              AWS_REGION: ${{ secrets.AWS_REGION }}
              AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
              AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      
          - name: Push Docker image to ECR
            uses: docker/build-push-action@v2
            with:
              context: .
              push: true
              tags: |
                ${{ secrets.ECR_REPOSITORY_URI }}:${{ github.sha }}
                ${{ secrets.ECR_REPOSITORY_URI }}:latest
              cache-from: type=gha
              cache-to: type=gha,mode=max
  5. dev에 코드 푸시 후 ECR 푸시 확인
  6. 클러스터 생성
    • 인프라: AWS Fargate(서버리스)
  7. 서비스 생성
    • 환경
      • 컴퓨팅 옵션: 시작 유형
    • 배포구성
      • 애플리케이션 유형: 서비스
      • 태스크 정의: 앞에서 정의한 Task 선택
      • 원하는 태스크 수 설정
    • 네트워킹
      • vpc 및 서브넷, 보안그룹 설정
    • 로드 밸런싱
      • 로드 밸런서 유형: Application Load Balancer
      • 로드밸런서, 리스너, 대상그룹 설정
    • 서비스 자동크기 조정
      • 최소, 최대 대스크 개수 설정
      • 조정 정책 유형 : 대상추적
        • 지표, 대상값, 확장 휴지 기간, 축소 휴지기간 등 설정
  8. 서비스 생성 후 Task 생성 및 컨테이너 동작 확인
  9. Github Repository에 환경변수 추가
    • AWS_ECS_CLUSTER : 앞에서 생성한 클러스터 명
    • AWS_ECS_CLUSTER_SERVICE : 앞에서 생성한 서비스 명
    • AWS_ECS_TASK_DEFINITION : 앞에서 정의한 태스크 개정명
  10. workflow 파일 수정
    • ECR 푸시 이후 자동으로 서비스 업데이트하여 배포되도록 수정
    • ci-cd.yml
      name: Update, Delete Old Autoscaling Policy, and Apply New Policy for ECS Service
      
      on:
        push:
          branches:
            - dev
      
      jobs:
        update-and-autoscale:
          runs-on: ubuntu-latest
      
          environment:
            name: ${{ github.ref_name == 'main' && 'main' || 
                      github.ref_name == 'dev' && 'dev' }}
      
          steps:
          - name: Checkout code
            uses: actions/checkout@v2
      
          - name: Set up Docker Buildx
            uses: docker/setup-buildx-action@v1
      
          - name: Get Git Repository URL
            id: get_repo_url
            run: echo "REPOSITORY_URL=$(git config --get remote.origin.url)" >> $GITHUB_ENV
      
          - name: Login to Amazon ECR
            id: login-ecr
            uses: aws-actions/amazon-ecr-login@v1
            env:
              AWS_REGION: ${{ secrets.AWS_REGION }}
              AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
              AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      
          - name: Push Docker image to ECR
            uses: docker/build-push-action@v2
            with:
              context: .
              push: true
              tags: |
                ${{ secrets.ECR_REPOSITORY_URI }}:${{ github.sha }}
                ${{ secrets.ECR_REPOSITORY_URI }}:latest
              cache-from: type=gha
              cache-to: type=gha,mode=max
      
          - name: Configure AWS credentials
            uses: aws-actions/configure-aws-credentials@v1
            with:
              aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
              aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
              aws-region: ${{ secrets.AWS_REGION }}
      
          - name: Update ECS service
            run: |
              aws ecs update-service \
                --cluster ${{ secrets.AWS_ECS_CLUSTER }} \
                --service ${{ secrets.AWS_ECS_CLUSTER_SERVICE }} \
                --task-definition ${{ secrets.AWS_ECS_TASK_DEFINITION }} \
                --force-new-deployment
  11. dev에 푸시 후 service 업데이트 및 배포 확인

트러블 슈팅

대상그룹에서 상태체크 에러

  • 현상 서비스 생성 후 배포시에 대상그룹에서 task 인스턴스가 상태체크를 통과하지못하고 계속 unheathly로 취급되어 계속 task가 드레이닝되고 새로운 태스크가 생성는 과정이 반복됨
  • 원인 task 인스턴스 내부에서 실행중인 애플리케이션 프로젝트에서 헬스체크 요청에 대해서 ALLOWED_HOST에 포함되지않은 요청이라고 Bad Request를 반환하고 있었음.
  • 해결방법
    • 처음에는 task를 정의한 json 파일에 해당 task의 private ip를 환경변수로 설정하고 애플리케이션 프로젝트에서 해당 환경변수를 받아 ALLOWED_HOST에 포함시키려고했는데 json파일 정의 단계에서 private ip를 알 수 있는 방법이 없었음.
    • Task 메타데이터를 통해 알 수 있었는데 해당 데이터를 json 정의 단계에서는 참조할 수 없었음.
    • 그래서 메타데이터 엔드포인트를 이용해 애플리케이션 프로젝트가 로딩될때 엔드포인트에 get요청으로 메타데이터를 요청해서 private ip를 가져와 ALLOWED_HOST에 추가하는 것으로 해결

요금 계산

Fargate 2vCPU, 4GB로 10개 태스크 상시 유지시

  • 단위 변환 관리 이벤트
    • 태스크 또는 포드 수: 10 일별 * (730 시간(1달 기준) / 24 시간(하루 기준)) = 304.17 월별
    • 평균 기간: 1 days = 24 hours
  • 요금 계산 304.17 태스크 x 2 vCPU x 24 시간 x 0.04656 시간당 USD = 679.78 vCPU 시간 요금 USD 304.17 태스크 x 4.00 GB x 24 시간 x 0.00511 시간당 GB당 USD = 149.21 GB 시간 요금 USD 20 GB - 20 GB(추가 요금 없음) = 0.00 태스크당 청구 가능한 임시 스토리지 GB 679.78 vCPU 시간 요금 USD + 149.21 GB 시간 요금 USD = 828.99 합계 USD Fargate 비용 (월별): 828.99 USD
profile
kimphysicsman

0개의 댓글