Develop | Production | |
---|---|---|
Build | Bind mount | COPY |
Deploy | docker compose (localhost) | - Separate service - Cloud Managed service(ECS) |
Q. 굳이 ECS 같은 서비스를 쓰는 이유가 무엇인가?
A. 개발자가 직접 컨테이너를 VCS에 올리고 관리하고 pull 해오는 절차가 까다롭고 불편하기 때문이다.
코드 한줄 바뀔 때마다 이미지 build, push, pull 을 한다고 상상해보라. 끔찍하다.
AWS 의 ECR + ECS 는 컨테이너 애플리케이션 관리 및 운영에 도움을 주는 서비스다.
도커에서는 이미지 생성시 기본적으로 [네임스페이스]/[이미지 이름]:[태그명] 을 갖는데, 네임스페이스는 자주 생략된다.
생략된 네임스페이스의 Default 는 DockerHub 가된다.
여기서는 ECR 레포지토리를 사용해야 하므로 네임스페이스가 위에서 복사한 ECR URI 가 된다.
$ docker tag [image-name]:[tag-name] [ECR-Repsotiroy-URI]/[image-name]:[tag-name]
$ aws ecr get-login-password --region [region] \
| docker login --username AWS --password-stdin [aws_account_id].dkr.ecr.[region].amazonaws.com
# login succeed!
# ECR-URI 는 태깅에서 지정한 [image-name]:[tag-name] 과 같다.
$ docker push [ECR-URI]/[image-name]:[tag-name]
# 예시
$ docker push [account-id].dkr.ecr.ap-northeast-2.amazonaws.com/hostname-test:v1.0.0
큰 그룹 순서로 나열하면
이중 가장 친숙한 개념이 Task Definition (태스크 정의) 탭이다.
Docker 유저라면 docker-compose 를 한 번 이상 써봤을 텐데, Task Definition 이 docker-compose 역할을 하고 있다고 보면 된다.
Cluster, Service 는 AWS 에서 오케스트레이션과 로드밸런싱, 보안 정책 등 관리를 위해 추가로 넣은 개념이다. (그냥 몇번 따라하고 대시보드 구성 살펴보면 이해된다.)
ECS 는 docker 와 달리 bridge network 를 기본 사용하지 않는다.
host network 를 사용한다.
즉, 하나의 Task Definition 에서 정의된 컨테이너들은 동일한 EC2 내에 호스트 네트워크를 그대로 사용한다.
따라서localhost
로 컨테이너간 통신이 가능하다.
Fargate is serverless service.
거진 Lambda 와 유사하다. Labmda 가 FaaS (Function as a Service) 라면 Fargate 는 Container serivce 라는 것 정도가 차이다.
Cluster: Logical group of services or tasks. Base infrastructure
ex) EC2 groups or Fargate
Task 하나에 여러 컨테이너 매핑 가능.
보통 docker-compose 에서 생명주기를 함께하는 services 가 하나의 그룹이 되고 service 하나가 개별 컨테이너 1개가 된다.
이 예제에서는 docker-compose 기준 다음 3개의 컨테이너를 하나의 task 로 묶는다.
컨테이너 정의(container definition)는 Docker 데몬에 전달되는 컨테이너에 대한 세부 정보 및 리소스 요구 사항을 제공합니다. 태스크 정의에는 하나 이상의 컨테이너 정의가 포함될 수 있습니다.
여러 컨테이너가 필요한 애플리케이션의 경우 다음 조건에서 컨테이너를 동일한 태스크 정의에 그룹화해야 합니다. - AWS Docs
하나의 Task 내의 모든 컨테이너는 같은 instance 에서 실행됨이 보장된다
스케쥴러 / 오케스트레이션 역할.
일정 개수 이상의 task 가 실행되도록 보장한다.
로드밸런서 설정은 서비스 생성에만 가능하니 주의
롤링 업데이트를 기본 지원한다.
Fargate 는 기본적으로 Task definition 을 변경할 때마다 modify 하는게 아니라 새로운 버전을 개정 (revision) 한다.
이런 방식을 택한 이유는 새 버전으로 배포를 시도할 때마다 자동 Rollback 처리하기 위함이다.
AWS 의 센스가 돋보이는 대목이다.
Cluster 에서 Fargate 또는 EC2 구성을 했을 텐데, Fargate 의 경우에는 bind mount 사용시 유의해야한다. Fargate 는 일시적으로 EC2 에 탑재되었다가 해제되기 때문에 volume 을 영속적으로 쓸 수가 없다.
이런 경우에는 EFS 를 써야한다.
ecs-excution-role 에 s3 랑 efs IAM Policy 붙여주는게 필요할 수 있음.
exec format error
만약 실행시 위와 같은 에러가 발생했다면 아마 나처럼 M1, M2 macOS 를 사용중일 것이다.
AWS 도 차츰 arm 아키텍처를 적극지원하지만, 아직은 대다수의 환경이 amd64(intel) 로 되어 있는 것은 어쩔 수없다.
docker image build 는 M1 (arm64) 에서 했기 때문에 arm64 이미지를 생성한 상태고, ECS Fargate 아키텍쳐는 x86_64 (amd64) 기 때문이다.
즉 이미지를 만든 provider와 task executor 실행 환경이 다르기 때문에 발생한 문제다.
실행 환경과 동일한 x86_64(amd64) 기준으로 build 하면된다.
docker 는 Multiplatform build 에 사용하는 도구인 buildx 를 제공한다.
사용방법은 간단하다. 다음을 우선 따라하라.
처음 기본 상태는 다음과 같다.
근데 굳이 buildx 없이도 docker build
--platform
옵션 쓰면 되지 않을까요? 🤔
안된다.
아래 메시지를 확인해보시라.
$ docker build . -t "practice-backend:v1.3" --platform=linux/arm64,linux/amd64
[+] Building 0.0s (0/0)
ERROR: multiple platforms feature is currently not supported for docker driver.
Please switch to a different driver (eg. "docker buildx create --use")
2023-05-03 기준 아직 multiplatform 이미지 빌드를 원하면 그냥 buildx 를 쓰는게 맞다.
완전 관리를 위해서는 Fargate 를 쓰는게 편하겠지만
Fargate 는 인프라에 직접 SSH 로 접근해서 서버 인스턴스 구성을 바꾸거나, 로그를 보는 행위가 불가능하다.
개발자가 직접 운영 가능하는 EC2 에서 ECS 서비스를 운영하는 방식이다.
EC2 내에서 ECS 를 운영하면 ECS 자체에 대한 운영 요금이 과금되지 않는다.
AWS - Elastic Container Service - Cluster create
AmazonEC2ContainerServiceforEC2Role IAM 을 EC2 에 부여해야한다.
⚠️ IAM 권한이 없는 EC2 인스턴스는 컨테이너 인스턴스로 등록되지 않는다.
서비스
컨테이너를 한 클러스터 패밀리 내에 지정한 인스턴스 갯수에서 동시 실행되도록함.
ex) 태스크 수 4 지정 -> 한 클러스터 내에 task-definition에서 정의한 패밀리 (컨테이너)를 총 4개의 인스턴스에 각각 1개씩 실행함.
특정 서비스가 동작하지 않으면 다른 인스턴스를 실행시켜서라도 갯수를 자동으로 맞춰줌. (Auto-Scaling 과 유사)
👉🏻 일반적으로 앱 실행시 이 방식으로 배포.
태스크
여러 function의 배치작업시 유리
기본적으로 다음과 같은 항목으로 구성됨.
{
// Task definition 이름
"family": "ecs_nginx_example",
// docker 에서 사용할 네트워크 모드 (Default: bridge)
"networkMode": "bridge",
// 컨테이너 정의 (여러개 가능)
"containerDefinitions": [
{
// 컨테이너 이름
"name": "nginx-print-hostname",
"essential": true,
// image URI in ECR Repository
"image": "[aws-account-id].dkr.ecr.[region].amazonaws.com/[image-name]:[tag-name]",
// CPU limit (0: no limit)
"cpu": 0,
// memory limit: 512 MB
"memory": 512,
// -p 80:80/tcp (호스트:컨테이너/프로토콜)
"portMappings": [
{
"hostPort": 80,
"containerPort": 80,
"protocol": "tcp"
}
]
}
]
}
자세한 CLI 명령어 참고는 이 링크를 참조하자.
$ aws ecs register-task-definition \
--cli-input-json file://[file-path]/[file-name].json
서비스는 태스크 데피니션과 매핑됨.
task 실행 방식에 따라 2가지 타입이 있다.
Replica type | Daemon type |
---|---|
실행하려는 task 갯수 지정 필수 | 모든 컨테이너 인스턴스에 해당하는 task 하나씩 실행 |
{
"serviceName": "print-hostname",
"taskDefinition": "ecs_nginx_example:7",
// 태스크를 실행할 인스턴스 수
"desiredCount": 1
}
$ aws ecs create-service \
--cluster=[cluster-name] --cli-input-json file://[service-directory-path]/[filename].json
Elastic Load Balance 가 처음이라면 이 문서를 보자. 혈압이 오를 것이다.
그냥 뇌비우고 유튜브 하나 보면서 따라하길 추천한다.
요새 개발자들은 3줄 요약도 읽기 귀찮아하는 머글들이 많아서..
SSH 연결시 아래와 같은 에러가 발생했다면?
❗ Authentication failed (publickey)
아래 2개중 하나가 원인일 것이다.
실수로 SSH 연결시 실수로 다른 개인키 파일을 선택한 경우다.
EC2 - 세부 정보 - 키 페어 이름을 확인하고 올바른 키 파일을 선택하면 바로 연결 성공.
기본 사용자명을 잘못 입력한 경우다.
AMI 타입에 따라 기본 사용자명이 아래와 같이 달라진다.
자세한 사항은 공식 문서에서 확인하자.
AMI Type | Default username |
---|---|
Ubuntu AMI | ubuntu |
ECS optimized AMI ✅ | ec2-user |
이 포스트에서는 ECS의 서비스를 실행할 EC2 인스턴스를 생성했으므로 ec2-user
로 연결해야한다.
aws ecr get login | docker login
=> ECR push / pull => trigger service?