개요
요즘엔 프로젝트나 개인 Repository에도 소스를 작성하면 거의 도커라이징을 적용하고 있습니다. 어떤 환경(집, 카페, 다른 장소)에서든 스펙에 맞춰 실행하면 빠른 개발 환경을 구성할 수 있어 편리하기 때문이죠.
보통 Django나 Flask를 도커라이징을 많이 적용했습니다만 이번에 겪은 일은 특정 시간에 한 번만 실행하는 Python Script를 컨테이너로 만들어서 ECS에 올려야 했습니다.
ECS가 뭔데?
ECS는 Elastic Container Service로 클러스터에서 컨테이너를 쉽게 실행, 중지, 및 관리할 수 있게 해주는 컨테이너 관리 서비스입니다.
ECS를 사용하기 이전에 먼저 설정해줘야 하는 부분들이 있는데 바로 'ECR'과 'Task Definition'입니다.
ECR은 Elastic Container Registry로서 build 된 컨테이너 image를 올리는 곳이며 'Task Definition'은 ECR에 등록한 컨테이너 이미지로부터 ECS를 통해 실행할 옵션을 설정해 주는 부분입니다.
이 글에서는 ECR에 컨테이너 이미지를 올리는 방법과 Task Definition 설정에 대한 세부 과정을 다루지는 않습니다.
Python Script를 실행하는 컨테이너 만들기
ECS에서 Python Script를 Scheduling 하기 위해 Docker 컨테이너를 실행하면 Python Script가 한 번만 실행되고 종료되는 컨테이너를 만들어봅시다.
우선 간단히 Hello World 문자열을 찍는 app.py를 만듭니다.
# app.py
print("Hello, World")
그리고 당연히 이를 컨테이너 만들기 위한 Dockerfile이 필요합니다. 다음과 같이 작성합니다.
# Dockerfile
FROM python:3.10-slim-buster
WORKDIR /usr/src/app
COPY . .
ENTRYPOINT ["python3", "/usr/src/app/app.py"]
주의할 점은 ENTRYPOINT로써 존재하는 명령입니다.
Dockerfile에서 ENTRYPOINT는 CMD와 인스트럭션과 같이 컨테이너가 수행하게 될 명령을 정의합니다. 컨테이너가 무슨 일을 하는지 결정하게 되는 단계를 정의하기 때문에 ENTRYPOINT와 CMD는 다음과 같은 차이가 있습니다.
- ENTRYPOINT
- 컨테이너 실행 시 argument를 받지 않는다
- CMD
- 컨테이너 실행 시 argument를 지정하여 CMD 인스트럭션에 정의된 명령 외에 다른 명령을 수행할 수 있다.
- 컨테이너 실행 시 argument를 지정하여 CMD 인스트럭션에 정의된 명령 외에 다른 명령을 수행할 수 있다.
여기서 저는 외부로터 인자를 받아 실행할 일이 없기 때문에 ENTRYPOINT를 사용해 Dockerfile을 작성했습니다. 선택 사항이지만 편의를 위해 저는 docker-compose.yml도 같이 작성해 두곤 합니다.
# docker-compose.yml
version: "3"
services:
app:
build: .
command:
- python3 /usr/src/app/app.py
지금까지 작성한 파일들을 디렉터리에서 보면 다음과 같습니다.
╰─$ tree -L 1
.
├── Dockerfile
├── app.py
└── docker-compose.yml
이제 docker-compose를 수행하여 image를 빌드해 봅시다.
╰─$ sudo docker-compose up -d
Password:
[+] Building 6.6s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 145B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/python:3.10-slim-buster 5.0s
=> [internal] load build context 0.0s
...
=> [2/3] WORKDIR /usr/src/app 0.0s
=> [3/3] COPY . . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:1ad76ef6cdc679fb4bc2054414b2d49a4b4768dd3fa99c09c35234a9ce3b5bef 0.0s
=> => naming to docker.io/library/script-container-app 0.0s
[+] Running 2/2
⠿ Network script-container_default Created 0.1s
⠿ Container script-container-app-1 Started
╰─$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
script-container-app latest 1ad76ef6cdc6 9 seconds ago 112MB
docker image를 실행하면 Hello World가 출력되는 걸 확인할 수 있습니다.
╰─$ sudo docker run script-container-app
Hello, World
이제 ECS에 올려보자
지금까지 구성한 내용을 ECS에 올리기 위해서는 "예약된 작업"이라는 항목을 사용해야 합니다.
이 "예약된 작업"을 생성할 때 스케줄링을 하기 위해서 등록하는 옵션을 설정할 수 있습니다.
제 경우엔 cron 식을 이용했는데 주의할 점은 AWS의 Cron 식은 GMT를 기준으로 합니다. 즉 한국에서 실행하기 위해서는 시간대를 맞춰주는 작업이 필요합니다. 저는 편의를 위해 https://savvytime.com/converter/gmt-to-kst에서 시간대를 참고하여 적용했습니다.
예를 들어, 월요일에서부터 금요일에 한국 시간으로 오후 6시에 실행하고 싶다면 다음과 같이 적용합니다.
cron(0 9 ? * MON-FRI *) # 9시는 GMT이며 한국시간으로 오후 6시
잘 돌아가나?
생성에 완료된 경우 ECS 클러스터의 "작업" 탭에서 설정된 시간에 맞춰 인스턴스가 준비되는 결과를 볼 수 있습니다. 또한 이렇게 등록된 Cron 식은 AWS의 EventBridge에서 Scheduling 된 예약 시간을 확인할 수도 있습니다.
사실 이렇게까지 구성하고도 컨테이너 안에 작성된 소스를 모니터링해야 되는 상황도 있습니다. 이럴 땐 Slack으로 Notification을 발생한다든지 혹은 CloudWatch를 이용해 로그를 남기는 선택도 고려해 볼 수 있습니다.
끝으로..
ECS를 선택해 환경을 구성하기 전에 최근 사용했던 App Runner를 사용해 볼까도 고려했습니다. App Runner를 사용하지 못한 이유는 다음과 같습니다.
- App Runner는 특성상 Web에 맞춰져 있기 때문에 Python Script를 실행할 수 있는 방법을 구성할 수 있을지에 대한 정보를 찾지는 못함
- App Runner는 배포 시간이 ECS보다 늦기 때문에 특정 Event에 알림을 처리해 주는 단발성 작업에 대한 소요 시간이 더 많아질 거라 판단
컨테이너를 통해 수행할 작업이 무엇인지에 따라 적절한 방법을 택하는 것이 중요하다 봅니다.
'Architecture > AWS' 카테고리의 다른 글
[AWS] AWS에서의 Public 및 Private Subnet 설정 (0) | 2024.03.24 |
---|---|
[AWS] EC2(Amazon Linux 2023)에 Service 파일 생성하기 (0) | 2024.01.25 |
[AWS] EC2(Amazon Linux 2023)에 Python 설치하기 (0) | 2024.01.20 |
[AWS] App Runner를 사용해보자. (3) | 2023.01.05 |