MAS 같은 마이크로 서비스 아키텍쳐등이 유행?이라고 하더라고요 그런 배포방식을 위해선 도커와 도커 오케스트레이션 툴을 이용한 배포가 가장 최적의 방법입니다. AWS에선 컨테이너 오케스트레이션 툴로써 ECS와 EKS(쿠버네티스 툴)을 제공하고 있습니다. 저는 오늘 ECS를 통해 flask 웹 페이지를 배포하는 방법을 알아보겠습니다.
이론
들어가기 전에 간단하게 알고들어가면 좋은 단어들과 뜻 알려드리겠습니다.
용어 | 내용 |
ECS Task | 서비스가 실행되는 Docker 컨테이너 |
ECS Taskdefinition | ECS 서비스가 구동되기 위한 조건을 서술한 테이블 ex) 사용할 컨테이너 이미지, 네트워크 모드 등등을 서술 |
Task 실행 역할 | task가 task definition을 통해 작업을 수행하기 위해 필요한 역할 |
Task 역할 | Task 즉 Docker 컨테이너가 아마존 리소스 예를 들어 S3와 같은 리소스에 접근해야할때 부여하는 권한 |
ECS agent | EC2에서 Task를 돌리기 위한 |
ECS cluster | ECS 컨테이너들이 관리되는 공간 |
ECS namespace | Cluster와 같은 공간에 대한 개념, cluster = namespace로 생성되고 namespace 하위에 namespace를 두어 cluster를 좀 더 세분화해서 관리할 수 있음 |
ECS Service | Task 즉 컨테이너를 통해 배포되는 서비스 |
ECS 최적화 AMI | ECS agent와 docker가 이미 설치되어 구동되고 있는 AMI |
네트워킹
토폴로지와 같이 가용영역 2개, Public subent 2개, Private subnet 2개를 생성합니다.
컨테이너를 위한 NAT 또는 Endpoint
ECS를 Private에 배포하기 위해선 이 점을 고려해야 합니다. ECS 컨테이너가 프라이빗에 위치하지만 ECR은 S3 처럼 글로벌 인터넷에 위치하기 때문에 ECR의 이미지를 받아오기 위해선 Endpoint 또는 NAT가 필요합니다. NAT를 구성할 시에는 항상 하던대로 NAT를 생성하고 프라이빗 라우팅 테이블에 추가해주면 됩니다.
Endpoint 구성
엔드포인트는 구성할 시 총 3개 S3,ECS,ECR을 구성해야 하는데 Fargate를 이용하여 서비스를 배포할땐 ECS 엔드포인트를 설정해줄 필요가 없습니다.
라우팅 테이블이 Endpoint를 검색하고 라우팅하기 위해서 먼저 VPC로 이동해 VPC 설정 편집창에서 DNS 호스트 이름 허용을 해주셔야 합니다. Endpoint용 보안 그룹을 생성해야하는데 Endpoint는 443번 포트를 이용하여 통신하기 때문에 443번 포트를 허용해주시고 입 맛에 맞게 설정하시면 됩니다.
ECR용 엔드포인트 구성
vpc → endpoint 로 이동합니다
com.amazonaws.ap-northeast-2.ecr.dkr, com.amazonaws.ap-northeast-2.ecr.api 이렇게 두가지 엔드포인트가 필요합니다.
한번에 설정해주지 못하기 때문에 2개 다 만드셔야 합니다. ECS,S3,ECR용 엔드포인트를 모두 생성한다고 쳤을때 총 6개의 엔드포인트를 생성해야합니다.
엔드포인트가 위치할 서브넷은 ECS 클러스터의 서비스가 배포될 서브넷으로 설정합니다. 저는 프라이빗에 설정할거니까 프라이빗 서브넷으로 지정했습니다.
전체 액세스 선택하고 완성합니다.
S3용 엔드포인트 구성
S3용 엔드포인트를 구성하는 이유는 ECR이 리포지토리 이름을 S3에 저장하고 참조한다고 합니다. 따라서 설정해줘야 한다고 배웠는데 확실하진 않습니다.
ECR이나 ECS와는 다르게 서비스에서 s3 검색 후 Interface 유형이 아닌 Gateway 유형 선택 해주셔야 합니다.
게이트웨이 이기 때문에 서브넷 선택이 아닌 라우팅 테이블 선택입니다. 클러스터가 존재하는 서브넷의 라우팅 테이블에 추가해주시면 됩니다. 정상적으로 생성됐다면 라우팅 테이블에서 pl~~ 하고 접두사 목록을 확인하실 수 있습니다. S3도 전체 엑세스하고 완성합니다.
ECS용 엔드포인트 구성
com.amazonaws.ap-northeast-2.ecs-agent, com.amazonaws.ap-northeast-2.ecs-telemetry, com.amazonaws.ap-northeast-2.ecs
위 3가지 엔드포인트가 필요합니다. 구성은 위 ECR 내용과 같기 때문에 따로 기술하지 않겠습니다.
IAM 구성
총 3가지 정책이 필요합니다. ECS task가 taskdefinition의 내용을 수행하도록 하는 AmazonECSTaskExecutionRole 권환, Bastion이 ECR에 접근할 수 있도록 Poweruser 또는 EC2Registryfullaccess, 서비스 배포용 ASG EC2가 클러스터에 종속될 수 있도록하는 EC2ContainerServiceforEC2Role 이렇게 3가지 정도 필요합니다.
ECS task execution role
역할 신뢰대상을 Elastic Container Service Task로 구성합니다.
위에서 말했듯이 AmazonECSTaskExecutionRole 권한을 추가합니다. \
EC2ContainerServiceRole && EC2Poweruser
EC2를 신뢰대상으로 가지는 각각의 권한을 가진 역할 2개를 생성해야합니다.
Bastion용 역할 -> 권한 Poweruser 또는 EC2ContainerRegistryFullAccess
서비스 배포용 ASG EC2 역할 -> AmazonEC2ContainerServiceforEC2Role
나중에 System manager로 편하게 관리하고 싶다면 AmazonSSMManagedInstanceCore 역할도 추가해놓으시면 좋을 것 같습니다.
ECR 구성
AWS ECR → 리포지토리 → 리포지토리 생성 ECR이 검색창에 뜨지 않는다면 ECS로 이동 후 리포지토리로 이동하시면 됩니다.
프라이빗 선택하고 이름 적고 생성합니다.
Bastion 구성
앞서 생성한 역할을 EC2에 할당합니다. 사실 Bastion은 입맛에 맞게 설정하시고 Bastion을 구성하는 이유는 Dockerfile을 통해 flask앱을 컴파일하고 ECR에 올리기 위해서입니다. Window에서도 AWS CLI 자격증명이 돼 있다면 Window에서도 하셔도 됩니다. Poweruser 권한을 EC2에 할당해놓으면 AWS CLI 유저 액세스 없이도 액세스 할 수 있더라고요. 사실 그렇다해도 AWS CONFIG 파일을 직접 생성해줘야 하는데 이 점은 따로 찾아보시고 진행해주세요 쓸게 너무 많아서 하나하나 다 쓰기엔 좀 그렇네요
Bastion에서 ECR에 자격증명
ECR에 도커 이미지를 push 하기 위해선 자격증명이 필요합니다. 아래 코드를 통해서 자격증명을 하게 됩니다.
aws ecr get-login-password --region <이용하는 리젼> | docker login --username AWS --password-stdin <생성한 URI 리포지토리>
Login Succeeded 라고 위 사진처럼 로그가 나타나야 성공한 것입니다.
FLASK 코드
#예시 코드
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello Flask World'
if __name__ == '__main__':
app.run()
#flask 기본 코드 포트 5000번
포트는 상황에 따라서 필요한 포트로 여시고 간단한 flask 코드를 이용하면 됩니다.
Dockerfile 작성
app.py가 존재하는 디렉토리에 Dockerfile을 생성해줘야 합니다.
FROM python #무슨 코드로 만들건지
COPY . /app #현재 디렉토리의 파일들 컨테이너의 /app으로 복사
RUN pip3 install flask #컨테이너에서 flask가 돌아갈 수 있도록 flask 설치
WORKDIR /app #실행될 파일 위치 작성
CMD ["python3","-m","flask","run","--host=0.0.0.0"]
#포트 지정은 "-p <port num>"
파일이름은 꼭 Dockerfile이여야합니다. 코드에는 따로 포트에 대해 명시할 필요 없이 Dockerfile에서 필요한 포트를 설정해주시면 됩니다.
docker build -t [만들 이미지 이름]:[지정할 이미지 태그] [DOCKERFILE 경로]
#ex)
docker build -t flaskweb .
위 명령어의 형식으로 Dockerfile을 통해서 컴파일 해줍니다.
docker images 명령어로 build된 것을 확인할 수 있습니다.
ECR에 도커 이미지 업로드
docker tag [업로드할 이미지 명] [리포지토리 URI]:[새로 지정할 tag]
#EX) docker tag flaskweb [uri]:latest
docker push [리포지토리 URI]:[지정했던 TAG]
#EX) docker push [uri]:latest
위 명령어들을 통해서 ECR에 이미지를 업로드합니다.
정상적으로 진행됐다면 이런식으로 리포지토리에 이미지가 업로드 돼 있습니다. 같은 태그 내용으로 다시 업로드하게 되면 전의 이미지는 untagged 되고 새 이미지가 해당 태그를 가지게 됩니다.
ECS 클러스터 구축
ASG를 이용한 방법으로 구성하겠습니다. Fargate는 ASG를 통해 구성하는 방법을 알면 공부하지 않아도 알 수 있을 정도로 쉽기 때문에 기술하지 않겠습니다.
Taskdefinition 구성
ECS → 리포지토리 → 생성한 리포지토리로 이동
URI를 복사합니다.
ECS → 테스크 정의로 이동
앞에서 복사한 이미지 uri를 입력합니다. 컨테이너 포트는 배포할 포트로 설정합니다. 사진에선 80번 포트인데 저는 Dockerfile에 명시하여 열어주신 포트를 입력해야합니다.
위의 도커파일에서 /app 파일로 flask 코드를 옮겼기 때문에 도커 구성에서 /app을 입력해주어야 합니다.
초기엔 Fargate로 돼 있을텐데 전 EC2인스턴스를 이용할테니까 EC2 인스턴스로 변경해줍니다.
가장 중요한 부분입니다. 아까 생성해줬던 taskexecution 권한을 가진 역할을 태스크 실행 역할에 부여합니다. 일단 저는 네트워크 모드를 awsvpc로 설정하고 진행할텐데 이때 네트워크 모드가 정말 중요합니다. 네트워크 모드에 따라서 Target Group 구성에서도 구성값이 변합니다. 크게 중요하게 알아가야 할 친구들은 awsvpc와 Bridge 모드입니다.
awsvpc 모드
awsvpc 모드는 위 사진처럼 컨테이너의 IP가 곧 EC2 인스턴스의 NIC IP가 되는 것입니다. 따라서 컨테이너가 3개가 돌아간다면 인스턴스의 IP할당도 3개를 받습니다. 제가 제대로 이해한게 맞는진 모르겠다만 아마 awsvpc 모드에서는 컨테이너 끼리 통신할 수 없고 Bridge모드에서는 컨테이너 끼리 통신할 수 있는 것으로 알고 있습니다.
Bridge 모드
Bridge 모드는 우리가 VMware를 또는 HOST에 컨테이너 환경을 구축할때처럼 이런 형식으로 컨테이너 끼리 다른 IP를 가지고 NIC에 매핑되는 것입니다.
Cluster 구성
이름을 적고 네트워킹에서 프라이빗 서브넷을 선택하고 생성해줍니다. 인프라 칸에서는 이미 ecs가 설치되고 ecs.config에 클러스터가 명시된 인스턴스나 ASG가 있다면 인스턴스를 선택해주시면 됩니다. 하지만 저는 아직 생성하지 않았기 때문에 그냥 넘어가고 생성하겠습니다.
ALB 및 Target Group 구성
EC2 → Target Group
앞에서 얘기했듯이 네트워크 모드 선택에 따라 타겟 그룹 설정이 변합니다. awsvpc 모드를 선택했다면 IP주소를 선택해야하고 Bridge모드를 선택했다면 인스턴스를 선택합니다. 포트는 직접 설정하신 포트를 지정해야합니다. 코드를 5000번 포트로 열었다면 포트가 5000인 것이고 8080이면 8080, 80이면 80입니다.
ALB는 근데 사실 항상 하던대로 인터넷 경계, 퍼블릭 서브넷 선택, 리스너는 80, 앞에서 설정한 대상그룹을 선택합니다.
ASG 생성
- ECS Agent가 구동되어야 한다
- ECS Cluster가 지정되어 있어야 한다
- Docker가 구성되어 있어야 한다
이렇게 ASG를 클러스터에 등록하기 위해선 3가지 조건이 필요합니다. ECS 최적화 AMI를 사용하면 손쉽게 구성이 가능합니다만 분명 AWS 공식 문서에서는 ECS agent가 깔려있고 구동된다고 하는데 실습에서 한번도 그런적이 없습니다... 아무튼 저 따라하시면 문제없이 배포해볼 수 있습니다.
EC2 → 시작 탬플릿 → 시작 탬플릿 생성
AMI는 최적화 AMI인 아마존 리눅스 2를 이용합니다. 인스턴스 유형이 정말 중요한데 테스크 정의를 생성할때도 볼 수 있으셨겠지만 생성되는 Task와 컨테이너가 메모리를 3GB를 먹습니다(변경 가능합니다). 그럼 당연하겠지만 이 컨테이너가 올라가는 인스턴스가 컨테이너 메모리보다 적으면 프로비저닝 상태에서 무한로딩이 걸려 배포가 절대 안끝나는 오류가 발생합니다. 따라서 넉넉한 인스턴스 유형을 선택해주셔야 합니다. 저는 실습때 t3.large 사용했습니다.
인스턴스에 자동으로 IAM 역할 부여하는 설정으로 앞에서 생성했던 AmazonEC2ContainerServiceforEC2Role 정책이 지정돼 있는 IAM 역할을 부여해줘야합니다. 정말 중요한 설정입니다. 이거 설정 안해주면 클러스터가 EC2를 인식할 수 없습니다.
배포하신 flask 서비스에 접속할 수 있는 포트가 열린 보안그룹을 지정해주어야 합니다.
그리고 가장 중요한 설정! 사용자 데이터란에 아래의 내용을 꼭 입력해주셔야 합니다. /etc/ecs/ecs.config라는 파일에 클러스터의 이름이 적혀있어야지 이 인스턴스가 이 클러스터에 종속돼있다를 알려줄 수 있습니다. 또한 ecs agent를 설치해주고 활성화해주어야 합니다. --no-block 이라는 신기한 옵션을 볼 수 있는데 이 명령어가 있어야지 정상작동합니다. 정확한 사유는 기억나지 않지만 저 옵션이 없다면 데몬이 실행되면서 충돌이 발생해 ecs가 절대 실행되지 않습니다.....
#!/bin/bash
mkdir /etc/ecs
echo ECS_CLUSTER=[사용할 클러스터 이름] > /etc/ecs/ecs.config
#외부에서 생성하기 때문에 종속될 클러스터를 지정해주어야 함
amazon-linux-extras install -y ecs
#amazon linux2의 경우 최적화 AMI로 이미 docker가 구성되어 있음
#ecs agent가 깔리면서 docker를 같이 설치하기도 함
systmectl enable --now --no-block ecs.service
systemctl enable --now docker
EC2 → Auto Scaling 그룹 생성
앞서 생성한 시작 탬플릿을 선택해 ASG를 생성합니다. ASG에서 서브넷을 클러스터가 존재하는 프라이빗 서브넷으로 지정해줍니다.
서비스 배포
클러스터에 ASG 등록
ECS → 클러스터 → 인프라
앞서 생성했던 ASG를 선택하고 클러스터에 등록해줍니다.
사진 처럼 지정한 용량 공급자의 이름으로 컨테이너 인스턴스에 활성화된 인스턴스가 존재하면 성공적으로 구성하신겁니다.
서비스 배포
ECS → 클러스터 → 서비스 생성
용량공급자 전략을 선택하고 저와 똑같이 따라했다면 사용자 지정 선택을 해줍니다. 앞에서 등록해줬던 용량 공급자를 선택합니다.
서비스를 선택하고 패밀리는 앞에서 생성했던 task definition을 선택합니다.
앞에서 설정했던 로드 밸런서와 타겟 그룹을 연결해주고 배포합니다.
정상적으로 배포가 됐다면 위와같이 마지막 상태가 실행중으로 떠야하며 타겟 그룹에 정상 상태의 타겟이 하나 이상 등록되어 있습니다. 타켓그룹에서 대상이 정상 상태가 아니라면 서비스가 배포됐다 실패했다 반복하는 오류가 발생할 수 있으니 확인해보시길 바랍니다.
이제 ALB를 통해 접속하면 정상적으로 배포된 것을 확인할 수 있습니다.
'네트워크 > 클라우드 컴퓨팅' 카테고리의 다른 글
Kinesis,API gateway,Glue를 이용한 ETL 파이프라인 구성 (0) | 2023.04.09 |
---|---|
ALB path based routing(컨테이너 배포) (1) | 2023.04.09 |
Lambda와 Event Bridge 이용해서 EC2 실행,종료 제어 (0) | 2023.03.16 |
AWS ECR 컨테이너 이미지 업로드, 다운로드 (1) | 2023.03.15 |
AWS GuardDuty (0) | 2023.03.09 |