1. K3s

작년인가? 확실친 않은데 k8s의 경량화된 버전인 k3s가 나왔고..한번 테스트 해봐야지 하다가 게으름을 이기지 못해 안하다가
이번에 레거시 UtoL 전환 프로젝트를 하고 있어서 겸사겸사 쿠버네티스에 디플로이까지 해봤다.

쿠버네티스라는 플랫폼 자체가 좀 더 빠르게 개발하고 좀 더 빠르게 배포하자 라는 그 목적과 취지에 부합하는 컨테이너 오케스트레이션 플랫폼인 것은 맞으나
러닝커브가 꽤 있는 편이므로 그나마 좀더 간편한 K3s로 로컬 Macbook 에서 구성해본 내역을 포스팅 한다.
Kubernetes에 대해 아예 모르는 사람들은 차라리 minikube로 시작해 보는게 어떨까 싶다.

Kubernetes를 줄여서 K8s라고 부르는데 그것보다 좀더 경량화 된 버전을 Rancher에서 개발한 것이 K3s 인 것으로 보인다.
K3s 공식 홈에서도 "The certified Kubernetes distribution built for IoT & Edge computing" 라고 표현 하고 있다.

경량화된 Kubernetes라고는 하는데 안에를 들여다 보면 Master/Worker Node에 있을 건 다 있다.
차이점이..etcd를 사용하지 않고 sqlite를 사용하는 것 같다.

2. VM deployments used multipass on mac

K3s Master 노드(1)와 Worker 노드(2), 그리고 컨테이너 이미지들을 저장할 프라이빗 레지스트리(1)까지 총 4대의 노드가 필요한데 
KVM이나 버추얼박스 같은 툴을 직접 설치하여 사용하지 않고 Multipass를 사용 하였다. 

구글링 하다보면 k3s를 배포할때 multipass를 이용하는 포스팅들을 찾아볼 수 있는데 현재와 좀 안맞는 내용들도 많고
컨테이너화 된 어플리케이션을 배포할때 도커허브에 있는 이미지를 가져와 배포하는 시나리오가 대부분 이었다.

나같은 경우 Docker Registry 노드를 따로 구성해 K3s에서 Private Registry에 있는 이미지를 가져올 수 있게 구성 했는데
여기에서 삽질을 좀 하게 됐다. K3s + Registry 이 두 가지의 조합으로 구성할 계획이라면 이 포스팅이 많이 도움이 되지 않을까 싶다.

multipass는 Canonical에서 배포하는 VM 설치 도구로서 CLI 단 몇 줄 만으로 정말 빠르게 VM을 배포 할수 있게 해주며 
Mac 뿐만 아니라 Windows도 지원한다.
Linux 환경에서는 KVM, Windows에서는 Hyper-v, MacOS에선 HyperKit을 사용하여 VM을 생성, 관리하게 된다.

# brew search multipass
# brew cask install multipass

# multipass launch --name k3s-master --cpus 1 --mem 1G --disk 5G
# multipass launch --name k3s-worker1 --cpus 1 --mem 1G --disk 5G
# multipass launch --name k3s-worker2 --cpus 1 --mem 1G --disk 5G
# multipass launch --name registry --cpus 1 --mem 1G --disk 10G
# multipass list
# multipass exec k3s-master -- /bin/bash -c "curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE="644" sh -"
# K3S_NODEIP_MASTER="https://$(multipass info k3s-master | grep "IPv4" | awk -F' ' '{print $2}'):6443"
# K3S_TOKEN="$(multipass exec k3s-master -- /bin/bash -c "sudo cat /var/lib/rancher/k3s/server/node-token")"
# multipass exec k3s-worker1 -- /bin/bash -c "curl -sfL https://get.k3s.io | K3S_TOKEN=${K3S_TOKEN} K3S_URL=${K3S_NODEIP_MASTER} sh -"
# multipass exec k3s-worker2 -- /bin/bash -c "curl -sfL https://get.k3s.io | K3S_TOKEN=${K3S_TOKEN} K3S_URL=${K3S_NODEIP_MASTER} sh -"

10줄의 커맨드라인으로 VM 생성 부터 K3s 배포까지 완료 되었다.

<!-- 
Local MacOS, K3s all nodes, registry 노드에 모두 아래 내용을 삽입 한다.
k3s, registry의 ip는 아래와 다를 수 있으니 multipass list로 확인 되는 정보를 삽입 하면 된다.
-->
$ sudo vi /etc/hosts
192.168.64.4 k3s-master k3s-container.info
192.168.64.5 k3s-worker1
192.168.64.6 k3s-worker2
192.168.64.8 registry

k3s 노드 정보를 확인 한다.
$ multipass exec k3s-master kubectl get nodes

3. Copy multipass vm kubectl config locally

<!-- 
계속 multipass cli를 사용하기는 귀찮으므로 k3s master node에서 k3s.yaml 파일을 로컬 Mac으로 가져온다.
kubectl은 미리 Mac에 설치 되어 있었고, 없다면 설치 해야 한다.
-->
$ multipass copy-files k3s-master:/etc/rancher/k3s/k3s.yaml ${HOME}/.kube/k3s.yaml
$ sed -ie s,https://127.0.0.1:6443,${K3S_NODEIP_MASTER},g ${HOME}/.kube/k3s.yaml
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml get nodes

4. Configure cluster node roles and taint

$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml label node k3s-master node-role.kubernetes.io/master=""
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml label node k3s-worker1 node-role.kubernetes.io/node=""
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml label node k3s-worker2 node-role.kubernetes.io/node=""
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml taint node k3s-master node-role.kubernetes.io/master=effect:NoSchedule
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml get nodes -o wide

5. Install Registry

<!-- 
registry node에 docker registry를 설치하는 과정이다. 
k3s에서 프라이빗 레지스트리에서 이미지를 가져오게 하려면 Mirrors 설정을 해줘야 하는데 아래에서 설명 하겠다.
k3s nodes, registry node 접속에 사용될 ssh key를 원하는 위치에 카피 해 둔다.
-->
$ mkdir multipass-ssh;cd multipass-ssh
$ sudo cp /var/root/Library/Application\ Support/multipassd/ssh-keys/id_rsa id_rsa


<!-- 
registry node에 접속하고 docker, registry 차례대로 설치한다.
-->
$ ssh -i id-rsa ubuntu@registry
$ sudo su - 
# apt update
# apt install apt-transport-https ca-certificates curl software-properties-common
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
# apt update
# apt-cache policy docker-ce
# apt install docker-ce

# docker pull registry:2.6.2
# docker run -d -it --name docker-registry -p 5000:5000 registry --restart=always
# curl -X GET http://localhost:5000/v2/_catalog

<!-- 
configure insecure registry, 안해도 될거 같은데..어플리케이션 배포할때 레지스트리 관련 삽질의 흔적이다.
그냥 해두길..
-->
# docker info | grep -i insecure -A4
# vi /etc/docker/daemon.json
{
        "insecure-registries": ["registry:5000"]
}

<!-- restart docker -->
# systemctl restart docker
# docker info | grep -i insecure -A4


<!-- Registry 인증서 진행 registry:2 -> registry:2.6.2로 바꿔야 가능 -->
# apt install apache2-utils
# mkdir certs; cd certs; 
# openssl genrsa -out server.key 2048
# openssl req -new -key server.key -out server.csr
# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
# cp server.crt /usr/share/ca-certificates/
# echo "server.crt" | sudo tee -a /etc/ca-certificates.conf
$ update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
# systemctl restart docker

# docker stop docker-registry && docker rm docker-registry
# mkdir auth && docker run --entrypoint htpasswd registry:2.6.2 -Bbn testuser testpassword > auth/htpasswd
# docker run -d -p 5000:5000 --restart=always --name docker-registry \
-v /root/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/server.key \
-v /root/certs/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2.6.2


<!-- 
Registry 노드에서 server.crt, key, csr 3가지 인증서 파일을 /home/ubuntu에 복사 해둔다. 
이 파일들을 다른 k3s 노드에 복사 후 업데이트 하며, 로컬 Mac엔 레지스트리에 이미지를 푸시 해야 하므로 
키가 필요한데 server.crt만 있으면 된다.
-->
$ cp server.* /home/ubuntu
$ chown ubuntu:ubuntu /home/ubuntu/server.*

<!-- Local Mac에서 
Registry 노드의 server.crt를 mac에 복사 후 k3s 노드에 카피, mac에는 crt만 있으면 되고 k3s노드에는 3개 모두 복사 mirrors 때문-->
$ mkdir registry-cert;cd registry-cert
$ multipass copy-files registry:/home/ubuntu/server.crt server.crt
$ multipass copy-files registry:/home/ubuntu/server.key server.key
$ multipass copy-files registry:/home/ubuntu/server.csr server.csr

$ multipass copy-files server.crt k3s-master:/home/ubuntu/server.crt
$ multipass copy-files server.key k3s-master:/home/ubuntu/server.key
$ multipass copy-files server.csr k3s-master:/home/ubuntu/server.csr
$ multipass copy-files server.crt k3s-worker1:/home/ubuntu/server.crt
$ multipass copy-files server.key k3s-worker1:/home/ubuntu/server.key
$ multipass copy-files server.csr k3s-worker1:/home/ubuntu/server.csr
$ multipass copy-files server.crt k3s-worker2:/home/ubuntu/server.crt
$ multipass copy-files server.key k3s-worker2:/home/ubuntu/server.key
$ multipass copy-files server.csr k3s-worker2:/home/ubuntu/server.csr


<!-- 인증서 3개 모든 vm 노드 카피 후 인증서 적용
아래의 내용을 k3s nodes 3개 모두 동일하게 적용 한다.-->
$ ssh -i id-rsa ubuntu@{k3s-master|k3s-worker1|k3s-worker2}
$ sudo su - 
# cd /home/ubuntu/
# chmod 600 server.key
# cp server.crt /usr/share/ca-certificates/
# echo "server.crt" | sudo tee -a /etc/ca-certificates.conf
server.crt
# update-ca-certificates


<!-- Local Mac에서도 가져온 server.crt를 이용해 Registry에 로그인 해보고 이미지를 푸시 한다.-->
$ docker login https://registry:5000
user : testuser
password : testpassword

$ docker tag container:latest registry:5000/container:latest
$ docker push registry:5000/container:latest

$ curl -X GET https://registry:5000/v2/_catalog --user testuser:testpassword  --cacert server.crt
{"repositories":["container"]}

6. vm 모든 노드에 registries.yaml 생성

<!--
k3s master는 /etc/rancher/k3s 폴더가 있지만, worker 노드는 k3s 폴더가 없다..없어서 만들었음.
worker에 없어도 될거 같은데 rancher 메뉴얼에서는 각 노드라고 표현되어 있어서 모든 노드에 생성 
아래 registries.yaml을 k3s nodes 3개 모두 동일하게 만들어 준다.

k3s에서 프라이빗 레지스트리에 존재 하는 이미지를 가져와 배포하게 하려면 이 mirrors 지시자가 필요한데
현재 2020년 7월 기준으로 내가 뭘 잘못한건지 모르겠으나 without TLS 설정은 먹히질 않는다. 어플리케이션 배포 이후 Pods 생성 실패 되는데 
TLS 에러로 거의 하루를 날려 먹었다.
구글링 해보고 K3s 깃헙 이슈를 뒤져봐도 마땅한 해결책이 없었다.
그래서 위에서 Insecure Registry 설정도 해보고 mirrors 설정을 다 해봤지만 TLS 없이는 실패하게 되므로 Registry를 생성할때 꼭 위에서 한 것처럼
TLS와 Auth 설정을 하는 것이 정신건강에 이롭다.

그리고 Documents에 약간 애매모호하게 설명 되어 있는 것들이 있어..
k3s nodes에 server.crt를 update 안해도 될지도 모르는데 난 그냥 했다.
-->
$ vi /etc/rancher/k3s/registries.yaml
mirrors:
  docker.io:
    endpoint:
      - "https://registry.com:5000"
configs:
  "registry:5000":
    auth:
      username: testuser
      password: testpassword
    tls:
      cert_file: /home/ubuntu/server.crt
      key_file: /home/ubuntu/server.key
      ca_file: /home/ubuntu/server.csr

7. restart k3s all nodes on mac

$ multipass exec k3s-master sudo systemctl restart k3s
$ multipass exec k3s-worker1 sudo systemctl restart k3s-agent
$ multipass exec k3s-worker2 sudo systemctl restart k3s-agent
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml get nodes

8. Deploy app on mac

$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml apply -f deployment.yml
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml get events
$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml get pods
NAME                               READY   STATUS    RESTARTS   AGE
k3s-container-577c5b9866-9smbg     1/1     Running   0          79s

9. /etc/hosts ingress 삽입 on mac

<!-- Mac에 k3s master node ip로 원하는 도메인을 하나 잡아주면
매핑된 Ingress로 배포된 어플리케이션을 확인해 볼 수 있다. 위에 hosts 설정에 이미 되어 있다.
-->
192.168.64.4 k3s-container.info

9. 사용한 deployment.yml

샘플이므로 꼭 똑같이 카피해서 사용할 필요는 없다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k3s-container
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: k3s-container
  template:
    metadata:
      labels:
        app: k3s-container
    spec:
      containers:
        - name: k3s-container
          image: registry:5000/container:latest

---
apiVersion: v1
kind: Service
metadata:
  name: k3s-container
  namespace: default
spec:
  ports:
  - name: http
    targetPort: 8080
    port: 80
  selector:
    app: k3s-container

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: k3s-container
  annotations:
    kubernetes.io/ingress.class: "traefik"

spec:
  rules:
  - host: k3s-container.info
    http:
      paths:
      - path: /
        backend:
          serviceName: k3s-container
          servicePort: http

10. 굳이 안 읽어도 되는

2일 동안 테스트 했던 내용인데 막상 정리하고 보니 별거 없다. 항상 그렇지 머
몰랐을 때나 어렵지 반복해서 2~3번 하고, 포스팅 하기 위해 정리 하다 보면 이걸 구현 하는데 이렇게 오래 걸렸어? 하게 된다.

포스팅이 좀 오랜만인데 그동안 온프레미스 레거시 환경에 Bamboo를 가지고 CI/CD 파이프라인을 개발 하는 작업들을 주로 하였다.
이것과 관련된 내용들을 아마 다음 포스팅에서 하게 될 것 같다.

그리고 만약 이것을 보고 따라할 때 registry, mirrors 관련 에러들을 마주하게 되어 구글링을 하다보면 아래와 같이 registry secret을 생성 해야 한다는
글들을 볼 수 있는데 안해도 된다.

$ kubectl --kubeconfig=${HOME}/.kube/k3s.yaml create secret docker-registry docker-registry-login \
--docker-server=registry:5000 \
--docker-username=testuser \
--docker-password=testpassword \
--namespace=default

이거 안해도 되요.

참고한 문서

k3s : k3s doc

k3s deploy on mac : Someone’s blog

k3s running : k3s jp doc

k3s github : k3s github