pkos) gcp에서의 kops 체험기
올해도 어김없이 스터디에 참여할 수 있는 기회가 생겨서 ‘24단계 실습으로 정복하는 쿠버네티스’ 책과 함께 진행되는 쿠버네티스 운영 환경 스터디에 참여하게 되었다.
kops 🔗
kops(Kubernetes Operations)는 kubernetes cluster를 단일 instance들 위에 쉽게 배포, 관리해주는 툴이다. kops controller server가 설정 파일을 gcs에 생성하고 해당 파일을 따라 kubernetes cluster구성으로 배포된다. 스터디 때에는 aws환경에서 진행되었었는데, 어떤식으로 구성되는지 궁금하여 gcp에서 테스트해보았다. 문서를 보면 gcp도 공식지원한다고는 하지만, 막상 테스트해보니 aws만큼까지는 아닌 것 같다.
kops controller server 생성
test server os는 centos7이고 별다른 수정없이 기본값으로 테스트했다. 변경 한 것은 gcs에 접근해야하기 때문에 cloud api access scopes을 조정해 준 것과 시작스크립트를 작성했다. 계속 쓸 것이 아니고 일회성으로 테스트 할 것이라 들어갔을 때 바로 쓸 수 있도록 아래 스크립트를 넣었다.
### startup-script
### install kubectl, helm, yh(yaml syntax highlighter)
#!/bin/bash
hostnamectl --static set-hostname kops
# Change Timezone
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# Install Packages
cd /root
yum -y install tree jq git htop wget unzip
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
curl -Lo kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x kops
mv kops /usr/local/bin/kops
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
echo 'alias vi=vim' >> /etc/profile
echo 'sudo su -' >> /home/$USER/.bashrc
curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
wget https://github.com/andreazorzetto/yh/releases/download/v0.4.0/yh-linux-amd64.zip
unzip yh-linux-amd64.zip
mv yh /usr/local/bin/
생성된 kops instance에 접속하여 확인 ⬇️
[root@kops ~]# kubectl version --client=true -o yaml | yh
clientVersion:
buildDate: "2023-02-22T13:39:03Z"
compiler: gc
gitCommit: fc04e732bb3e7198d2fa44efa5457c7c6f8c0f5b
gitTreeState: clean
gitVersion: v1.26.2
goVersion: go1.19.6
major: "1"
minor: "26"
platform: linux/amd64
kustomizeVersion: v4.5.7
[root@kops ~]# kops version
Client version: 1.25.3 (git-v1.25.3)
kops cluster 설치 🔗
🧑💻 설치에 앞서 cloud dns에 public으로 dns zone이 생성되어있어야 한다.
# create kops config storage
[root@kops ~]# gcloud storage buckets create gs://nyoung-k8s --location=asia-northeast3
Creating gs://nyoung-k8s/...
# set environment variables
[root@kops ~]# export REGION=asia-northeast3
[root@kops ~]# export KOPS_CLUSTER_NAME=test.nyoung.xyz
[root@kops ~]# export KOPS_STATE_STORE=gs://nyoung-k8s
[root@kops ~]# export PROJECT=`gcloud config get-value project`
[root@kops ~]# echo 'export REGION=asia-northeast3' >>~/.bashrc
[root@kops ~]# echo 'export KOPS_CLUSTER_NAME=test.nyoung.xyz' >>~/.bashrc
[root@kops ~]# echo 'export KOPS_STATE_STORE=gs://nyoung-k8s' >>~/.bashrc
[root@kops ~]# echo 'export PROJECT=`gcloud config get-value project`' >>~/.bashrc
# create cluster
# sa권한은 editor와 storage object admin를 부여함
[root@kops ~]# kops create cluster --name=$KOPS_CLUSTER_NAME --networking=kubenet \
--cloud=gce --master-size=e2-medium --node-size=e2-medium --node-count=2 \
--zones="$REGION"-a --state=$KOPS_STATE_STORE --project=$PROJECT -y \
--gce-service-account="kops-855@$PROJECT_ID.iam.gserviceaccount.com" \
--api-loadbalancer-type=public
.
.
.
Cluster is starting. It should be ready in a few minutes.
cluster가 시작한다는 메세지를 보고 cloud dns에 레코드 추가가 필요하다. 원래는 자동등록되는 것이 맞지만.. gcp는 api.master-public-name이 등록되어있지 않았다. 미리 만들어 둔 cloud dns를 보면 내부용 api가 추가된 것을 확인 할 수 있다. validate을 위해, master node의 lb ip와 함께 a 레코드로 추가해준다.
# validate
# master node가 health check에 통과되기까지 조금 기다려야한다.
[root@kops ~]# kops validate cluster
Validating cluster test.nyoung.xyz
I0310 01:02:47.095337 2309 gce_cloud.go:295] Scanning zones: [asia-northeast3-a asia-northeast3-b asia-northeast3-c]
INSTANCE GROUPS
NAME ROLE MACHINETYPE MIN MAX SUBNETS
master-asia-northeast3-a Master e2-medium 1 1 asia-northeast3
nodes-asia-northeast3-a Node e2-medium 2 2 asia-northeast3
NODE STATUS
NAME ROLE READY
master-asia-northeast3-a-pd4x master True
nodes-asia-northeast3-a-4xqs node True
nodes-asia-northeast3-a-b2jd node True
Your cluster test.nyoung.xyz is ready
확인 및 테스트
설치가 완료되어 validate까지 통과 되었으니 내부 확인 후 테스트를 띄워보았다.
# dns 확인
[root@kops ~]# gcloud dns record-sets list -z test-nyoung-xyz --filter="type=A"
NAME TYPE TTL DATA
api.test.nyoung.xyz. A 300 34.64.90.59
api.internal.test.nyoung.xyz. A 60 10.0.16.4
kops-controller.internal.test.nyoung.xyz. A 60 10.0.16.4
# cluster 확인
[root@kops ~]# kops get cluster
NAME CLOUD ZONES
test.nyoung.xyz gce
[root@kops ~]# kubectl cluster-info
Kubernetes control plane is running at https://api.test.nyoung.xyz
CoreDNS is running at https://api.test.nyoung.xyz/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
# node 확인
[root@kops ~]# kops get ig
NAME ROLE MACHINETYPE MIN MAX ZONES
master-asia-northeast3-a Master e2-medium 1 1 asia-northeast3-a
nodes-asia-northeast3-a Node e2-medium 2 2 asia-northeast3-a
[root@kops ~]# kubectl get no
NAME STATUS ROLES AGE VERSION
master-asia-northeast3-a-pd4x Ready control-plane 7m55s v1.25.7
nodes-asia-northeast3-a-4xqs Ready node 6m41s v1.25.7
nodes-asia-northeast3-a-b2jd Ready node 6m41s v1.25.7
# kubenetes compoenets pod들도 잘 떠있는 것을 확인 가능
[root@kops ~]# kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
NAME IP STATUS
cloud-controller-manager-p6h2c 10.0.16.4 Running
coredns-5687f9bc7f-btxcr 100.96.2.2 Running
coredns-5687f9bc7f-vmqjw 100.96.1.2 Running
coredns-autoscaler-975545559-8kb4l 100.96.1.3 Running
dns-controller-6d6bfdfb95-6t4gk 10.0.16.4 Running
etcd-manager-events-master-asia-northeast3-a-pd4x 10.0.16.4 Running
etcd-manager-main-master-asia-northeast3-a-pd4x 10.0.16.4 Running
kops-controller-946pz 10.0.16.4 Running
kube-apiserver-master-asia-northeast3-a-pd4x 10.0.16.4 Running
kube-controller-manager-master-asia-northeast3-a-pd4x 10.0.16.4 Running
kube-proxy-master-asia-northeast3-a-pd4x 10.0.16.4 Running
kube-proxy-nodes-asia-northeast3-a-4xqs 10.0.16.2 Running
kube-proxy-nodes-asia-northeast3-a-b2jd 10.0.16.3 Running
kube-scheduler-master-asia-northeast3-a-pd4x 10.0.16.4 Running
# 테스트 pod, svc 생성
[root@kops ~]# curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/1/mario.yaml
[root@kops ~]# kubectl apply -f mario.yaml
deployment.apps/mario created
service/mario created
[root@kops ~]# kubectl get deploy,svc,ep mario
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mario 1/1 1 1 3m27s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mario LoadBalancer 100.70.23.173 34.64.179.13 80:31077/TCP 3m27s
NAME ENDPOINTS AGE
endpoints/mario 100.96.2.3:8080 3m26s
🤗
external dns 🔗
스터디 시간에 애드온으로 있는 external dns를 봤는데, 너무 편리하고 좋아보여서 gcp에서도 해보고 싶었다. 문서에는 gke에서 적용하는 것 말고는 못찾겠어서, 그냥 해봤는데 됐다..! aws에서는 master와 node instance profiles에게 dns record set에 대한 change, list, get 권한을 추가해주었기 때문에, gcp에서도 각 노드들이 가지고 있는 service account의 권한이 있으면 될 것 같다. 현재 service account은 editor로 dns record set에 대해 읽거나 변경이 가능하여 다른 권한은 추가하지 않았다.
# cluster config에서 spec 하위에 externalDns를 추가
[root@kops ~]# kops edit cluste
spec:
externalDns:
provider: external-dns
# update
[root@kops ~]# kops update cluster --yes
[root@kops ~]# kops rolling-update cluster
# lb에 external dns annotation 추가
[root@kops ~]# kubectl annotate service mario "external-dns.alpha.kubernetes.io/hostname=mario.$KOPS_CLUSTER_NAME"
# 확인
[root@kops ~]# dig +short mario.$KOPS_CLUSTER_NAME
34.64.179.13
[root@kops ~]# echo -e "Maria Game URL = http://mario.$KOPS_CLUSTER_NAME"
Maria Game URL = http://mario.test.nyoung.xyz
# cloud dns를 확인해보면 추가된 것을 확인 가능
[root@kops ~]# gcloud dns record-sets list -z test-nyoung-xyz --filter="type=A"
NAME TYPE TTL DATA
api.test.nyoung.xyz. A 300 34.64.116.234
api.internal.test.nyoung.xyz. A 60 10.0.16.4
kops-controller.internal.test.nyoung.xyz. A 60 10.0.16.4
mario.test.nyoung.xyz. A 300 34.64.179.13
🚧 해당 테스트를 진행하며 마주했던 오류들
모든 오류가.. cluster 생성하면서 발생했었다.. .😇
오류 1 Error: error populating configuration: error fetching network “2-nyoung-xyz”: googleapi: Error 400: Invalid value for field ‘network’: ‘2-nyoung-xyz’. Must be a match of regex ‘a-z?|[1-9][0-9]{0,19}’, invalid
▶️ 맨 앞이 문자로 지정되어있어서 dns는 숫자로 시작하면 안된다. dns 변경..
오류2 Error: error running tasks: deadline exceeded executing task ProjectIAMBinding/serviceaccount-nodes. Example error: error updating IAM for project $PROJECT_ID: googleapi: Error 403: Policy update access denied., forbidden
▶️ –gce-service-account: …if not set, VMs will run as default compute service account. 라고 되어있지만.. 보면 instance를 만들면서 처음보는 service account로 지정해버린다..
오류3 error running task “ServiceAccount/shared” (3m44s remaining to succeed): unexpected format for ServiceAccount email “default_compute_engine_sa@developer.gserviceaccount.com”
▶️ 오류2와 같은 상황에서 compute engine default sa로 지정하고 생성하려는 형식이 다르다고 오류가 났다. 여기도 오류1처럼 sa에 대한 형식을 지정한거같은데, 그게 아무래도 문자열@project_id.iam.gserviceaccount.com 인 것 같다. 새로 만들어서 생성 시 이상없이 잘된다.
오류4 unexpected error during validation: unable to resolve Kubernetes cluster API URL dns: lookup api.test.nyoung.xyz on 169.254.169.254:53: no such host
▶️ validate 하려는데 api.nyoung.xyz가 없다하여 직접 등록함
오류5 설치 후 아무리 기다려도 master node의 health check가 통과되지 않아 로그를 확인해봤다. got error running nodeup (will retry in 30s): error loading Cluster “gs://nyoung-k8s/test.nyoung.xyz/cluster-completed.spec”: error reading gs://nyoung-k8s/test.nyoung.xyz/cluster-completed.spec: googleapi: got HTTP response code 403 with body: kops-855@$PROJECT_ID.iam.gserviceaccount.com does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist).
▶️ storage.objects.get 권한 필요. 참고로 editor에는 없음..
⛔️ 만약, 배포하다 오류가 난다면 하나씩 삭제를 해야한다.
- kops delete cluster –yes 시도 (잘 지워진 것처럼 나와도 아래 절차를 확인해야함)
- gce instance group, template, vpc 삭제
- storage bucket 비우기
- cloud dns a 레코드 삭제
참고