minikube, kind, multipass, lima 비교


minikube

  • 로컬에서 쿠버네티스(Kubernetes) 클러스터를 쉽게 실행할 수 있게 해주는 도구
  • 다양한 드라이버 지원(Docker, VirtualBox, Hyperkit, KVM 등)
  • 쿠버네티스 학습, 개발, 테스트에 적합
  • 다양한 애드온, 대시보드, 멀티노드 등 실습에 편리한 기능 제공

kind (Kubernetes IN Docker)

  • Docker 컨테이너 위에 쿠버네티스 클러스터를 실행하는 도구
  • 모든 노드(컨트롤 플레인, 워커)가 Docker 컨테이너로 동작
  • CI/CD 환경, 빠른 테스트, GitHub Actions 등에서 많이 사용
  • VM이 아닌 컨테이너 기반이므로 리소스 소모가 적고, 클러스터 생성/삭제가 매우 빠름
  • 네트워크, 스토리지 등 실제 환경과 차이가 있을 수 있음

multipass

  • Canonical(우분투 개발사)에서 만든 경량 VM 관리 도구
  • minikube, kind와 달리 쿠버네티스 전용이 아니라, 다양한 용도의 Ubuntu VM을 빠르게 생성/관리
  • minikube, k3s, microk8s 등 쿠버네티스 환경을 multipass 위에 올려서 사용할 수 있음
  • macOS, Windows, Linux에서 동일한 방식으로 VM 관리 가능

lima (Linux Machines)

  • macOS에서 리눅스 VM을 쉽게 실행할 수 있게 해주는 도구
  • Docker Desktop 없이도 컨테이너, 쿠버네티스 환경을 구축할 수 있음
  • nerdctl, k3s, rancher 등과 연동해 개발 환경 구성에 활용
  • Lima 위에 kind, minikube, k3s 등 다양한 쿠버네티스 환경을 올릴 수 있음

요약 비교

도구목적/특징쿠버네티스 전용실행 방식
minikube로컬 쿠버네티스, 다양한 드라이버 지원OVM/컨테이너
kindDocker 기반 쿠버네티스, 빠른 테스트ODocker 컨테이너
multipass경량 Ubuntu VM 관리XVM
limamacOS 리눅스 VM, 컨테이너 개발환경XVM

minikube 실습 환경 구성


minikube 설치

brew install minikube

minikube 실행

meatsby ~ minikube start
😄  Darwin 15.6.1 (arm64) 의 minikube v1.37.0
  자동적으로 docker 드라이버가 선택되었습니다. 다른 드라이버 목록: virtualbox, ssh
📌  Docker Desktop 드라이버를 루트 권한으로 사용
👍  "minikube" 클러스터의 "minikube" primary control-plane 노드를 시작하는
🚜  기본 이미지 v0.0.48를 가져오는 ...
💾  쿠버네티스 v1.34.0 다운로드 ...
    > preloaded-images-k8s-v18-v1...:  332.38 MiB / 332.38 MiB  100.00% 22.68 M
🔥  docker container (CPUs=2, 메모리=4000MB) 를 생성하는 중 ...
🐳  쿠버네티스 v1.34.0 Docker 28.4.0 런타임으로 설치하는
🔗  bridge CNI (Container Networking Interface) 를 구성하는 중 ...
🔎  Kubernetes 구성 요소를 확인...
 이미지 gcr.io/k8s-minikube/storage-provisioner:v5 사용
🌟  애드온 활성화 : storage-provisioner, default-storageclass
🏄  끝났습니다! kubectl이 "minikube" 클러스터와 "default" 네임스페이스를 기본적으로 사용하도록 구성되었습니다
 
meatsby ~ k get componentstatuses
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-0               Healthy   ok

dashboard 실행

meatsby ~ minikube dashboard
🔌  대시보드를 활성화하는 ...
 이미지 docker.io/kubernetesui/metrics-scraper:v1.0.8 사용
 이미지 docker.io/kubernetesui/dashboard:v2.7.0 사용
💡  Some dashboard features require the metrics-server addon. To enable all features please run:
 
	minikube addons enable metrics-server
 
🤔  Dashboard 상태를 확인 중입니다 ...
🚀  프록시를 시작하는 ...
🤔  Proxy 상태를 확인 중입니다 ...
🎉  Opening http://127.0.0.1:57530/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...

Ingress 실습

# ingress addon 설치
meatsby ~ minikube addons enable ingress
 
💡  ingress is an addon maintained by Kubernetes. For any concerns contact minikube on GitHub.
You can view the list of minikube maintainers at: https://github.com/kubernetes/minikube/blob/master/OWNERS
💡  애드온이 활성화된 "minikube tunnel"을 실행하면 인그레스 리소스를 "127.0.0.1"에서 사용할 있습니다
 이미지 registry.k8s.io/ingress-nginx/controller:v1.13.2 사용
 이미지 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.2 사용
 이미지 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.2 사용
🔎  ingress 애드온을 확인 중입니다 ...
🌟  'ingress' 애드온이 활성화되었습니다
 
# nginx IngressClass 생성된다.
meatsby ~ k get ingressclass
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       9s
 
# ingress-controller 로 nginx 파드가 구동중이다.
meatsby ~ k get pods -n ingress-nginx
NAME                                       READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-77dsb       0/1     Completed   0          14s
ingress-nginx-admission-patch-wgw4h        0/1     Completed   1          14s
ingress-nginx-controller-9cc49f96f-gf9mt   1/1     Running     0          14s
 
# 예제 ingress 를 배포해보자.
meatsby ~ kubectl apply -f https://storage.googleapis.com/minikube-site-examples/ingress-example.yaml
pod/foo-app created
service/foo-service created
pod/bar-app created
service/bar-service created
ingress.networking.k8s.io/example-ingress created
 
# ingress 생성 확인.
meatsby ~ k get ingress -w
NAME              CLASS   HOSTS   ADDRESS   PORTS   AGE
example-ingress   nginx   *                 80      6s
example-ingress   nginx   *       192.168.49.2   80      59s
 
# mac 은 minikube tunnel 을 실행해줘야한다.
meatsby ~ minikube tunnel
  Tunnel successfully started
 
📌  NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...
 
  The service/ingress example-ingress requires privileged ports to be exposed: [80 443]
🔑  sudo permission will be asked for it.
🏃  example-ingress 서비스의 터널을 시작하는 중/
# minikube tunnel 실행 후 새로운 터미널에서 curl 요청을 보내보자.
# bar-app 으로 트래픽이 전달된다.
meatsby ~ curl 127.0.0.1/bar
Request served by bar-app
 
HTTP/1.1 GET /bar
 
Host: 127.0.0.1
Accept: */*
User-Agent: curl/8.7.1
X-Forwarded-For: 10.244.0.1
X-Forwarded-Host: 127.0.0.1
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Scheme: http
X-Real-Ip: 10.244.0.1
X-Request-Id: 8fcbd14bfef458a7f88a9cef50b6698d
X-Scheme: http
 
# foo-app 으로 트래픽이 전달된다.
meatsby ~ curl 127.0.0.1/foo
Request served by foo-app
 
HTTP/1.1 GET /foo
 
Host: 127.0.0.1
Accept: */*
User-Agent: curl/8.7.1
X-Forwarded-For: 10.244.0.1
X-Forwarded-Host: 127.0.0.1
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Scheme: http
X-Real-Ip: 10.244.0.1
X-Request-Id: d1903d0f75a0cb744c3b5d01f08bd419
X-Scheme: http
 
# X-Forwarded-For: 10.244.0.1 가 뭔지 살펴보자.
meatsby ~ minikube node list
minikube	192.168.49.2
 
# bridge 10.244.0.1 에서 Pod 로 전달된다.
meatsby ~ minikube ssh
docker@minikube:~$ ip addr show | grep bridge: -A5
13: bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 66:bc:1e:16:98:64 brd ff:ff:ff:ff:ff:ff
    inet 10.244.0.1/16 brd 10.244.255.255 scope global bridge
       valid_lft forever preferred_lft forever
    inet6 fe80::64bc:1eff:fe16:9864/64 scope link
       valid_lft forever preferred_lft forever

metrics-server 설치

# top 명령을 사용하려면 metrics-server 가 구동중이어야한다.
meatsby ~ k top no
error: Metrics API not available
 
meatsby ~ k top po
error: Metrics API not available
 
# metrics-server addon 설치
meatsby ~ minikube addons enable metrics-server
💡  metrics-server is an addon maintained by Kubernetes. For any concerns contact minikube on GitHub.
You can view the list of minikube maintainers at: https://github.com/kubernetes/minikube/blob/master/OWNERS
 이미지 registry.k8s.io/metrics-server/metrics-server:v0.8.0 사용
🌟  'metrics-server' 애드온이 활성화되었습니다
 
# addon list 확인
meatsby ~ minikube addons list
┌────────────── ┬─────┬──────┬────────────────────┐
         ADDON NAME PROFILE   STATUS               MAINTAINER
├────────────── ┼─────┼──────┼────────────────────┤
 ambassador minikube disabled 3rd party (Ambassador)                 │
 amd-gpu-device-plugin minikube disabled 3rd party (AMD)                        │
 auto-pause minikube disabled minikube
 cloud-spanner minikube disabled Google
 csi-hostpath-driver minikube disabled Kubernetes
 dashboard minikube enabled Kubernetes
 default-storageclass minikube enabled Kubernetes
 efk minikube disabled 3rd party (Elastic)                    │
 freshpod minikube disabled Google
 gcp-auth minikube disabled Google
 gvisor minikube disabled minikube
 headlamp minikube disabled 3rd party (kinvolk.io)                 │
 inaccel minikube disabled 3rd party (InAccel [info@inaccel.com]) │
 ingress minikube enabled Kubernetes
 ingress-dns minikube disabled minikube
 inspektor-gadget minikube disabled 3rd party (inspektor-gadget.io)        │
 istio minikube disabled 3rd party (Istio)                      │
 istio-provisioner minikube disabled 3rd party (Istio)                      │
 kong minikube disabled 3rd party (Kong HQ)                    │
 kubeflow minikube disabled 3rd party
 kubetail minikube disabled 3rd party (kubetail.com)               │
 kubevirt minikube disabled 3rd party (KubeVirt)                   │
 logviewer minikube disabled 3rd party (unknown)                    │
 metallb minikube disabled 3rd party (MetalLB)                    │
 metrics-server minikube enabled Kubernetes
 nvidia-device-plugin minikube disabled 3rd party (NVIDIA)                     │
 nvidia-driver-installer minikube disabled 3rd party (NVIDIA)                     │
 nvidia-gpu-device-plugin minikube disabled 3rd party (NVIDIA)                     │
 olm minikube disabled 3rd party (Operator Framework)         │
 pod-security-policy minikube disabled 3rd party (unknown)                    │
 portainer minikube disabled 3rd party (Portainer.io)               │
 registry minikube disabled minikube
 registry-aliases minikube disabled 3rd party (unknown)                    │
 registry-creds minikube disabled 3rd party (UPMC Enterprises)           │
 storage-provisioner minikube enabled minikube
 storage-provisioner-gluster minikube disabled 3rd party (Gluster)                    │
 storage-provisioner-rancher minikube disabled 3rd party (Rancher)                    │
 volcano minikube disabled third-party (volcano)                  │
 volumesnapshots minikube disabled Kubernetes
 yakd minikube disabled 3rd party (marcnuri.com)               │
└────────────── ┴─────┴──────┴────────────────────┘
 
# 이제 metric 을 확인할 수 있다.
meatsby ~ k top no
NAME       CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)
minikube   309m         3%       1110Mi          14%
 
meatsby ~ k top po
NAME      CPU(cores)   MEMORY(bytes)
bar-app   1m           8Mi
foo-app   1m           6Mi

multi-node cluster 구성

meatsby ~ minikube node list
minikube	192.168.49.2
 
# worker node 3대 추가
meatsby ~ minikube node add --worker
😄  노드 m02 클러스터 minikube [worker] 로 추가합니다
  CNI 없이 클러스터가 생성되었으므로, 클러스터에 노드를 추가하면 네트워킹이 중단될 있습니다.
👍  "minikube" 클러스터의 "minikube-m02" worker 노드를 시작하는
🚜  기본 이미지 v0.0.48를 가져오는 ...
🔥  docker container (CPUs=2, 메모리=2200MB) 를 생성하는 중 ...
🐳  쿠버네티스 v1.34.0 Docker 28.4.0 런타임으로 설치하는
🔎  Kubernetes 구성 요소를 확인...
🏄  m02 minikube 성공적으로 추가하였습니다!
 
meatsby ~ minikube node add --worker
😄  노드 m03 클러스터 minikube [worker] 로 추가합니다
👍  "minikube" 클러스터의 "minikube-m03" worker 노드를 시작하는
🚜  기본 이미지 v0.0.48를 가져오는 ...
🔥  docker container (CPUs=2, 메모리=2200MB) 를 생성하는 중 ...
🐳  쿠버네티스 v1.34.0 Docker 28.4.0 런타임으로 설치하는
🔎  Kubernetes 구성 요소를 확인...
🏄  m03 minikube 성공적으로 추가하였습니다!
 
meatsby ~ minikube node add --worker
😄  노드 m04 클러스터 minikube [worker] 로 추가합니다
👍  "minikube" 클러스터의 "minikube-m04" worker 노드를 시작하는
🚜  기본 이미지 v0.0.48를 가져오는 ...
🔥  docker container (CPUs=2, 메모리=2200MB) 를 생성하는 중 ...
🐳  쿠버네티스 v1.34.0 Docker 28.4.0 런타임으로 설치하는
🔎  Kubernetes 구성 요소를 확인...
🏄  m04 minikube 성공적으로 추가하였습니다!
 
meatsby ~ minikube node list
minikube	192.168.49.2
minikube-m02	192.168.49.3
minikube-m03	192.168.49.4
minikube-m04	192.168.49.5
 
meatsby ~ k get nodes
NAME           STATUS   ROLES           AGE     VERSION
minikube       Ready    control-plane   3h39m   v1.34.0
minikube-m02   Ready    <none>          2m8s    v1.34.0
minikube-m03   Ready    <none>          114s    v1.34.0
minikube-m04   Ready    <none>          99s     v1.34.0
 
# worker node labeling
meatsby ~ k label node minikube-m02 node-role.kubernetes.io/worker=worker
node/minikube-m02 labeled
 
meatsby ~ k label node minikube-m03 node-role.kubernetes.io/worker=worker
node/minikube-m03 labeled
 
meatsby ~ k label node minikube-m04 node-role.kubernetes.io/worker=worker
node/minikube-m04 labeled
 
meatsby ~ k get nodes
NAME           STATUS   ROLES           AGE     VERSION
minikube       Ready    control-plane   3h40m   v1.34.0
minikube-m02   Ready    worker          2m52s   v1.34.0
minikube-m03   Ready    worker          2m38s   v1.34.0
minikube-m04   Ready    worker          2m23s   v1.34.0

multipass


multipass 란

Mac 하드웨어 (Apple Silicon, EL0/EL1/EL2/EL3)
│
├─ EL2: Apple 하이퍼바이저  ←── Hypervisor.framework가 여기 접근권을 유저에 열어줌
│                                (게스트 CPU를 물리코어에서 네이티브 실행 + 메모리 가상화)
│
└─ macOS 커널 XNU (EL1)
   ├─ vmnet.framework ─── 가상 스위치 / NAT / DHCP(bootpd)
   │     └─ bridge100 (기본망, 192.168.64.0/24, 항상 NAT, VM 관리용)
   │            └─ member: vmenet0 (VM ↔ 스위치 연결)
   │
   ├─ en0 (실제 Wi-Fi, 192.168.0.122) ──→ 공유기(192.168.0.1) ──→ 인터넷
   │        ▲
   │        └─ [BRIDGE 모드일 때만] vmnet이 여기에 브리지로 얹어 VM NIC2 생성
   │
   └─ 유저스페이스
      ├─ multipassd (root 데몬) ── VM 생성/삭제/상태
      │
      ├─ qemu-system-aarch64 [controlplane]   ← 프로세스 1개 = VM 1대
      │   ├─ (CPU/메모리 → Hypervisor.framework 호출)
      │   ├─ (디스크 → virtio-blk → Mac 이미지파일)
      │   └─ 게스트 Ubuntu
      │       ├─ enp0s1  → bridge100(192.168.64.x)  = 관리용 NIC1
      │       ├─ enp0s2  → 공유기(192.168.0.x)        = PRIMARY_IP, default gw ★ (BRIDGE 모드)
      │       └─ kubelet / kube-apiserver / containerd ...
      │
      ├─ qemu-system-aarch64 [node01]
      └─ qemu-system-aarch64 [node02]
  • Canonical 이 만든 경량 VM 관리 도구
  • multipass 설치 시 상시 데몬 multipassd 가 VM 수명주기를 관리
  • multipass launch/shell/exec/delete 명령어로 multipassd 에 요청하여 VM 생성
  • Apple Silicon 에서 multipass 는 QEMU 를 사용
  • macOS 에선 네트워크를 인터넷 공유/vmnet 으로 자동 구성(192.168.64.x)해 별도 설정 없이 VM이 바로 인터넷 통신 가능
  1. multipass launch
  2. multipassd 가 qemu 프로세스 실행 (qemu 가 기본 드라이버, AppleVZ 드라이버도 있음)
    • VM 하나 = mac 위의 QEMU 프로세스 하나(qemu-system-aarch64)
    • 즉, controlplanenode01node02 는 각각 별개의 QEMU 프로세스로 구성된다
      • ps aux | grep qemu 로 실제로 볼 수 있다.
  3. qemu 가 vCPU, 메모리, 가상 디스크, 가상 인터페이스 생성
    1. vCPU, 메모리는 Hypervisor.framework(커널 수준 API) 호출해서 할당
      • AppleVZ 드라이버 일 경우 Virtualization.framework 호출
    2. 가상 디스크는 mac 디스크 위 이미지 파일을 virtio-blk 디바이스로 에뮬레이트
    3. 가상 인터페이스는 vmnet.framework 호출해서 bridge100, vmenet0virtio-net 디바이스 생성
      • --bridged 로 실행 시 bridge101, vmenet1 등 인터페이스를 생성하여, mac 이 사용하는 공유기에 붙을 스위치 bridge101 에 mac 의 nic en0 와 vm 의 nic vmenet1 을 구성한다.

QEMU 란

  • 오픈소스 머신 에뮬레이터 겸 가상화기(VMM, Virtual Machine Monitor)
  • VM 의 가상 하드웨어(CPU, 메모리, 디스크=virtio-blk, NIC=virtio-net)를 만들어 게스트 OS(Ubuntu ARM64)를 실행
  • Apple Silicon 에서는 CPU/메모리 가상화를 Hypervisor.framework(hvf 가속기)에 위임해 게스트를 네이티브 속도로 돌림
  • multipass 에서 QEMU 는 VM 당 프로세스 1개(qemu-system-aarch64)로 뜨며, multipassd 가 이 프로세스를 관리
 multipass launch --name test jammy

위 명령어를 실행하면 multipassd 가 QEMU 프로세스를 실행해서 vCPU, 메모리, 가상 디스크, 가상 NIC 등을 생성하여 VM 을 구성한다.

 ps -ef | grep multipass
    0 24431     1   0  8:04PM ??         0:01.65 /Library/Application Support/com.canonical.multipass/bin/multipassd --verbosity debug
    0 29125 24431   0  8:30PM ??         0:36.87 /Library/Application Support/com.canonical.multipass/bin/qemu-system-aarch64 -machine virt,gic-version=3 -accel hvf -drive file=/Library/Application Support/com.canonical.multipass/bin/../Resources/qemu/edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on -cpu host -nic vmnet-shared,start-address=192.168.252.1,end-address=192.168.252.254,subnet-mask=255.255.255.0,model=virtio-net-pci,mac=52:54:00:f3:88:54 -device virtio-scsi-pci,id=scsi0 -drive file=/var/root/Library/Application Support/multipassd/qemu/vault/instances/testn/ubuntu-22.04-server-cloudimg-arm64.img,if=none,format=qcow2,discard=unmap,id=hda -device scsi-hd,drive=hda,bus=scsi0.0 -smp 2 -m 3072M -qmp stdio -chardev null,id=char0 -serial chardev:char0 -nographic -cdrom /var/root/Library/Application Support/multipassd/qemu/vault/instances/testn/cloud-init-config.iso -uuid f5988137-9236-3bdf-ae37-fc70257638a9
  501 29555 23812   0  8:32PM ttys003    0:00.00 rg --color=auto multipass
 
 ps -ef | grep bootpd
    0 29169     1   0  8:30PM ??         0:00.03 /usr/libexec/bootpd
  501 29580 23812   0  8:32PM ttys003    0:00.00 rg --color=auto bootpd

ps -efmultipassd 가 QEMU 프로세스를 실행한 것과 DHCP 역할을 수행하는 bootpd 를 직접 확인할 수 있다.

multipass 네트워킹

qemu 는 macOS kernel 의 vmnet.framework 를 호출해서 virtio-net 디바이스를 만들어 가상 스위치, 가상 인터페이스를 구현한다.

NAT 네트워킹

 multipass launch --disk 10G --memory 3G --cpus 2 --name testn jammy
Launched: testn
 
 multipass list
Name                    State             IPv4             Image
testn                   Running           192.168.252.11   Ubuntu 22.04 LTS
 
 ifconfig
...
vmenet0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
        ether d2:55:69:80:5b:1b
        media: autoselect
        status: active
bridge100: flags=8a63<UP,BROADCAST,SMART,RUNNING,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
        options=3<RXCSUM,TXCSUM>
        ether be:d0:74:14:cc:64
        inet 192.168.252.1 netmask 0xffffff00 broadcast 192.168.252.255
        inet6 fe80::bcd0:74ff:fe14:cc64%bridge100 prefixlen 64 scopeid 0x1a
        inet6 fd65:8bbd:f4af:d0b3:8e0:63a:ad73:f9a2 prefixlen 64 autoconf secured
        Configuration:
                id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
                maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
                root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
                ipfilter disabled flags 0x0
        member: vmenet0 flags=3<LEARNING,DISCOVER>
                ifmaxaddr 0 port 25 priority 0 path cost 0
        nd6 options=201<PERFORMNUD,DAD>
        media: autoselect
        status: active
 
 multipass shell testn
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-185-generic aarch64)
...
 
ubuntu@testn:~$ ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:f3:88:54 brd ff:ff:ff:ff:ff:ff
    inet 192.168.252.11/24 metric 100 brd 192.168.252.255 scope global dynamic enp0s1
       valid_lft 3380sec preferred_lft 3380sec
    inet6 fd65:8bbd:f4af:d0b3:5054:ff:fef3:8854/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 2591935sec preferred_lft 604735sec
    inet6 fe80::5054:ff:fef3:8854/64 scope link
       valid_lft forever preferred_lft forever

multipass 는 기본적으로 NAT 모드로 실행된다. NAT 모드는 macOS 에 가상 스위치 bridge100 과 VM 이 사용할 가상 인터페이스 vmenet0 을 만든다.

192.168.252.0/24 범위의 서브넷에서 macOS 의 bootpd 가 192.168.252.1 에서 게이트웨이/DHCP/DNS 역할을 수행해 새로 생성되는 VM 들을 bridge100 에 연결한다. NAT 모드는 bridge100 을 통해 인터넷 egress 는 되지만 ingress 는 안되고 일반적으로 multipass shell/exec 을 통해 multipass cli 와 소통하는 역할을 한다? NAT 구성에서는 VM이 도는 네트워크가 하이퍼바이저가 관리하는 NAT 게이트웨이를 통해 공유기 네트워크와 격리된다. VM은 밖으로 나갈 수 있지만(인터넷 접속 가능), 밖에서 안은 볼 수 없다(브라우저로 NodePort 접속 불가). 현재 Multipass에서 포트포워딩 룰 설정은 불가능하다. VM이 쓰는 네트워크는 Multipass가 고른다?

Bridge 네트워킹

 multipass launch --bridged --disk 10G --memory 3G --cpus 2 --name testb jammy
Launched: testb
 
 multipass list
Name                    State             IPv4             Image
testb                   Running           192.168.252.9    Ubuntu 22.04 LTS
                                          192.168.0.131
 
 ifconfig
...
vmenet0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
        ether d2:55:69:80:5b:1b
        media: autoselect
        status: active
bridge100: flags=8a63<UP,BROADCAST,SMART,RUNNING,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
        options=3<RXCSUM,TXCSUM>
        ether be:d0:74:14:cc:64
        inet 192.168.252.1 netmask 0xffffff00 broadcast 192.168.252.255
        inet6 fe80::bcd0:74ff:fe14:cc64%bridge100 prefixlen 64 scopeid 0x1a
        inet6 fd65:8bbd:f4af:d0b3:8e0:63a:ad73:f9a2 prefixlen 64 autoconf secured
        Configuration:
                id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
                maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
                root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
                ipfilter disabled flags 0x0
        member: vmenet0 flags=3<LEARNING,DISCOVER>
                ifmaxaddr 0 port 25 priority 0 path cost 0
        nd6 options=201<PERFORMNUD,DAD>
        media: autoselect
        status: active
vmenet1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
        ether fa:52:a3:b8:ea:9d
        media: autoselect
        status: active
bridge101: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        options=3<RXCSUM,TXCSUM>
        ether be:d0:74:14:cc:65
        Configuration:
                id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
                maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
                root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
                ipfilter disabled flags 0x0
        member: vmenet1 flags=3<LEARNING,DISCOVER>
                ifmaxaddr 0 port 27 priority 0 path cost 0
        member: en0 flags=8003<LEARNING,DISCOVER,MACNAT>
                ifmaxaddr 0 port 14 priority 0 path cost 0
        media: autoselect
        status: active
 
 multipass shell testb
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-185-generic aarch64)
...
 
ubuntu@testb:~$ ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:e8:e5:9c brd ff:ff:ff:ff:ff:ff
    inet 192.168.252.9/24 metric 100 brd 192.168.252.255 scope global dynamic enp0s1
       valid_lft 2587sec preferred_lft 2587sec
    inet6 fd65:8bbd:f4af:d0b3:5054:ff:fee8:e59c/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 2591998sec preferred_lft 604798sec
    inet6 fe80::5054:ff:fee8:e59c/64 scope link
       valid_lft forever preferred_lft forever
3: enp0s2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:d3:0d:2a brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.131/24 metric 200 brd 192.168.0.255 scope global dynamic enp0s2
       valid_lft 6190sec preferred_lft 6190sec
    inet6 fe80::5054:ff:fed3:d2a/64 scope link
       valid_lft forever preferred_lft forever

multipass 를 Bridge 모드로 실행하면 kubernetes 노드가 로컬 네트워크(집 공유기)에 추가 머신처럼 나타나고, IP 는 공유기가 동적으로 준다. 위 예시에선 enp0s2 주소가 공유기가 할당한 내 브로드밴드(홈) 네트워크상의 주소이다. 덕분에 브라우저로 배포한 NodePort 서비스에 접속할 수 있다. 같은 와이파이 공유기에 연결된 스마트폰 등에서 192.168.0.131 로 접속할 수 있다.

Bridge 모드 동작 방식 때문에 각 VM 은 2개의 nic 를 생성한다. enp0s1 은 multipass 가 사용하는 nic 이고 enp0s2 는 노드에서 default gateway 가 있는 네트워크에 연결된 인터페이스의 IP 로 노드가 다른 노드 및 인터넷과 통신할 때 쓰는 nic 이다. kubernetes 컴포넌트가 enp0s1 에 바인딩될 수 있기 때문에 모든 VM /etc/default/kubeletKUBELET_EXTRA_ARGS='--node-ip 192.168.0.131' 를 집어넣어 kubelet 이 사용할 nic 를 명시적으로 지정해준다.

DHCP leases 정리

 multipass delete testn testb && multipass purge
 sudo nvim /var/db/dhcpd_leases

multipass 는 자동으로 DHCP leases 정리하지 않기 때문에 직접 정리하지 않으면 결국 multipass VM 네트워크에서 IP 주소가 부족해질 수 있다.

kubernetes-the-multipass-kubeadm-way


apt 패키지 인덱스 업데이트 및 쿠버네티스 apt 레포 사용을 위한 패키지 설치

sudo apt-get update
# apt-transport-https는 더미 패키지일 수 있다. 그렇다면 해당 패키지를 건너뛸 수 있다
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

쿠버네티스 패키지 리포지터리용 public signing key 다운로드

# 데비안 12와 우분투 22.04보다 오래된 릴리스에서는 `/etc/apt/keyrings` 디렉터리가 기본적으로 존재하지 않으며, curl 명령 전에 생성되어야 한다.
# `/etc/apt/keyrings` 디렉터리가 존재하지 않으면, curl 명령 전에 생성해야 한다.
# sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

쿠버네티스 apt 리포지터리 추가

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

apt package index 업데이트,  kubelet, kubeadm, kubectl 설치

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
 
kubeadm version  # kubeadm 버전 확인

containerd 설치

sudo apt update
sudo apt install -y containerd

kubeadm 은 기본적으로 쿠버네티스 컴포넌트를 파드로 실행하기 때문에 master, worker 모두 containerd 컨테이너 런타임을 설치해야 한다.

kubelet cgroup driver 설정

Linux 에선 cgroup 을 사용해 프로세스(컨테이너)마다 할당할 리소스를 제한한다. kubelet 과 container runtime 은 cgroup driver 를 통해 cgroup 을 사용하여 파드와 컨테이너의 리소스를 제한한다. 때문에 kubelet 과 container runtime 은 동일한 cgroup driver 를 가져야한다.

cgroup driver 의 종류는 아래와 같다.

  • cgroupfs
  • systemd
ps -p 1  # PID 1 이 systemd 인지 확인

kubelet 의 기본 cgroup 드라이버는 cgroupfs 드라이버이다. 리눅스 배포판의 init 시스템이 systemd 인 경우, init 프로세스는 root control group(cgroup)을 생성 및 사용하는 cgroup 관리자로 작동하기 때문에 cgroup 드라이버 역시 systemd 로 사용해야 한다. systemd 를 cgroup 드라이버로 사용하려면, KubeletConfiguration 의 cgroupDriver 옵션을 systemd 로 설정해야 한다. v1.22 이후 버전부터 kubeadm 으로 클러스터를 생성할 경우 사용자가 KubeletConfiguration 하위의 cgroupDriver 필드를 설정하지 않아도 kubeadm 이 기본값으로 systemd 를 사용한다.

containerd cgroup driver 설정

systemd 를 kubelet 의 cgroup 드라이버로 구성했다면, 반드시 컨테이너 런타임의 cgroup 드라이버 또한 systemd 로 설정해야 한다. runc 와 함께 /etc/containerd/config.toml 에서 systemd cgroup 드라이버를 사용하려면, 사용 중인 containerd 버전에 따라 다음과 같이 설정한다.

Containerd 1.x 버전:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
  ...
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true

Containerd 2.x 버전:

[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc]
  ...
  [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
    SystemdCgroup = true

cgroup v2 를 사용할 경우 systemd cgroup 드라이버가 권장된다.

sudo mkdir -p /etc/containerd
containerd config default \
  | sed 's/SystemdCgroup = false/SystemdCgroup = true/' \
  | sudo tee /etc/containerd/config.toml
sudo systemctl restart containerd

containderd config default 명령어는 containerd 의 기본 config.toml 을 추력한다. 기본 출력을 | sed 's/SystemdCgroup = false/SystemdCgroup = true/' 에 넘겨 systemd cgroup 드라이버를 사용하도록 수정하고 | sudo tee /etc/containerd/config.toml 로 설정파일 경로에 저장한다.

컨트롤 플레인 노드 초기화

ip add  # 마스터 노드 enp0s8 로 --apiserver-advertise-address 설정
 
sudo kubeadm init \
  --apiserver-advertise-address "192.168.56.11" \
  --pod-network-cidr "10.244.0.0/16" \  # 기본 pod 네트워크 cidr
  --upload-certs

여기서 etcd, kube-apiserver, kube-controller-manager, kube-scheduler 이 설치되고 ca certificate 이 생성된다.

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes  # 아직 네트워크 애드온이 설치되지 않아서 마스터 노드가 NotReady 상태

kubeadm init 으로 생성된 /etc/kubernetes/admin.conf 을 사용하여 kubectl 을 통해 kube-apiserver 와 통신할 수 있다.

CNI 설치

kubectl apply -f ${add-on}.yaml

CNI 네트워크 애드온 중 하나를 설치한다.

Worker node join

kubeadm join 192.168.56.11:6443 \
  --token xxxxxx.xxxxxxxxxxxx \
  --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

kubeadm init 이후 나온 출력에서 join 명령어를 사용해 워커 노드를 클러스터에 조인한다.

References