Featured image of post k8s에 Windows 설치하기

k8s에 Windows 설치하기

잘못 읽은신거 아닙니다. "k8s" 위에 "Windows" 맞습니다.

⚠ 주의 : 본 포스트에서는 k8s에 Windows를 설치하는 방법에 대한 정보를 담고 있습니다.

MS 법무팀과 평생 기억에 남을 찐한 티 타임을 가지고 싶은게 아니라면 Windows ContainerMac OS Container 페이지에도 나와있듯, 별도 라이센스 없이 상업적으로 사용하는 것은 삼가도록 합시다.


문제의 시작

나는 깔끔한 걸 굉장히 좋아하는 성격이다.
회사에서나 집에서나 컴퓨터 바탕화면에는 항상 필요한 최소한의 아이콘만 배치하고 있다.

특히나 Docker를 접하게 된 이후로는 개발자 컴퓨터라면 응당 설치되어 있을법한 python이나 nodejs, java 심지어 Database Client 같은 것들도 모두 컨테이너를 통해 사용하고 있다.

하지만 반드시 Windows 혹은 Mac에서만 동작하도록 만들어진 프로그램들도 있기 마련이다.
예컨대 카카오톡의 경우 Docker Container로 사용하기가 대단히 어렵거나 하더라도 매끄럽게 동작하질 않는다.

이러한 것들 중 내가 특히나 불편하게 여기는 게 있다. 바로 은행 보안 프로그램이다.


로그인 하려면 이것들부터 설치해야 한다

한 달에 한 번씩 정기적으로 가계부를 정리하는 습관을 가지고 있는데,
금융 관련 보안에 대해서는 굉장히 민감한 편이라 반드시 집에 있는 메인 PC로만 작업하곤 한다.

문제는 다음과 같다.

  1. 매번 은행업무 볼 때마다 각종 은행 보안 프로그램이 설치 / 업데이트 / 실행 된다.

  2. 은행들끼리 통일도 안 되어 있는 건지 사용하는 프로그램들이 조금씩 다 다르다.

  3. 개인적으로 사용하는 보안 프로그램과 충돌나는 경우가 잦다.

  4. 은근히 컴퓨터 자원을 많이 잡아 먹는다.

더 이상 메인 PC에 이런 자질구레한 보안 프로그램들이 설치되는 걸 용납할 수가 없었다.
그렇다고 은행업무 전용 PC를 산다는 건 지나친 투자이다. 빈대 잡자고 초가삼간 태울 순 없는 노릇 아닌가.

그렇담 가상화는?

이 생각도 안 해본 건 아니다. 아니 생각 정도가 아니라 실제로 VMWare로 시도도 해봤다. 동작도 잘 한다.
그런데, 메인 PC에 은행업무 하나 보자고 가상환경 설치하는 것 자체가 너무나도 번거럽고 부담스러운 일이었다.


이것도 싫고 저것도 싫고… 그렇다면 결론은 하나다.

k8s 클러스터 위에 Windows를 설치하는 수밖에!

아니 대체 왜 이렇게까지…


사실 진짜 이유


해결방안 - k8s에 Windows를 설치하자!


사용할 컨테이너 이미지는 dockurr/windows이다.

이게 그 Windows 컨테이너라는 건가?

싶을 수도 있는데, 아니다.

Windows Container는 Host OS가 Windows일 때 Windows App을 격리하여 실행하기 위해 있는 것으로, 우리가 흔히 Container라고 부르는 것은 십중팔구 Linux Container를 의미한다. Linux Container와 Windows Container는 근본적으로 다르다.

  • Linux Container는 Linux OS 위에서만 동작한다.
  • Unix Container라는 것도 있다. Unix OS에서만 동작한다.
  • 마찬가지로 Windows Container는 Windows 위에서만 동작한다.

같은 개발자분이 작성한 다른 이미지들을 보면 Windows 이외에도 MacOS, Casa등 다양하게 준비되어 있는 걸 볼 수 있는데, 이 이미지들은 모두 Linux 시스템 위에서 동작하는 걸 전제로 만들어져 있다.

이런게 가능한 이유는 Container 수준이 아니라 아예 커널 레벨에서의 가상화를 쓰기 때문이다.
핵심 컴포넌트는 KVM(kernel-based Virtual Machine)이다.


KVM (kernel-based Virtual Machine)

KVM은 Linux Kernel을 하이퍼바이저로 변환하는 가상화 기술로, 단일 물리적 컴퓨터에서 여러 개의 격리된 가상 머신(VM), 즉 “게스트” 운영체제를 실행할 수 있게 해준다.

각 VM을 메모리, 스토리지, 네트워크 카드와 같은 가상화된 하드웨어 구성 요소를 갖춘 일반 Linux 프로세스로 처리함으로써 효율적이고 고성능의 가상화를 제공한다.

동작 원리

  • Linux Kernel 통합: KVM은 Linux Kernel 자체 내의 모듈이기 때문에 하이퍼바이저 역할을 하기 위한 별도의 운영체제가 필요하지 않다.

  • 하드웨어 가상화: KVM은 Intel VT-x 또는 AMD-V와 같은 하드웨어 가상화 확장 기능을 활용하여 완전한 하드웨어 수준의 가상화를 제공한다.

  • QEMU: KVM이 핵심 가상화 기능을 제공하는 반면, QEMU 에뮬레이터와 함께 작동하여 게스트 VM에 하드웨어 에뮬레이션 및 가상 장치를 제공한다.

  • 가상 머신: 각 게스트 OS는 자체 전용 가상 하드웨어를 갖춘 별도의 Linux 프로세스로 실행되어 다른 VM과의 격리를 보장한다.

주요 특징

  • 오픈소스 및 무료: KVM은 오픈소스이며 무료로 사용할 수 있다.

  • 성능: Linux Kernel과의 깊은 통합으로 인해 높은 성능과 효율성을 자랑한다.

  • 유연성: Linux와 Windows를 포함한 다양한 운영체제를 게스트 VM으로 실행할 수 있다.

  • 비용 효율성: Linux의 구성 요소로서 값비싼 라이선스 비용이 필요하지 않다.

일반적인 사용 사례

  • 클라우드 컴퓨팅: KVM은 많은 퍼블릭 및 프라이빗 클라우드 인프라의 핵심 기술로, 확장 가능한 온디맨드 컴퓨팅 리소스를 제공한다.

  • 소프트웨어 개발 및 테스트: 개발자들은 KVM을 사용하여 다양한 운영체제에서 소프트웨어를 테스트하기 위한 격리된 환경을 만든다.

  • 서버 통합: 조직에서는 KVM을 사용하여 여러 서버를 더 적은 수의 물리적 머신으로 통합하여 비용과 에너지를 절약한다.


사전준비

말이 길었는데, 결국 k8s를 구성하는 Node에서 kvm을 지원 해줘야 OS(Linux)의 한계를 뛰어넘는 Container를 사용해 볼 수 있다.

KVM 지원 확인

KVM 자체는 Linux Kernel에 이미 있기 때문에 하드웨어 가상화만 지원하면 된다. k8s Node에 터미널로 접속해 다음 명령어를 입력해보자.

1
lsmod | grep kvm

다음과 같이 출력된다면 이미 활성화가 되어 있는 것이다.

1
2
3
4
kvm_amd               208896  4
kvm                  1409024  3 kvm_amd
irqbypass              12288  1 kvm
ccp                   143360  4 kvm_amd

Intel의 CPU를 사용한다면 kvm_intel이라고 출력될 것이다.

출력이 안 되었다면, VMWare나 VirtualBox 설치할 때 처럼 해당 노드의 Bios에서 활성화를 해줘야 한다.

Host OS가 Debian 계열이라면 다음의 명령어로 더 간편하게 확인 가능하다.

1
2
sudo apt install cpu-checker
sudo kvm-ok

Windows 컨테이너 정보

Windows 11을 설치할 것이다. 사양은 Windows 11 시스템 요구사항에 맞춰 구성했다.

  • 운영체제 : Windows 11
  • CPU Core : 2
  • 메모리 : 4GB
  • 저장공간 : 64GB

kubernetes 구성

실제 구현은 CDK for Terraform Single Stack으로 작업했다.

Namespace 생성

1
kubectl create namespace windows



Windows 사용자 로그인 정보 Secret 생성

1
2
3
4
kubectl create secret generic windows-user-credential-secret \
  -n windows \
  --from-literal=username=<Windows 유저명> \
  --from-literal=password=<Windows 패스워드>



추가 Installation Script Configmap 생성 (선택사항)

Container에 Windows가 처음 설치된 이후 1번 실행될 스크립트이다.
Container의 /oem/install.bat파일이 실행된다.

1
2
3
.
├── install.bat
└── remove-unnecessary-apps.ps1

이렇게 2개의 파일로 구성되어 있다. 각 파일의 내용은 다음과 같다.

  1. install.bat

    밑의 2번 PowerShell 파일을 실행하는 bat파일이다.

    1
    2
    3
    4
    5
    
    @echo off
    
    echo Running initial installation scripts...
    
    powershell -ExecutionPolicy Bypass -File "%~dp0remove-unnecessary-apps.ps1"
    

  1. remove-unnecessary-apps.ps1

    불필요한 Windows 기본 앱들을 제거해주는 PowerShell 스크립트이다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    C:\WINDOWS\System32\OneDriveSetup.exe /uninstall
    $appNamesToDelete = @(
        "Clipchamp.Clipchamp",
        "Microsoft.BingNews",
        "Microsoft.BingSearch",
        "Microsoft.BingWeather",
        "Microsoft.GamingApp",
        "Microsoft.MicrosoftOfficeHub",
        "Microsoft.MicrosoftSolitaireCollection",
        "Microsoft.MicrosoftStickyNotes",
        "Microsoft.OutlookForWindows",
        "Microsoft.PowerAutomateDesktop",
        "Microsoft.Todos",
        "Microsoft.WebMediaExtensions",
        "Microsoft.Windows.Photos",
        "Microsoft.WindowsAlarms",
        "Microsoft.WindowsCalculator",
        "Microsoft.WindowsCamera",
        "Microsoft.WindowsFeedbackHub",
        "Microsoft.WindowsSoundRecorder",
        "Microsoft.Xbox.TCUI",
        "MicrosoftCorporationII.QuickAssist",
        "MSTeams",
        "Microsoft.Copilot",
        "Microsoft.ZuneMusic",
        "Microsoft.ScreenSketch",
        "Microsoft.WindowsAppRuntime.1.3",
        "Microsoft.Paint",
        "Microsoft.YourPhone",
        "Microsoft.Windows.DevHome",
        "Microsoft.XboxGamingOverlay",
        "Microsoft.XboxSpeechToTextOverlay",
        "Microsoft.WindowsStore",
        "Microsoft.XboxIdentityProvider"
    )
    Write-Host 'Removing unnecessary apps...'
    foreach ($eachAppNameToDelete in $appNamesToDelete) {
        Get-AppxPackage | Where-Object { $_.Name -eq $eachAppNameToDelete } | Remove-AppxPackage
    }
    Write-Host 'Unnecessary apps removal completed.'
    

이는 예시로, 원하는 대로 설치 스크립트를 구성할 수 있다.

이 두 파일을 담는 ConfigMap을 생성한다.

1
2
3
4
kubectl create configmap windows-oem-assets-configmap \
    -n windows \
    --from-file=install.bat \
    --from-file=remove-unnecessary-apps.ps1



PersistentVolumeClaim 메니페스트 파일

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: windows-persistent-volume-claim
  namespace: windows
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 64Gi



Deployment 메니페스트 파일

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: windows-deployment
  namespace: windows
spec:
  replicas: 1
  selector:
    matchLabels:
      app: windows
  template:
    metadata:
      labels:
        app: windows
    spec:
      terminationGracePeriodSeconds: 120
      containers:
        - name: windows
          image: dockurr/windows
          env:
            - name: VERSION
              value: '11'
            - name: CPU_CORES
              value: '2'
            - name: RAM_SIZE
              value: '4G'
            - name: DISK_SIZE
              value: '64G'
            - name: LANGUAGE
              value: 'Korean'
            - name: REGION
              value: 'ko-KR'
            - name: KEYBOARD
              value: 'ko-KR'
            - name: USERNAME
              valueFrom:
                secretKeyRef:
                  name: windows-user-credential-secret
                  key: username
            - name: PASSWORD
              valueFrom:
                secretKeyRef:
                  name: windows-user-credential-secret
                  key: password
          ports:
            - name: http
              containerPort: 8006
              protocol: TCP
            - name: rdp-tcp
              containerPort: 3389
              protocol: TCP
            - name: rdp-udp
              containerPort: 3389
              protocol: UDP
            - name: vnc
              containerPort: 5900
              protocol: TCP
          securityContext:
            privileged: true
            capabilities:
              add:
                - NET_ADMIN
          volumeMounts:
            - name: windows-persistent-volume-claim
              mountPath: /storage
            - name: windows-oem-assets-configmap
              mountPath: /oem
            - name: dev-kvm
              mountPath: /dev/kvm
            - name: dev-tun
              mountPath: /dev/net/tun
      volumes:
        - name: windows-persistent-volume-claim
          persistentVolumeClaim:
            claimName: windows-persistent-volume-claim
        - name: windows-oem-assets-configmap
          configMap:
            name: windows-oem-assets-configmap
        - name: dev-kvm
          hostPath:
            path: /dev/kvm
        - name: dev-tun
          hostPath:
            path: /dev/net/tun
            type: CharDevice


⚠️ 여기서 잠깐!

KVM이 활성화 된 Node만 필터링 해서 스케쥴링을 해야 할 수도 있다.
경우엔 가상화가 가능한 Node들에 다음의 예시처럼 Labeling을 해주자.

1
kubectl label node <KVM 사용 가능한 노드의 이름> kvm.enabled=true

이후 deployment.ymlspecnodeAffinity를 다음과 같이 추가해줘야 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# deployment.yml
# --- 기존 코드 --- *
spec:
  template:
    spec:
      containers:
      # --- 기존 코드 --- *
      volumes:
      # --- 기존 코드 --- *
      affinity:
        nodeAffinity:
          # 스케쥴링 시엔 '필수' 만족, 실행 중엔 값이 변경되어도 무시
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kvm.enabled  # 설정한 레이블 키
                operator: In
                values:
                - "true"         # 레이블 값

이렇게 함으로써 KVM 사용이 가능한 Node만 선택적으로 골라내 windows를 배포 할 수 있다.




Service 메니페스트 파일

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# service.yml
apiVersion: v1
kind: Service
metadata:
  name: windows-service
  namespace: windows
spec:
  selector:
    app: windows
  ports:
    - name: http
      port: 8006
      targetPort: 8006
      protocol: TCP
    - name: rdp-tcp
      port: 3389
      targetPort: 3389
      protocol: TCP
    - name: rdp-udp
      port: 3389
      targetPort: 3389
      protocol: UDP
    - name: vnc
      port: 5900
      targetPort: 5900
      protocol: TCP



Ingress 메니페스트 파일 (선택)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: windows-ingress
  namespace: windows
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: 'HTTP'
    nginx.ingress.kubernetes.io/rewrite-target: '/'
    # OAuth2 Proxy 사용 시 아래 예시처럼 사용
    # nginx.ingress.kubernetes.io/auth-url: "https://oauth2-proxy.example.com/oauth2/auth"
    # nginx.ingress.kubernetes.io/auth-signin: "https://oauth2-proxy.example.com/oauth2/start?rd=$scheme://$host$request_uri"
spec:
  ingressClassName: nginx
  rules:
    - host: windows.yourdomain.com # 도메인으로 변경
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: windows-service
                port:
                  number: 8006



매니페스트 배포

1
2
3
4
kubectl apply -f pvc.yml
kubectl apply -f deployment.yml
kubectl apply -f service.yml
kubectl apply -f ingress.yml

배포 상태 확인

1
2
kubectl get pods -n windows
kubectl logs -n windows -l app=windows -f



동작 테스트

웹 접속

ingress 설정까지 마쳤다면 연결한 도메인으로 접속해보자.
별도 도메인이 없다면, 다음의 커맨드로 포트포워딩 후 localhost:8006으로 접속하자.

1
2
3
4
kubectl port-forward \
    -n windows \
    --address localhost \
    svc/windows-service 8006:8006

처음에 Windows 설치파일을 다운로드 받는다


다운로드가 완료되면 설치가 시작된다

여기서 제법 시간이 오래 소요된다. 커피라도 한 잔 하고 오자.


설치가 끝나면 Windows 바탕화면이 반겨준다

OEM 스크립트가 정상 동작했다면, 대부분의 불필요한 앱들이 삭제되었을 것이다.


원격 데스크탑(RDP) 접속

웹으로 보는 건 속도랑 반응성이 처참하므로 실 사용에서는 원격 데스크탑을 사용하는 것을 추천한다.

Service 구성에서 아예 NodePort로 3389 포트를 빼주거나 포트포워딩 해서 접속하는 것도 가능하다.

포스트에 굳이 명시하진 않았는데, 내 경우 Nginx Ingress Controller가 LoadBalancer의 특정 포트를 할당하는 형태로 마무리 했다.

원격 데스크톱 연결



한계점

클라우드에서 사용하는 경우

앞서 클러스터를 구성하는 Node에서 KVM이 허용 되어야지만 windows pod 생성이 가능하다고 했다.

본 포스트에서 사용한 Node는 On-Premise 환경에서 동작하는 Desktop 사양의 컴퓨터이다.
고로 비슷한 환경이라면 십중팔구 문제 없이 적용이 될 것이다.

하지만 만일 클라우드에서 KVM을 동작하길 원한다면 프로바이더 별로 몇 가지 제약사항이 있다.

  • Amazon Web Service (AWS)

    • EC2 : 사용 불가능
      EC2는 보안/성능상의 이유로 중첩 가상화(Nested Virtualizaion)를 허용하지 않는다.

    • BMI : 사용 가능
      BMI(Bare Metal Instance)는 물리적인 서버 전체를 임대해주는 서비스다.
      이 경우엔 사용 가능하다. (무지하게 비싸서 문제지)

  • Google Cloud Platform (GCP)

    사용 가능하다.
    다만, 다음의 제약사항을 준수해야 한다.

    1. 인스턴스 생성시 --enable-nested-virtualization 플래그를 넣어줘야 한다.
    2. AMD / Arm 프로세서은 지원되지 않는다. Intel CPU만 가능하다.
    3. 일부 VM 유형(E2 VM등) 지원되지 않는다.

    커맨드 예시:

    1
    2
    3
    4
    
      gcloud compute instances create <사용할 인스턴스 명> \
      --enable-nested-virtualization \
      --zone=<배치할 Zone> \
      --min-cpu-platform="Intel Haswell"
    
  • Microsoft Azure

    Azure도 가능은 한데 GCP보다도 더 까다롭다.

    1. Dv3, Ev3등 충분히 큰 크기의 VM에서만 사용 가능하다.
    2. Linux VM에 직접 KVM 관련 패키지를 설치해야한다.
      1
      2
      3
      4
      5
      
        apt-get update
        apt-get install kvm qemu-kvm libvirt-bin virtinst
        apt install virt-manager 
        adduser `id -un` libvirt
        adduser `id -un` kvm
      
    3. 마찬가지로 Linux VM에 KVM 게스트 VM의 인터넷 연결
      및 통신을 위한 가상 브리지, NAT 설정을 직접 구성해야한다.
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      
        iface br0 inet static
        address 192.168.0.100
        network 192.168.0.0
        netmask 255.255.255.0
        broadcast 192.168.0.255
        gateway 192.168.0.1
        bridge_ports eth0
        bridge_fd 9
        bridge_hello 2
        bridge_maxage 12
        bridge_stp off
      
  • Oracle Cloud Infrastructure (OCI)

    Oracle은 여러가지 옵션을 제공한다.

    • BMI: 사용 가능

    • Oracle Linux KVM Image: 사용 가능
      얘네는 특이하게도 KVM을 사용할 수 있는 별도의 전용 OS를 아예 제공한다.
      하지만 AMD/Arm에선 사용 할 수 없고 Intel CPU에서만 가능하다.

    • 일반 Oracle Instance: 사용 가능
      가능은 한데, Azure의 경우와 마찬가지로 Linux 안에 libvirt와 같은 패키지를
      직접 설치해야 하고 Shape도 AMD E-Series (예: VM.Standard.E5.Flex) 또는 Intel X-Series (VM.Standard3.Flex) 등을 사용해야 한다.


리소스 소모량

Linux와는 근본적으로 상이한 OS가 컨테이너로 올라가다 보니
구체적으로 클러스터에 얼마나 부담이 되는지가 걱정이 되었다.

스토리지

LongHorn Volume 탭을 확인해보니, 할당한 크기 64GB34GB 사용중으로 나온다.

이 정도면 아주 큰 문제는 없어 보인다


CPU 및 메모리

Prometheus 기록상으론 의외로 CPU는 처음 설치 시점에만 높고 이후에는 잠잠해진다.

그런데 메모리는 할당한 4GB가 전부 로드되어 있다.

Windows Container 내부 작업 관리자에서 봐도 제법 많은 메모리가 소모되고 있다.

Windows 11 자체가 idle 상태에서도 생각보다 많은 메모리가 필요한 모양이다.
아니면 KVM이 메모리를 Reserved하게 할당하는 걸 수도 있다.
좀 더 지켜봐야겠지만, 대처방안은 고려 해야겠다.

  1. 필요할 때만 잠깐 배포하거나

  2. 메모리 할당량을 늘리거나

  3. 메모리를 잡아먹는 프로세스를 비활성화 하거나

이런식으로 접근해야 할 듯 하다.



마치며

보안에 대해

Windows라는 OS는 기본적으로 사용자명비밀번호 이 2가지로 인증 처리를 한다.
따라서 어떤 형태든 추가적인 보안 레이어를 마련해 둘 필요가 있다.
가장 좋은 방법은 애초에 외부 접근을 아예 못 하도록 설정하는 것이다.

Service를 보면 총 3개의 포트를 쓰는 것을 볼 수 있다.

  • http
  • rdp
  • vnc

http의 경우 내부적으로 noVNC로 서비스 되는데, 내 경우 ingress annotation에 oauth2 proxy를 설정해서 외부에서 아무나 들어올 수 없도록 추가적인 보안 레이어를 마련해두었다.

문제는 RDP와 VNC이다. Public으로 이 2가지 프로토콜을 열어두는 건 아무리 생각해도 너무 꺼림칙해 아예 VPN을 통해서만 들어올 수 있도록 설정 해두었다.

요컨대 Windows에 원격으로 접속/인증하는 방식이 근원적으로 안전하지 않기 때문에 반드시 이 점을 충분히 고려해서 서비스 네트워크를 구성하길 바란다.


개인적인 의견

idle 상태에서도 windows가 필요로 하는 메모리가 제법 많다는 걸 고려하면 비용 최적화 측면에서 그다지 현명한 접근 방법은 아니라고 본다.

Windows 혼자 타노스급으로 메모리를 폭식중인 모습이다
심지어 색도 보라색이다

그렇다고 마냥 삐딱하게 볼 건 아니다.

Windows나 MacOS에서만 동작하는 어떤 프로그램이 있다고 생각해보자.
당신의 상사가 이를 k8s에 올려서 서비스 하라고 지시했다.
만일 무슨 짓을 써도 매끄럽게 컨테이너화가 불가능한 상황이 온다면-

KVM 기반의 컨테이너가 구원의 손길이 될지도 모른다. (물론 정식 라이센스 쓰고)

Hugo로 만듦
JimmyStack 테마 사용 중