出落ち
間違っていない
アリストテレス的三段論法にて示します。
- KubeVirt / OpenShift Virtualizationでは、仮想マシン(VM)をKubernetesの上で扱うことができる。
- 故にVMをKubernetesのリソースとしてYAMLファイルで表現できる。
- よってVMのGitOpsは寧ろ推進されるべき営みである。
各種CustomResourceについて
KubeVirt / OpenShift Virtualizationの中で仮想マシンを管理する上で必要なCustomResourceDefinition
について以下、軽く説明します。
DataVolume
CRD
仮想マシンのルートボリューム(rootdisk
)を定義するCRDです。今回使うYAMLを示します。
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: fedora-datavolume
spec:
source: #OSイメージの在り処を指定します。今回はURL直指定です。
http:
url: https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2
pvc: #永続ボリュームのスペックをPVCの書式に従って記載します
accessModes:
- ReadWriteMany
volumeMode: Block
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
resources:
requests:
storage: 30Gi
聞き慣れないリソースなので難しいと感じてしまうかもしれませんが、中身を見るととても簡単です。DataVolume
では主に2つの要素を定義します。
ひとつはsource
です。ゲストマシンのOSイメージの在り処(取得先)を指定します。今回はFedoraプロジェクトがホームページで公開している「KVM仮想環境向けのイメージ(QCOW2形式)」のダウンロードURLを直接指定します。
QCOW2形式についての詳細はこちらを御覧ください
source
は今回の場合はhttp.url
としていますが、既にDeploy済みの仮想マシンのDataVolume
から作成されたPersistentVolumeClaim
を指定したり、仮想マシンのスナップショットを指定したりもできます。
ちなみに、source
部分だけを更にDataSource
というCRDで外だしして記載することも可能ですが、今回はやりません。
次にpvc
です。これは仮想マシンの永続ボリューム要求をPersistentVolumeClaim
の書式そのままを記載します。storageClass
やaccessModes
(仮想マシンをLive Migrationさせたい場合はReadWriteMany
でのProvisioningが必須です)等を指定します。
VirtualMachine
CRD
Kubernetes上の仮想マシンインスタンス(VMI)は、CustomResourceDefinitionである、文字通りVirtualMachine
によって定義されます。コンテナアプリケーションでいうところのDeployment
に近しい存在かと私個人は理解しており、VirtualMachine
からvirt-launcher
(VMが起動しているコンテナを内包するPod
)等のリソースが作成されます。
今回使うManifestを以下に示します。
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-example
labels:
app: vm-example
os.template.kubevirt.io/fedora: 'true'
spec:
running: true #仮想マシン作成時の挙動を指定します。trueにすると作成後、自動的に起動します。
template:
metadata:
labels:
app: vm-example #VMに付与するlabelです。Serviceと紐づける為に付与しておきます
kubevirt.io/domain: vm-example
spec:
accessCredentials: #SSH公開鍵との紐づけを設定します
- sshPublicKey:
propagationMethod:
noCloud: {}
source:
secret: #public-key.yamlというSecretファイルに公開鍵情報を盛り込んでいます
secretName: public-key
domain:
cpu: #仮想マシンにアロケーションするCPUリソースを定義します
cores: 1
sockets: 1
threads: 1
devices: #仮想マシンに接続する仮想デバイスを定義します
disks:
- disk:
bus: virtio
name: rootdisk #rootdiskとしてVirtIOデバイスを指定します
- disk:
bus: virtio
name: cloudinitdisk #仮想マシン起動時の設定(cloud-init)を実行する為のディスクを定義します
interfaces:
- name: eth0 #Podに最初から付与されているvNIC(eth0)の名称です。任意のものに変更できます
masquerade: {} #eth0は必ずmasqueradeに指定してください。
macAddress: '02:c7:36:00:00:2b' #eth0に指定したいMACアドレスを記載します
model: virtio
memory: #仮想マシンにアロケーションするメモリを定義します
guest: 2Gi
hostname: vm-example
networks: #仮想マシンインスタンスのvNICが接続するNWの定義を記載します
- name: eth0 #eth0という名称のvNICをKubernetesのPod Network(デフォルトのOverlay Network)に参加させます。これは必須の設定です
pod: {}
volumes:
- name: rootdisk #rootdiskとしてDataVolume名を参照しています
dataVolume:
name: vm-datavolume
- name: cloudinitdisk #cloud-init設定内容を実行するディスク
cloudInitNoCloud: #cloud-initの書式に従って記載された内容を実行します
secretRef: #cloud-init設定内容をuserdata-secret.yamlに記載しています
name: userdata-secret
spec.template.spec.volumes
で2つのボリューム(仮想ディスク)を指定しています。rootdisk
は先程示したDataVolume
そのものになります。cloudinitdisk
は仮想マシン起動時の初期設定(cloud-init)をディスクとしてマウントするものです。中にはユーザ/パスワード情報を含むcloud-initで設定可能な情報を記載しますので、センシティブな情報です。VirtualMachine
YAML自体に記載することも可能ですが、別にSecretファイルを用意したほうがよさそうです。なお、今回の設定内容は以下のとおりです。
#cloud-config
user: cloud-user
password: cloud-password
chpasswd:
expire: false
ssh_pwauth: true
runcmd:
- sudo yum install httpd -y
- sudo systemctl start httpd.service
Fedora OSのユーザ/パスワード情報を記載しています。また、以下の記事でも触れているのですが、起動時に特定のコマンドを実行しています。今回はApacheをインストールし、Webサーバの起動をしています。
この内容をuserdata-secret.yaml
に記載しています。
kind: Secret
apiVersion: v1
metadata:
name: userdata-secret
data:
userData: I2Nsb3VkLWNvbmZpZw0KdXNlcjogY2xvdWQtdXNlcg0KcGFzc3dvcmQ6IGNsb3VkLXBhc3N3b3JkDQpjaHBhc3N3ZDoNCiAgZXhwaXJlOiBmYWxzZQ0Kc3NoX3B3YXV0aDogdHJ1ZQ0KcnVuY21kOg0KICAtIHN1ZG8geXVtIGluc3RhbGwgaHR0cGQgLXkNCiAgLSBzdWRvIHN5c3RlbWN0bCBzdGFydCBodHRwZC5zZXJ2aWNl
#以下のcloud-configをBASE64エンコードしたものを`userData`に記載します
#cloud-config
#user: cloud-user
#password: cloud-password
#chpasswd:
#expire: false
#ssh_pwauth: true
#runcmd:
#- sudo yum install httpd -y
#- sudo systemctl start httpd.service
cloud-initについてはこの記事が大変参考になりました!
用意しているYAML一覧
以下の通りの構成としています。
root/
├ app-of-apps/
| └ application.yaml
├ applications/
| ├ namespace-application.yaml
| ├ configs-application.yaml
| ├ datavolume-application.yaml
| └ virualmachine-application.yaml
└ manifest/
├ namespace/
| └ vm-namespace.yaml
├ configs/
| ├ public-key
| ├ userdata-secret.yaml
| ├ vm-route.yaml
| └ vm-service.yaml
├ datavolume/
| └ vm-datavolume.yaml
└ virtualmachine/
└ vm-example.yaml
複数のManifestを順序を意識してApplyしたい(例:まずはNameSpace
のManifestからApplyしたい、DataVolume
をVirtualMachine
よりも先にDeployしたい 等)ので、「app of apps pattern」を取り入れています。親Application
から子Application
をsync-waveの順序でApplyしていく様に設計しています。これにより、Manifestの適用順序をコントロールします。
以下に全てのファイルの中身を貼ります。
まずは親/子Application
です。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-of-apps
namespace: openshift-gitops
spec:
destination:
namespace: openshift-gitops
server: 'https://kubernetes.default.svc'
source:
path: applications
repoURL: >-
https://<your_git_repositry_url>
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: namespace
namespace: openshift-gitops
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
destination:
namespace: vm-example
server: 'https://kubernetes.default.svc'
source:
path: manifest/namespace
repoURL: >-
https://<your_git_repositry_url>
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ApplyOutOfSyncOnly=true
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: configs
namespace: openshift-gitops
annotations:
argocd.argoproj.io/sync-wave: "2"
spec:
destination:
namespace: vm-example
server: 'https://kubernetes.default.svc'
source:
path: manifest/configs
repoURL: >-
https://<your_git_repositry_url>
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ApplyOutOfSyncOnly=true
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: datavolume
namespace: openshift-gitops
annotations:
argocd.argoproj.io/sync-wave: "3"
spec:
destination:
namespace: vm-example
server: 'https://kubernetes.default.svc'
source:
path: manifest/datavolume
repoURL: >-
https://<your_git_repositry_url>
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ApplyOutOfSyncOnly=true
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: virtualmachine
namespace: openshift-gitops
annotations:
argocd.argoproj.io/sync-wave: "4"
spec:
destination:
namespace: vm-example
server: 'https://kubernetes.default.svc'
source:
path: manifest/virtualmachine
repoURL: >-
https://gitlab.com/masaki-oomura/openshift-virt-argocd.git
targetRevision: main
project: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ApplyOutOfSyncOnly=true
次に各Manifestです。これらはステートレスなリソースを生成します。
apiVersion: v1
kind: Namespace
metadata:
name: vm-example
labels:
argocd.argoproj.io/managed-by: openshift-gitops #Argo CDインスタンス(openshift-gitops)から管理できる為に必要なlabelです
kind: Secret
apiVersion: v1
metadata:
name: public-key
data:
key: <公開鍵(id_rsa.pub)の中身をBASE64エンコードしたもの>
type: Opaque
kind: Secret
apiVersion: v1
metadata:
name: userdata-secret
data:
userData: I2Nsb3VkLWNvbmZpZw0KdXNlcjogY2xvdWQtdXNlcg0KcGFzc3dvcmQ6IGNsb3VkLXBhc3N3b3JkDQpjaHBhc3N3ZDoNCiAgZXhwaXJlOiBmYWxzZQ0Kc3NoX3B3YXV0aDogdHJ1ZQ0KcnVuY21kOg0KICAtIHN1ZG8geXVtIGluc3RhbGwgaHR0cGQgLXkNCiAgLSBzdWRvIHN5c3RlbWN0bCBzdGFydCBodHRwZC5zZXJ2aWNl
#以下のcloud-configをBASE64エンコードしたものを`userData`に記載します
#cloud-config
#user: cloud-user
#password: cloud-password
#chpasswd:
#expire: false
#ssh_pwauth: true
#runcmd:
#- sudo yum install httpd -y
#- sudo systemctl start httpd.service
apiVersion: v1
kind: Service
metadata:
name: vm-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: vm-example
clusterIP: None
kind: Route
apiVersion: route.openshift.io/v1
metadata:
name: vm-route
spec:
to:
kind: Service
name: vm-service
port:
targetPort: 80
Fedora VMにApacheをインストールしてWebサーバとしてインターネット公開するためには、Service
とRoute
が必要ですので、それも一緒にApplyしてしまいます。
最後にDataVolume
とVirtualMachine
です。こちらは上述の再掲。
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: fedora-datavolume
spec:
source: #OSイメージの在り処を指定します。今回はURL直指定です。
http:
url: https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2
pvc: #永続ボリュームのスペックをPVCの書式に従って記載します
accessModes:
- ReadWriteMany
volumeMode: Block
storageClassName: ocs-storagecluster-ceph-rbd-virtualization
resources:
requests:
storage: 30Gi
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: vm-example
labels:
app: vm-example
os.template.kubevirt.io/fedora: 'true'
spec:
running: true #仮想マシン作成時の挙動を指定します。trueにすると作成後、自動的に起動します。
template:
metadata:
labels:
app: vm-example #VMに付与するlabelです。Serviceと紐づける為に付与しておきます
kubevirt.io/domain: vm-example
spec:
accessCredentials: #SSH公開鍵との紐づけを設定します
- sshPublicKey:
propagationMethod:
noCloud: {}
source:
secret: #public-key.yamlというSecretファイルに公開鍵情報を盛り込んでいます
secretName: public-key
domain:
cpu: #仮想マシンにアロケーションするCPUリソースを定義します
cores: 1
sockets: 1
threads: 1
devices: #仮想マシンに接続する仮想デバイスを定義します
disks:
- disk:
bus: virtio
name: rootdisk #rootdiskとしてVirtIOデバイスを指定します
- disk:
bus: virtio
name: cloudinitdisk #仮想マシン起動時の設定(cloud-init)を実行する為のディスクを定義します
interfaces:
- name: eth0 #Podに最初から付与されているvNIC(eth0)の名称です。任意のものに変更できます
masquerade: {} #eth0は必ずmasqueradeに指定してください。
macAddress: '02:c7:36:00:00:2b' #eth0に指定したいMACアドレスを記載します
model: virtio
memory: #仮想マシンにアロケーションするメモリを定義します
guest: 2Gi
hostname: vm-example
networks: #仮想マシンインスタンスのvNICが接続するNWの定義を記載します
- name: eth0 #eth0という名称のvNICをKubernetesのPod Network(デフォルトのOverlay Network)に参加させます。これは必須の設定です
pod: {}
volumes:
- name: rootdisk #rootdiskとしてDataVolume名を参照しています
dataVolume:
name: vm-datavolume
- name: cloudinitdisk #cloud-init設定内容を実行するディスク
cloudInitNoCloud: #cloud-initの書式に従って記載された内容を実行します
secretRef: #cloud-init設定内容をuserdata-secret.yamlに記載しています
name: userdata-secret
では早速、以下の要件を満たすOpenShiftクラスタにて、application.yaml
を適用してみます。
- OpenShift Virtualizationが利用できる
- OpenShift Data Foundationが利用できる
- OpenShift GitOpsが利用できる
Red Hatのサポート対象外ではありますが、こちらの記事のROSA(Red Hat OpenShift Service on AWS)で上記が実現可能です。
app of appsの親Application
を貼っつけて「CREATE」をクリック。
しばらく待つと全てのApplication
のステータスがHealthy
になります。
OpenShiftのコンソール画面でDeployされた仮想マシンを確認してみます。
無事にDeployできました。「リソース」タブからRouteのURLにアクセスしてみます。
期待通り、Feroda VMがWebサーバとして公開されています。最後にVMにSSHしてみます。
~ % virtctl -n vm-example ssh cloud-user@vm-example --identity-file=~/.ssh/id_rsa
[cloud-user@vm-example ~]$
SSHで接続できました。
仮想マシンへのSSHについてはこちらを参照ください
おわりに
この様に、仮想マシンのDeployがGitOpsで実現できるようになりました。今回はNameSpace
内に単一のVMアプリケーションのみをDeployしましたが、GitOpsを活用できるということは、コンテナアプリケーションも混在してDeployすることが可能になります。KubeVirt / OpenShift Virtualizationによって、アプリケーションがコンテナで作られていても、VMであっても、一体的にリソース管理・Deployし、Immutable Infrastructureを推進できます。