[AEKS2] 8주차 - EKS IaC: ACK (AWS Controller for Kubernetes)
이전 글에서 테라폼으로 생성한 클러스터 위에서 작업하겠다!
테라폼으로 클러스터를 만들었으면
이제 AWS 리소스는 ACK (AWS Controller for Kubernetes)로 생성해 볼 것이다.
ACK는 매니페스트로 AWS 리소스를 관리할 수 있어, GitOps의 장점을 가져갈 수 있다.
AWS Controllers for Kubernetes
S3 Controller를 예로 든 그림을 참고해 본다
kuberentes api server에 요청하면 해당 컨트롤러가 AWS에 요청하게 되는 방식을 가진다.
ACK가 지원하는 AWS 서비스로는
24년 4월을 기준으로 20개 서비스는 정식 출시되었고, 18개는 Preview 상태이다.
아래 링크 참고하자!
그리고 모든 컨트롤러는 AWS 리소스를 생성하기 위한 권한으로 IRSA를 사용한다!
1. ACK S3 Controller
ACK S3 Controller를 쿠버네티스 클러스터 상에 설치하면 AWS 리소스 S3를 쿠버네티스 매니페스트 파일로 관리할 수 있다.
helm으로 설치
helm으로 ACK S3 Controller를 최신 버전으로 다운받는다.
export SERVICE=s3
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
그리고 네임스페이스를 하나 만들어서 다운받은 헬름 패키지를 배포한다.
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install --create-namespace -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" $SERVICE-chart-$RELEASE_VERSION.tgz
설치하고 나면 deployment(rs,pod), crd가 생성된다.
이제 AmazonS3FullAccess
권한을 가진 IRSA가 필요하다.
이 IRSA를 통해 S3 Controller가 S3 버킷에 대한 풀권한을 가질 수 있다.
eksctl create iamserviceaccount \
--name ack-$SERVICE-controller \
--namespace ack-system \
--cluster $CLUSTER_NAME \
--attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3FullAccess`].Arn' --output text) \
--override-existing-serviceaccounts --approve
AWS 콘솔에 보면 해당 Role이 생성되었고
Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된걸 확인할 수 있다.
이제 S3 Controller가 해당 Role을 적용할 수 있게 S3 Controller Deployment를 재시작한다.
kubectl -n ack-system rollout restart deploy ack-$SERVICE-controller-$SERVICE-chart
재시작하고 나서 deployment를 조회해 보면 AWS_ROLE_ARN
과 볼륨 aws-iam-token
이 추가된 것을 확인할 수 있다.
S3 버킷 생성 테스트
일단 AWS Account ID와 버킷명을 환경변수로 지정한다.
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
export BUCKET_NAME=my-ack-s3-bucket-$AWS_ACCOUNT_ID
생성할 버킷명을 지정한 매니페스트를 생성한다.
read -r -d '' BUCKET_MANIFEST <<EOF
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
name: $BUCKET_NAME
spec:
name: $BUCKET_NAME
EOF
echo "${BUCKET_MANIFEST}" > bucket.yaml
kubectl create -f bucket.yaml
으로 리소스를 생성하면 s3 버킷이 실시간으로 생성된다!
AWS 콘솔에서도 s3 버킷은 생성되어 있었다!
클러스터 내에도 리소스 생성된 것을 확인했다!
S3 버킷 업데이트 및 삭제 테스트
태그 값이 들어간 매니페스트 파일을 다시 만들었다.
read -r -d '' BUCKET_MANIFEST <<EOF
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
name: $BUCKET_NAME
spec:
name: $BUCKET_NAME
tagging:
tagSet:
- key: myTagKey
value: myTagValue
EOF
echo "${BUCKET_MANIFEST}" > bucket.yaml
kubectl create -f bucket.yaml
으로 리소스를 다시 apply하면 태그가 업데이트된다.
AWS 콘솔에서 확인하면 태그가 업데이트된 것을 확인할 수 있다.
마찬가지로 kubectl delete -f bucket.yaml
로 바로 s3 버킷 삭제가 가능하다
ACK S3 Controller 삭제
헬름으로 설치한 컨트롤러를 삭제하고
IRSA도 삭제해 준다!
helm uninstall -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller
eksctl delete iamserviceaccount --cluster myeks --name ack-$SERVICE-controller --namespace ack-system
2. EC2 & VPC
ACK EC2 Controller를 쿠버네티스 클러스터 상에 설치하면 AWS 리소스를 쿠버네티스 매니페스트 파일로 관리할 수 있다.
특히 vpc 리소스들과 EC2 리소스들, 여러 가지가 있는데 한꺼번에 관리할 수 있다.
이 것은 GitOps로 관리하기 위함인 것 같다.
헬름으로 ACK EC2 Controller 설치
helm으로 ACK EC2 Controller를 최신 버전으로 다운받는다.
export SERVICE=ec2
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
위에서 생성한 ack-system 네임스페이스에 EC2 Controller를 배포한다.
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" $SERVICE-chart-$RELEASE_VERSION.tgz
EC2 controller의 pod와 crd가 생성되었다.
crd는 EC2와 연관된 리소스들이다.
이제 AmazonEC2FullAccess
권한을 가진 IRSA가 필요하다.
이 IRSA를 통해 EC2 Controller가 EC2 인스턴스에 대한 풀권한을 가질 수 있다.
eksctl create iamserviceaccount \
--name ack-$SERVICE-controller \
--namespace $ACK_SYSTEM_NAMESPACE \
--cluster $CLUSTER_NAME \
--attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonEC2FullAccess`].Arn' --output text) \
--override-existing-serviceaccounts --approve
AWS 콘솔에 보면 해당 Role이 생성되었고
Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된걸 확인할 수 있다.
이제 EC2 Controller가 해당 Role을 적용할 수 있게 EC2 Controller Deployment를 재시작한다.
kubectl -n $ACK_SYSTEM_NAMESPACE rollout restart deploy ack-$SERVICE-controller-$SERVICE-chart
재시작하고 나서 deployment를 조회해 보면 AWS_ROLE_ARN
과 볼륨 aws-iam-token
이 추가된 것을 확인할 수 있다.
VPC 및 Subnet 생성
VPC 생성을 위한 매니페스트를 생성한다.
cat <<EOF > vpc.yaml
apiVersion: **ec2.services.k8s.aws/v1alpha1**
kind: **VPC**
metadata:
name: **vpc-tutorial-test**
spec:
cidrBlocks:
- **10.0.0.0/16**
enableDNSSupport: true
enableDNSHostnames: true
EOF
kubectl apply -f vpc.yaml
으로 리소스를 생성하면 vpc가 실시간으로 생성된다!
쿠버네티스 상에 리소스가 생성되고
AWS 콘솔에서도 실제 VPC 리소스가 생성된 걸 확인할 수 있었다.
이제 서브넷을 생성할 것이다.
VPCID=$(kubectl get vpcs vpc-tutorial-test -o jsonpath={.status.vpcID})
cat <<EOF > subnet.yaml
apiVersion: **ec2**.services.k8s.aws/v1alpha1
kind: **Subnet**
metadata:
name: **subnet-tutorial-test**
spec:
cidrBlock: **10.0.0.0/20**
vpcID: $VPCID
EOF
역시 kubectl apply -f subnet.yaml
으로 리소스를 생성하면 subnet이 실시간으로 생성된다!
역시나 쿠버 상에 리소스가 생성되고
AWS 리소스도 생성되어 있다.
마무리는 kubectl delete -f subnet.yaml && kubectl delete -f vpc.yaml
으로 삭제 확인!
VPC Workflow 생성
그 밖에 VPC, IGW, NAT GW & EIP, Route Table, Subnet. SG를 한꺼번에 생성할 수도 있다!
매니페스트는 너무 길어서 아래 링크를 참조하자!
Manage a VPC Workflow with the ACK EC2-Controller
근데 프라이빗 서브넷은 쿠버 상에 리소스는 생성됐는데 실제 AWS 리소스에는 생성되지 않다가 한 5분정도 뒤에 생성되었다. 이는 NAT GW가 생성되고 라우팅 테이블 ID가 확인되고 그 후 서브넷 ID가 확인되는 데까지 걸리는 시간이다.
전체 생성되면 아래와 같은 리소스맵 확인이 가능하다.
퍼블릭 서브넷에 EC2 인스턴스 생성
위에 만든 퍼블릭 서브넷에 인스턴스를 생성할 것이다.
배스천 역할을 하는 인스턴스이다.
우선 환경변수로 퍼블릭 서브넷, SG, 최신 AMI, 키페어를 설정한다.
PUBSUB1=$(kubectl get subnets tutorial-public-subnet1 -o jsonpath={.status.subnetID})
TSG=$(kubectl get securitygroups tutorial-security-group -o jsonpath={.status.id})
AL2AMI=$(aws ec2 describe-images --owners **amazon** --filters "Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2" --query 'Images[0].ImageId' --output text)
MYKEYPAIR=키페어이름
퍼블릭 인스턴스를 하나 생성하기 위한 매니페스트를 만들고
cat <<EOF > tutorial-bastion-host.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: Instance
metadata:
name: tutorial-bastion-host
spec:
imageID: $AL2AMI # AL2 AMI ID - ap-northeast-2
instanceType: t3.medium
subnetID: $PUBSUB1
securityGroupIDs:
- $TSG
keyName: $MYKEYPAIR
tags:
- key: producer
value: ack
EOF
kubectl apply -f tutorial-bastion-host.yaml
로 리소스를 생성한다.
**자동으로 인스턴스가 생성되었다!
쿠버상에도 리소스가 생성되고
AWS 콘솔에도 인스턴스가 생성되었다.
EC2에 접속해서 ping을 날려봤지만 외부 인터넷 통신이 되지 않는다.
보안그룹 아웃바운드 규칙에 아무것도 없기 때문!
SG를 생성하기 위해 매니페스트를 작성한다.
ingressRules은 기존 생성할 때 만들어 둔 것이고 기존 것은 그대로 두고 egressRules을 추가한다!
cat <<EOF > modify-sg.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: SecurityGroup
metadata:
name: tutorial-security-group
spec:
description: "ack security group"
name: tutorial-sg
vpcRef:
from:
name: tutorial-vpc
ingressRules:
- ipProtocol: tcp
fromPort: 22
toPort: 22
ipRanges:
- cidrIP: "0.0.0.0/0"
description: "ingress"
egressRules:
- ipProtocol: '-1'
ipRanges:
- cidrIP: "0.0.0.0/0"
description: "egress"
EOF
kubectl apply -f modify-sg.yaml
로 SG 리소스를 생성하고 다시 확인해 보자,
아웃바운드 규칙도 생성되어 있고
EC2 인스턴스에서 외부 인터넷으로 ping이 나가는 것도 확인할 수 있었다!
프라이빗 서브넷에 EC2 인스턴스 생성
이제 프라이빗 서브넷에 EC2를 생성해 볼 것이다.
일반적으로 서비스를 운영하면 거의 프라이빗 환경에 구성하게 된다.
일단 생성하기 앞서 프라이빗 서브넷 ID를 환경변수로 지정했다.
나머지는 위에서 사용한 환경변수들이다.
PRISUB1=$(kubectl get subnets tutorial-private-subnet1 -o jsonpath={.status.subnetID})
echo $PRISUB1 , $TSG , $AL2AMI , $MYKEYPAIR
프라이빗 인스턴스 생성을 위한 매니페스트를 작성하고
cat <<EOF > tutorial-instance-private.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: Instance
metadata:
name: tutorial-instance-private
spec:
imageID: $AL2AMI
instanceType: t3.medium
subnetID: $PRISUB1
securityGroupIDs:
- $TSG
keyName: $MYKEYPAIR
tags:
- key: producer
value: ack
EOF
kubectl apply -f tutorial-instance-private.yaml
로 리소스를 생성하면 역시 인스턴스도 바로 생성된다!
AWS 콘솔 상에서도 확인할 수 있고
퍼블릭 IP는 없다!
쿠버상에 리소스에도 두 개의 인스턴스 리소스가 생성되어 있다.
프라이빗 존에 있는 인스턴스에 접속하기 위해서는 퍼블릭 인스턴스를 통해 터널링을 구성해야 한다.
우선 퍼블릭 인스턴스를 접속할 때 -L
옵션을 통해 프라이빗 인스턴스를 위한 로컬 포트를 열어 두자
그럼 프라이빗 인스턴스는 해당 포트로 접속할 수 있다.
참고로 외부 통신도 되는데 (아웃바운드만), 이는 NAT GW를 통하기 때문이다.
그래서 curl로 공인아이피를 찍어보면 NAT GW의 IP가 찍히는 것을 확인할 수 있다.
실습 후 리소스 삭제!
인스턴스 2대와 vpc workflow를 삭제한다.
kubectl delete -f tutorial-bastion-host.yaml && kubectl delete -f tutorial-instance-private.yaml
kubectl delete -f vpc-workflow.yaml
헬름으로 설치한 EC2 Controller와 IRSA도 삭제한다!
export SERVICE=**ec2**
helm uninstall -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller
eksctl delete iamserviceaccount --cluster myeks --name ack-$SERVICE-controller --namespace ack-system
3. ACK RDS Controller
마지막으로 RDS도 매니페스트 파일로 관리하고자 한다!
헬름으로 설치
최신 버전을 가진 ACK RDS Controller의 헬름을 다운받는다.
export SERVICE=rds
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/${SERVICE}-controller/releases/latest | jq -r '.tag_name | ltrimstr("v")')
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
네임스페이스를 지정해서 해당 헬름을 설치한다.
export ACK_SYSTEM_NAMESPACE=ack-system
export AWS_REGION=ap-northeast-2
helm install -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller --set aws.region="$AWS_REGION" $SERVICE-chart-$RELEASE_VERSION.tgz
RDS Controller 파드와 RDS 관련된 리소스를 가진 crd가 설치되었다.
이제 AmazonRDSFullAccess
역할을 가진 IRSA를 생성한다.
이 IRSA를 통해 RDS Controller가 RDS에 대한 풀권한을 가질 수 있다.
eksctl create iamserviceaccount \
--name ack-$SERVICE-controller \
--namespace $ACK_SYSTEM_NAMESPACE \
--cluster $CLUSTER_NAME \
--attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonRDSFullAccess`].Arn' --output text) \
--override-existing-serviceaccounts --approve
AWS 콘솔에 보면 해당 Role이 생성되었고
Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된 걸 확인할 수 있다.
이제 RDS Controller가 해당 Role을 적용할 수 있게 RDS Controller Deployment를 재시작한다.
kubectl -n $ACK_SYSTEM_NAMESPACE rollout restart deploy ack-$SERVICE-controller-$SERVICE-chart
재시작하고 나서 deployment를 조회해 보면 AWS_ROLE_ARN
과 볼륨 aws-iam-token
이 추가된 것을 확인할 수 있다.
AWS RDS for MariaDB 생성 및 삭제
Deploy PostgreSQL, MySQL, MariaDB Instances Using the ACK RDS Controller
위 링크를 참조하여 실습을 진행한다.
우선 DB 암호를 위한 Secret을 생성한다.
RDS_INSTANCE_NAME=myrds
RDS_INSTANCE_PASSWORD=qwe12345
kubectl create secret generic "${RDS_INSTANCE_NAME}-password" --from-literal=password="${RDS_INSTANCE_PASSWORD}"
RDS MariaDB를 생성하기 위해 매니페스트를 작성한다.
DB 사양, 이름, 버전, DB 유저 및 패스워드를 설정할 수 있다.
cat <<EOF > rds-mariadb.yaml
apiVersion: rds.services.k8s.aws/v1alpha1
kind: DBInstance
metadata:
name: "${RDS_INSTANCE_NAME}"
spec:
allocatedStorage: 20
dbInstanceClass: db.t4g.micro
dbInstanceIdentifier: "${RDS_INSTANCE_NAME}"
engine: mariadb
engineVersion: "10.6"
masterUsername: "admin"
masterUserPassword:
namespace: default
name: "${RDS_INSTANCE_NAME}-password"
key: password
EOF
kubectl apply -f rds-mariadb.yaml
으로 리소스를 생성하면 AWS RDS도 자동 생성되기 시작한다.
RDS가 생성되면서 Status가 creating → backing-up → available로 변경된다.
AWS 콘솔에서 RDS 생성을 확인하였다.
MariaDB에 접속하기 위해서 FieldExport
리소스를 생성한다.
FieldExport
리소스를 사용하여 DB 연결 정보를 추출해서 파드에서 사용할 수 있다.
FieldExport 리소스에 대한 파일은 너무 길어서 아래 링크를 참조하자!
Deploy PostgreSQL, MySQL, MariaDB Instances Using the ACK RDS Controller
추출된 값이 자동으로 ConfigMap에 data로 들어가 있다.
현재 DB 리소스의 status를 확인하면 실행중인 RDS의 정보를 확인할 수 있다.
myrds-conn-cm
Configmap 리소스로 파드에 RDS 연결 정보를 설정해 줄 수도 있다!
리소스 삭제!
일단 쿠버네티스 리소스를 삭제하면 RDS는 자동 삭제된다.
kubectl delete pod app
kubectl delete -f rds-mariadb.yaml
RDS Controller를 삭제하고
export SERVICE=rds
helm uninstall -n $ACK_SYSTEM_NAMESPACE ack-$SERVICE-controller
IRSA도 삭제해 준다.
eksctl delete iamserviceaccount --cluster myeks --name ack-$SERVICE-controller --namespace ack-system
마지막으로 모든 실습을 마치고 네임스페이스도 삭제한다.
kubectl delete namespace ack-system
마무리
실습을 마치고 나서 느낀 점은…
매니페스트로 관리하여 GitOps의 장점을 가져갈 순 있겠으나,
굳이 쿠버네티스에서 사용할까라는 생각이 들었다.
왜냐면 노드가 죽으면 관련 리소스에 영향이 갈텐데… 그럼 크리티컬하지 않을까?라는 생각이 들었다.
양방향으로 정합성이 맞을까라는 불안감도 들고
일단 개발 환경에서는 사용할 의향은 있다!
운영 환경은 모르겠다..
Leave a comment