AWS 인프라 프로비저닝(Provision AWS Infrastructure)

AWS 인프라 프로비저닝(Provision AWS Infrastructure)

Confident AI가 실행되는 클라우드 인프라를 프로비저닝해요: EKS 클러스터, RDS PostgreSQL 데이터베이스, S3 버킷, 그리고 키리스 IAM 연결(EKS Pod Identity). 완료되면 실행 중인 클러스터와 다음 페이지의 Helm 차트에 넣을 출력값들이 생겨요.

출처: 문서

본문

리전은 AWS provider와 프로파일에서 오며 리전 변수가 없어요. 리전과 두 개의 가용영역(AZ)을 한 번 설정하세요:

export AWS_REGION=us-east-1
export AWS_AZ_1=${AWS_REGION}a
export AWS_AZ_2=${AWS_REGION}b

네트워크 생성 (선택)

서로 다른 AZ에 두 개의 비공개 서브넷이 있고 NAT 게이트웨이를 통해 인터넷에 도달하는 VPC를 이미 갖고 있다면 이 단계는 건너뛰세요. 다음 단계에서 기존 ID를 사용하세요.

두 개의 가용영역에 걸쳐 두 개의 공개·비공개 서브넷과 NAT 게이트웨이가 있는 VPC를 만들어요. EKS와 RDS는 비공개 서브넷에서, 공개 서브넷은 NAT 게이트웨이와 인터넷 연결 로드 밸런서를 담당해요. 블록을 순서대로 실행하세요. 각 블록이 다음 블록에 ID를 전달해요.

VPC를 만들고 클러스터와 데이터베이스가 비공개 호스트네임을 확인할 수 있게 DNS를 켜요:

VPC_ID=$(aws ec2 create-vpc --cidr-block 10.20.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=confident-prod-vpc}]' \
  --query Vpc.VpcId --output text)
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-support

인터넷 게이트웨이를 연결해요. 공개 서브넷은 인터넷 접근을 위해 여기로 라우팅돼요:

INTERNET_GATEWAY_ID=$(aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text)
aws ec2 attach-internet-gateway --internet-gateway-id $INTERNET_GATEWAY_ID --vpc-id $VPC_ID

가용영역마다 하나씩, 공개·비공개 서브넷을 각각 두 개 만드세요:

PUBLIC_SUBNET_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.0.0/20  --availability-zone $AWS_AZ_1 --query Subnet.SubnetId --output text)
PUBLIC_SUBNET_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.16.0/20 --availability-zone $AWS_AZ_2 --query Subnet.SubnetId --output text)
PRIVATE_SUBNET_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.128.0/20 --availability-zone $AWS_AZ_1 --query Subnet.SubnetId --output text)
PRIVATE_SUBNET_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.20.144.0/20 --availability-zone $AWS_AZ_2 --query Subnet.SubnetId --output text)

ClickHouse는 이 모듈이 만드는 IPv4 전용 EKS 클러스터에서 깔끔하게 실행돼요: 차트가 오퍼레이터 파드를 IPv4(0.0.0.0)로 청취하도록 고정해서, 주소 패밀리 오류 없이 듀얼 스택 설정이 필요 없어요. 듀얼 스택 VPC만으로는 바뀌지 않아요. 클러스터 자체가 듀얼 스택으로 생성되지 않는 한 노드는 IPv4 전용으로 유지돼요.

나중에 로드 밸런서 컨트롤러가 발견할 수 있게 서브넷에 태그를 붙여요:

aws ec2 create-tags --resources $PUBLIC_SUBNET_1 $PUBLIC_SUBNET_2   --tags Key=kubernetes.io/role/elb,Value=1
aws ec2 create-tags --resources $PRIVATE_SUBNET_1 $PRIVATE_SUBNET_2 --tags Key=kubernetes.io/role/internal-elb,Value=1

첫 번째 공개 서브넷에 NAT 게이트웨이를 만들어요. 비공개 노드는 아웃바운드 풀을 위해 이걸 통해 인터넷에 도달하되, 인바운드 노출은 없어요:

NAT_EIP_ALLOCATION_ID=$(aws ec2 allocate-address --domain vpc --query AllocationId --output text)
NAT_GATEWAY_ID=$(aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET_1 --allocation-id $NAT_EIP_ALLOCATION_ID --query NatGateway.NatGatewayId --output text)
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_GATEWAY_ID

공개 서브넷을 인터넷 게이트웨이로 라우팅해요:

PUBLIC_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text)
aws ec2 create-route --route-table-id $PUBLIC_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $INTERNET_GATEWAY_ID
aws ec2 associate-route-table --route-table-id $PUBLIC_ROUTE_TABLE_ID --subnet-id $PUBLIC_SUBNET_1
aws ec2 associate-route-table --route-table-id $PUBLIC_ROUTE_TABLE_ID --subnet-id $PUBLIC_SUBNET_2

비공개 서브넷을 NAT 게이트웨이로 라우팅해요:

PRIVATE_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text)
aws ec2 create-route --route-table-id $PRIVATE_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_GATEWAY_ID
aws ec2 associate-route-table --route-table-id $PRIVATE_ROUTE_TABLE_ID --subnet-id $PRIVATE_SUBNET_1
aws ec2 associate-route-table --route-table-id $PRIVATE_ROUTE_TABLE_ID --subnet-id $PRIVATE_SUBNET_2

Terraform 설정에 붙여 넣을 VPC와 비공개 서브넷 ID를 출력해요:

echo "confident_vpc_id             = \"$VPC_ID\""
echo "confident_private_subnet_ids = [\"$PRIVATE_SUBNET_1\", \"$PRIVATE_SUBNET_2\"]"

코드 샌드박스 이미지 미러링 (필수)

코드 기반 및 트랜스포머 지표는 샌드박스형 Lambda에서 실행되며, confident-code-sandbox-aws 이미지를 실행해요. Lambda는 같은 계정의 ECR에 있는 컨테이너 이미지만 실행할 수 있으므로, 공개 이미지를 여러분의 ECR로 미러링해야 해요.

ECR 리포지토리를 만들고 Docker를 로그인해요:

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_REGISTRY=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

aws ecr create-repository --repository-name confident-code-sandbox-aws --region $AWS_REGION
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY

공개 이미지를 가져와 ECR로 푸시해요. 마지막 줄이 Terraform 설정용 이미지 URI를 출력해요:

docker pull confidentai/confident-code-sandbox-aws:latest
docker tag confidentai/confident-code-sandbox-aws:latest $ECR_REGISTRY/confident-code-sandbox-aws:latest
docker push $ECR_REGISTRY/confident-code-sandbox-aws:latest

echo "confident_code_executor_lambda_image_uri = \"$ECR_REGISTRY/confident-code-sandbox-aws:latest\""

Terraform 설정 작성

공개 모듈을 참조하는 main.tf를 만들고, 앞의 두 단계에서 나온 값을 채워요. 각 변수의 역할:

  • 네트워크: confident_vpc_id와 confident_private_subnet_ids는 네트워크 단계에서 (또는 자체 VPC를 써서).
  • 네이밍: confident_environment와 confident_environment_code가 prod 네이밍 컨벤션을 모든 리소스에 적용해요.
  • 접근: confident_public_eks는 클러스터 API를 머신에 노출해요. 비공개 전용 엔드포인트를 원하면 false로.
  • 내구성: confident_rds_deletion_protection은 데이터베이스를 실수 삭제에서 보호해요.
  • 관리형 서비스: confident_create_secrets_manager와 confident_managed_redis_enabled가 권장 시크릿 스토어와 Redis를 켜고, confident_database_ingress_cidrs는 노드가 Redis에 닿게 해요 (VPC CIDR 사용).
  • 코드 실행기: confident_code_executor_lambda_image_uri는 미러링 단계가 출력한 ECR 이미지 URI.
provider "aws" {
  region = "us-east-1"
}

module "confident_ai" {
  source  = "confident-ai/confident-ai/aws"
  version = "~> 0.1"

  confident_vpc_id             = "vpc-xxxxxxxx"
  confident_private_subnet_ids = ["subnet-aaaa", "subnet-bbbb"]

  confident_environment      = "prod"
  confident_environment_code = "p"

  confident_public_eks = true

  confident_rds_deletion_protection = true

  confident_create_secrets_manager = true
  confident_managed_redis_enabled  = true
  confident_database_ingress_cidrs = ["10.20.0.0/16"]

  confident_code_executor_enabled          = true
  confident_code_executor_lambda_image_uri = "<ECR image URI from the mirror step>"
}

output "helm_values" {
  value     = module.confident_ai.helm_values
  sensitive = true
}

리포지토리 안에서 작업하고 싶다면 terraform-aws-confident-ai를 클론해 module 블록 대신 terraform.tfvars 파일에 같은 변수를 넣으세요.

원격 상태 구성 (선택)

이걸 건너뛰면 로컬 상태를 사용해요. 팀이나 실제 환경이라면 상태를 S3 버킷에 두세요.

terraform {
  backend "s3" {
    bucket = "confident-tfstate"
    key    = "confident-ai/aws/terraform.tfstate"
    region = "us-east-1"
  }
}

적용(Apply)

terraform init
terraform plan
terraform apply

EKS 클러스터와 RDS를 만드는 데는 대략 15-20분이 걸려요.

클러스터 연결

eval "$(terraform output -raw configure_kubectl)"
kubectl get nodes

EKS에는 기본 스토리지 클래스가 없고, ClickHouse와 Redis는 디스크가 필요해요. gp3 기본값을 한 번 만들어요:

kubectl apply -f - <<'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  encrypted: "true"
allowVolumeExpansion: true
EOF

출력값 읽기

다음 페이지의 Helm 차트가 이 값들을 필요로 해요. terraform output helm_values가 바로 붙여 넣을 수 있는 스니펫을 출력하거나, 개별로 읽어요. 각 줄의 주석은 그것이 넣어줄 Helm 값을 나타내요:

terraform output -raw database_url            # secrets.data.DATABASE_URL
terraform output test_cases_bucket            # storage.testCasesBucket
terraform output payloads_bucket              # storage.payloadsBucket
terraform output -raw region                  # storage.aws.region
terraform output code_executor_function_name  # codeExecutor.aws.lambdaFunctionName

앱이 EKS Pod Identity를 사용하므로 ServiceAccount에 어노테이션이 필요 없고, Terraform이 이미 IAM 역할을 연결했어요.

관리형 시크릿과 Redis (권장)

위의 모듈 블록이 둘 다 프로비저닝해요. Deploy 페이지가 External Secrets Operator를 설치하고 차트에 연결해요:

  • Secrets Manager + External Secrets Operator (confident_create_secrets_manager = true): Terraform이 빈 시크릿을 만들고 Pod Identity를 통해 ESO에 읽기 접근을 부여해요.
  • ElastiCache for Redis (confident_managed_redis_enabled = true): 클러스터 내부 대신 관리형 Redis. confident_database_ingress_cidrs가 노드가 닿을 수 있도록 VPC에 포트 6379를 열어요. terraform output -raw redis_url이 redis.externalUrl 값을 줘요.

더 간단한 구성이 좋다면 둘 다 false로 두어 시크릿을 Kubernetes Secret에 담고 Redis를 클러스터에서 실행하세요. Deploy 페이지의 더 간단한 옵션을 참고하세요.

입력 참조(Inputs reference)

가장 자주 설정할 변수들이에요. 완전하고 항상 최신인 목록은 Terraform Registry의 모듈 입력을 참고하세요.

필수

Variable 설명
confident_vpc_id 배포할 기존 VPC.
confident_private_subnet_ids 서로 다른 가용영역에 있는 기존 비공개 서브넷 두 개 이상 (EKS와 RDS).

자주 설정 (선택, 프로덕션 기본값 포함)

Variable 기본값 설명
confident_environment / confident_environment_code stage / s 리소스에 적용되는 네이밍 컨벤션 (prod / p 사용).
confident_public_eks false 클러스터 API 엔드포인트 노출.
confident_eks_admin_arn "" cluster-admin을 부여받는 추가 IAM 프린시펄.
confident_node_instance_types / confident_node_group_desired_size ["m6i.2xlarge"] / 4 노드 그룹 크기.
confident_code_executor_enabled / confident_code_executor_lambda_image_uri true / "" 코드 실행기 Lambda와 그 ECR 이미지 URI.
confident_create_secrets_manager false Secrets Manager 시크릿과 ESO Pod Identity.
confident_managed_redis_enabled / confident_database_ingress_cidrs false / [] ElastiCache와, 그에 닿도록 허용된 CIDR.
confident_rds_deletion_protection / confident_rds_multi_az false / true RDS 내구성 옵션.

리전은 AWS provider에서 오며 리전 변수가 없어요.

다음 단계

Helm으로 배포

Terraform 출력값으로 앱을 설치하고 HTTPS로 노출.

더 알아보기