AWS에서 Terraform, ECS Fargate 및 CloudMap을 사용하여 마이크로 서비스 인프라를 구축하는 방법 | part 2
- -
2023.05.30 - [AWS] - AWS에서 Terraform, ECS Fargate 및 CloudMap을 사용하여 마이크로 서비스 인프라를 구축하는 방법 | part 1
이 게시물에서는 ECR Repository를 배포하는 방법과 서비스 코드 및 repository 대해 알아봅니다.
ECR
/stacks/ecr/main.tf
resource "aws_ecr_repository" "gyko-uno" {
name = "gyko-uno"
}
resource "aws_ecr_repository" "gyko-due" {
name = "gyko-due"
}
resource "aws_ecr_repository" "gyko-tre" {
name = "gyko-tre"
}
리소스 aws_ecr_repository 는 AWS Elastic Container Registry(ECR) 리포지토리를 생성합니다. 이 경우 fgms-uno, fgms-due 및 fgms-tre의 세개의 ECR 리포지토리가 생성됩니다.
각 리포지토리는 다음 형식의 코드 블록으로 정의됩니다.
resource "aws_ecr_repository" "REPOSITORY_NAME" {
name = "REPOSITORY_NAME"
}
name 에는 ECR 리포지토리의 이름을 지정합니다. 이는 provider 에 지정된 리전 및 계정 내에서 고유해야 합니다.
Terraform 코드가 적용되면 이 세 개의 ECR 리포지토리가 지정된 AWS 계정 및 리에 생성됩니다. Docker 이미지를 저장하고 수명 주기를 관리하는 데 사용할 수 있습니다.
Service Architecture
각 애플리케이션이 수행하는 작업입니다.
— uno-service는 로드 밸런서로부터 HTTP 요청을 수신하고 Cloud Map을 내부 DNS로 사용하여 Request를 due-service 및 tre-service로 라우팅합니다.
— uno-service는 due-service 및 tre-service의 Response를 사용하여 로드 밸런서에 대한 응답을 준비합니다.
※ uno-service는 마이크로서비스에서 정보를 수집하고 외부의 요청에 응답하는 프런트엔드 앱으로 생각할 수 있습니다.
Appliations Code
어플리케이션 코드는 정적 응답을 반환하는 기본 nodejs express 앱입니다. uno-service는 프록시 역할을 가지고 있기 때문에 due-service 및 tre-service에 대한 Axios의 HTTP 요청 코드가 포함되어 있습니다.
uno-service code
const express = require('express')
const fetch = require('node-fetch');
const app = express()
const port = 3000
app.get('/', async (req, res) => {
const dueResponse = await fetch(`${process.env.DUE_SERVICE_API_BASE}:3000`)
const treResponse = await fetch(`${process.env.TRE_SERVICE_API_BASE}:3000`)
const dueData = await dueResponse.json();
const treData = await treResponse.json();
res.json({
msg: "Hello world! (from uno)",
due:{
url: process.env.DUE_SERVICE_API_BASE,
data: dueData,
},
uno:{
url: process.env.TRE_SERVICE_API_BASE,
data: treData,
}
})
})
app.get('/healthcheck', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
due/tre-service code
const express = require('express')
const fetch = require('node-fetch');
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.json({
msg: "Hello world! (from tre)",
})
})
app.get('/healthcheck', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Appications Dockerfile
FROM node:8-alpine
WORKDIR /usr/app
COPY package.json .
RUN npm i --quiet
COPY . .
RUN npm install -g pm2@4.2.1
CMD ["pm2-runtime", "./index.js"]
FROM node:8-alpine 빌드 중인 Docker 이미지의 Base Image를 지정합니다. 여기에서는 이미지가 node:8-alpineAlpine Linux 배포를 기반으로 하는 Node.js 런타임 환경의 경량 버전(alpine)인 이미지를 기반으로 해야 함을 지정합니다.
WORKDIR /usr/app Docker 파일의 후속 명령에 대한 작업 디렉터리를 설정합니다. 작업 디렉토리를 /usr/app으로 설정했습니다.
COPY package.json . Dockerfile이 포함된 디렉터리에서 작업 디렉터리(/usr/app)로 package.json 파일을 복사하는 Dockerfile 지시어입니다.
RUN npm i --quietnpm i --quiet 컨테이너에서 npm i --quiet 명령을 실행하는 Dockerfile 지시어입니다. 이 명령은 package.json 파일에 지정된 모든 종속성을 설치합니다. --quiet 플래그는 npm의 진행률 출력을 표시하지 않습니다.
COPY . . 모든 파일을 컨텍스트(Docker 파일이 들어 있는 디렉토리)에서 작업 디렉토리(/usr/app)로 복사합니다.
RUN npm install -g pm2@4.2.1 컨테이너에 전체적으로 pm2 패키지(버전 4.2.1)를 설치하는 도커 파일 지시어입니다. pm2는 Node.js 응용 프로그램의 프로세스 관리자입니다.
CMD ["pm2-runtime", "./index.js"] 컨테이너가 시작될 때 실행할 기본 명령을 지정하는 도커 파일 지시문입니다. 이 경우 pm2-runtime 명령을 ./index.js 인수로 실행해야 함을 지정합니다. 그러면 pm2를 사용하여 ./index.js의 Node.js Application이 시작됩니다.
ECR 리포지토리에 도커 이미지 업로드
ecr-deploy-script.sh 파일에는 ECR에 컨테이너 이미지를 업로드하기 위한 AWS-CLI와 bash 명령어가 포함되어 있습니다.
모든 앱(uno, due, tre)에 대해 다음 명령을 실행해야 합니다.
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin <YOUR AWS ACCOUNT ID>.dkr.ecr.ap-northeast-2.amazonaws.com
docker build -t uno ./uno
# docker buildx build --platform=linux/amd64 -t uno ./uno !!apple m1 칩을 사용하는 경우 위 명령 대신 이 명령을 사용하세요!!
docker tag uno <YOUR AWS ACCOUNT ID>.dkr.ecr.ap-northeast-2.amazonaws.com/gyko-uno:v1
docker push <YOUR AWS ACCOUNT ID>.dkr.ecr.ap-northeast-2.amazonaws.com/gyko-uno:v1
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin <YOUR AWS ACCOUNT ID>.dkr.ecr.ap-northeast-2.amazonaws.com
AWS Identity and Access Management(IAM)에서 인증 토큰을 검색한 다음 이를 사용하여 ap-northeast-2 리전의 Amazon Elastic Container Registry(ECR)에 로그인하는 명령입니다. | 기호는 aws 명령의 결과를 docker login 명령으로 전달합니다.
docker build -t uno ./uno 는 ./uno 디렉토리의 Dockerfile 에서 Docker 이미지를 빌드하고 이미지에 uno 라는 태그를 지정하는 명령입니다.
docker push .dkr.ecr.ap-northeast-2.amazonaws.com/gyko-uno:v1</your aws account id> Docker 이미지를 지정된 Amazon ECR Repository 로 push 하는 명령입니다.
Service Terraform stack
stacks/services 폴더 안에 Infra를 생성하는데 필요한 모든 AWS 서비스 리소스가 있습니다.
resource "aws_ecs_task_definition" "gyko_uno_td" {
family = "gyko_uno_td"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"
execution_role_arn = aws_iam_role.gyko_uno_task_role.arn
container_definitions = jsonencode(
[
{
cpu : 256,
image : "248581660709.dkr.ecr.ap-northeast-1.amazonaws.com/gyko-uno:v1",
memory : 512,
name : "gyko-uno",
networkMode : "awsvpc",
environment : [
{
name : "DUE_SERVICE_API_BASE",
value : "http://${data.terraform_remote_state.services-due.outputs.gyko_due_service_namespace}.${data.terraform_remote_state.dns.outputs.gyko_private_dns_namespace}"
},
{
name : "TRE_SERVICE_API_BASE",
value : "http://${data.terraform_remote_state.services-tre.outputs.gyko_tre_service_namespace}.${data.terraform_remote_state.dns.outputs.gyko_private_dns_namespace}"
}
],
portMappings : [
{
containerPort : 3000,
hostPort : 3000
}
],
logConfiguration : {
logDriver : "awslogs",
options : {
awslogs-group : "/ecs/gyko_log_group",
awslogs-region : "ap-northeast-1",
awslogs-stream-prefix : "uno"
}
}
}
]
)
}
resource "aws_ecs_service" "gyko_uno_td_service" {
name = "gyko_uno_td_service"
cluster = data.terraform_remote_state.ecs_cluster.outputs.gyko_ecs_cluster_id
task_definition = aws_ecs_task_definition.gyko_uno_td.arn
desired_count = "1"
launch_type = "FARGATE"
network_configuration {
security_groups = ["${aws_security_group.ecs_tasks_sg.id}"]
subnets = ["${data.terraform_remote_state.vpc.outputs.gyko_private_subnets_ids[0]}"]
}
load_balancer {
target_group_arn = aws_alb_target_group.gyko_uno_tg.id
container_name = "gyko-uno"
container_port = 3000
}
service_registries {
registry_arn = aws_service_discovery_service.gyko_uno_service.arn
}
}
resource "aws_security_group" "ecs_tasks_sg" {
name = "ecs_tasks_sg"
description = "allow inbound access from the ALB only"
vpc_id = data.terraform_remote_state.vpc.outputs.gyko_vpc_id
ingress {
protocol = "tcp"
from_port = "3000"
to_port = "3000"
security_groups = ["${data.terraform_remote_state.alb.outputs.gyko_alb_sg_id}"]
}
ingress {
protocol = -1
from_port = 0
to_port = 0
self = true
}
egress {
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_alb_target_group" "gyko_uno_tg" {
name = "gyko-uno-tg"
port = 80
protocol = "HTTP"
vpc_id = data.terraform_remote_state.vpc.outputs.gyko_vpc_id
target_type = "ip"
health_check {
path = "/healthcheck"
}
}
resource "aws_alb_listener" "gyko_uno_tg_listener" {
load_balancer_arn = data.terraform_remote_state.alb.outputs.gyko_alb_id
port = "80"
protocol = "HTTP"
default_action {
target_group_arn = aws_alb_target_group.gyko_uno_tg.id
type = "forward"
}
}
리소스 aws_ecs_task_definition은ECS 작업 정의(Task Definition)를 생성합니다. 이 정의는 ECS 작업의 Blueprint입니다. 작업 정의는 작업에 사용할 도커 이미지, 사용할 CPU 및 메모리 장치 수 및 작업이 시작될 때 실행할 명령을 지정합니다. 또한 작업 정의는 태스크가 사용해야 하는 IAM 역할과 태스크가 시작될 때 컨테이너로 전달되어야 하는 환경 변수를 지정합니다.
리소스 aws_ecs_service는 ECS 서비스를 생성합니다. ECS 서비스는 일반적으로 웹 애플리케이션을 호스팅하는 데 사용되는 장시간 실행 작업입니다. 이 서비스는 코드 블록에 정의된 ECS 클러스터 및 작업 정의와 연결되며 실행할 작업 수와 시작 유형(Fargate)을 지정합니다. 서비스는 네트워크 구성 및 로드 밸런서 설정도 지정합니다. 서비스는 다른 서비스에서 검색할 수 있도록 서비스 검색 서비스에도 등록됩니다.
리소스 aws_security_group은 ECS 작업으로 들어오고 나가는 인바운드 및 아웃바운드 트래픽을 제어하는 가상 방화벽인 Amazon Elastic Compute Cloud(Amazon EC2) Security Group을 생성합니다. Security Group은 지정된 Security Group의 포트 3000에 대한 인바운드 트래픽만 허용하고 모든 아웃바운드 트래픽을 허용합니다.
리소스 aws_alb_target_group은 트래픽을 ECS 작업으로 라우팅하는 데 사용되는 Amazon Elastic Load Balancer(ALB) 대상 그룹을 생성합니다. 대상 그룹은 지정된 보안 그룹 및 포트와 연결됩니다.
리소스 aws_alb_listener_rule은 대상 그룹으로 트래픽을 라우팅하는 방법을 지정하는 ALB 수신기 규칙을 생성합니다. 리스너 규칙은 지정된 호스트 헤더 및 경로 패턴을 기반으로 지정된 ALB 리스너에서 대상 그룹으로 트래픽을 라우팅합니다.
클라우드 맵과 관련하여 DNS 스택을 종속성으로 지정한 리소스는 다음과 같습니다
resource "aws_service_discovery_service" "gyko_uno_service" {
name = var.gyko_uno_service_namespace
dns_config {
namespace_id = data.terraform_remote_state.dns.outputs.gyko_dns_discovery_id
dns_records {
ttl = 10
type = "A"
}
routing_policy = "MULTIVALUE"
}
health_check_custom_config {
failure_threshold = 2
}
}
aws_service_discovery_service리소스는 Amazon Route 53 서비스 검색 서비스를 생성하여 서비스가 DNS를 통해 서로를 검색할 수 있도록 합니다. 서비스에 name 매개 변수를 사용하여 지정되고 변수에서 파생된 이름이 지정됩니다.
dns_config블록은 서비스 검색 서비스에 대한 DNS 구성을 지정합니다. 서비스가 사용해야 하는 Amazon Route 53 네임스페이스의 ID와 서비스에 대한 DNS 레코드 및 라우팅 정책을 지정합니다. DNS 레코드는 TTL(Time To Live) 값과 서비스 유형을 지정합니다.
health_check_custom_config블록은 서비스 검색 서비스에 대한 health check 구성을 지정합니다. 상태 확인 상태가 실패로 간주되기 전에 필요한 연속 실패 횟수를 지정합니다.
Deploy
이 자습서의 part one 에서 설명한 Terraform 명령을 사용하여 모든 서비스(ECR에 도커 이미지를 업로드한 후)를 배포해 보겠습니다 .
$ cd stacks/dns
$ terraform init
$ terraform plan
$ terraform apply
서비스 배포 순서는 다음과 같습니다.
- due
- tre
- uno
uno는 due 및 tre의 클라우드 맵 DNS 레코드 값이 필요하므로 마지막에 배포되어야 합니다.
$ cd stacks/services/due
$ terraform init
$ terraform plan
$ terraform apply
$ cd stacks/services/tre
$ terraform init
$ terraform plan
$ terraform apply
$ cd stacks/services/uno
$ terraform init
$ terraform plan
$ terraform apply
다음 이미지는 프라이빗 클라우드 맵 호스팅 영역 내의 모든 레코드를 나타냅니다.
TEST
모든 구성이 완료되었습니다. Application 을 테스트 하겠습니다.
Terraform 으로 생성한 ALB 콘솔 화면으로 이동합니다.
콘솔화면의 DNS name 을 복사하여 브라우저에서 해당 URL로 이동하면 다음과 같은 응답을 받아야 합니다.
이와 같은 출력이 표시되면 이 자습서를 성공적으로 완료하고, 그렇지 않으면 이 페이지에서 누락된 내용이 있는지 확인합니다.
The End.
'AWS' 카테고리의 다른 글
AWS EC2 Blue/Green Deployment (Feat. Jenkins, CodeBuild, CodeDeploy) - Part 1 (0) | 2023.10.05 |
---|---|
SSM(Session Manager) Plugin 을 사용하여 Local PC 에서 AWS Private 자원에 접근하기 (0) | 2023.09.19 |
AWS Lambda 를 이용하여 CloudFront Cache 무효화 처리하기 (0) | 2023.08.29 |
AWS MSK를 활용한 AWS RDS PostgreSQL CDC (0) | 2023.07.05 |
AWS에서 Terraform, ECS Fargate 및 CloudMap을 사용하여 마이크로 서비스 인프라를 구축하는 방법 | part 1 (0) | 2023.05.30 |
소중한 공감 감사합니다