kubernetes + UIPath Window - 1
kubevirt 사용한 Custom Window ISO 가상머신
by HOON
1
Last updated on April 5, 2025, 7:59 p.m.
안녕하세요. 오늘은 Kubernetes에서 window 가상머신을 올려보도록 하겠습니다.
언급한 UIPath는 사실 window에 최적화 되어있는 자동화 툴 입니다.
이전 포스트에서 Linux Robot을 사용해서, 간단하게 k8s HPA까지 작동하는걸 확인했었는데요.
이번에는 윈도우에서 작동 시킬 방법이 없을까 생각하다가, 컨테이너 이미지를 window로 생성해서 RPA를 운영하는데에는 다소 무리가 있다 판단해서, 가상머신을 사용 할 예정입니다.
사용할 기술 스택은 다음과 같습니다.
Kubernetes 클러스터, kubevirt(가상머신 생성), WSIM, Custom Window ISO(autoattend.xml), RealVNC 등
제가 생각하는 최종 목표는 k8s가 자동으로 window 가상머신을 늘리고 자동으로 window 설치 및
UIPath Robot 설치와 라이센스 연결 까지를 목표로 해보겠습니다.
오늘은 그 첫번째로 우선 window custom iso 생성과 kubevirt 사용 및 k8s에서 가상머신으로 생성한 custom iso을 올리는걸 작성해보겠습니다.
Custom Window ISO
Custom Window ISO를 왜 생성해야할까요??
윈도우를 설치 해 보신분이라면 알겠지만, 윈도우는 기본적으로 설치 단계에 클릭과 타이핑으로 사용자의 상호작용을 유도합니다.
저희 목표는 가상환경 생성 시 모든 설정파일이 자동으로 기입되고 설치가 되어야하기 때문에 Custom Window ISO가 필요합니다.
생성방법은 생각보다 간단합니다.
일단 아래 경로에서 Window ISO 이미지를 하나 받아주세요. 저는 Window10 Home 버전을 선택했습니다.
Window ISO 10
다운받은 ISO 파일을 압축 해제 후 저희가 필요한 파일들을 넣을거에요.
그 다음, Window ADK를 설치합니다.
Window ADK Download Link
ADK를 설치하면 다음 프로그램이 같이 설치 될 거에요. 이후에 사용해야합니다.
먼저, autounattend.xml 파일을 작성해볼까요?? 아래 WSIM을 사용해서 작성해도되지만,
저희는 더 편리한 방법을 사용해보겠습니다.
기존에는 이렇게 필요한 파라미터를 하나씩 지정하는 방식으로 진행했었으나, 구글링을 하다보니 간단하게 xml파일만 작성 할 수 있더라구요.
저는 해당 사이트를 참고했습니다.
(https://schneegans.de/windows/unattend-generator/)
저는 윈도우 설치 시 모든 과정을 자동화 할 예정이기때문에 window 에디션, 언어, 디스크 파티션, 라이센스 키 등록, 자동 로그인 등 모든 작업을 자동화 했습니다.
<SetupUILanguage>
<UILanguage>ko-KR</UILanguage>
</SetupUILanguage>
<InputLocale>0412:00000412</InputLocale>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME</Key>
<Value>Windows 10 Home</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>2</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
일부만 보여드렸으나, 이런식으로 미리 정의하면 윈도우 설치 시 자동으로 넘어가게 됩니다. 한층 제가 원하는 자동화의 모습에 가까워졌죠??
자세한 내용은 MS docs를 참고해주세요. InstallForm에 대한 docs만 링크를 남기겠습니다.
InstallForm 문서
이렇게 생성된 autounattned.xml파일은 꼭!! 루트 시스템폴더에 들어가야합니다.
├─ (루트)
│ ├─ boot
│ ├─ efi
│ ├─ sources
│ ├─ bootmgr
│ ├─ bootmgr.efi
│ └─ Autounattend.xml ← 여기
다음으로 xml파일이 생성되었다면, 이제 xml파일을 포함한 custom iso파일을 생성해주도록 하겠습니다. 여기에 OSCDIMG 프로그램이 필요합니다.
아래 이미지처럼 명령어를 작성하면 됩니다.
oscding -u2 -m -o -h -b"D:\wintest\Windows\boot\etfsboot.com" "D:\wintestt\Windows" "D:\wintest\CustomWindows.iso"
# Windows 내 etfsboot.com을 사용해서, \wintest\Windows 아래 모든 파일을 \wintest\CustomWindows.iso 이라는 이름으로 생성하겠다.
생성 후 파일을 보면 실제로 CustomWindows.iso 가 생긴 걸 확인 할 수 있습니다.
KubeVirt,CDI
kubevirt는 클라우드 네이티브 환경에서 가상머신을 배포/운영 할 수 있도록 도와주는 기술입니다.
k8bs의 다른 리소스들과 동일하게 manifest 파일을 작성해서 적용하고 사용 할 수 있습니다.
자세한 내용은 docs에서 확인 할 수 있습니다.
kubevirt
우선 kubevrit부터 설치해야겠죠?? 설치 과정은 간단하게 명령어만 남기겠습니다.
export KUBEVIRT_VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases/latest | jq -r .tag_name)
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-operator.yaml
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-cr.yaml
CDI는 VM 이미지(예: QCOW2, RAW)를 K8s PVC로 자동 import하는 기능을 제공합니다.
export CDI_VERSION=$(curl -s https://api.github.com/repos/kubevirt/containerized-data-importer/releases/latest | jq -r .tag_name)
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/${CDI_VERSION}/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/${CDI_VERSION}/cdi-cr.yaml
kubevirt와 CDI를 성공적으로 설치했다면 아래와같이 여러개의 pod들이 running 상태가 됩니다.
k8s-master@k8s-master:~$ k get pods -A | grep -i cdi
cdi cdi-apiserver-75b7dbf99f-wr96s 1/1 Running 7 (103m ago) 2d16h
cdi cdi-deployment-68fb77cdb4-t5ln4 1/1 Running 15 (98m ago) 2d16h
cdi cdi-operator-55d5f88bdc-xf478 1/1 Running 14 (98m ago) 2d16h
cdi cdi-uploadproxy-66c44c5c77-9fdx4 1/1 Running 3 (103m ago) 26h
k8s-master@k8s-master:~$ k get pods -A | grep -i kubevirt
kubevirt virt-api-c8c86b5b-kct8q 1/1 Running 1 (104m ago) 40h
kubevirt virt-api-c8c86b5b-xkffz 1/1 Running 1 (104m ago) 40h
kubevirt virt-controller-5f57b7cc79-vtgx2 1/1 Running 1 (104m ago) 40h
kubevirt virt-controller-5f57b7cc79-wr4xq 1/1 Running 3 (96m ago) 40h
kubevirt virt-handler-fmp7v 1/1 Running 8 (104m ago) 2d17h
kubevirt virt-launcher-windows-vm-zhx6t 2/2 Running 0 45m
kubevirt virt-operator-8846fd88d-gbnq9 1/1 Running 4 (97m ago) 27h
kubevirt virt-operator-8846fd88d-lp8m7 1/1 Running 4 (98m ago) 27h
api, 컨트롤러, 핸들러, 오퍼레이터가 꼭 있는지 확인해주세요!!
그럼 이제 PVC, DV(Data Volume), Virtualmachine 파일을 작성해볼게요.
우선 PVC는 가상머신의 하드디스크 역할을 합니다. 윈도우도 설치할때는 하드디스크가 필요하잖아요??
다음 DV는 ISO를 PVC에 연결시켜주는 역할을 할 수 있어요. ISO 이미지를 PVC 하드디스크에 마운트 한다고 생각하면 됩니다.
그럼 먼저, 하드디스크 역할인 PVC부터 생성해볼까요?
저는 테스트상황을 고려해서 모든 리소스 스펙은 낮게 설정했습니다.
k8s-master@k8s-master:~/window$ cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-windows-disk-pvc
namespace: kubevirt
annotations:
volume.kubernetes.io/selected-node: "k8s-node1"
spec:
storageClassName: local-path # 또는 storage class 필요
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
node1에 20Gi의 용량을 갖는 PVC를 생성했어요.
다음 DV는 명령어를 통해서 실행해볼게요.
DV는 image를 업로드 할 때 프록시 url을 사용하기때문에 포트포워딩을 통해 먼저 proxy url을 연결 후
image 업로드를 시도할게요. (ingress를 사용해도 됩니다.)
k8s-master@k8s-master:~$ kubectl port-forward -n cdi svc/cdi-uploadproxy 8443:443
Forwarding from 127.0.0.1:8443 -> 8443
Forwarding from [::1]:8443 -> 8443
Handling connection for 8443
Handling connection for 8443
k8s-master@k8s-master:~/window$ virtctl image-upload dv windows-iso-dv \
> --uploadproxy-url=https://127.0.0.1:8443 \
> --image-path=/home/k8s-master/window/CustomWindows.iso \
> --size=20Gi \
> --insecure
Using existing PVC kubevirt/windows-iso-dv
Uploading data to https://127.0.0.1:8443
8.67 GiB / 8.67 GiB [---------------------------------------------------------------------------] 100.00% 30.07 MiB p/s 4m56s
Uploading data completed successfully, waiting for processing to complete, you can hit ctrl-c without interrupting the progress
Processing completed successfully
Uploading /home/k8s-master/window/CustomWindows.iso completed successfully
windows-iso-dv 라는 dv를 생성해서, pvc가 iso파일을 인식 할 수 있도록 하는 명령어입니다.
업로드가 성공적으로 끝났다는 메세지를 보게된다면, pvc와 dv를 확인해보세요.
k8s-master@k8s-master:~/window$ k get dv,pvc
NAME PHASE PROGRESS RESTARTS AGE
datavolume.cdi.kubevirt.io/windows-iso-dv Succeeded N/A 103m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/my-windows-disk-pvc Bound pvc-f645607f-1b85-4656-a9ae-ca5460e92aa8 20Gi RWO local-path <unset> 104m
persistentvolumeclaim/windows-iso-dv Bound pvc-ee5f471b-86c5-4734-85f0-30a8b10d9126 20Gi RWO local-path <unset> 103m
dv의 상태가 Succeeded로 변경되면서 pvc가 Bound 되어야 합니다.
(my-windows-disk-pvc는 이전에 생성한 하드디스크 pvc 입니다.)
여기까지 됐다면, 다음은 virtualmachine을 작성해줘야겠죠??
k8s-master@k8s-master:~/window$ cat windows-vm.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: windows-vm
spec:
running: false
template:
spec:
domain:
firmware:
bootloader:
bios: {} # 또는 efi: {} - 원하는 모드로
cpu:
cores: 2
resources:
requests:
memory: 4Gi
devices:
disks:
- name: iso
bootOrder: 1 # 중요!!
cdrom:
bus: sata
- name: win-disk
bootOrder: 2
disk:
bus: sata
volumes:
- name: iso
persistentVolumeClaim:
claimName: windows-iso-dv
- name: win-disk
persistentVolumeClaim:
claimName: my-windows-disk-pvc
bootloader는 custom iso를 만들 때 efi 모드를 사용하지 않았다면, 그대로 bios로 두면 됩니다.
만약 efi 모드를 사용했다면, efi: {} 로 작성하면 됩니다.
위에선 설명드리지 않았지만, 저희가 pvc, dv를 이용하는 방식은 가상머신이 cd-rom으로 인식되도록 설정한거에요. 따라서 메니페스트 파일 내에도 cdrom으로 지정해줘야합니다.
추가로, bootOrder: 1 로 지정해서 부팅순서를 1번으로 정의해줘야 해요.
그렇지 않으면 No Bootable Device라는 문구가 출력되면서 부팅 하지 못합니다.
volumes 탭을 보면 저희가 지정했던 pvc들이 들어가있는걸 확인 할 수 있습니다.
VirtualMachine
자, 이제 가상머신을 구동하기위한 최소조건들이 다 맞춰졌습니다. 이제 테스트해봐야겠죠??
kubectl apply -f windows-vm.yaml
을 통해 생성한 메니페스트파일을 적용해주고 보면
k8s-master@k8s-master:~/window$ k get virtualmachine
NAME AGE STATUS READY
windows-vm 5s Stopped False
셍성은 됐고 이제 가상머신을 실행시켜볼게요.
k8s-master@k8s-master:~/window$ virtctl start windows-vm
VM windows-vm was scheduled to start
k8s-master@k8s-master:~/window$ k get virtualmachine windows-vm --watch
NAME AGE STATUS READY
windows-vm 17s Starting False
windows-vm 18s Running False
windows-vm 18s Running True
k8s-master@k8s-master:~/window$ k get pods | grep -i launch
virt-launcher-windows-vm-zhx6t 2/2 Running 0 89m
성공적으로 Running 이라고 출력되고, 실제 vm이 올라갈때 생성되는 virt-launcher까지 생성됐어요!
그럼 실제 RealVNC를 통해서 확인해볼까요??
여기서도 저는 포트포워딩과 ssh 터널링을 사용하겠습니다.
우선 virtctl 명령어를 통해 생성한 가상머신을 포트포워딩 할게요.
k8s-master@k8s-master:~/window$ virtctl vnc windows-vm --proxy-only --address=0.0.0.0 --port 30050
{"port":30050}
{"component":"portforward","level":"info","msg":"connection timeout: 1m0s","pos":"vnc.go:149","timestamp":"2025-04-04T04:19:05.421141Z"}
{"component":"portforward","level":"info","msg":"VNC Client connected in 3.796949114s","pos":"vnc.go:162","timestamp":"2025-04-04T04:19:09.217959Z"}
그 이후 터미널을 하나 더 열어서 ssh 터널링을 하겠습니다.
ssh -L 30050:localhost:30050 -p 2222 k8s-master@127.0.0.1
k8s-master@127.0.0.1's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-211-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information disabled due to load higher than 2.0
. . .
*** System restart required ***
Last login: Fri Apr 4 04:16:58 2025 from 10.0.2.2
. . .
그리고 실제로 프로그램에서 localhost:30050 으로 접속해보면??
아무런 작업을 하지 않아도 저희가 정의해둔 autounatnned.xml에 정의해둔대로 자동으로 설치가 되는 중입니다!!
근데 한가지 문제점이 있어요. 윈도우가 자동으로 설치되긴 하지만 너무 느립니다. RPA가 빠르게 가동 되어야 한다면 이런 느린 방식은 비효율적이에요.
그럼 빠르게 할 수 있는 방법이 뭐가있을지 고민해보다 스냅샷이 떠올랐습니다.
다음 포스트는 스냅샷 이미지를 기반으로 가상머신을 생성하도록 해보겠습니다.
감사합니다.
Leave a Comment: