Skip to main content

kOps with Cloud9

· 10 min read
Jinwoong Kim

PKOS Study

가시다님의 Production Kubernetes Online Study (=PKOS) 2기 멤버가 되서 쿠버네티스 스터디를 진행하고 있다. 이정훈님의 집필하신 "24단계 실습으로 정복하는 쿠버네티스" 책을 기반으로 하는 스터디이며 총 4주간 진행이 되고 있고 첫번째 스터디 일정이 마무리 되었다. 실제로 kubeadm, kubespray로는 경험이 있지만 이번 스터디에서 대부분의 실습은 kOps로 구성이 되어 새로운 방식으로 설치를 진행했다.

kOps

  • Kubernetes Operations (kOps) - Production Grade k8s Installation, Upgrades and Management

https://kops.sigs.k8s.io/ https://github.com/kubernetes/kops

kOps는 클라우드 플랫폼(aws, gcp, azure 등)에서 쉽게 k8s 를 설치할 수 있도록 도와주는 도구로 서버 인스턴스와 네트워크 리소스 등을 클라우드에서 자동으로 생성해 k8s 를 설치하는 도구이고 kOps는 AWS 의 다양한 서비스와 유연하게 연동되어 사용 가능한게 장점이다.

글을 작성하는 날짜 기준(23년 3월 5일), 버전은 1.25.3 으로 진행을 한다.

Bastion 환경 구성

Basition은 Cloud9으로 구성을 했다. https://github.com/aws-samples/aws-cloud9-bootstrapping-example 를 참고하여 구성했고 여러 툴을 bootstrap 형태로 구성하기 위해 추가적으로 여러 도구들을 설치한다.

  • kubectl
  • kops
  • awscli v2
  • k9s
  • helm
  • jq, git, htop, tree, gettext, bash-completion 등

자세한 내용은 https://github.com/jinwoongk/aws-cloud9-bootstrapping-example/blob/main/example_instancestack.yaml 에서 확인할 수 있다. 해당 yaml 에서 본인이 주로 접속하는 Console 접속 정보(ARN)을 아래 OwnerArn 구문과 같이 업데이트를 진행한다.

################## INSTANCE #####################
ExampleC9InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: "/"
Roles:
- Ref: ExampleC9Role

ExampleC9Instance:
Description: "-"
DependsOn: ExampleC9BootstrapAssociation
Type: AWS::Cloud9::EnvironmentEC2
Properties:
Description: AWS Cloud9 instance for Examples
AutomaticStopTimeMinutes: 3600
InstanceType:
Ref: ExampleC9InstanceType
Name:
Ref: AWS::StackName
# OwnerArn: !If [Create3rdPartyResources, !Ref ExampleOwnerArn, !Ref "AWS::NoValue" ]
OwnerArn: "arn:aws:sts::265664683898:assumed-role/AWSReservedSSO_AWSAdministratorAccess_574b6756fcc31821/jinwoong"

해당 yaml 을 가지고 아래 명령으로 Cloudformation을 실행하게 되면 여러가지 유틸이 설치된 상태의 Cloud9 인스턴스가 생성되게 된다.

aws cloudformation deploy  --stack-name kops-test --template ./example_instancestack.yaml

Cloud9을 위해 생성된 권한을 활용하기 위해 Cloud9 설정에서 AWS managed temporary credentials 기능을 비활성화 하자.

cloud9

kOps 배포

AWS 환경에 kOps를 배포하기 위해서는 아래와 같은 IAM 권한이 필요하다.

  • AmazonEC2FullAccess
  • AmazonRoute53FullAccess
  • AmazonS3FullAccess
  • IAMFullAccess
  • AmazonVPCFullAccess
  • AmazonSQSFullAccess
  • AmazonEventBridgeFullAccess

kOps는 Cluster State 저장을 위한 S3 버킷과 DNS 레코드를 활용해서 클러스터 생성을 하게 되는데 편의를 위해 개인 사용중인 도메인의 NS서버를 Route53에 연결하고 이미 생성해놓은 버킷을 가지고 배포를 진행한다.

# 배포 시 참고할 정보를 환경 변수에 저장
## export NAME=<자신의 퍼블릭 호스팅 메인 주소>
## export KOPS_STATE_STORE=s3://(버킷 이름)
export KOPS_CLUSTER_NAME=prosv.kr
export KOPS_STATE_STORE=s3://jinwoong-k8s-s3
export AWS_PAGER=""
export REGION=ap-northeast-2

Cloud9 환경에서 kops 명령을 통해 클러스터를 구성하게 되는데 VPC CNI를 사용하기 위해 amazonvpc로 구성하고 1.24 버전의 master 노드 1대, work 노드 3대를 생성한다.

$ kops create cluster --zones="$REGION"a,"$REGION"c \
--networking amazonvpc --cloud aws \
--master-size t3.medium --node-size t3.medium --node-count=3 \
--network-cidr 172.30.0.0/16 --ssh-public-key ~/.ssh/id_rsa.pub \
--name=$KOPS_CLUSTER_NAME --discovery-store=s3://jinwoong-k8s-s3 \
--kubernetes-version "1.24.10" -y

클러스터가 생성되는 동안 유효성을 체크하기 위해 10분간 대기하고 구성이 완료되면 아래와 같이 초기 구성한 클러스터가 ready 상태인지 알 수 있다. kops get 명령을 통해 다른 정보들을 확인할 수 있다.

$ kops validate cluster --wait 10m

Validating cluster prosv.kr

INSTANCE GROUPS
NAME ROLE MACHINETYPE MIN MAX SUBNETS
master-ap-northeast-2a Master t3.medium 1 1 ap-northeast-2a
nodes-ap-northeast-2a Node t3.medium 2 2 ap-northeast-2a
nodes-ap-northeast-2c Node t3.medium 1 1 ap-northeast-2c

NODE STATUS
NAME ROLE READY
i-053ca67a20cd7bf7f node True
i-0a7dfc0939b58c2fc node True
i-0aa3db1bfe96bf280 master True
i-0b665798ce9dd857f node True

Your cluster prosv.kr is ready

$ kops get cluster
NAME CLOUD ZONES
prosv.kr aws ap-northeast-2a,ap-northeast-2c

$ kops get instances

ID NODE-NAME STATUS ROLES STATE INTERNAL-IP INSTANCE-GROUP MACHINE-TYPE
i-053ca67a20cd7bf7f i-053ca67a20cd7bf7f UpToDate node 172.30.49.113 nodes-ap-northeast-2a.prosv.kr t3.medium
i-0a7dfc0939b58c2fc i-0a7dfc0939b58c2fc UpToDate node 172.30.77.157 nodes-ap-northeast-2c.prosv.kr t3.medium
i-0aa3db1bfe96bf280 i-0aa3db1bfe96bf280 UpToDate master 172.30.63.194 master-ap-northeast-2a.masters.prosv.kr t3.medium
i-0b665798ce9dd857f i-0b665798ce9dd857f UpToDate node 172.30.54.137 nodes-ap-northeast-2a.prosv.kr t3.medium

기본 CRI 컨테이너 런타임이 containerd 인 것도 확인할 수 있다. 각 노드가 External-IP를 할당받은 것을 확인할 수 있다.

$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
i-053ca67a20cd7bf7f Ready node 42m v1.24.10 172.30.49.113 43.201.14.97 Ubuntu 20.04.5 LTS 5.15.0-1028-aws containerd://1.6.10
i-0a7dfc0939b58c2fc Ready node 42m v1.24.10 172.30.77.157 43.201.77.155 Ubuntu 20.04.5 LTS 5.15.0-1028-aws containerd://1.6.10
i-0aa3db1bfe96bf280 Ready control-plane 44m v1.24.10 172.30.63.194 43.200.6.4 Ubuntu 20.04.5 LTS 5.15.0-1028-aws containerd://1.6.10
i-0b665798ce9dd857f Ready node 42m v1.24.10 172.30.54.137 13.125.115.198 Ubuntu 20.04.5 LTS 5.15.0-1028-aws containerd://1.6.10

kubectl cli 플러그인 매니저인 쿠버네티스 krew를 설치하고 여러가지 도구를 설치한다. 설치되는 플러그인 말고도 다양한 플러그인들은 아래 링크에서 확인이 가능하다.

$ kubectl krew install ctx ns df-pv get-all ktop neat oomd view-secret

Add-on 구성

ExternalDNS와 같은 add-on을 구성하고 Kubernetes 서비스 또는 인그레스 생성 시 도메인을 설정하면, AWS(Route 53)와 같은 클라우드 DNS 서비스에서 A 레코드(TXT 레코드)를 자동 생성/삭제할 수 있다. Route53에서 동작을 위해 다음과 같은 IAM Policy가 필요하다.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/*"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}

해당 json으로 IAM Policy를 생성하고 master, worker 노드 instance profile에 추가한다.

aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json

export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)

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

이 상태에서 externalDNS를 구성한다. externalDNS 와 같은 여러 애드온들은 kOps와 쿠버네티스 라이프사이클에 따라 구성, 관리된다. 기본적으로 API 서버는 메트릭 서버 TLS 인증서를 확인하지 않기 때문에 TLS를 사용하려면 클러스터 spec에서 certManager도 같이 추가한다. 또한 HPA 구성을 위해 metricServer 도 구성을 진행한다.

$ kops edit cluster
...
spec:
certManager:
enabled: true
externalDns:
provider: external-dns
metricsServer:
enabled: true
insecure: false
...

$ kops update cluster --yes && echo && sleep 3 && kops rolling-update cluster --yes

일단 php-apache deployment를 시작한다.

apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
type: LoadBalancer

바로 HPA 테스트를 위해 간단하게 사용률을 기반으로 스케일링이 동작하도록 구성하고 CPU 로드를 주입한다. load-generator에서는 0.01초 단위로 리퀘스트를 보내게 된다.

https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/

$ kubectl autoscale deployment php-apache --cpu-percent=10 --min=1 --max=4

$ kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
If you don't see a command prompt, try pressing enter.
OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!

다음으로, HPA가 부하 증가에 어떻게 반응하는지 확인할 수 있다.

$ kubectl get hpa php-apache --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 93%/10% 1 4 4 1m

마지막으로 CLB에 ExternalDNS로 도메인 연결을 하기 위해서는 expose를 원하는 Service에 다음과 같이 annotation을 추가하는 방법으로 외부 도메인을 쉽게 연결할 수 있다.

$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.$KOPS_CLUSTER_NAME"

정리

항상 EKS만 활용하는 프로젝트를 진행하다보니 스터디 후에 오랜만에 직접 구성하는 경험을 해보니 트러블슈팅하는 재미가 있어서 좋은것 같다. 이런저런 add-on을 추가적으로 구성해서 더 해보고 싶지만 역시나 시간이 없는 관계로 일단 여기 까지 구성을 하고 이번에 설치한 Cloud9으로 배포되는 클러스터 구성은 CKS를 준비하고 팀 내부 세션을 진행하는 도구로 활용을 해볼 예정이다.

해당 포스팅은 현재 재직중인 회사에 관련이 없고, 개인 역량 개발을 위한 자료로 활용할 예정입니다.