MoreLean

Kubernetes for M1 with Docker Desktop 로컬 환경 설정 본문

Programmer/Kubernetes

Kubernetes for M1 with Docker Desktop 로컬 환경 설정

judeKim' 2022. 1. 8. 23:43
반응형

목적

M1 (Apple Silicon) 맥북에서 Kubernetes(이하 k8s)의 로컬 개발 환경을 구축하기 위한 방법을 찾기 위해 정리를 시작하였습니다. 

Docker Desktop의 라이선스 정책이 곧 변경된다고 알고 있지만, 익숙하지 않은 환경을 기반으로 하기에는 너무 많은 삽질이 필요할 듯하여 일단 한 사이클을 진행해보고 다른 방법을 시도해보려 합니다. 


Docker Desktop 설치

Docker Deskop은 이미 M1 용으로 나와 있어서 손쉽게 설치가 가능합니다. 

https://www.docker.com/products/docker-desktop

 

Docker Desktop for Mac and Windows | Docker

Learn why Docker Desktop is the preferred choice for millions of developers building containerized applications. Download for Mac or Windows.

www.docker.com

사이트 접속해서 `Mac With Apple Chip` 을 눌러 다운로드하여 설치하면 됩니다. 

DMG 포맷이므로 실행하면 이렇게 간단히 Drag & Drop으로 설치가 가능합니다. 


k8s 설치 

Docker Desktop을 통해서 손쉽게 설치가 가능합니다. 

Docker 실행 후 상단 Docker 아이콘을 눌러 환경설정으로 들어가면 Kubernetes 메뉴에서 Enable Kubernetes를 체크하고 Apply & Restart 클릭하여 설치가 가능합니다. 

설치하는데 다소 시간이 걸리고, 정상적이라면 위의 이미지 하단처럼 쿠버네티스 아이콘의 영역이 초록색으로 바뀝니다. 

이제 설치는 완료하였습니다. 

 

k8s 사용하기

k8s는 kubectl이라는 중요 콘솔 명령어(CLI)를 활용하여 터미널로 관리할 수 있습니다. 

간단히 몇 가지 명령어를 실행해보겠습니다. 

# kubectl 의 버전 확인 
# Client 및 Server 의 버전을 확인할 수 있습니다. - Server가 보이지 않는다면 kubernetes가 제대로 설치되지 않거나 실행되지 않은 것이니 docker를 실행하여 상태를 확인해주세요. 
$> kubectl version 
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:33:37Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"darwin/arm64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.4", GitCommit:"b695d79d4f967c403a96986f1750a35eb75e75f1", GitTreeState:"clean", BuildDate:"2021-11-17T15:42:41Z", GoVersion:"go1.16.10", Compiler:"gc", Platform:"linux/arm64"}

# 클러스터 정보 확인 
# k8s의 컨트롤 패널의 정보를 가져올 수 있는 endpoint를 알려줍니다. 
# kubernetes.docker.internal 은 k8s 설치시 hosts 파일에 자동으로 등록된 정보입니다. 127.0.0.1 kubernetes.docker.internal
$> kubectl cluster-info
Kubernetes control plane is running at https://kubernetes.docker.internal:6443
CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

# 노드 정보 확인
$> kubectl get nodes
NAME             STATUS   ROLES                  AGE   VERSION
docker-desktop   Ready    control-plane,master   16m   v1.22.4

 

간단한 샘플 앱(googles-samples/kubernetes-bootcamp)을 설치해보겠습니다. 

# google 이 제공하는 샘플 앱을 설치합니다. 
$> kubectl create deployment k8s-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
deployment.apps/k8s-bootcamp created

# 어떤 변화가 있었는지 확인해봅니다. 
$> kubectl get -o wide
NAME           READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS            IMAGES                                         SELECTOR
k8s-bootcamp   1/1     1            1           85s   kubernetes-bootcamp   gcr.io/google-samples/kubernetes-bootcamp:v1   app=k8s-bootcamp

# 배포가되었고, 1개(default)중에 1개가 성공적으로 떠 있는 상태이고, docker image 정보와 이 앱을 선택하기 위한 label정보(selector)인 app=k8s-bootcamp 로 설정되었습니다. 
# 좀더 구체적인 pod 정보를 확인해보겠습니다. 
$> kubectl get pods -o wide
NAME                           READY   STATUS    RESTARTS   AGE    IP         NODE             NOMINATED NODE   READINESS GATES
k8s-bootcamp-85d87c8fd-49kqx   1/1     Running   0          4m8s   10.1.0.6   docker-desktop   <none>           <none>
# name 중 k8s-bootcamp 는 앞서 deployment의 이름  - 85d87c8fd (deployments정보) - 49kqx(random pod 정보) 로 구성되며 IP및 현재 배포된 서버(node)의 정보를 알 수 있습니다.

앱을 k8s에 설치는 했지만 (기본적으로) 네트워크가 private 한 상태로 고립되어 있어서 접근은 불가능합니다. 

pod에 대한 컨트롤을 직접 하기 위해서 다음과 같은 명령어 등을 사용할 수 있습니다. 

# pods에 대한 세부 정보 - 이벤트 로그 확인 가능
$> kubectl describe pods

# 자주 쓰는 POD_NAME은 변수를 만들어 활용하는 것이 좋다.
$> export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
echo Name of the Pod: $POD_NAME

# 콘솔에 출력된 로그 정보 보기
$> kubectl logs ${POD_NAME}
Kubernetes Bootcamp App Started At: 2022-01-08T05:39:39.384Z | Running On:  k8s-bootcamp-85d87c8fd-49kqx

# pod에 명령어 실행하기
$> kubectl exec ${POD_NAME} -- env
HOME=/root
NODE_VERSION=6.3.1
NPM_CONFIG_LOGLEVEL=info
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
HOSTNAME=k8s-bootcamp-85d87c8fd-49kqx
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Pod 에 접속하기 
$> kubectl exec -ti ${POD_NAME} -- bash
docker $> cat server.js

# 로컬에서 호출시에는 접속이 가능합니다. 
docker $> curl localhost:8080
Hello Kubernetes bootcamp! | Running on: k8s-bootcamp-85d87c8fd-49kqx | v=1
docker $> exit

# Service 정보 조회 
$> kubectl get services 
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   129m

# k8s-bootcamp 이름의 deployment를 8080으로 노출시킨다. 
$> kubectl expose deployment/k8s-bootcamp --type="NodePort" --port 8080

# 다시 조회
$> kubectl get services 
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
k8s-bootcamp   NodePort    10.97.119.147   <none>        8080:31271/TCP   9s
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP          130m

# 31271port로 접속이 이제 가능해졌다.
$> curl localhost:31271
Hello Kubernetes bootcamp! | Running on: k8s-bootcamp-85d87c8fd-49kqx | v=1

# 세부 service 정보 조회
$> kubectl describe services/k8s-bootcamp
Name:                     k8s-bootcamp
Namespace:                default
Labels:                   app=k8s-bootcamp
Annotations:              <none>
Selector:                 app=k8s-bootcamp
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.97.55.141
IPs:                      10.97.55.141
LoadBalancer Ingress:     localhost
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  32063/TCP
Endpoints:                10.1.0.6:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# 중요한 개념인 label 로 조회해보자

$> kubectl get pods -l app=k8s-bootcamp
NAME                           READY   STATUS    RESTARTS   AGE
k8s-bootcamp-85d87c8fd-49kqx   1/1     Running   0          115m
# 물론 pods 외에 service 도 조회 가능
$> kubectl get services -l app=k8s-bootcamp
NAME           TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
k8s-bootcamp   NodePort   10.97.55.141   <none>        8080:32063/TCP   3m46s

# label을 추가할 수는 없나? 당연히 있다. 
$> kubectl label pods ${POD_NAME} version=v1
$> kubectl describe pods 
Labels:       app=k8s-bootcamp
              pod-template-hash=85d87c8fd
              version=v1
              
# 라벨 삭제도 가능하다. key와 뒤에 -(minus) 를 붙여주면 된다. 
$> kubectl label pods ${POD_NAME} version-

 

이것저것 많이 해봤지만 궁극적으로 `curl localhost:31271` 을 통해서 접속이 가능해졌습니다. 

하지만, 서비스에서 사용하는 well known 포트를 통해서 접속하는 것을 확인하고 싶었는데, 이 부분은 진행이 어려웠습니다. 

우선 ingress라는 것을 통해서 외부에서 서비스가 가능하도록 만들어 줄 수 있다고 들었고, 이걸 nginx ingress를 통해서 진행이 가능하다고 찾았으나, 실제로 해보니 정상적으로 동작하지 않았습니다. 


Kubernetes GUI tool 설치

이것저것 확인할 때 CLI 보단 GUI가 필요하다고 생각했고, 그래서 k8s GUI툴을 찾아봤습니다. 

쿠버네티스에서 제공하는 Dashboard가 있어 이걸 설치해봤습니다.  

https://kubernetes.io/ko/docs/tasks/access-application-cluster/web-ui-dashboard/

 

쿠버네티스 대시보드를 배포하고 접속하기

웹 UI(쿠버네티스 대시보드)를 배포하고 접속한다.

kubernetes.io

설치는 비교적 간단합니다. 

$> kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml

하지만 접속을 위해선 proxy 설정이 필요합니다. 

$> kubectl proxy

이 명령을 실행하게 되면 터미널을 하나 사용 못하게 되니 탭을 추가해서 콘솔을 사용하거나, & 를 붙여 detached 모드(백그라운드 실행)로 띄워줍니다. 

그리고, 브라우저에서 다음 주소를 접속합니다. 

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login

하지만 아래 이미지에서 확인할 수 있듯이 인증정보를 요구합니다. 

뭔지 잘 모르지만, Authentication 링크에서 추가 적인 정보를 얻을 수 있을 것 같다고 생각했지만, 오산이었습니다. 

https://kubernetes.io/docs/reference/access-authn-authz/authentication/

 

Authenticating

This page provides an overview of authenticating. Users in Kubernetes All Kubernetes clusters have two categories of users: service accounts managed by Kubernetes, and normal users. It is assumed that a cluster-independent service manages normal users in t

kubernetes.io

 

대시보드 문서의 "대시보드 UI 접근"섹션에 "샘플 사용자 만들기" 링크가 존재하고, 결과적으로 그 문서를 참고하여 해결하였습니다. 

https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md

 

GitHub - kubernetes/dashboard: General-purpose web UI for Kubernetes clusters

General-purpose web UI for Kubernetes clusters. Contribute to kubernetes/dashboard development by creating an account on GitHub.

github.com

요약하면 

  • Service Account 생성
  • ClusterRoleBinding 생성

이 두 가지의 과정을 거쳐야 요구하는 Bearer 토큰을 얻을 수 있었습니다. 

우선 Service Account 생성을 위한 아래 내용의 dashboard-adminuser.yaml을 생성합니다. 그리고, kubectl apply -f 명령어를 통해 Service Account를 생성합니다. 

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
# Service Account 를 생성
$> kubectl apply -f dashboard-adminuser.yaml
serviceaccount/admin-user created

ClusterRoleBinding을 위한 아래 내용의 cluster-role-binding.yaml 파일을 생성하고, kubectl apply -f 명령어를 통해 ClusterRoleBinding을 생성합니다. 

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
# ClusterRoleBinding 
$> kubectl apply -f cluster-role-binding.yaml
clusterrolebinding.rbac.authorization.k8s.io/admin-user created

이제 API를 호출하고 호출한 API의 결과에서 Token을 얻을 수 있습니다. 

$> kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"

콘솔상에 출력된 긴 문자를 복사해서 아까 Token에 입력하면 아래와 같은 Dashboard를 확인할 수 있습니다. 

이제 다시 설정을 위해 돌아와 보겠습니다. 


다시 Ingress 설정

이제 외부에서 접근할 수 있도록 재시도록 해보겠습니다. 

궁극적으로는 curl http://first.k8s.io:8080으로 접속을 성공하는 것이었습니다만, 

하지만 이 부분은 크게 2가지의 문제가 있습니다. 
첫째, 해당 도메인을 접속하려면 hosts의 별도 등록이 필요합니다. 특이하게도 k8s.localdev.me의 도메인은 그냥 사용이 가능했습니다. ( private DNS 서버가 존재할 것이라 생각했지만 이렇게 파기엔 너무 광범위해져 다음으로 미루어야.. )
둘째, 8080은 nginx 가 사용하지 않는 포트입니다. ingress는 아래 정의에서도 나와있지만, http 및 https 경로를 노출한다고 되어 있습니다. 즉, 80 및 443 포트만 사용할 수 있습니다.  

위의 사항을 기반으로, 목표를 바꾸어 http://k8s-bootcamp.localdev.me로 접속을 시도해보겠습니다. 

 

우선 Ingress가 정확하게 뭘 의미하는지 알아보겠습니다. 

https://kubernetes.io/ko/docs/concepts/services-networking/ingress/

 

인그레스(Ingress)

FEATURE STATE: Kubernetes v1.19 [stable] 클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리함. 인그레스는 부하 분산, SSL 종료, 명칭 기반의 가상 호스팅을 제공

kubernetes.io

위의 문서에 따르면, 인그레스는 클러스터 외부에서 클러스터 내부 서비스로 HTTP와 HTTPS 경로를 노출하고, 트래픽 라우팅은 인그레스 리소스에 정의된 규칙에 의해 컨트롤된다고 나와있습니다.  

저위의 설명은 Local 컴퓨팅 환경에 대한 가이드가 아닌 듯 보여

https://kubernetes.io/ko/docs/tasks/access-application-cluster/ingress-minikube/

 

NGINX 인그레스(Ingress) 컨트롤러로 Minikube에서 인그레스 설정하기

인그레스는 클러스터의 서비스에 대한 외부 액세스를 허용하는 규칙을 정의하는 API 객체이다. 인그레스 컨트롤러는 인그레스에 설정된 규칙을 이행한다. 이 페이지에서는 HTTP URI에 따라 요청을

kubernetes.io

여기를 참조하여 진행해봤습니다만, 일단 minikube는 Apple Silicon 칩을 지원하지 않습니다. 시간이 한참 지나서 혹시나 하는 마음에 희망을 걸고 알아봤지만, 현재(2022-01-08)도 지원하지 않는다는 것을 확인했습니다.  다시 확인해보니 지원하지 않는 것은 아닙니다. 
다만, minikube 에서 정상적으로 외부 네트워크 접속을 확인하기 어려움이 있었습니다. ( 제가 방법을 몰라서 일 수도.. )

하지만 위의 방식에서 nginx-ingress를 활용했다는 얘기가 있어서 설치를 진행했습니다. 

직접 참조한 문서는 https://kubernetes.github.io/ingress-nginx/deploy/입니다. 

 

Installation Guide - NGINX Ingress Controller

Installation Guide There are multiple ways to install the NGINX ingress controller: with Helm, using the project repository chart; with kubectl apply, using YAML manifests; with specific addons (e.g. for minikube or MicroK8s). On most Kubernetes clusters,

kubernetes.github.io

# nginx-ingress controller 설치
$> kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yaml
# ingress 를 추가합니다. 
$> kubectl create ingress k8s-bootcamp --class=nginx --rule="k8s.localdev.me/*=k8s-bootcamp:8080"
ingress.networking.k8s.io/k8s-bootcamp created

$> curl k8s.localdev.me
Hello Kubernetes bootcamp! | Running on: k8s-bootcamp-85d87c8fd-47hqs | v=1

# ingress를 하나 더 추가합니다. (k8s-bootcamp2)
# first.k8s.io 도메인으로 접속이 되도록 설정합니다. 
# 사전에 hosts 파일에는 등록해두었습니다. 127.0.0.1 first.k8s.io
$> kubectl create ingress k8s-bootcamp2 --class=nginx --rule="first.k8s.io/*=k8s-bootcamp:8080"
ingress.networking.k8s.io/k8s-bootcamp2 created

$> curl first.k8s.io
Hello Kubernetes bootcamp! | Running on: k8s-bootcamp-85d87c8fd-47hqs | v=1

위의 create ingress 명령어에서 --class=nginx 의 class 설정이 있어야 정상적으로 동작합니다. 

https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/

 

Basic usage - NGINX Ingress Controller

Basic usage - host based routing ingress-nginx can be used for many use cases, inside various cloud provider and supports a lot of configurations. In this section you can find a common usage scenario where a single load balancer powered by ingress-nginx wi

kubernetes.github.io

 

몇 번의 삽질 끝에 일단 완료했네요. 

이렇게 정리하니 대략적으로 어떤 형상인지 머릿속으로 그려지기 시작하네요. ( Active Learning!! )

반응형