PKOS2) kOps를 사용한 클러스터 구성

CloudNet@팀에서 진행하는 쿠버네티스 실무 실습 스터디 참가글입니다.

24단계 실습으로 정복하는 쿠버네티스 도서 내용을 기반으로 진행됩니다.


1. kOps 소개

1.1. kOps란?

kOps – Kubernetes OPerationS

‘The easiest way to get a production grade Kubernetes cluster up and running.’

https://kops.sigs.k8s.io/
kops는 클러스터용 kubectl이라고 생각할 수 있습니다.

kops를 사용하면 프로덕션에 적합하고 고가용성을 갖춘 Kubernetes 클러스터를 생성, 삭제, 업그레이드, 유지 보수할 뿐만 아니라 필요한 클라우드 인프라를 프로비저닝할 수 있습니다.

현재 공식적으로 지원되는 클라우드 프로바이더는 AWS (Amazon Web Services)와 GCE (Google Cloud Platform)이며, 베타 지원으로 DigitalOcean, Hetzner, OpenStack가 있으며, Azure는 알파 지원입니다.

kops는 명령줄 도구로, 클라우드 프로바이더에서 쉽게 Kubernetes 클러스터를 배포하고 관리할 수 있습니다. 또한 선언적인 구성 파일을 사용하여 클러스터의 원하는 상태를 정의합니다.

https://kops.sigs.k8s.io/#what-is-kops

1.2. 다른 k8s 구성 도구와의 차이점은?

스터디 이전에는 kOps를 알고 있지 못하였다. 매니지드 k8s 서비스가 아닌 클러스터를 구축하려면 kubeadm과 kubespray와 같은 도구를 사용할 수 있다고 알고 있었다.

  • kubeadm – 필요 패키지/바이너리 수동 설치 및 구성
  • kubespray- Ansible 기반 설치 과정 자동화

kOps 또한 k8s 공식문서에 소개된 배포 도구 중 하나이며, 특징은 아래와 같다.

  • cli 기반으로 클러스터 관리(생성/삭제/업그레이드등) 가능
  • AWS, GCE와 같은 클라우드 환경의 VM에 구성가능
  • kubectl의 CRD로 구성과정 자동화(되는듯함)

클라우드환경에서 빠르게 k8s 환경을 구성한다면 kOps 사용이 제일 적절한듯하다. EKS와 같은 매니지드 서비스는 컨트롤플레인등 구성을 위해 다소의 시간이 소요되기 때문이다.


2. 구성 실습

2.1. 실습 Architecture 및 과정

  1. kops-ec2 생성
    – kops가 설치된 인스턴스를 생성
    – 생성된 kops-ec2가 kops클러스터를 관리할 수 있도록 필요값 구성
  2. kops cluster 생성
    – kops-ec2를 사용하여, k8s 클러스터를 구성
  3. kops 클러스터 활용
    – ExternalDNS 실습
    – Helm > WordPress 배포
  4. 추가 도전과제
    – 기존 VPC에 Private HA k8s 클러스터 구축하기
    – 기존 VPC/Subnet 정보를 활용한 Public k8s 클러스터 생성
    – 마스터 노드 1>3으로 Multiple master 구성 변경
    – Public cluster를 Private cluster로 이전

2.2. 사전 준비사항

  • 실습용 IAM 사용자 계정 (편의를 위해 AdministratorAccess 권한 부여)
  • ec2 접근을 위한 키페어 생성
  • DNS 실습을 위한 퍼블릭 도메인 구입

2.3. kops-ec2 생성

생성은 스터디팀에서 제공한 클라우드 포메이션을 사용해 생성한다.

# yaml 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/kops-new-ec2.yaml

# 배포
# aws cloudformation deploy --template-file ~/Downloads/kops-new-ec2.yaml --stack-name mykops --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region <리전>
예시) aws cloudformation deploy --template-file ~/Downloads/kops-new-ec2.yaml --stack-name mykops --parameter-overrides KeyName=nasirk17 SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[*].OutputValue' --output text
예시) 3.35.137.31

# kOps-ec2 에 SSH 접속
예시) ssh -i <My SSH Keyfile> ec2-user@3.35.137.31
ssh -i ~/.ssh/nasirk17.pem ec2-user@$(aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[0].OutputValue' --output text)
Cloudformation Stack이 생성되서 구성된 결과

2.4. kops-ec2 구성

위의 클라우드포메이션으로 생성된 ec2에 접속하여, 잘 생성되었는지 확인한뒤
kops 클러스터 생성을 위해 필요한 값(AWS Key, S3 버킷, 환경변수 등)을 설정한다.

# AWS Credentials을 환경 변수로 저장
export AWS_ACCESS_KEY_ID=개인액세스키
export AWS_SECRET_ACCESS_KEY=개인프라이빗키
export AWS_DEFAULT_REGION=ap-northeast-2
echo 'export AWS_ACCESS_KEY_ID=개인액세스키'>>~/.bashrc
echo 'export AWS_SECRET_ACCESS_KEY=개인프라이빗키'>>~/.bashrc
echo 'export AWS_DEFAULT_REGION=ap-northeast-2'>>~/.bashrc

# k8s 설정파일이 저장될 버킷 생성
REGION=ap-northeast-2  # 서울 리전 사용
aws s3 mb s3://버킷<유일한 이름> --region $AWS_DEFAULT_REGION

# 배포 시 참고할 정보를 환경 변수에 저장
export AWS_PAGER="" 
export REGION=ap-northeast-2 
export KOPS_CLUSTER_NAME=devxmonitor.click
export KOPS_STATE_STORE=s3://kops-nasir
echo 'export AWS_PAGER=""' >>~/.bashrc
echo 'export REGION=ap-northeast-2' >>~/.bashrc
echo 'export KOPS_CLUSTER_NAME=devxmonitor.click' >>~/.bashrc 
echo 'export KOPS_STATE_STORE=s3://kops-nasir' >>~/.bashrc

2.5. kOps 클러스터 생성

2.4.를 통해 저장된 환경변수를 반영하여, kOps 클러스터를 생성한다.

# kops 설정 파일 생성(s3) 및 k8s 클러스터 배포 : 6분 정도 소요
## CNI는 aws vpc cni 사용, 마스터 노드 1대(t3.medium), 워커 노드 2대(t3.medium), 파드 사용 네트워크 대역 지정(172.30.0.0/16)

kops create cluster --zones="$REGION"a,"$REGION"c --networking amazonvpc --cloud aws \
--master-size t3.medium --node-size t3.medium --node-count=2 --network-cidr 172.30.0.0/16 \
--ssh-public-key ~/.ssh/id_rsa.pub --name=$KOPS_CLUSTER_NAME --kubernetes-version "1.24.10" -y
자동화된 생성과정 캡쳐

2.6. 클러스터 구성 확인

설치 완료 후, 잘 구성되었는지 확인한다.

# kops 명령어를 통한 구성 확인 (전체 과정 중 일부)
kops get cluster
kops get ig
kops get instances

2.7. 구성 실패 시 수동 삭제 방법

구성 과정 중 이상이 있다던가 하여 롤백이 필요한 경우 수동으로 삭제하고 재시도 하여야한다.

  1. EC2 Auto Scaling 그룹 : 3개 삭제
  2. EC2 시작 템플릿 Launch Templates : 3개 삭제
  3. S3 버킷 비우기
  4. Route53에 추가된 A 레코드 3개 삭제
  5. CloudFormation 삭제

2.8. 관리 편의성 도구 설치

alias 등록

# 자동 완성 및 alias 축약 설정
source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -F __start_kubectl k' >> ~/.bashrc
exit
exit
# 이후 재접속

krew 플러그인

krew는 kubectl cli의 플러그인 매니저이다.

플러그인들을 쉽게 설치하고, 업데이트하는등 관리 할 수 있으며

230311 현재 213개의 플러그인이 등록되어있으며, 추천된 플러그인들과 용도는 아래와 같다.

  • ctx) kubeconfig 상의 context간 전환
  • ns) k8s 네임스페이스간 전환
  • df-pv) 리눅스의 df처럼, PV의 디스크 사용량 조회
  • get-all) k get all과 비슷하지만, 더많은걸 보여줌
  • ktop) 사용량 메트릭 보기위한 top 명령어
  • neat) k8s리소스 조회시, 불필요한 정보 제외
  • oomd) 메모리부족으로 죽은 파드들 조회
  • view-secret) base64인코딩을 간단히 디코딩
# 설치
curl -fsSLO https://github.com/kubernetes-sigs/krew/releases/download/v0.4.3/krew-linux_amd64.tar.gz
tar zxvf krew-linux_amd64.tar.gz
./krew-linux_amd64 install krew
tree -L 3 /root/.krew/bin

# PATH 추가
export PATH="${PATH}:/root/.krew/bin"
echo 'export PATH="${PATH}:/root/.krew/bin"' >>~/.bashrc

# krew 확인
kubectl krew
kubectl krew update
kubectl krew search
kubectl krew list
kubectl krew

# 플러그인 설치
kubectl krew install ctx ns df-pv get-all ktop neat oomd view-secret

2.9. ExternalDNS 실습

ExternalDNS

ExternalDNS는 외부로 개방된 k8s의 서비스와 인그레스를, 설정된 DNS Provider(AWS-Route53, Azure-DNS, GCP-Cloud DNS)에 적절한 DNS record로 동기화 시켜준다.

ExternalDNS Addon 설치

# 모니터링
watch -d kubectl get pod -A

# 정책 생성 -> 마스터/워커노드에 정책 연결
curl -s -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.json
aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json

# ACCOUNT_ID 변수 지정
export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)

# EC2 instance profiles 에 IAM Policy 추가(attach)
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name nodes.$KOPS_CLUSTER_NAME

# 설치
kops edit cluster
# --------------------------
# spec:
#   certManager:    # 없어도됨!
#     enabled: true # 없어도됨!
#   externalDns:
#     provider: external-dns
# --------------------------

# 업데이트 적용
kops update cluster --yes && echo && sleep 3 && kops rolling-update cluster

# externalDns 컨트롤러 파드 확인
kubectl get pod -n kube-system -l k8s-app=external-dns
# NAME                            READY   STATUS    RESTARTS   AGE
# external-dns-6b7d6d55b6-5nsn5   1/1     Running   0          5m2s

DNS 연결

# CLB에 ExternalDNS 로 도메인 연결
kubectl annotate service mario "external-dns.alpha.kubernetes.io/hostname=mario.$KOPS_CLUSTER_NAME"

# 확인
dig +short mario.$KOPS_CLUSTER_NAME
kubectl logs -n kube-system -l k8s-app=external-dns

# 웹 접속 주소 확인 및 접속
echo -e "Mario Game URL = http://mario.$KOPS_CLUSTER_NAME"

# 도메인 체크
echo -e "My Domain Checker = https://www.whatsmydns.net/#A/mario.$KOPS_CLUSTER_NAME"

완료 후 삭제

kubectl delete deploy,svc mario

2.10. Helm > wordpress 실습

Redhat에는 yum이 있고, Devian에는 apt가 있고, python에는 pip이 있는것처럼 k8s 에는 helm이 있다.

Helm은 쿠버네티스의 패키지 관리도구이다. 헬름은 오픈 소스 소프트웨어로 공개되어 있고, 쿠버네티스상에 올릴 수 있는 많은 서비스들에 관한 리소스를 사전정의한 패키지(Chart)가 있다.

배포 준비(워드프레스 레포 추가)

# 아티팩트 허브에서 검색
helm search hub wordpress

# repo 추가 및 확인
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo list

# wordpress 검색
helm search repo wordpress

헬름 차트를 통한 배포

# 네임스페이스 생성
kubectl create ns wordpress

# 터미널1
watch kubectl get pod,svc

# 기본 파라미터를 덮어쓸수 있게 --set 으로 지정
helm install myblog \
  --set wordpressUsername=admin \
  --set wordpressPassword=password \
  --set wordpressBlogName="PKOS BLOG" \
  --namespace wordpress bitnami/wordpress --version 15.2.22

# wordpress 네임스페이스로 변경
kubectl ns wordpress

# 설치 확인
kubectl get all,secret,cm,pvc
kubectl get-all -n wordpress
kubectl df-pv

---

# CLB에 ExternanDNS 로 도메인 연결
kubectl annotate service myblog-wordpress "external-dns.alpha.kubernetes.io/hostname=wordpress.$KOPS_CLUSTER_NAME"

# 확인
dig +short wordpress.$KOPS_CLUSTER_NAME
kubectl logs -n kube-system -l k8s-app=external-dns

# 웹 접속 주소 확인 및 접속
echo -e "Wordpress Blog URL = http://wordpress.$KOPS_CLUSTER_NAME"
echo -e "Wordpress Admin URL = http://wordpress.$KOPS_CLUSTER_NAME/admin"

배포된 helm 차트(패키지)관리

# helm 설치한 릴리즈 list 확인 : 설치 후 릴리즈로 관리된다
helm list

# 릴리즈 삭제하기
helm uninstall myblog

# 네임스페이스 default 로 변경
kubectl ns default


3. 추가 도전과제 – 기존 VPC에 Private HA 클러스터 구성

추가 목표를 수행하던 도중, 기존 실습환경은 퍼블릭 서브넷으로만 구성되어 폐쇄망 테스트를 이어서 할 수 없다는 것을 알았다.

Private cluster 구성을 위해서는 최소 하나의 private subnet 필요

그래서 추가목표로, 퍼블릭/프라이빗 서브넷이 존재하는 VPC를 생성한 뒤

  1. 해당 VPC/서브넷 내부에 kOps 클러스터를 배포해보고,
  2. AZ간/AZ내부에서의 Multiple master로 변경해보고,
  3. 퍼블릭에서 프라이빗으로 이전한 Private cluster로 변경하고자한다.

3.1. 기존 VPC 에 AWS kOps 클러스터 설치

필요한 vpc는 terraform을 통해 구축하였다. 웹 콘솔로 만들어도 되지만 테라폼을 사용한 이유는 종료 후 VPC 삭제시 NAT 삭제/EIP 릴리즈 기다리지않고 terraform destroy -auto-approve으로 편하게 지우고 싶어서이다.

# main.tf
module "pkos-vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name                  = "pkos"
  cidr                  = "172.32.0.0/16"
  secondary_cidr_blocks = ["172.33.0.0/16", "172.34.0.0/16"]

  azs                   = ["ap-northeast-2a", "ap-northeast-2c"]
  public_subnets        = ["172.32.1.0/24", "172.32.2.0/24"]
  private_subnets       = ["172.32.11.0/24", "172.32.12.0/24"]
  database_subnets      = ["172.32.101.0/24", "172.32.102.0/24"]
  public_subnet_names   = ["pkos-public-az1", "pkos-public-az2"]
  private_subnet_names  = ["pkos-private-az1", "pkos-private-az2"]
  database_subnet_names = ["pkos-database-az1", "pkos-database-az2"]

  enable_nat_gateway                 = true
  single_nat_gateway                 = true // az별 nat 게이트웨이 사용시 해당 옵션 삭제
  create_database_subnet_route_table = true
  create_database_nat_gateway_route  = true

  enable_dns_hostnames = true
  enable_dns_support   = true

  public_subnet_tags = {
    "kubernetes.io/cluster/${local.cluster_name}" = "shared"
    "kubernetes.io/role/elb"                      = "1"
  }

  private_subnet_tags = {
    "kubernetes.io/cluster/${local.cluster_name}" = "shared"
    "kubernetes.io/role/internal-elb"             = "1"
  }
}
  • 필수) EnableDNSHostnames=true

기본적으로, 기존 VPC에(Shared VPC) kOps 클러스터 생성 시 VPC와 Internet Gateway를 공유하고 새로운 서브넷과 라우팅 테이블을 갖고 구성한다.

기존 서브넷 (Shared subnet) 설정을 통해 기존 서브넷에 클러스터를 구축할수도 있으며, 그럴 경우 해당 서브넷에서는 kOps 작동을 위해 필요한 tag가 붙게 된다.

Shared VPC에 클러스터를 생성한 뒤, kops edit cluster 명령어를 사용해 Shared Subnet로 이전을 할 것이다.

기존의 VPC/Subnet 환경에 클러스터 구축은 .spec.networkID와 .spec.subnets 항목의 수정을 통해 가능하다.

1) Shared VPC에 생성

기존 클러스터 배포 구문에서 추가/변경되는 부분만 환경변수로 저장하여 생성하였다.

기존 vpc에 생성할 예정이므로, vpc id를 network-id에 넣어주고, 해당 vpc의 cidr 대역 또한 전달한다.

# 추가/변경된 부분만 환경변수로 설정
export VPC_ID=vpc-12345678 # replace with your VPC id
export NETWORK_CIDR=172.32.0.0/16 # replace with the cidr for the VPC ${VPC_ID}


kops create cluster --zones="$REGION"a,"$REGION"c --networking amazonvpc --cloud aws --master-size t3.medium --node-size t3.medium --node-count=2 --network-cidr=${NETWORK_CIDR} --ssh-public-key ~/.ssh/id_rsa.pub --name=$KOPS_CLUSTER_NAME --kubernetes-version "1.24.10" --network-id=${VPC_ID} -y

문서에서는 vpc id를 –vpc로 전달하라고 가이드 되어있지만, 해당 옵션은 deprecated 되었다. 문서의 업데이트가 필요할 듯.

kops create cluster --zones="$REGION"a,"$REGION"c --networking amazonvpc --cloud aws --master-size t3.medium --node-size t3.medium --node-count=2 --network-cidr=${NETWORK_CIDR} --ssh-public-key ~/.ssh/id_rsa.pub --name=$KOPS_CLUSTER_NAME --kubernetes-version "1.24.10" --vpc=${VPC_ID} -y

Flag --vpc has been deprecated, use --network-id instead

생성이 완료된 후, 기존의 VPC에 신규 Subnet이 생성되었고 그곳에 클러스터가 생겼음을 확인할 수 있다.

kops get cluster

# --------------------------
# spec:
#  subnets:
#  - cidr: 172.32.32.0/19
#    name: ap-northeast-2a
#    type: Public
#    zone: ap-northeast-2a
#  - cidr: 172.32.64.0/19
#    name: ap-northeast-2c
#    type: Public
#    zone: ap-northeast-2c
# --------------------------
VPC의 서브넷 목록

2) Shared Subnet으로 업데이트

기존 서브넷에 재생성되어야 하므로, 클러스터 정보를 편집하여 subnet을 지정하고 저장한다.

다만 입력시 zone과 name의 차이점이 무엇인지 모르겠다. zone은 AZ를 의미하고 name은 Tag:Name인 줄 알았는데 그건 AWS의 관점인듯하고

kOps관점에서 name은 kops내 중복되지않은 고유값이지않은가? 싶다.

kops edit cluster

# -------------------------
# spec:
#   subnets:
#   - cidr: 172.32.1.0/24
#     id: subnet-08e9ecc096e4fbbe3
#     name: ap-northeast-2a
#     type: Public
#     zone: ap-northeast-2a
#   - cidr: 172.32.2.0/24
#     id: subnet-0256bd7610045ff01
#     name: ap-northeast-2c
#     type: Public
#     zone: ap-northeast-2c
# --------------------------

저장된 변경사항을 실제 클러스터에 반영하기위해, kops update cluster를 입력한다.

terraform plan결과처럼, 수정으로 인한 변경 예정을 보여주며, kops update cluster -y 로 –yes 인자를 같이 던져줘야 실제 구성 파일이 변경된다.

kops update cluster로 수정 후 변경 예상

구성파일이 변경되어도, 현재 생성된 마스터/워커노드가 재생성 되지는 않는다. 강제로 이동시키기 위해 –force 인자와 함께 롤링 업데이트를 수행한다.

노드들 내리기 전마다 cluster validating하고, taint로 올라가있는 파드 빼내고, 인스턴트 종료되면서 https 콜이 끊긴다. 30초마다 살아있니? 신호 보내다가 새로 노드 뜨면서 https콜 받고 kube-system의 중요파드들 다 뜨면 클러스터에 조인하는거 보는건 좀 재밌었다. 심정지한 사람이 다시 살아나서 걸어오는거 보는 기분.

kops rolling-update cluster --force --yes

# 주요 로그 기록
# 컨트롤 플레인 rolling-update
I0311 22:10:26.934732   11870 instancegroups.go:501] Validating the cluster.
I0311 22:10:27.506886   11870 instancegroups.go:537] Cluster validated.
I0311 22:10:27.507521   11870 instancegroups.go:342] Tainting 1 node in "control-plane-ap-northeast-2a" instancegroup.
I0311 22:10:27.517317   11870 instancegroups.go:431] Draining the node: "i-076c2258adc9971ed".
I0311 22:10:33.621308   11870 instancegroups.go:625] Stopping instance "i-076c2258adc9971ed", node "i-076c2258adc9971ed", in group "control-plane-ap-northeast-2a.masters.devxmonitor.click" (this may take a while).
I0311 22:11:13.624430   11870 instancegroups.go:550] Cluster did not validate, will retry in "30s": error listing nodes: Get "https://api.devxmonitor.click/api/v1/nodes": http2: client connection lost.
I0311 22:12:13.625827   11870 instancegroups.go:550] Cluster did not validate, will retry in "30s": error listing nodes: Get "https://api.devxmonitor.click/api/v1/nodes": dial tcp 43.201.72.133:443
...
...
...
I0311 22:16:13.625827   11870 instancegroups.go:550] Cluster did not validate, will retry in "30s": error listing nodes: Get "https://api.devxmonitor.click/api/v1/nodes": dial tcp 43.201.72.133:443
I0311 22:16:44.359887   11870 instancegroups.go:560] Cluster did not pass validation, will retry in "30s": system-cluster-critical pod "ebs-csi-controller-5778cfd49-m87z9" is pending.
I0311 22:17:15.023302   11870 instancegroups.go:540] Cluster validated; revalidating in 10s to make sure it does not flap.
I0311 22:17:25.627018   11870 instancegroups.go:537] Cluster validated.

# 워커노드 rolling-update
I0311 22:17:25.627552   11870 instancegroups.go:501] Validating the cluster.
I0311 22:17:26.302210   11870 instancegroups.go:537] Cluster validated.
I0311 22:17:26.302684   11870 instancegroups.go:342] Tainting 1 node in "nodes-ap-northeast-2a" instancegroup.
I0311 22:17:26.315888   11870 instancegroups.go:602] Detaching instance "i-09eafd4a18097d53c", node "i-09eafd4a18097d53c", in group "nodes-ap-northeast-2a.devxmonitor.click".
I0311 22:17:26.604796   11870 instancegroups.go:203] waiting for 15s after detaching instance
I0311 22:17:41.604962   11870 instancegroups.go:501] Validating the cluster.
I0311 22:17:42.469239   11870 instancegroups.go:560] Cluster did not pass validation, will retry in "30s": machine "i-05de7b29d122eec3a" has not yet joined cluster.
...
...
...
I0311 22:19:42.469239   11870 instancegroups.go:560] Cluster did not pass validation, will retry in "30s": machine "i-05de7b29d122eec3a" has not yet joined cluster.
I0311 22:19:14.516158   11870 instancegroups.go:560] Cluster did not pass validation, will retry in "30s": node "i-05de7b29d122eec3a" of role "node" is not ready, system-node-critical pod "aws-node-t547g" is pending, system-node-critical pod "ebs-csi-node-lwt7c" is pending.
I0311 22:19:45.364280   11870 instancegroups.go:540] Cluster validated; revalidating in 10s to make sure it does not flap.
I0311 22:19:56.056477   11870 instancegroups.go:537] Cluster validated.

# 두번째 워커노드 롤링로그 생략, 업데이트 완료 (총 15분)
I0311 22:23:41.868842   11870 rollingupdate.go:234] Rolling update completed for cluster "devxmonitor.click"!
기존 노드들 종료 후 기존 서브넷에 새로 뜬 모습

3.2. AWS kOps multiple master 설정하기

현재 클러스터는 pkos-public-az1에 1대의 master가 떠있다. 고가용성을 위해

  1. az간 HA) 또다른 az인 pkos-public-az2에 2번째 master를 띄우고
  2. az내 HA) pkos-public-az1의 마스터를 3대로 늘려볼 생각이다.

근데 controlplane의 leader를 선출하는 quorum알고리즘을 따를려면 마스터는 홀수개여야할텐데.. 4개로 구성이 되나?

> az간 HA하려고 az2에 두번째 마스터를 추가하자마자 쿼럼 깨져서 실패

# error populating cluster spec: [spec.etcdClusters[0].etcdMembers: Invalid value: 2: Should be an odd number of control-plane-zones for quorum. Use --zones and --control-plane-zones to declare node zones and control-plane zones separately, spec.etcdClusters[1].etcdMembers: Invalid value: 2: Should be an odd number of control-plane-zones for quorum. Use --zones and --control-plane-zones to declare node zones and control-plane zones separately]

> az1,2에 각각 1대의 마스터를 추가하여, az1에 마스터2대 az2에 1대 마스터로 구성

Multiple master의 구성은 .spec.etcdClusters항목 수정 + 마스터 instancegroup생성을 통해 가능하다.

1) kops edit cluster

.spec.etcdClusters에는 .spec.etcdClusters.name (main,events)에 따라 구분되는 2개의 영역이 있다. 각각 영역에 생성 예정인 마스터 instancegroup을 먼저 입력한다.

영역말고 각 부분을 지칭하는 명확한 말이 있을듯한데 잘 모르겠다. field?

# 기존 구성

# -------------------------
# spec:
#   etcdClusters:
#   - cpuRequest: 200m
#     etcdMembers:
#     - encryptedVolume: true
#       instanceGroup: control-plane-ap-northeast-2a
#       name: a
#     memoryRequest: 100Mi
#     name: main
#   - cpuRequest: 100m
#     etcdMembers:
#     - encryptedVolume: true
#       instanceGroup: control-plane-ap-northeast-2a
#       name: a
#     memoryRequest: 100Mi
#     name: events
# --------------------------

# 변경 구성

# -------------------------
# spec:
#   etcdClusters:
#   - cpuRequest: 200m
#     etcdMembers:
#     - encryptedVolume: true
#       instanceGroup: control-plane-ap-northeast-2a
#       name: a
#     - instanceGroup: master-ap-northeast-2c
#       name: c
#     - instanceGroup: control-plane-ap-northeast-2a-1
#       name: a-1
#     memoryRequest: 100Mi
#     name: main
#   - cpuRequest: 100m
#     etcdMembers:
#     - encryptedVolume: true
#       instanceGroup: control-plane-ap-northeast-2a
#       name: a
#     - instanceGroup: master-ap-northeast-2c
#       name: c
#     - instanceGroup: control-plane-ap-northeast-2a-1
#       name: a-1
#     memoryRequest: 100Mi
#     name: events
# --------------------------

2) kops create instancegroup

이제 위에 입력한 instanceGroup 명에 맞춰 –role이 Master인 ig들을 생성한다.

kops create ig master-ap-northeast-2c --subnet ap-northeast-2c --role Master
kops create ig control-plane-ap-northeast-2a-1 --subnet ap-northeast-2a --role Master

만약 ig 먼저 생성하려고 한다면 1) kops edit cluster에서 수행한 etcd 클러스터 구성에 해당 ig가 명시되어있지 않아 생성이 거부된다.

Error: [spec.metadata.name: Forbidden: InstanceGroup "master-ap-northeast-2c" with role ControlPlane must have a member in etcd cluster "main", spec.metadata.name: Forbidden: InstanceGroup "master-ap-northeast-2c" with role ControlPlane must have a member in etcd cluster "events"]

3) kops update cluster -y && kops rolling-update cluster -y

변경내용을 실제 클러스터에 적용하고, Multiple cluster 생성을 위해 롤링 업데이트를 수행한다.

# 기존 워커노드는 유지되고, 마스터노드2대가 추가된 모습
[root@kops-ec2 ~]# k get node
NAME                  STATUS   ROLES           AGE     VERSION
i-020de919c26eaef00   Ready    control-plane   8m39s   v1.24.10
i-02ff770be98ce022b   Ready    control-plane   8m47s   v1.24.10
i-05de7b29d122eec3a   Ready    node            78m     v1.24.10
i-07ff115a4595fc2c8   Ready    control-plane   2m39s   v1.24.10
i-0db3ea4c2e5d6901a   Ready    node            75m     v1.24.10

3.4. AWS kOps Private Cluster 설정하기

이 파트에서는, 퍼블릭 서브넷에 있는 5대의 노드(마스터3,워커2)를 외부 인터넷망으로부터 트래픽을 받지않는 프라이빗 서브넷으로 이전할것이다.

현재 클러스터의 엔드포인트인 api.devxmonitor.click을 nslookup으로 조회한 결과가 이전 전/후로 어떻게 바뀌는 것으로 확인할 것이다.

  • Public / Private / Utility 서브넷
    • kops에서는 각 기능에 따라 서브넷을 퍼블릭/프라이빗/유틸리티로 나눈다
    • Public: 인터넷으로부터의 트래픽을 받을 수 있는 서브넷
    • Private: 인터넷으로부터의 트래픽을 받을 수 없는 서브넷(단, NAT 설정등으로 트래픽 보내기는 가능)
    • Utility: Public과 동일하지만 노드가 배치되지 않는다. 대신 로드밸런서나 NAT같은 리소스들이 배치된다.

Private cluster의 구성은 .spec.api.loadBalancer / .spec.subnets / .spec.topology 의 항목 수정을 통해 가능하다.

1) kops edit cluster

  1. 노드들의 이동을 위해 .spec.topology.masters와 .spec.topology.nodes를 private으로 변경
  2. 리소스가 생성될 서브넷 설정을 위해, .spec.subnets에서 기존 Public을 Utility로 변경하고, Private 서브넷 추가. single NAT G/W사용을 위해 egress 통일
  3. 생성과정동안 k8s 통신을 위해, .spec.api.loadBalancer에 Public NLB 추가
# 기존 구성

# -------------------------
# spec:
#   api:
#     dns: {}
#   subnets:
#   - cidr: 172.32.1.0/24
#     id: subnet-08e9ecc096e4fbbe3
#     name: ap-northeast-2a
#     type: Public
#     zone: ap-northeast-2a
#   - cidr: 172.32.2.0/24
#     id: subnet-0256bd7610045ff01
#     name: ap-northeast-2c
#     type: Public
#     zone: ap-northeast-2c
#   topology:
#     dns:
#       type: Public
#     masters: public
#     nodes: public
# --------------------------

# 변경 구성

# -------------------------
# spec:
#   api:
#     loadBalancer:
#       class: Network
#       type: Public
#   subnets:
#   - cidr: 172.32.1.0/24
#     id: subnet-08e9ecc096e4fbbe3
#     name: utility-ap-northeast-2a
#     type: Utility
#     zone: ap-northeast-2a
#   - cidr: 172.32.2.0/24
#     id: subnet-0256bd7610045ff01
#     name: utility-ap-northeast-2c
#     type: Utility
#     zone: ap-northeast-2c
#   - cidr: 172.32.11.0/24
#     egress: nat-011b85026c4d9600e
#     id: subnet-0645c9f5ae498575c
#     name: ap-northeast-2a
#     type: Private
#     zone: ap-northeast-2a
#   - cidr: 172.32.12.0/24
#     egress: nat-011b85026c4d9600e
#     id: subnet-081ce19fc41805d7d
#     name: ap-northeast-2c
#     type: Private
#     zone: ap-northeast-2c
#   topology:
#     dns:
#       type: Public
#     masters: private
#     nodes: private
# --------------------------

2) kops rolling-update cluster –force -y

변경 사항을 적용한다

3) NLB Internet-facing > Internal 전환

AWS에서 생성된 로드밸런서의 type 변경은 불가능 하기 때문에, 기존 NLB를 지우고 재생성 하여야 한다.

  1. kops edit cluster: .spec.loadBalancer.type을 Internal로 변경
  2. 웹 콘솔에서 NLB 삭제
  3. kops update cluster -y
  4. kops rolling-update cluster –cloudonly –force –instance-group-roles master –yes
    • LB가 삭제되어 k8s API 통신이 불가능하기 때문에, –cloudonly 옵션사용
    • 새로운 Internal NLB가 생성되고, master 역할의 인스턴스그룹(=컨트롤플레인)이 NLB에 등록됨

4) 결과 확인

# 폐쇄망 이동 이전

[root@kops-ec2 ~]# nslookup
> api.devxmonitor.click            
Server:         10.0.0.2
Address:        10.0.0.2#53

Non-authoritative answer: // node Public IP
Name:   api.devxmonitor.click
Address: 54.180.151.52
Name:   api.devxmonitor.click
Address: 3.36.125.83
Name:   api.devxmonitor.click
Address: 54.180.106.83

# 폐쇄망 이동 이후
[root@kops-ec2 ~]# nslookup api.devxmonitor.click
Server:         10.0.0.2
Address:        10.0.0.2#53

Non-authoritative answer: // NLB(Internal) IP
Name:   api.devxmonitor.click
Address: 172.32.12.58
Name:   api.devxmonitor.click
Address: 172.32.11.218
NLB에 부여된 ENI의 ip와 동일한지 확인

3.5. 마무리

  • 매니지드 k8s에서는 할 수 없는 컨트롤플레인 갖고놀기

처음에 왜 다른 주제들 중에서 굳이 private cluster / multiple master가 끌렸을까 생각했을때, EKS같은 관리형 쿠버네티스에서는 하기 힘든 마스터노드 관련 설정이라서 그런듯하다. 파드/노드 스케일링이나 IRSA같은 친구들은 EKS워크샵에도 있지만 마스터노드 HA구성관련 실습은 지금처럼 직접 클러스터 구성하지 않으면 할 수 없으니?

다만 작동하는 파드 몇개 남겨두고 진행했으면 더 좋았을걸하는 아쉬움이 남는다. 편의상 롤링 업데이트되고 k get node로 조회만 되면 넘어갔는데, 마리오 파드 띄워놓고 새로 노드그룹이 join되면 다시떠서 DNS 레코드 변경되고 접속확인까지 했으면 좋았을듯 하다.

  • (명령어) –dry-run -o yaml >>{파일명}.yaml

CKA 공부하면서도 느꼈지만, 명령어 실행 전에 그 결과를 구성 파일로 먼저 보는건 쿠버네티스 공부할때 정말 큰 도움이 된다.

위의 실습과정 또한, 개념/방법/순서는 공식 문서를 참조했지만, 실제 구성파일 수정시에는 현재 구성 상태와 dry-run 결과를 diff해서 어느 부분을 어떻게 수정하면 되는지 파악할 수 있어서 좋았다. kops edit cluster 만 잘하고 나면 kops update cluster -y && kops rolling-update –force -y 결과만 기다리는 수준.

# 현재 전체 설정 현황 저장
kops get all -o yaml >>current-all.yaml

# 현재 클러스터 설정 저장
kops get cluster -o yaml >>cluster.yaml

# AZ간 Multiple master 구성
https://kops.sigs.k8s.io/single-to-multi-master/ 문서 예시와 비교해서 kops edit cluster

# AZ내 Multiple master 구성
kops create cluster --node-count 3 --master-count 3 --zones ap-northeast-2a,ap-northeast-2c --master-zones ap-northeast-2a,ap-northeast-2c --name oneaz.devxmonitor.click --dry-run -o yaml >>sameaz.yaml

# Private cluster 구성
kops create cluster --node-count 3 --master-count 3 --zones ap-northeast-2a,ap-northeast-2c --master-zones ap-northeast-2a,ap-northeast-2c --name private.devxmonitor.click -t private --dry-run -o yaml >>private.yaml

4. 리소스 삭제

  • kOps 클러스터 삭제: kops delete cluster –yes
  • (클러스터 삭제 완료 확인 후) AWS CloudFormation 스택 삭제 : aws cloudformation delete-stack –stack-name mykops

Leave a Reply

This Post Has One Comment

  1. 가시다

    스터디 모임장 가시다입니다.

    Aws kOps의 Multi-Master 이나 Private(Subnet) Cluster 는 저도 아직 해보지 않았는데,

    정리 잘 해주셔서 쉽게 이해하고 확인할 수 있었습니다.

    남은 스터디 기간도 잘 부탁드립니다!