ECR ECS

참고

IAM 과정

IAM과정
IAM(인증)에서 USER 생성         ===>        Access Key와 Secret Access Key 발급
            ROLE 부여                       (외부로 노출되면 안됨) DB나 다른 애플리케이션에서 받아와야 함
                - S3 Full Access
                - Cloud Front(CDN) Full Access

위 방식 대신 Open ID Connect 이용 추천
aws와 github에 각각 사용할 서비스만 등록
키를 사용하면 광범위한 서비스에 액세스 가능하기 때문

패키지 설치 주의사항

  • python이나 node 설치 시
    • 학습을 할 때는 패키지를 Global로 설치해도 됨
    • 배포를 할 때는 python은 가상환경을 생성하고 작업하고 node는 local에 설치
      • python은 가상환경 사용 안하면 사이즈가 너무 커짐, 파이썬은 global 경로에 설치된 패키지 전부 등록
        • 가상 환경 사용시 가상 환경의 Lib 안에 패키지가 설치됨
      • node는 global 설치 시 실행에러 발생 가능, package.json에 적지 않음
      • 패키지를 같이 업로드하면 소스 레지스트리에서 거부 => 패키지 정보를 저장하는 파일을 생성
        • requirements.txt 관용화된 파일이름 (python)
        • package.json (node)

빌드 주의 사항

  • go는 Dockerfile 생성 시 다단계 빌드가 필요
    • 소스 코드 => 실행가능한 파일 생성 => 실행
    • 소스 코드는 실행 시 불필요, 포함 시 이미지 크기가 커짐
    • go나 c는 머신 종속적으로 실행가능 파일 만듬 => 미리 만들어서 빌드 파일 만들어 업로드가 불가
    • 실행 환경에서 빌드하고 컴파일 해야
  • mysql도 과거에는 소스 코드만 제공해서 빌드를 써야 했음
    • 과거에는 모토로라 CPU와 인텔 CPU 등과 같은 여러 가지 CPU존재
  • Java는 image 크기 축소 가능

1.Django 애플리케이션을 Docker Image로 만들어서 ECR에 업로드

1)프로젝트 생성

  • 가상 환경 생성 및 설정
python3 -m venv 가상환경이름(Windows는 python)
source 가상환경이름/bin/activate(Windows는 가상환경이름/Scripts/activate: cd 가상환경이름 -> cd Scripts -> activate)

python3 -m venv venv
source ./venv/Scripts/activate
  • 필요한 패키지 설치
    pip install django djangorestframework
  • 프로젝트 생성
    django-admin startproject apiserver .
  • 프로젝트 실행
    python manage.py runserver
    • 기본적으로 8000번 포트로 실행
  • 장고 프로젝트를 외부 서버에 배포할 때 주의할 점
    • 장고 프로젝트는 고정 IP를 이용해서 배포를 할 때 runserver 다음에 IP를 설정하고 포트를 설정해서 배포하는 것이 가능
    • 기본 설정으로 배포를 하게 되면 특정 IP를 기재해서 배포를 해야 하는데 이렇게 하면 배포 명령을 매번 입력해야 함
  • settings.py 파일의 기본 설정 변경
    • 장고는 외부라이브러리를 설치하거나 application을 만들면 등록을 해줘야 함
    • Time Zone
      • DB와 Application이 여러 대의 컴퓨터에서 동작할 때 동기화 중요
      • 쿠버네티스도 마스터노드와 워커 노드 시간 대역 맞추기가 첫번째
ALLOWED_HOSTS = ['*']

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'apiserver'
]

TIME_ZONE = 'Asia/Seoul'

2)사용자의 요청 처리

  • apiserver 애플리케이션 디렉터리에 views.py 파일을 생성하고 요청을 처리하는 함수를 추가
views.py
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status

@api_view(['GET'])
def index(request):
    data = {"result": "success",
            "data": [{"id":"itstudy", "name":"군계"},
                     {"id":"ggangpae1", "name":"아담"}]}
    return Response(data, status=status.HTTP_200_OK)
  • urls.py 파일에 요청 처리 코드를 추가
urls.py
from django.contrib import admin
from django.urls import path

from .views import index
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index)
]

3)가상 환경에 설치된 패키지 목록을 텍스트 파일로 내보내기

  • 루트 디렉터리에서 명령 실행 pip freeze > requirements.txt

4)도커 이미지 생성 및 실행

  • 루트 디렉터리에 Dockerfile을 생성하고 작성
FROM --platform=linux/amd64 python:3.8-slim-buster as build

RUN apt-get update \
  && apt-get install -y --no-install-recommends \
  && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .

EXPOSE 80
CMD ["python", "manage.py", "runserver", "0.0.0.0:80"]
  • 이미지 빌드
    docker build -t apiserver .

    • 이미지 빌드하다가 패키지 버전 문제로 에러가 발생할 수 있음
      이런 경우에는 패키지 버전을 수정하던가 베이스 이미지 버전을 수정해서 해결
  • 이미지를 컨테이너로 만들어서 실행
    docker run -d --name apiserver -p 80:80 apiserver

5)도커 이미지를 ECR에 Git Action을 이용해서 업로드

  • Docker Hub에 업로드
    • Docker Hub에서 토큰을 발급(read-write 권한)
    • 레포지토리 생성: djangoecs
    • github에 시크릿 추가
    • git action을 위한 폴더를 루트에 생성 후 ./github/workflows/gitaction.yaml 파일을 생성하고 작성
gitaction.yaml
name: django

on:
    push:
        branches: ["main"]
    pull_request:
        branches: ["main"]

jobs:
    build:
        runs-on: ubuntu-latest

        steps:
            - name: Checkout
              uses: actions/checkout@v3
            
            - name: Python Setup
              uses: actions/setup-python@v4
              with:
                python-version: '3.8'
            - name: Install Dependencies
              run: |
                python -m pip install --upgrade pip
                pip install -r requirements.txt                
            - name: Login to Dockerhub
              uses: docker/login-action@v1
              with:
                username: ${{ secrets.DOCKERHUB_USERNAME }}
                password: ${{ secrets.DOCKERHUB_TOKEN }}
            - name: DockerHub Upload
              env:
                NAME: ${{ secrets.DOCKERHUB_USERNAME}}
                REPO: djangoecs
                IMAGE_TAG: ${{ github.sha }}
              run: |
                docker build -t $REPO .
                docker tag $REPO:latest $NAME/$REPO:$IMAGE_TAG
                docker push $NAME/$REPO:$IMAGE_TAG                
  • Docker Image를 만들 때 고정된 태그를 붙일 수 있고 계속 변하는 태그를 사용할 수 있음
    고정된 태그를 붙이면 Registry에서 이미지를 업데이트
    이전에 만든 이미지는 소멸되고 새로운 이미지가 계속 업데이트
    태그를 수정하면 이미지는 추가

    이미지가 변경되었을 때 컨테이너 또는 pod를 다시 배포하고자 하는 경우 태그가 고정인 경우는 컨테이너나 pod를 재시작하는 것만으로는 애플리케이션을 재배포 할 수 없으므로 이 경우에는 기존 이미지를 삭제하고 컨테이너나 파드를 다시 생성해야 함
    태그가 변경되는 경우에는 컨테이너나 파드를 재생성하라고만 명령을 내리면 됨

    • ECS나 EKS가 아니더라도 여기까지 구현을 하고 EC2 서버에 도커나 쿠버네티스를 설치한 후 Git Action에 EC2 컴퓨터에 SSH로 접속을 하도록 해서 CI/CD를 구현하는 것이 가능
      • 가변 태그일 경우 이전 이미지 삭제해야
      • 고정 태그일 경우 컨테이너 중지하고 삭제 후 실행해야
        • latest tag: health check 후 종료되는 경우 이미지를 가지고 있지 않으면 돌릴 수 없음
    • 루트에 .gitignore 파일 생성
    .gitignore
    venv/
  • ECR(Private Registry - 외부에 공개된 서버나 로컬 컴퓨터에 연결된 네트워크 상의 컴퓨터: 연결 가능한 상태로 만들고 생성)에 업로드

    • ECR은 AWS에서 제공하는 Docker Image Registry
      public 과 private 설정이 모두 가능
      별도의 Image Registry를 만들어서 관리가 어려운 경우 사용
      public 으로 만들면 외부에서 Image를 pull 하는 것이 가능

      이 저장소를 외부 애플리케이션이나 Git Hub 같은 곳에서 사용하고자 하면 push 가 가능한 IAM 이나 Open ID Connect를 만들어 주어야 함

      외부 Application이 사용하고자 할 때는 access-key 와 secret access-key를 사용하고 git hub가 사용하고자 할 때는 Open ID Connect를 사용해야 함

접근권한
access-key & secret-key-User > Role > Policy
Policy(정책):
    - 제공되는 권한: S3 Full Access(모든 버킷 접근 가능)
    - 제공되는 권한 + 제약(특정 버킷만 접근 가능)

Open ID Connect: Role
누가: github > user > repository > branch
누가 어떤 repo의 어떤 branch를 쓸지 설정
  • AWS의 Elastic Container Registry 서비스에 접속해서 하나의 Repository를 생성: djangoecs

  • 위의 레포지토리에 이미지를 push 할 수 있는 정책 생성
    IAM 의 정책 탭에서 정책 생성을 클릭 > [JSON]선택 후 작업 추가 > 리소스 추가 > 정책 이름 작성 > 정책 생성 클릭

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"ecr:CompleteLayerUpload",
				"ecr:UploadLayerPart",
				"ecr:InitiateLayerUpload",
				"ecr:BatchCheckLayerAvailability",
				"ecr:PutImage"
			],
			"Resource": [
				"arn:aws:ecr:ap-northeast-2:AWS계정:repository/djangoecs"
			]
		},
        {
            "Effect": "Allow",
            "Action": "ecr:GetAuthorizationToken",
            "Resource": "*"
        }

	]
}

역할 할당 클릭

웹 자격 증명 클릭

조직에는 Git Hub의 유저 이름
repository에 레포지토리 이름(미설정시 모든 repo 해당)
branch에 브랜치 이름을 설정(미설정시 모든 branch 해당)

권한 정책 추가

앞에서 만든 권한을 선택하고 역할 이름을 생성
django_image_push 검색 후 추가

역할 이름 설정

ARN을 확인

  • 로그인 되는지 확인: git action의 yaml 파일 최상단 레벨에 추가
permissions:
  id-token: write
  contents: read
  • 로그인 되는지 확인: git action의 yaml 파일의 steps에 추가
            - name: AWS Role을 이용한 로그인
              uses: aws-actions/configure-aws-credentials@v4
              with:
                role-to-assume: arn:aws:iam::AWS계정:role/django_image_push
                role-session-name: sampleSessionName
                aws-region: ap-northeast-2
  • ECR에 업로드: git action의 yaml 파일의 steps에 추가
            - name: ECR에 로그인
              id: login-ecr
              uses: aws-action/amazon-ecr-login@v1
            
            - name: build and push image to Amazon ECR
              id: build-image
              env:
                ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
                IMAGE_TAG: ${{ github.sha }}
              run: |
                docker build -t $ECR_REGISTRY/djangoecs:$IMAGE_TAG .
                docker push $ECR_REGISTRY/djangoecs:$IMAGE_TAG                

2. ECS(Elastic Container Service)

개요

  • 운영 중인 도커 컨테이너나 인스턴스가 몇 개 정도라면 오케스트레이션 도구 없이 운영이 가능하지만 수십 개에서 수백 개가 필요하다면 어디에 어떤 컨테이너가 실행되고 있고 어떤 문제점이 있는지 모니터링하고 관리할 수 있는 도구가 필요한데 이러한 도구가 컨테이너 오케스트레이션
    이러한 목적으로 Docker Swarm 이나 Kubernetes 를 사용
  • AWS의 ECS는 Auto Scaling Task 기반, IAM 지원, Application Load Balancer 지원, Fargate 지원 등 AWS에 맞춤화된 기능을 추가해서 편리하고 컨테이너 오케스트레이션을 활용할 수 있도록 해줌
  • Task를 정의하고 서비스 관리 콘솔에서 옵션만 설정하면 됨

구성 요소

참고
Dockerfile: 하나의 이미지
docker-compose: 1개 이상의 이미지를 가지고 컨테이너를 생성
docker에서는 실행 중인 컨테이너를 Image로 생성
  • Cluster
    • ECS의 가장 기본적인 단위
    • Docker Container를 실행할 수 있는 가상의 공간
    • Cluster 자체는 기본적으로 컴퓨팅 자원을 포함하지 않는 논리적인 단위
    • 작업을 정의하고 서비스를 만들어 달라고 요청을 하면 그 때 컴퓨팅 자원을 연결해서 실행
  • Task: Pod
    • ECS에서 컨테이너를 실행하는 최소 단위
    • 클러스터 상에서 동작하는 1개 또는 1개 이상의 컨테이너들을 지칭
    • 하나의 컨테이너로 구성 될 수도 있고 여러 개의 컨테이너로 구성될 수도 있음
  • Task Definition: docker-compose와 유사
    • 실제 실행될 컨테이너를 만들기 위한 도면
    • docker 명령으로 실행할 수 있는 거의 모든 옵션이 설정 가능
    • 이 파일의 내용을 가지고 컨테이너를 생성
  • Service: Deployment와 유사
    • 작업을 하나의 오케스트레이션 단위로 묶은 것
    • 이 단위로 로드 밸런서 그리고 오토 스케일링을 연결해서 애플리케이션 단위로 조절
  • 컨테이너 인스턴스
    • 컨테이너를 실행할 수 있도록 해주는 객체(인스턴스, 컴퓨터 등)
    • 파게이트 서비스를 선택하면 인스턴스를 직접 생성하거나 설정할 필요가 없이 AWS에서 알아서 생성

Fargate

  • AWS Serverless Service 중 하나
  • 매니지드 컨테이너 오케스트레이션 서비스인 ECS와 EKS를 기반으로 작동하는 서비스
  • Docker Container를 EC2 인스턴스 없이 독립적으로 실행할 수 있게 함
  • EC2 인스턴스보다 vCPU나 RAM을 세세하게 설정할 수 있음

배포 방법

  • 태스크로 배포: 단순하게 배포하는 것으로 오토 스케일링이나 로드 밸런서를 추가할 수 없음
  • 태스크를 만든 후 서비스로 배포

Github의 Action을 이용해서 ECS에 배포

  • Docker는 기본적으로 docker-hub와 local의 image만 사용가능
  • ECS, EKS의 Docker는 ECR의 이미지도 사용가능
  • Task가 여러 개일 때 로드밸런서 사용해 하나의 IP로 접속가능
  • Task가 하나일때는 퍼블릭IP를 할당해야 함
graph LR
    A[Code Push] --> B[Image Build]
    B --> C[Docker Hub Push]
    B --> D[ECR Push]
    C --> E[Task Definition 수행(Image 수정)]
    D --> E[Task Definition 수행(Image 수정)]
    E --> F[ECS Service 업데이트]
  • ECS 서비스를 생성
    • 클러스터를 생성(djangoecscluster)
    • task를 생성(djanagoecs)(GitAction으로는 task definition 생성 불가)
    • 태스크를 이용해서 서비스를 생성
    • Load Balancer의 DNS나 서비스 내부의 태스크의 Public IP를 이용해서 접속 확인
  • 생성한 태스크의 json 파일을 다운로드 받아서 프로젝트의 루트 디렉터리에 복사
    • json의 image 항목 제거: image는 image registry에 배포되는 이미지를 사용할 것이므로
    • task-definition.json으로 파일 이름 수정
  • gitaction yaml 파일 수정
name: django

on:
    push:
        branches: ["main"]
    pull_request:
        branches: ["main"]

permissions:
    id-token: write
    contents: read

env:
  AWS_REGION: ap-northeast-2
  ECR_REPOSITORY: djangoecs
  ECS_TASK_DEFINITION: ./task-definition.json
  CONTAINER_NAME: djangoecs
  ECS_SERVICE: djangoecs
  ECS_CLUSTER: djangoecscluster

jobs:
    build:
        runs-on: ubuntu-latest

        steps:
            - name: Checkout
              uses: actions/checkout@v3
            
            - name: Python Setup
              uses: actions/setup-python@v4
              with:
                python-version: '3.8'
            - name: Install Dependencies
              run: |
                python -m pip install --upgrade pip
                pip install -r requirements.txt                
            - name: Login to Dockerhub
              uses: docker/login-action@v1
              with:
                username: ${{ secrets.DOCKERHUB_USERNAME }}
                password: ${{ secrets.DOCKERHUB_TOKEN }}
                
            - name: DockerHub Upload
              env:
                NAME: ${{ secrets.DOCKERHUB_USERNAME}}
                REPO: djangoecs
                IMAGE_TAG: ${{ github.sha }}
              run: |
                docker build -t $REPO .
                docker tag $REPO:latest $NAME/$REPO:$IMAGE_TAG
                docker push $NAME/$REPO:$IMAGE_TAG                
            
            - name: AWS Role을 이용한 로그인
              uses: aws-actions/configure-aws-credentials@v4
              with:
                role-to-assume: arn:aws:iam::905418305225:role/django_image_push
                aws-region: ap-northeast-2
            
            # ECR에 로그인
            - name: ECR에 로그인
              id: login-ecr
              uses: aws-action/amazon-ecr-login@62f4f872db3836360b72999f4b87f1ff13310f3a
            
            # 이미지를 빌드하고 ECR에 푸시
            - name: build and push image to Amazon ECR
              id: build-image
              env:
                ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
                IMAGE_TAG: ${{ github.sha }}
              run: |
                docker build -t $ECR_REGISTRY/djangoecs:$IMAGE_TAG .
                docker push $ECR_REGISTRY/djangoecs:$IMAGE_TAG
                echo "image=$ECR_REGISTRY/djangoecs:$IMAGE_TAG" >> $GITHUB_OUTPUT                

            #푸시된 이미지를 가지고 ECS 서비스 재시작
            - name: Fill in the new Image in the ECS task Definition
              id: task-def
              uses: aws-actions/amazon-ecs-render-task-definition@c804dfbdd57f713b6c079302a4c01db7017a36fc
              with:
                task-definition: ${{ env.ECS_TASK_DEFINITION }}
                container-name: ${{ env.CONTAINER_NAME }}
                image: ${{ steps.build-image.outputs.image }}

            - name: Deploy ECS task definigion
              uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a
              with:
                task-definition: ${{ steps.task-def.outputs.task-definition }}
                service: ${{ env.ECS_SERVICE }}
                cluster: ${{ env.ECS_CLUSTER }}
  • git hub push를 수행하면 ecs에 대한 권한이 없어서 에러
    • aws 의 IAM에 접속해서 현재 사용 중인 role에 ecs 사용 권한을 추가하고 git hub에서 action을 rerun
    • ECSfullaccess 권한 추가

이미지 배포 시 태그 문제

  • Amazon의 ECS 나 EKS는 배포를 하게되면 이미지를 무조건 다운로드 받는데 직접 설치한 Docker 나 Kubernetes는 이미지의 태그명까지가 일치하면 다운로드 받지 않음
  • ECS 나 EKS에 이미지를 배포할 때는 고정된 태그를 사용해도 되지만 직접 설치한 Docker 나 Kubernetes를 이용할 때는 고정된 태그를 사용하면 이미지를 업데이트하지 않음

도메인 연결

  • Route53에서 호스팅 영역에서 [새 레코드 생성]을 누르고 도메인을 설정하고 Load Balance를 연결해주면 됨

HTTPS 설정

  • Certificate Manager에서 인증서 요청 > 인증서 발급 후 [Route53에서 레코트 생성] 클릭해서 인증서 검증 > 발급됨
  • Load Balancer에서 리스너 추가를 이용해서 프로토콜과 포트를 설정하고 인증서를 선택해주면 됨
    • 로드밸런서 > 리스너 규칙 > 리스너 추가 > HTTPS > [라우팅 액션] 대상 그룹으로 전달 > 인증서 선택 > [추가]
  • 주의할 점은 서버가 HTTPS를 사용하면 클라이언트도 HTTPS를 사용해야 함
    서버 & 클라이언트 애플리케이션의 경우는 HTTPS 설정은 양쪽 모두에서 수행해야 함
    일반적으로 서버에서 먼저하고 테스트를 한 후 클라이언트에 적용