OpenShift Virtualization (OCP-V) - Create VM - Custom Image - Registry

### OpenShift Virtualization

Red Hat® OpenShift® Virtualization は Red Hat OpenShift に含まれる機能であり、組織が新規および既存の仮想マシン (VM) ワークロードを実行およびデプロイするための先進的なプラットフォームを提供します。このソリューションにより、従来の仮想マシンを、信頼できる、一貫した包括的なハイブリッドクラウド・アプリケーション・プラットフォームに簡単に移行できます。

OpenShift Virtualization は、VM の移行を単純化するとともに、クラウドネイティブ・アプリケーション・プラットフォームのシンプルさと速度を利用してインフラストラクチャのモダナイゼーションの道筋を提供します。また、先進的な管理の原則を取り入れつつ既存の仮想化への投資を維持することを目指しており、Red Hat の包括的な仮想化ソリューションの基盤となります。

https://www.redhat.com/ja/technologies/cloud-computing/openshift/virtualization

### Creating VMs from custom images

OCP 4.17 Bare Metal 環境 OpenShift Virtualization (OCP-V) で Virtual Machine (VM) を作成してみます。ここでは以下の手順を参考に、独自に作成した `Custom Image` から作成してみます。

Creating VMs from custom images

https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/virtualization/virtual-machines#creating-vms-from-custom-images

`Custom Image` では以下の4つの方法がありますが、今回は1番目の方法で行ってみます。

> - Importing the image as a container disk from a registry.
    Optional: You can enable auto updates for your container disks. See Managing automatic boot source updates for details.
> - Importing the image from a web page.
> - Uploading the image from a local machine.
> - Cloning a persistent volume claim (PVC) that contains the image. 

なお、それぞれの方法によってサポートされるフォーマットが指定されています。

CDI supported operations matrix

https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/virtualization/storage#virt-cdi-supported-operations-matrix_virt-preparing-cdi-scratch-space

また、処理の過程で `Scratch Space (一時ストレージ領域)` を使用します。

Preparing CDI scratch space

https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/virtualization/storage#virt-about-scratch-space_virt-preparing-cdi-scratch-space

#### Importing the image as a container disk from a registry
##### Building and uploading a container disk

ドキュメントの記載を参考に、以下の `Dockerfile` で Container Image を作成します。

```shell
$ cat Dockerfile
FROM registry.access.redhat.com/ubi9/ubi:9.5-1742918310 AS builder
ADD --chown=107:107 rhel-9.5-x86_64-kvm.qcow2 /disk/
RUN chmod 0440 /disk/*

FROM scratch
COPY --from=builder /disk/* /disk/
```
`Base Image` として UBI9 を使用します。今回は、実施時点の最新版を明示するために Tag を指定しています。

Red Hat Universal Base Image 9 

https://catalog.redhat.com/software/containers/ubi9/ubi/615bcf606feffc5384e8452e

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/c34f90cd-210d-4926-ae38-bc472898a5a3.png)

`rhel-9.5-x86_64-kvm.qcow2 (QCOW2 Format File)` は、以下の URL からダウンロードしておきます。

Download Red Hat Enterprise Linux

https://access.redhat.com/downloads/content/479/ver=/rhel---9/9.5/x86_64/product-software

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/8060f60b-cb45-4d31-9721-feae33e8ecd9.png)

`podman build` で `Customer Image` を作成します。今回は、登録先として OpenShift Image Registry を想定した Tag を指定しています。

https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/registry/registry-overview-1

```shell
$ podman build -t $(oc get route default-route -n openshift-image-registry -o jsonpath='{.spec.host}')/z-ocpv-vmtest-custom/ubi9-custom-image:1.0 .
[1/2] STEP 1/3: FROM registry.access.redhat.com/ubi9/ubi:9.5-1742918310 AS builder
Trying to pull registry.access.redhat.com/ubi9/ubi:9.5-1742918310...
Getting image source signatures
Checking if image destination supports signatures
Copying blob 36b61797c1e3 done   |
Copying blob e996822e3bf3 done   |
Copying config 0222f91775 done   |
Writing manifest to image destination
Storing signatures
[1/2] STEP 2/3: ADD --chown=107:107 rhel-9.5-x86_64-kvm.qcow2 /disk/
--> c53746ae6e05
[1/2] STEP 3/3: RUN chmod 0440 /disk/*
--> 0aefa30b7a82
[2/2] STEP 1/2: FROM scratch
[2/2] STEP 2/2: COPY --from=builder /disk/* /disk/
[2/2] COMMIT default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image:1.0
--> 4ebbd96b913b
Successfully tagged default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image:1.0
4ebbd96b913b484f1454d7b1412d6c4eea3b99e0608c5abb44c2e2ed76dd3b53
```

念のため、`Custom Image` の内容を確認してみます。
```shell
$ podman images
REPOSITORY                                                                                                    TAG             IMAGE ID      CREATED         SIZE
default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image    1.0             4ebbd96b913b  48 seconds ago  974 MB
<none>                                                                                                        <none>          0aefa30b7a82  55 seconds ago  2.16 GB

$ podman inspect 4ebbd96b913b
[
     {
          "Id": "4ebbd96b913b484f1454d7b1412d6c4eea3b99e0608c5abb44c2e2ed76dd3b53",
          "Digest": "sha256:c43e854298e96221ee9aa1615ee6192519d414247da40593e104d14c0bcdc49b",
          "RepoTags": [
               "default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image:1.0"
          ],
          "RepoDigests": [
               "default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image@sha256:c43e854298e96221ee9aa1615ee6192519d414247da40593e104d14c0bcdc49b"
          ],
          "Parent": "",
          "Comment": "",
          "Created": "2025-04-09T07:04:13.471781606Z",
          "Config": {
               "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
               ],
               "Labels": {
                    "io.buildah.version": "1.37.2"
               }
          },
          "Version": "",
          "Author": "",
          "Architecture": "amd64",
          "Os": "linux",
          "Size": 974392794,
          "VirtualSize": 974392794,
～省略～
```
`podman push` で `Custom Image` を登録します。
```shell
$ podman push default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image:1.0
Getting image source signatures
Copying blob 1d9fbd8b26fc done   |
Copying config 4ebbd96b91 done   |
Writing manifest to image destination
```
登録した `Custom Image` を確認してみます。
```shell
$ skopeo inspect docker://default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image:1.0
{
    "Name": "default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image",
    "Digest": "sha256:b63fb9bc8ebfbe8cbc970f6ea80742e1736ab2cd7525f83bb4d5c492a0f4eace",
    "RepoTags": [
        "1.0"
    ],
    "Created": "2025-04-09T07:04:13.471781606Z",
    "DockerVersion": "",
    "Labels": {
        "io.buildah.version": "1.37.2"
    },
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
        "sha256:86477b46fbc2263fbed5f41669c3c694b821a8e9495316ef5c287acbdbe6cf32"
    ],
    "LayersData": [
        {
            "MIMEType": "application/vnd.oci.image.layer.v1.tar+gzip",
            "Digest": "sha256:86477b46fbc2263fbed5f41669c3c694b821a8e9495316ef5c287acbdbe6cf32",
            "Size": 967107954,
            "Annotations": null
        }
    ],
    "Env": [
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ]
}
```
OCP Cluster 内部からも確認してみます。
```shell
$ oc project -q 
z-ocpv-vmtest-custom

$ oc get ImageStream
NAME                IMAGE REPOSITORY                                                                                               TAGS     UPDATED
ubi9-custom-image   default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image   1.0      18 seconds ago

$ oc get -o json ImageStream ubi9-custom-image | jq -r '.status | .dockerImageRepository, .publicDockerImageRepository'
image-registry.openshift-image-registry.svc:5000/z-ocpv-vmtest-custom/ubi9-custom-image
default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com/z-ocpv-vmtest-custom/ubi9-custom-image
```
##### Disabling TLS for a container registry
必要に応じて、`HyperConverged.spec.storageImport.insecureRegistries` を修正します。
```shell
$ oc explain HyperConverged.spec.storageImport.insecureRegistries
GROUP:      hco.kubevirt.io
KIND:       HyperConverged
VERSION:    v1beta1

FIELD: insecureRegistries <[]string>


DESCRIPTION:
    InsecureRegistries is a list of image registries URLs that are not secured.
    Setting an insecure registry URL
    in this list allows pulling images from this registry.
```
今回は、以下のように修正します。
```shell
  storageImport:
    insecureRegistries:
    - image-registry.openshift-image-registry.svc:5000
    - default-route-openshift-image-registry.apps.mycluster.ocp01.ens.com
```
なお、`InsecureRegistries` ではなく Registry TLS 証明書を登録・使用する場合は、`DataVolume.spec.source.registry.certConfigMap` に指定します。

##### Creating a VM from a container disk by using the web console

リンク先のフォーマットを参考に、OpenShift Image Registry アクセス用の Secret を作成します。
※ OCP Service Account の Image Pull Secret、 `kubernetes.io/dockercfg` / `kubernetes.io/dockerconfigjson` Type の Secret は使用できません。

```shell
apiVersion: v1
kind: Secret
metadata:
  name: endpoint-secret
  labels:
    app: containerized-data-importer
type: Opaque
data:
  accessKeyId: ""  # <optional: your key or user name, base64 encoded>
  secretKey:    "" # <optional: your secret or password, base64 encoded>
```

https://github.com/kubevirt/containerized-data-importer/blob/e17b60a1296e7b68446ba9807c38622d732118b0/manifests/example/endpoint-secret.yaml#L7

```shell
$ oc create secret generic datavolume-registry-secret \
--from-literal=accessKeyId=**** \
--from-literal=secretKey=*************

$ oc get secret datavolume-registry-secret
NAME                         TYPE     DATA   AGE
datavolume-registry-secret   Opaque   2      103s
```

OCP Console から Virtualization -> Catalog を表示します。画面が `Template catalog` Tab になっている事を確認します。

左上の Project に VM を作成する Project として `z-ocpv-vmtest-custom` を指定した上で、`Red Hat Enterprise Linux 9 VM` Template を選択します。`Disk source` から `Registry (creates PVC)` を選択し、`Container image` に ImageStream で確認した URL を指定します。今回は `ImageStream.status.dockerImageRepository` の値を指定しています。

その他の必要な箇所の設定変更を行います。今回は `VirtualMachine name` のみ `rhel9-custom01-registry` に変更しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/68dca25a-f167-4494-a43d-e85dcc0c2cca.png)

`Customize VirtualMachine` で `YAML` Tab から、先程作成した Secret を指定します。

```shell
spec:
  dataVolumeTemplates:
    - apiVersion: cdi.kubevirt.io/v1beta1
      kind: DataVolume
      metadata:
        creationTimestamp: null
        name: rhel9-custom01-registry
      spec:
        source:
          registry:
            url: 'docker://image-registry.openshift-image-registry.svc:5000/z-ocpv-vmtest-custom/ubi9-custom-image:1.0'
            secretRef: datavolume-registry-secret
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/88310cd8-ba51-494b-9529-b05d04ace762.png)

`Save` ボタンを押したのちに、`Create VirtualMachine` ボタンを押して VM を作成します。以下のような画面が表示され、`Status Running` になれば完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128125/bcca0e17-1a63-4da4-8209-a2dfa9f54bc0.png)

##### Creating a VM from a container disk by using the command line
詳細は割愛しますが、先述の OCP Console から作成した VM を参考に VirtualMachine Manifest を作成する事ができます。Registry に関する情報は、`VirtualMachine.spec.dataVolumeTemplates.spec.source.registry` に指定します。

```shell
$ oc explain VirtualMachine.spec.dataVolumeTemplates.spec.source.registry
GROUP:      kubevirt.io
KIND:       VirtualMachine
VERSION:    v1

FIELD: registry <Object>


DESCRIPTION:
    DataVolumeSourceRegistry provides the parameters to create a Data Volume
    from an registry source

FIELDS:
  certConfigMap <string>
    CertConfigMap provides a reference to the Registry certs

  imageStream   <string>
    ImageStream is the name of image stream for import

  pullMethod    <string>
    PullMethod can be either "pod" (default import), or "node" (node docker
    cache based import)

  secretRef     <string>
    SecretRef provides the secret reference needed to access the Registry source

  url   <string>
    URL is the url of the registry source (starting with the scheme: docker,
    oci-archive)
```

`virtctl create vm` で VirtualMachine Manifest の枠組みを作成した上で、必要箇所を修正する事もできます。

VM manifest creation commands

https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/virtualization/getting-started#vm-manifest-creation-commands_virt-using-the-cli-tools

https://kubevirt.io/user-guide/user_workloads/creating_vms/