Infra/Cloud

[AWS] EKS - Automation(스터디 7주차)

IMyoungho 2023. 6. 7. 21:35

스터디 마지막 7주차 주제는 EKS Automation이다.

 

벌써 마지막이라니.. 7주가 순식간에 지나갔다. 

 

포스팅전에 스터디 후기부터 작성해보려고한다.

 

kubernetes가 재미있어서, kubernetes 보안에 도움이 될 것 같아 신청한 스터디였는데 역시나 굉장히 많은 도움이 된 것 같다. 

(좋은 스터디에 참여할 기회를 주신 Gasida님 그리고 CloudNet@팀에 감사드립니다)

 

구체적으로 어떤점이 도움이 되었냐고 한다면...

 

1. 견문이 넓어졌다. 

Kubernetes의 Cluster에도 다양한 종류가 있다는 것을 알게되었다. 단순히 On-premise 환경만 알고 있었다면 관리형 Kubernetes인 EKS를 처음 마주했을 때 당황스러웠을 것 같다(다행..ㅎㅎ). 그리고 굉장히 편리한 꿀팁이나 tool, 오픈소스들을 배울 수 있었다. cloud나 kubernetes는 특정 기능에 대한 불편함을 해결/개선/향상시키기 위한 수많은 오픈소스와 plugin 등이 존재하는데 사용해보니 좋았던 내용들을 공유받을 수 있었다(이런 것이 있다는 걸 알고 있는 것만으로도 몸이 편해지는..ㅎㅎ 완전 편리한 것들이 많았다...)

 

 

2. 퀄리티 높은 공유 자료 & AWS와 친해지기

주변에서도 실수로 인해 많은 과금이 발생한적이 있어 과금이 살짝 부담스러웠지만 지금은 자주 접하다보니 굉장히 친숙해졌다. 그리고 스터디에서 제공해주는 자료의 퀄리티가 좋다... 때문에 공부 시간이나 시행착오를 겪는 시간을 굉장히 단축시켜주었다고 생각한다. 또한 같은 내용을 진행하더라도 스터디원들마다 보는 관점이나 생각, 학습내용이 조금씩 상이한데 내가 미쳐 생각하지 못한 부분까지, 또는 진행하지 못한 부분도 나눌 수 있었다(스터디의 긍정적인 순기능이라고 생각한다). 그리고 내용 정리를 잘하시는 분들이 굉장히 많다...(스터디가 끝났으니 차근차근 다시봐야겠다)

 

 

3. 스터디원분들이 실무자가 많았다(네트워킹 및 경험 발표)

특히 미래에 많은 도움이 될 거라고 생각하는 부분이다. Kubernetes, Cloud 관련 실무자 분들이 많이 계셨다. 엔지니어, 개발자, 인프라담당자, 관리자분들이 어떤식으로 AWS, EKS를 운영하고 있고 어떤 고민을 하고, 어떤 부분을 중요하게 생각하는지 들을 수 있었고 값진 경험 발표들을 들을 수 있었다. 미래에 내가 하고 싶어하는 보안 직무와 연결지어 생각하는데 도움이 되었고 고려해야할 점이나 서로의 입장에서의 이해관계, 생각의 범위가 넓어진 것 같아 커뮤니케이션에 많은 도움이 될 것 같다.

 

결론은..다음스터디도 반드시 참여하고싶다ㅎㅎ

 

 


 

ACK - AWS Controller for Kubernetes

ACK란 Kubernetes에서 AWS Resource들을 직접 정의해서 사용하는 것이다.

즉, kubectl 명령을 통해 (kube-apiserver를 통해) AWS Resource를 생성할 수 있게 할 수 있다.

쉽게말해 Kubernetes 환경에서 AWS Resource를 Kubernetes스럽게(?), Kubernetes native하게(?) 생성할 수 있다.

AWS IAM + Kubernetes RBAC가 필요하다.

 

How it Works

How the ACK controller works

aws-controllers-k8s.github.io

 

Services

Project status of each supported service

aws-controllers-k8s.github.io

[참고] ACK는 현재(23년 5월 29일 기준까지) Preview(테스트, 상용 서비스 비권장) 10개와 General Avaliability(상용 서비스 권장) 17개를 지원한다.

 

 

 

 

ACK Controller를  통해 S3 를 생성해보자

Kubernetes RBAC 권한은 ACK service Controller 설치 시, Helm이나 static manifests 파일을 통해 이미 설정이 완료된다.

 

Permissions Overview

Configuring RBAC and IAM for ACK

aws-controllers-k8s.github.io

# ACK S3 Controller Helm 차트 다운로드
export SERVICE=s3

# helm 차트 다운로드
export RELEASE_VERSION=$(curl -sL https://api.github.com/repos/aws-controllers-k8s/$SERVICE-controller/releases/latest | grep '"tag_name":' | cut -d'"' -f4 | cut -c 2-)
helm pull oci://public.ecr.aws/aws-controllers-k8s/$SERVICE-chart --version=$RELEASE_VERSION
tar xzvf $SERVICE-chart-$RELEASE_VERSION.tgz

# helm chart 확인
tree ~/$SERVICE-chart
 

ECR Public Gallery

Amazon ECR Public Gallery is a website that allows anyone to browse and search for public container images, view developer-provided details, and see pull commands

gallery.ecr.aws

=> AWS에서 제공하는 Elastic Container Registry(ECR)에서 helm chart를 install해주었다.

 

 

 

# ACK S3 Controller 설치
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


# 설치 확인
helm list --namespace $ACK_SYSTEM_NAMESPACE
kubectl -n ack-system get pods
kubectl get crd | grep $SERVICE
kubectl get all -n ack-system
kubectl get-all -n ack-system
kubectl describe sa -n ack-system ack-s3-controller

CRD로 bucket이 생성된 것을 확인
CRD로 등록되었기 때문에 kubectl api-resource 명령에서도 확인가능
ack-s3-controller 정상 설치 확인

=> helm install 옵션을 통해 namespace가 없더라도 생성되게 옵션을 주었으며 ack s3 controller를 우리가 원하는 ap-northeast2 region에 진행되로록 설치를 진행했다.

 

 

 

 

IRSA 설정

# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
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

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa -n ack-system
kubectl describe sa ack-$SERVICE-controller -n ack-system

# Restart ACK service controller deployment using the following commands.
kubectl -n ack-system rollout restart deploy ack-$SERVICE-controller-$SERVICE-chart

# IRSA 적용으로 Env, Volume 추가 확인
kubectl describe pod -n ack-system -l k8s-app=$SERVICE-chart
...
 

GitHub - aws-controllers-k8s/s3-controller: ACK service controller for Amazon Simple Storage Service (S3)

ACK service controller for Amazon Simple Storage Service (S3) - GitHub - aws-controllers-k8s/s3-controller: ACK service controller for Amazon Simple Storage Service (S3)

github.com

IRSA 적용 후 ServiceAccount Describe 전/후
CloudFormation 및 IAM Permission Policy 확인
Rollout restart 전/후 Pod Describe

=> Controller 별로 해당 repository 경로에 Recommand Policy를 명시해주고 있으니 참고해서 IRSA를 진행하면 된다. IRSA 적용 후 해당 serviceaccount를 확인해보면 Annotations에 role-arn이 추가된 것을 확인할 수 있다. 해당 serviceaccount를 재적용하기위해 pod rollout restart를 진행해주면 Pod에 aws-iam token mount와 Environment 값이 추가된 것을 확인할 수 있었다(Mutating Webhook에 의해 수정됨)

 

 

 

이제 kubectl을 통해 AWS S3 Resource를 생성해보자

 

Create an ACK Resource

Create, Update and Delete an S3 bucket

aws-controllers-k8s.github.io

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
kubectl describe bucket/$BUCKET_NAME

AWS S3 생성확인

=> 공식 예제를 참고해보면 S3의 이름은 다른사람들과 겹치면 안되기 때문인지 AWS_ACCOUNT_ID를 가져다 사용하도록 설정했다. ack-s3-controller(+ RBAC, IRSA)에 의해 kubectl로 AWS s3 Resource를 생성한 것을 확인할 수 있었다(물론 동일한 방법으로 삭제도 가능하다)

 

 

 

Clean up

# S3 bucket 삭제
kubectl delete -f bucket.yaml

# helm uninstall
export SERVICE=s3
helm uninstall -n ack-system ack-$SERVICE-controller

# ACK S3 Controller 관련 crd 삭제
kubectl delete -f ~/$SERVICE-chart/crds

# IRSA 삭제
eksctl delete iamserviceaccount --cluster myeks --name ack-$SERVICE-controller --namespace ack-system

# Namespace 삭제
kubectl delete ns ack-system

 

 


 

 

ACK Controller를 통해  EC2 & VPC 를 생성해보자

설정에 대해서는 위의 S3랑 다를게 없다. 환경변수 SERVICE 값과 이후에 IRSA에서 Policy만 달라질뿐 나머지는 그대로 진행하면 된다.

ack resource 명은 ec2지만 VPC와 관련된 내용도 생성이 가능하다.

 

 

[참고] ACK Controller Download & Install + IRSA 

 

GitHub - k8s-ho/eks-tools: I made a useful tool while doing eks study:)

I made a useful tool while doing eks study:). Contribute to k8s-ho/eks-tools development by creating an account on GitHub.

github.com

 

GitHub - aws-controllers-k8s/rds-controller: ACK service controller for Amazon Relational Database Service (RDS)

ACK service controller for Amazon Relational Database Service (RDS) - GitHub - aws-controllers-k8s/rds-controller: ACK service controller for Amazon Relational Database Service (RDS)

github.com

git clone https://github.com/k8s-ho/eks-tools
chmod +x ack.sh
./ack.sh [namespace] [ACK Resource]
./ack.sh ack-system ec2

=> 스터디에서 제공해주신 내용을 조금 수정하고 가공해서 만들어봤다. 사용법은 간단하다. 참고로 IRSA에서 사용되는 IAM Policy에 대해서는 ack controller github에서 recommand하는 정보를 자동으로 가져오게 했다.

 

 

VPC & Subnet 생성 & 삭제

# [터미널1] 모니터링
while true; do aws ec2 describe-vpcs --query 'Vpcs[*].{VPCId:VpcId, CidrBlock:CidrBlock}' --output text; echo "-----"; sleep 1; done

# 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
# 서브넷 생성
VPCID=$(kubectl get vpcs vpc-tutorial-test -o jsonpath={.status.vpcID})

# 모니터링
while true; do aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPCID" --query 'Subnets[*].{SubnetId:SubnetId, CidrBlock:CidrBlock}' --output text; echo "-----"; sleep 1 ; done

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

=> 역시나 제대로 생성 및 삭제가 완료되는 것을 확인할 수 있었다.

 

 

VPC Workflow 예제 배포

아래와 같은 VPC 환경을 ACK-ec2-Controller를 이용하여 배포해보겠다.

 

Manage a VPC Workflow with the ACK EC2-Controller

Create and manage a network topology using ACK EC2-Controller deployed on Amazon Elastic Kubernetes Service (EKS) The ACK service controller for Elastic Compute Cloud (EC2-Controller) lets users manage EC2 resources directly from Kubernetes. This guide dem

aws-controllers-k8s.github.io

VPC Workflow예제 구성(왼쪽) / ACK Controller 역할 그림(오른쪽)
sync 미완료로 모든정보가 보여지진 않음
sync가 아직 되지 않은 상황 확인(왼쪽) / 해당 Resource 상세보기를 통한 원인확인(오른쪽)
sync 완료

=> 오른쪽의 그림같이 ACK Controller가 AWS의 해당 Resource의 현재 상태를 체크하고 체크가 완료되어야 sync가 된다. 따라서  kubernetes의 CRD로 존재하는 AWS resource들을 확인했을 때 제대로 확인이 안될 수도 있다. 이는 상세한 정보들이 sync되기까지 어느정도 시간이 소요되기 때문이다.

 

 

 

 * VPC Workflow 예제 YAML file (더보기 확인)

더보기
cat <<EOF > vpc-workflow.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: VPC
metadata:
  name: tutorial-vpc
spec:
  cidrBlocks: 
  - 10.0.0.0/16
  enableDNSSupport: true
  enableDNSHostnames: true
  tags:
    - key: name
      value: vpc-tutorial
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: InternetGateway
metadata:
  name: tutorial-igw
spec:
  vpcRef:
    from:
      name: tutorial-vpc
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: NATGateway
metadata:
  name: tutorial-natgateway1
spec:
  subnetRef:
    from:
      name: tutorial-public-subnet1
  allocationRef:
    from:
      name: tutorial-eip1
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: ElasticIPAddress
metadata:
  name: tutorial-eip1
spec:
  tags:
    - key: name
      value: eip-tutorial
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: RouteTable
metadata:
  name: tutorial-public-route-table
spec:
  vpcRef:
    from:
      name: tutorial-vpc
  routes:
  - destinationCIDRBlock: 0.0.0.0/0
    gatewayRef:
      from:
        name: tutorial-igw
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: RouteTable
metadata:
  name: tutorial-private-route-table-az1
spec:
  vpcRef:
    from:
      name: tutorial-vpc
  routes:
  - destinationCIDRBlock: 0.0.0.0/0
    natGatewayRef:
      from:
        name: tutorial-natgateway1
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: Subnet
metadata:
  name: tutorial-public-subnet1
spec:
  availabilityZone: ap-northeast-2a
  cidrBlock: 10.0.0.0/20
  mapPublicIPOnLaunch: true
  vpcRef:
    from:
      name: tutorial-vpc
  routeTableRefs:
  - from:
      name: tutorial-public-route-table
---
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: Subnet
metadata:
  name: tutorial-private-subnet1
spec:
  availabilityZone: ap-northeast-2a
  cidrBlock: 10.0.128.0/20
  vpcRef:
    from:
      name: tutorial-vpc
  routeTableRefs:
  - from:
      name: tutorial-private-route-table-az1
---
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"
EOF

# VPC 환경 생성
kubectl apply -f vpc-workflow.yaml

 

 

 

생성된 VPC 환경의 Public subnet에 instance를 생성해보자.

# public 서브넷 ID 확인
PUBSUB1=$(kubectl get subnets tutorial-public-subnet1 -o jsonpath={.status.subnetID})
echo $PUBSUB1

# 보안그룹 ID 확인
TSG=$(kubectl get securitygroups tutorial-security-group -o jsonpath={.status.id})
echo $TSG

# Amazon Linux 2 최신 AMI 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)
echo $AL2AMI

# 각자 자신의 SSH 키페어 이름 변수 지정
MYKEYPAIR=<각자 자신의 SSH 키페어 이름>


# 변수 확인 > 특히 서브넷 ID가 확인되었는지 꼭 확인하자!
echo $PUBSUB1 , $TSG , $AL2AMI , $MYKEYPAIR


# [터미널1] 모니터링
while true; do aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table; date ; sleep 1 ; done

# public 서브넷에 인스턴스 생성
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

# 인스턴스 생성 확인
kubectl get instance
kubectl describe instance
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

instance가 정상적으로 생성된 것을 확인할 수 있었으며 AWS Console에서도 SG와 Tag 등 YAML file로 설정한대로 적용된 것을 확인할 수 있었다.

 

 

추가적으로 해당 Public instance가 외부통신을 하기위해서는 egress rule을 SG에 추가해주어야한다.

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

# 변경 확인 >> 보안그룹에 아웃바운드 규칙 확인
kubectl logs -n $ACK_SYSTEM_NAMESPACE -l k8s-app=ec2-chart -f

 

 

이번에는  Private subnet에 instance를 생성해보자.

# private 서브넷 ID 확인 >> NATGW 생성 완료 후 RT/SubnetID가 확인되어 다소 시간 필요함
PRISUB1=$(kubectl get subnets tutorial-private-subnet1 -o jsonpath={.status.subnetID})
echo $PRISUB1

# 변수 확인 > 특히 private 서브넷 ID가 확인되었는지 꼭 확인하자!
echo $PRISUB1 , $TSG , $AL2AMI , $MYKEYPAIR

# private 서브넷에 인스턴스 생성
cat <<EOF > tutorial-instance-private.yaml
apiVersion: ec2.services.k8s.aws/v1alpha1
kind: Instance
metadata:
  name: tutorial-instance-private
spec:
  imageID: $AL2AMI # AL2 AMI ID - ap-northeast-2
  instanceType: t3.medium
  subnetID: $PRISUB1
  securityGroupIDs:
  - $TSG
  keyName: $MYKEYPAIR
  tags:
    - key: producer
      value: ack
EOF
kubectl apply -f tutorial-instance-private.yaml

# 인스턴스 생성 확인
kubectl get instance
kubectl describe instance
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

 

 

 

생성된 Private Subnet에 존재하는 Instance에 Public  subnet의 Instance Tunneling을 통해 접속해보자

ssh -i <Keypair file> -L <임의 로컬 포트>:<private 서브넷의 인스턴스의 private ip 주소>:22 ec2-user@<public 서브넷에 인스턴스 퍼블릭IP> -v
ex) ssh -i abc.pem -L 9999:10.0.129.196:22 ec2-user@3.34.96.12 -v


ssh -i <Keypair file> -p 9999 ec2-user@localhost

tunneling을 통한 SSH 접근
Private subnet instance와 Public subnet의 instance가 ssh로 연결됨을 확인(왼쪽)  /  Private Subnet의 instance 공인 ip(오른쪽)

나의 로컬PC 포트 -> public subnet instance -> private subnet instance의 경로로 연결된다. 또한 private subnet에 존재하는 instance의 공인ip는 NAT Gateway의 ip이다.

 

 

 

Clean up

# vpc workflow 관련 리소스 삭제
kubectl delete -f tutorial-bastion-host.yaml && kubectl delete -f tutorial-instance-private.yaml
kubectl delete -f vpc-workflow.yaml # vpc 관련 모든 리소스들 삭제에는 다소 시간이 소요됨

# vpc, subnet 삭제
kubectl delete -f subnet.yaml && kubectl delete -f vpc.yaml

# helm uninstall
export SERVICE=ec2
helm uninstall -n ack-system ack-$SERVICE-controller

# ACK S3 Controller 관련 crd 삭제
kubectl delete -f ~/$SERVICE-chart/crds

# IRSA 삭제
eksctl delete iamserviceaccount --cluster myeks --name ack-$SERVICE-controller --namespace ack-system

# Namespace 삭제
kubectl delete ns ack-system

 


 

ACK Controller를  통해 RDS 생성해보자

관련 ACK Controller 및 IRSA setting은 자동화 스크립트로 진행해주었다

git clone https://github.com/k8s-ho/eks-tools
chmod +x ack.sh
./ack.sh ack-system rds

 

AWS RDS for MariaDB 생성

 

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

Create managed PostgreSQL, MySQL, and MariaDB instances in Amazon Relational Database Service (RDS) from a Amazon Elastic Kubernetes Service (EKS) deployment.

aws-controllers-k8s.github.io

# DB 암호를 위한 secret 생성
RDS_INSTANCE_NAME="<your instance name>"
RDS_INSTANCE_PASSWORD="<your instance password>"
RDS_INSTANCE_NAME=myrds
RDS_INSTANCE_PASSWORD=qwe12345
kubectl create secret generic "${RDS_INSTANCE_NAME}-password" --from-literal=password="${RDS_INSTANCE_PASSWORD}"

# 확인
kubectl get secret $RDS_INSTANCE_NAME-password

# [터미널1] 모니터링
RDS_INSTANCE_NAME=myrds
watch -d "kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'"

# RDS 배포 생성 : 15분 이내 시간 소요 >> 보안그룹, 서브넷 등 필요한 옵션들은 추가해서 설정해보자!
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

# 생성 확인
kubectl get dbinstances  ${RDS_INSTANCE_NAME}
kubectl describe dbinstance "${RDS_INSTANCE_NAME}"
aws rds describe-db-instances --db-instance-identifier $RDS_INSTANCE_NAME | jq

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         creating

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         backing-up

kubectl describe dbinstance "${RDS_INSTANCE_NAME}" | grep 'Db Instance Status'
  Db Instance Status:         available

# 생성 완료 대기 : for 지정 상태가 완료되면 정상 종료됨
kubectl wait dbinstances ${RDS_INSTANCE_NAME} --for=condition=ACK.ResourceSynced --timeout=15m
dbinstance.rds.services.k8s.aws/myrds condition met

 

 

환경변수를 이용하여 해당 RDS MariaDB에 바로 접근이 가능한 Pod를 만들어보자(feat. filedexport)

우선 fieldexport를 이용하여 필요정보들을 configmap으로 생성해주었다.

 

Bind Application to AWS Resources | EKS Workshop

Now that the RDS database has been created successfully, we can reconfigure the catalog component to use it for persistence instead of its existing pod-based MySQL. But how do we configure the catalog component with the RDS endpoint and credentials for the

www.eksworkshop.com

* fieldexport로 configmap 생성(더보기 참고)

더보기
RDS_INSTANCE_CONN_CM="${RDS_INSTANCE_NAME}-conn-cm"

cat <<EOF > rds-field-exports.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: ${RDS_INSTANCE_CONN_CM}
data: {}
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: ${RDS_INSTANCE_NAME}-host
spec:
  to:
    name: ${RDS_INSTANCE_CONN_CM}
    kind: configmap
  from:
    path: ".status.endpoint.address"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: ${RDS_INSTANCE_NAME}
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: ${RDS_INSTANCE_NAME}-port
spec:
  to:
    name: ${RDS_INSTANCE_CONN_CM}
    kind: configmap
  from:
    path: ".status.endpoint.port"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: ${RDS_INSTANCE_NAME}
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: ${RDS_INSTANCE_NAME}-user
spec:
  to:
    name: ${RDS_INSTANCE_CONN_CM}
    kind: configmap
  from:
    path: ".spec.masterUsername"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: ${RDS_INSTANCE_NAME}
EOF

kubectl apply -f rds-field-exports.yaml

kubectl describe dbinstance "${RDS_INSTANCE_NAME}"

=> Pod가 환경변수를 활용하여 RDS mariaDB에 바로 접근할 수 있도록 하기 위해서는 RDS의 Endpoint 주소 등, 몇가지 정보들이 필요하다. 이때 활용할 수 있는 것이 바로 Fieldexport이다. Fieldexport를 통해 우리가 활용하는데 필요한 정보를 configmap이나 secret으로 편리하게 생성할 수 있다.

 

 

 

* 생성된 Configmap 확인

# 상태 정보 확인 : address 와 port 정보 
kubectl get dbinstances myrds -o jsonpath={.status.endpoint} | jq

# 상태 정보 확인 : masterUsername 확인
kubectl get dbinstances myrds -o jsonpath={.spec.masterUsername} ; echo

# 컨피그맵 확인
kubectl get cm myrds-conn-cm -o yaml | kubectl neat | yh

# fieldexport 정보 확인
kubectl get crd | grep fieldexport
kubectl get fieldexport
kubectl get fieldexport myrds-host -o yaml | k neat | yh

 

 

Export한 configmap을 이용한 Pod 생성

APP_NAMESPACE=default
cat <<EOF > rds-pods.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app
  namespace: ${APP_NAMESPACE}
spec:
  containers:
   - image: busybox
     name: myapp
     command:
        - sleep
        - "3600"
     imagePullPolicy: IfNotPresent
     env:
      - name: DBHOST
        valueFrom:
         configMapKeyRef:
          name: ${RDS_INSTANCE_CONN_CM}
          key: "${APP_NAMESPACE}.${RDS_INSTANCE_NAME}-host"
      - name: DBPORT
        valueFrom:
         configMapKeyRef:
          name: ${RDS_INSTANCE_CONN_CM}
          key: "${APP_NAMESPACE}.${RDS_INSTANCE_NAME}-port"
      - name: DBUSER
        valueFrom:
         configMapKeyRef:
          name: ${RDS_INSTANCE_CONN_CM}
          key: "${APP_NAMESPACE}.${RDS_INSTANCE_NAME}-user"
      - name: DBPASSWORD
        valueFrom:
          secretKeyRef:
           name: "${RDS_INSTANCE_NAME}-password"
           key: password
EOF
kubectl apply -f rds-pods.yaml

# 생성 확인
kubectl get pod app

# 파드의 환경 변수 확인
kubectl exec -it app -- env | grep DB

=> 이를 통해 Pod 생성 시, 바로 DB에 접속할 수 있게 환경변수를 구성할 수 있다.

 

 

 

RDS DB 식별자 변경

kubectl patch dbinstance myrds --type=merge -p '{"spec":{"dbInstanceIdentifier":"studyend"}}'

변경한 식별자의 RDS가 생성되고 Creating -> back up -> Availiable 상태가 됨

kubectl get dbinstances myrds -o jsonpath={.status.endpoint} | jq
kubectl delete pod app && kubectl apply -f rds-pods.yaml
kubectl exec -it app -- env | grep DB

=> 변경한 DB 식별자의 RDS가 새로 생성되며 이는 Rolling Update가 아니라 기존의 RDS를 삭제하는 등의 관리가 진행되지 않는다.
=> 변경된 내용에 대한 tracking이 부족하다는 단점이 존재하므로 사용할거라면 이러한 문제를 고려해봐야한다(때문에 실습 이후 삭제는 AWS Console에서 수동으로 확실하게 전부 삭제해주는 것이 좋다)

 

=> 변경한 식별자와 관련된 환경변수 변화를 살펴보기 위해서는 Pod를 다시 생성해야한다(Pod 생성 시, configMap을 통해 Env를 설정했기 때문에 Pod를 재생성해야 정확한 확인이 가능). 시간이 조금 지나고 새로 생성된 RDS가 available 상태가 되면 변경된 Env 및 정보 확인을 할 수 있게 된다.

 

 


 

Flux

Flux는 Kubernetes에서 사용하는 gitops 도구이며 git에 정의되어있는 kubernetes resource(pod, service, deployment 등) 정보(manifests)를 kubernetes에 배포할 수 있다. github의 token이 필요하기 때문에 발급해서 진행해야한다.

 

* GitOps란 - Git Operation

DevOps의 실천방법 중 하나로 Application의 배포와 운영에 관련된 모든 요소들을 Git에서 관리한다는 의미이다.

 

EKS 스터디 - 7주차 flux 예제

이 글은 flux가 무엇이고 간단한 예제를 살펴봅니다. 예제를 쉽게 따라하기 위해 모든 예제는 flux CLI를 사용합니다. flux란? flux는 쿠버네티스를 위한 gitops 도구입니다. flux는 git에 있는 쿠버네티스

malwareanalysis.tistory.com

 

Installation

Flux install, bootstrap, upgrade and uninstall documentation.

fluxcd.io

# Flux CLI 설치
curl -s https://fluxcd.io/install.sh | sudo bash
. <(flux completion bash)

# 버전 확인
flux --version
flux version 2.0.0-rc.5

# 자신의 Github 토큰과 유저이름 변수 지정
export GITHUB_TOKEN=<your-token>
export GITHUB_USER=<your-username>

# Bootstrap
## Creates a git repository fleet-infra on your GitHub account.
## Adds Flux component manifests to the repository.
## Deploys Flux Components to your Kubernetes Cluster.
## Configures Flux components to track the path /clusters/my-cluster/ in the repository.
flux bootstrap github \
  --owner=$GITHUB_USER \
  --repository=fleet-infra \
  --branch=main \
  --path=./clusters/my-cluster \
  --personal

Flux 정상 설치 진행 및 완료

=> 위에서 진행한대로 github에 fleet-infra라는 이름의 private repository가 생성되었으며 몇가지 YAML file이 존재하는 것도 확인할 수 있었다.

 

 

 

 

gitops 도구 설치(flux 대시보드 설치 : admin / password)

 

Weave GitOps OSS | Weave GitOps

Installing Weave GitOps on your Cluster

docs.gitops.weave.works

# gitops 도구 설치 - ?Would you like to turn on analytics to help us improve our product? [Y/n] n으로 진행했음
curl --silent --location "https://github.com/weaveworks/weave-gitops/releases/download/v0.24.0/gitops-$(uname)-$(uname -m).tar.gz" | tar xz -C /tmp
sudo mv /tmp/gitops /usr/local/bin
gitops version

# flux 대시보드 설치
PASSWORD="password"
gitops create dashboard ww-gitops --password=$PASSWORD

# 확인
flux -n flux-system get helmrelease
kubectl -n flux-system get pod,svc

 

 

 

Ingress 설정 및 Dashboard 접속(+ ALB, External DNS)

# External DNS
MyDomain=im-youngho.com
echo "export MyDomain=im-youngho.com" >> /etc/profile
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

# AWS LB Controller
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller


CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# Ingress 설정
cat <<EOT > gitops-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitops-ingress
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
    alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
  - host: gitops.$MyDomain
    http:
      paths:
      - backend:
          service:
            name: ww-gitops-weave-gitops
            port:
              number: 9001
        path: /
        pathType: Prefix
EOT
kubectl apply -f gitops-ingress.yaml -n flux-system

# 배포 확인
kubectl get ingress -n flux-system

# GitOps 접속 정보 확인 >> 웹 접속 후 정보 확인
echo -e "GitOps Web https://gitops.$MyDomain"

 

 

 

Github에 있는 Nginx Manifest를 Kubernetes에 배포(악분님 예시 사용) - Kustomize 타입 사용

 

GitHub - sungwook-practice/fluxcd-test: fluxcd-test

fluxcd-test. Contribute to sungwook-practice/fluxcd-test development by creating an account on GitHub.

github.com

GITURL="https://github.com/sungwook-practice/fluxcd-test.git"
flux create source git nginx-example1 --url=$GITURL --branch=main --interval=30s

 

 

# [터미널] 모니터링
watch -d kubectl get pod,svc nginx-example1

# flux 애플리케이션 생성 : nginx-example1
flux create kustomization nginx-example1 --target-namespace=default --interval=1m --source=nginx-example1 --path="./nginx" --health-check-timeout=2m

# 확인
kubectl get pod,svc nginx-example1
kubectl get kustomizations -n flux-system
flux get kustomizations

Dashboad에서 확인

 

Clean up

# flux 애플리케이션 삭제 >> 파드와 서비스는? flux 애플리케이션 생성 시 --prune 옵션 false(default 값)
flux delete kustomization nginx-example1
flux get kustomizations
kubectl get pod,svc nginx-example1

# flux 애플리케이션 다시 생성 :  --prune 옵션 true
flux create kustomization nginx-example1 \
  --target-namespace=default \
  --prune=true \
  --interval=1m \
  --source=nginx-example1 \
  --path="./nginx" \
  --health-check-timeout=2m

# 확인
flux get kustomizations
kubectl get pod,svc nginx-example1

# flux 애플리케이션 삭제 >> 파드와 서비스는? 
flux delete kustomization nginx-example1
flux get kustomizations
kubectl get pod,svc nginx-example1

# flux 소스 삭제
flux delete source git nginx-example1

# 소스 확인
flux get sources git
kubectl -n flux-system get gitrepositories

flux uninstall --namespace=flux-system

flux delete kustomization을 진행했음에도 Pod와 service가 살아있음(왼쪽) /  --prune 옵션이 true인 경우 삭제된 것을 확인(오른쪽)

=> --prune 옵션이 기본 false이기 때문에 flux로 kustomization을 삭제해도 pod와 service는 남아있다. 때문에 해당 옵션을 true로 설정해야 같이 사라진다.

반응형