이전 글에서 테라폼으로 생성한 클러스터 위에서 작업하겠다!

테라폼으로 클러스터를 만들었으면

이제 AWS 리소스는 ACK (AWS Controller for Kubernetes)로 생성해 볼 것이다.

ACK는 매니페스트로 AWS 리소스를 관리할 수 있어, GitOps의 장점을 가져갈 수 있다.

AWS Controllers for Kubernetes

S3 Controller를 예로 든 그림을 참고해 본다

Untitled

How it Works

kuberentes api server에 요청하면 해당 컨트롤러가 AWS에 요청하게 되는 방식을 가진다.

ACK가 지원하는 AWS 서비스로는

24년 4월을 기준으로 20개 서비스는 정식 출시되었고, 18개Preview 상태이다.

아래 링크 참고하자!

Services

그리고 모든 컨트롤러는 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가 생성된다.

Untitled 1

이제 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이 생성되었고

Untitled

Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된걸 확인할 수 있다.

Untitled 3

이제 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이 추가된 것을 확인할 수 있다.

Untitled 4

Untitled 5

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 버킷이 실시간으로 생성된다!

S3-controller-1

AWS 콘솔에서도 s3 버킷은 생성되어 있었다!

Untitled 6

클러스터 내에도 리소스 생성된 것을 확인했다!

Untitled 7

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하면 태그가 업데이트된다.

Untitled 8

AWS 콘솔에서 확인하면 태그가 업데이트된 것을 확인할 수 있다.

Untitled 9

마찬가지로 kubectl delete -f bucket.yaml로 바로 s3 버킷 삭제가 가능하다

S3-controller-2

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와 연관된 리소스들이다.

Untitled 10

이제 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이 생성되었고

Untitled

Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된걸 확인할 수 있다.

Untitled 12

이제 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이 추가된 것을 확인할 수 있다.

Untitled 13

Untitled 14

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가 실시간으로 생성된다!

EC2-controller-vpc

쿠버네티스 상에 리소스가 생성되고

Untitled 15

AWS 콘솔에서도 실제 VPC 리소스가 생성된 걸 확인할 수 있었다.

Untitled 16

이제 서브넷을 생성할 것이다.

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이 실시간으로 생성된다!

EC2-controller-subnet

역시나 쿠버 상에 리소스가 생성되고

Untitled 17

AWS 리소스도 생성되어 있다.

Untitled

마무리는 kubectl delete -f subnet.yaml && kubectl delete -f vpc.yaml으로 삭제 확인!

EC2-controller-delete-vpcsubnet

VPC Workflow 생성

그 밖에 VPC, IGW, NAT GW & EIP, Route Table, Subnet. SG를 한꺼번에 생성할 수도 있다!

Untitled

매니페스트는 너무 길어서 아래 링크를 참조하자!

Manage a VPC Workflow with the ACK EC2-Controller

Untitled 20

근데 프라이빗 서브넷은 쿠버 상에 리소스는 생성됐는데 실제 AWS 리소스에는 생성되지 않다가 한 5분정도 뒤에 생성되었다. 이는 NAT GW가 생성되고 라우팅 테이블 ID가 확인되고 그 후 서브넷 ID가 확인되는 데까지 걸리는 시간이다.

Untitled 21

전체 생성되면 아래와 같은 리소스맵 확인이 가능하다.

Untitled 22

퍼블릭 서브넷에 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로 리소스를 생성한다.

**자동으로 인스턴스가 생성되었다!

EC2-controller-ec2

쿠버상에도 리소스가 생성되고

Untitled 23

AWS 콘솔에도 인스턴스가 생성되었다.

Untitled 24

EC2에 접속해서 ping을 날려봤지만 외부 인터넷 통신이 되지 않는다.

Untitled 25

보안그룹 아웃바운드 규칙에 아무것도 없기 때문!

Untitled 26

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이 나가는 것도 확인할 수 있었다!

Untitled 27

프라이빗 서브넷에 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로 리소스를 생성하면 역시 인스턴스도 바로 생성된다!

EC2-controller-pri-ec2

AWS 콘솔 상에서도 확인할 수 있고

퍼블릭 IP는 없다!

Untitled 28

쿠버상에 리소스에도 두 개의 인스턴스 리소스가 생성되어 있다.

Untitled 29

프라이빗 존에 있는 인스턴스에 접속하기 위해서는 퍼블릭 인스턴스를 통해 터널링을 구성해야 한다.

우선 퍼블릭 인스턴스를 접속할 때 -L옵션을 통해 프라이빗 인스턴스를 위한 로컬 포트를 열어 두자

그럼 프라이빗 인스턴스는 해당 포트로 접속할 수 있다.

Untitled 30

참고로 외부 통신도 되는데 (아웃바운드만), 이는 NAT GW를 통하기 때문이다.

그래서 curl로 공인아이피를 찍어보면 NAT GW의 IP가 찍히는 것을 확인할 수 있다.

Untitled 31

실습 후 리소스 삭제!

인스턴스 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가 설치되었다.

Untitled 32

이제 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이 생성되었고

Untitled

Kubernetes 클러스터 상에도 해당 Role을 가진 SA가 생성된 걸 확인할 수 있다.

Untitled 34

이제 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이 추가된 것을 확인할 수 있다.

Untitled 35

Untitled 36

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}"

Untitled 37

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로 변경된다.

Untitled 38

AWS 콘솔에서 RDS 생성을 확인하였다.

Untitled

MariaDB에 접속하기 위해서 FieldExport 리소스를 생성한다.

FieldExport 리소스를 사용하여 DB 연결 정보를 추출해서 파드에서 사용할 수 있다.

FieldExport 리소스에 대한 파일은 너무 길어서 아래 링크를 참조하자!

Deploy PostgreSQL, MySQL, MariaDB Instances Using the ACK RDS Controller

추출된 값이 자동으로 ConfigMap에 data로 들어가 있다.

Untitled 40

현재 DB 리소스의 status를 확인하면 실행중인 RDS의 정보를 확인할 수 있다.

Untitled 41

myrds-conn-cm Configmap 리소스로 파드에 RDS 연결 정보를 설정해 줄 수도 있다!

Untitled 42

리소스 삭제!

일단 쿠버네티스 리소스를 삭제하면 RDS는 자동 삭제된다.

kubectl delete pod app
kubectl delete -f rds-mariadb.yaml

Untitled 43

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의 장점을 가져갈 순 있겠으나,

굳이 쿠버네티스에서 사용할까라는 생각이 들었다.

왜냐면 노드가 죽으면 관련 리소스에 영향이 갈텐데… 그럼 크리티컬하지 않을까?라는 생각이 들었다.

양방향으로 정합성이 맞을까라는 불안감도 들고

일단 개발 환경에서는 사용할 의향은 있다!

운영 환경은 모르겠다..

Categories:

Updated:

Leave a comment