..

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

🤗

1-1

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

1-2

# 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에는 없음..

⛔️ 만약, 배포하다 오류가 난다면 하나씩 삭제를 해야한다.

  1. kops delete cluster –yes 시도 (잘 지워진 것처럼 나와도 아래 절차를 확인해야함)
  2. gce instance group, template, vpc 삭제
  3. storage bucket 비우기
  4. cloud dns a 레코드 삭제

참고