IBM Cloud: Classic InfratructureからVPCで利用可能なBYOL用途のRHEL8 Custom imageを作成する

#1. はじめに

IBM CloudのVPCにはcustom imageを作成する機能が存在し、自分のカスタマイズしたOSをVPC上に持ち込むことができる。基本的にはVPC上で提供されるOSイメージをそのまま使う方がずーーっと楽だが、custom imageを使うことでRHELやWindowsのBYOL(Bring Your Own License)をすることも可能である。

今回はBYOLをするためのクリーンなRHEL OSを作りたいと思っているので（そもそもSW導入などはVPC上で起動してからでも十分なので）、Classic Infrastructure上に新規にRHEL8を作成し、それをVPCにcustom imageとして持ってくる方法を試してみた。
なお、今回はプロビジョニングしたVSIをいきなり編集しているが、Kernelパラメーターを編集したり最終的にDHCP構成にするため、最終的には二度と起動してこない。よって、すでに利用中のVSIを直接編集するべきではない。そういうケースでは、一旦Image Templateを作成し、そのImage TemplateからプロビジョニングしたVSIを編集するような手順にするべきである（docsにもそのような手順が記載されている）。

参考docsは以下の通り。

- https://cloud.ibm.com/docs/vpc?topic=vpc-migrate-vsi-to-vpc&locale=en
- https://cloud.ibm.com/docs/vpc?topic=vpc-create-linux-custom-image&locale=en
- https://cloud.ibm.com/docs/vpc?topic=vpc-byol-vpc-about&locale=en

#2.Classic Infrastructure上での事前準備

以下の作業を実施する。なお、docsに記載されている前提条件はDHCPの構成以外は全て満たされていた。

##2-1. Classic InfrastructureでRHEL8を注文する。
https://cloud.ibm.com/gen1/infrastructure/provision/vs

VersionはRHEL8, Boot Diskは100GBを選択。あとは余計なオプションソフトウエアを導入せずにプロビジョニングする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/21bf4f4e-d92b-7083-47bb-a3fe537ce075.png)


##2-2. Kernel Parameterの確認

上記のRHEL8にログインする。docsによると、`/etc/default/grub`に

- nomodeset
- nofb
- vga=normal
- console=ttyS0

を`GRUB_CMDLINE_LINUX`に含めてgrub.cfgを生成するように記載があるが、最新のClassic InfrastructureのRHEL8 VSIイメージは既に構成されているようなので、特に追加の対応は不要。


```
[root@rhel8custom ~]# cat /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="net.ifnames=0 biosdevname=0 quiet elevator=noop pti=off nomodeset nofb vga=normal console=tty1 console=ttyS0 console=hvc0"
GRUB_DISTRIBUTOR="RedHat Enterprise Linux 8"
GRUB_ENABLE_BLSCFG=true
GRUB_GFXMODE=auto
GRUB_TERMINAL="serial"
GRUB_TIMEOUT=5
GRUB_DEFAULT=saved
GRUB_ENABLE_BLSCFG=true

[root@rhel8custom ~]# cat /boot/grub2/grub.cfg|grep kernelopts
# The kernelopts variable should be defined in the grubenv file. But to ensure that menu
# without a grubenv file, define a fallback kernelopts variable if this has not been set.
# The kernelopts variable in the grubenv file can be modified using the grubby tool or by
# the kernelopts variable in the grubenv file and the fallback kernelopts variable.
if [ -z "${kernelopts}" ]; then
  set kernelopts="root=UUID=9782055f-a08d-4789-80d6-c8ac78e2d99a ro  net.ifnames=0 biosdevname=0 quiet elevator=noop pti=off nomodeset nofb vga=normal console=tty1 console=ttyS0 console=hvc0"
```

##2-3. virtio driversの確認

docsによると、以下のカーネルモジュールが含まれていなければいけない。

- virtio_blk.ko
- virtio.ko
- virtio_pci.ko
- virtio_ring.ko

```
[root@rhel8custom ~]# grep -i virtio /boot/config-$(uname -r)
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_VIRTIO_VSOCKETS=m
CONFIG_VIRTIO_VSOCKETS_COMMON=m
CONFIG_VIRTIO_BLK=m
# CONFIG_VIRTIO_BLK_SCSI is not set
CONFIG_SCSI_VIRTIO=m
CONFIG_VIRTIO_NET=m
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_DRM_VIRTIO_GPU=m
CONFIG_VIRTIO=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_VDPA=m
CONFIG_VIRTIO_BALLOON=m
CONFIG_VIRTIO_INPUT=m
# CONFIG_VIRTIO_MMIO is not set
# CONFIG_RPMSG_VIRTIO is not set
CONFIG_VIRTIO_FS=m
# CONFIG_CRYPTO_DEV_VIRTIO is not set


[root@rhel8custom boot]# grep -e CONFIG_VIRTIO= -e CONFIG_VIRTIO_BLK= -e CONFIG_VIRTIO_PCI= -e CONFIG_VIRTIO_RING= /boot/config-$(uname -r)
CONFIG_VIRTIO_BLK=m
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
```

`=y`となっているものは、すでにKernelに組み込まれているので対応は不要である。そのため、`virtio.ko`と`virtio_pci.ko`については対応は不要である。また、RHEL8のKernelでは、[`virtio_ring.ko`は`virtio.ko`に組み込まれている](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=eccbb05a64fef867362ff05b5d266757e3c82b36)ため、こちらも対応は不要である。
（補足: RHEL7のKernelでは`virtio_ring.ko`はまだ`virtio.ko`には組み込まれていなさそう。）

そのため、virtio_blk.koがinitramfsに構成されている必要があるが、以下のようにClassic InfrastructureのRHEL8 VSIにはデフォルト構成で組み込まれていることがわかる。

```
[root@rhel8custom boot]# lsinitrd /boot/initramfs-$(uname -r).img | grep virtio
-rw-r--r--   1 root     root         9012 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/block/virtio_blk.ko.xz
-rw-r--r--   1 root     root        15144 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/char/virtio_console.ko.xz
drwxr-xr-x   2 root     root            0 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/gpu/drm/virtio
-rw-r--r--   1 root     root        25184 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/gpu/drm/virtio/virtio-gpu.ko.xz
-rw-r--r--   1 root     root        24728 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/net/virtio_net.ko.xz
-rw-r--r--   1 root     root         8528 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/scsi/virtio_scsi.ko.xz
drwxr-xr-x   2 root     root            0 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/virtio
-rw-r--r--   1 root     root         7548 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/virtio/virtio_balloon.ko.xz
-rw-r--r--   1 root     root         5004 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/virtio/virtio_input.ko.xz
-rw-r--r--   1 root     root         4200 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/drivers/virtio/virtio_vdpa.ko.xz
-rw-r--r--   1 root     root        11280 Aug  4  2020 usr/lib/modules/4.18.0-240.el8.x86_64/kernel/fs/fuse/virtiofs.ko.xz


[root@rhel8custom boot]# cat /etc/dracut.conf.d/ibmcloud.conf
add_drivers+="virtio_balloon virtio_input virtio_blk virtio_net virtio_scsi virtio_console virtio-gpu virtiofs"
```

よって特に追加アクションは不要である。

##2-4. DHCPの構成

```txt:デフォルトの構成
[root@rhel8custom ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=none
DEVICE=eth0
HWADDR=06:a0:10:a5:68:0f
IPADDR=10.212.15.53
NETMASK=255.255.255.192
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
USERCTL=no
```
これを以下のように書き換える。

```
[root@rhel8custom ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=eth0
HWADDR=06:a0:10:a5:68:0f
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
USERCTL=no
```


##2-5. cloud-initの構成

###2-5-1. Cloud-initのバージョンは0.7.9以上であることの確認。
以下のように特に問題なし。

```
[root@rhel8custom ~]# cloud-init --version
/usr/bin/cloud-init 19.4
```

###2-5-2. cloud-initが構成済みかつ自動起動されるように設定されていることの確認。
以下のように特に問題なし。

```
[root@rhel8custom ~]# systemctl cat cloud-init
# /usr/lib/systemd/system/cloud-init.service
[Unit]
Description=Initial cloud-init job (metadata service crawler)
Wants=cloud-init-local.service
Wants=sshd-keygen.service
Wants=sshd.service
After=cloud-init-local.service
After=NetworkManager.service network.service
After=NetworkManager-wait-online.service
Before=network-online.target
Before=sshd-keygen.service
Before=sshd.service
Before=systemd-user-sessions.service
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled

[Service]
Type=oneshot
ExecStart=/usr/bin/cloud-init init
RemainAfterExit=yes
TimeoutSec=0

# Output needs to appear in instance console output
StandardOutput=journal+console

[Install]
WantedBy=cloud-init.target

[root@rhel8custom ~]# systemctl status cloud-init
● cloud-init.service - Initial cloud-init job (metadata service crawler)
   Loaded: loaded (/usr/lib/systemd/system/cloud-init.service; enabled; vendor preset: disabled)
   Active: active (exited) since Thu 2021-08-19 19:47:38 CDT; 4 days ago
  Process: 863 ExecStart=/usr/bin/cloud-init init (code=exited, status=0/SUCCESS)
 Main PID: 863 (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 24640)
   Memory: 0B
   CGroup: /system.slice/cloud-init.service

 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: +++++++++++++++++++Route IPv6 info+++++++++++++++++++
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: +-------+-------------+---------+-----------+-------+
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: | Route | Destination | Gateway | Interface | Flags |
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: +-------+-------------+---------+-----------+-------+
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: |   1   |  fe80::/64  |    ::   |    eth0   |   U   |
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: |   2   |  fe80::/64  |    ::   |    eth1   |   U   |
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: |   4   |   ff00::/8  |    ::   |    eth0   |   U   |
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: |   5   |   ff00::/8  |    ::   |    eth1   |   U   |
 8月 19 19:47:38 rhel8custom.IBM.cloud cloud-init[863]: ci-info: +-------+-------------+---------+-----------+-------+
 8月 19 19:47:38 rhel8custom.IBM.cloud systemd[1]: Started Initial cloud-init job (metadata service crawler).
```

###2-5-3. /etc/cloud/cloud.cfgにおけるdatasource_listプロパティーの確認。

docsにはConfigDriveとNoCloudを含むか、datasource_list自体が削除されているかどちらかが必要と書かれている。以下のように特に問題なし。

```
[root@rhel8custom ~]# grep datasource_list /etc/cloud/cloud.cfg
datasource_list: [ ConfigDrive, NoCloud ]
```

###2-5-4. /etc/cloud/cloud.cfgのcloud_final_modulesセクションにscripts-vendorが含まれていることを確認。
以下のように特に問題なし。

```
[root@rhel8custom ~]# cat /etc/cloud/cloud.cfg |grep cloud_final_modules -A 100
cloud_final_modules:
 - snappy
 - package-update-upgrade-install
 - fan
 - lxd
 - puppet
 - chef
 - salt-minion
 - mcollective
 - rightscale_userdata
 - scripts-vendor
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user
 - ssh-authkey-fingerprints
 - keys-to-console
 - phone-home
 - final-message
 - power-state-change
```

##2-6. /etc/fstabのauxiliary storage（SWAPなど）にnofailがついていることを確認。
以下のように問題なし。

```
[root@rhel8custom ~]# cat /etc/fstab
UUID=9782055f-a08d-4789-80d6-c8ac78e2d99a / ext4 defaults,noatime 0 0
UUID=d78d5620-7f7b-4743-b66c-590dca2c4787 /boot ext4 defaults,noatime 0 0
LABEL=SWAP-xvdb1	swap	swap	defaults,nofail,x-systemd.device-timeout=500ms	0	0
```

以上で、VSIの編集は完了である。

#3. Image Templateの作成。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/45ab3276-7f50-ac8f-527b-038ff2f85ffa.png)
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/61ce5114-351d-216a-ab58-aba5d4eaaecf.png)


#4. ICOSにexport

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/c0b1caa3-b9c3-0ea4-3657-3e90053bfd27.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/1ca9ca45-00bc-3cd6-6c5e-6a2df415f132.png)

以下のように無事Exportされた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/2a03bd99-771c-3fd7-1d3b-1401d1ca4a5a.png)


#5. VPCにCustom Imageとしてimport
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/afaf1adb-2642-71a5-fa3c-37479159cfdb.png)

OSは、`red-8-amd-64`ではなく、必ず`red-8-amd-64-byol`を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/89c4d681-0c06-a600-c0f1-e0709294bf30.png)

無事importが完了すると、以下のようになる。（SourceがCOS bucketになっていたり、Display nameがBYOLになっている）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/53478975-e8d1-f8b8-bce1-3af7f50c6dc1.png)


#6.プロビジョニング

Custom Imageを指定してプロビジョニングする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/6d0e2f53-c5b5-75ed-292d-e432a5d8f953.png)

これで、SSHでログインできるようになるので、Red HatのSubscription Managerの登録をしたり、yumの構成（Classic Infrastructureは`rhncaptok0201.service.networklayer.com`のままなので）を変更したりする。

#7. 課金情報の確認

IBM Cloudポータルから、Manage -> Billing and Usage -> Usage -> Virtual Server for VPCのView Plans -> 該当リージョンのView Details -> 該当サーバーのView instance details と選択していくと、課金情報が確認できる。

以下のように、今回プロビジョニングしたRHELサーバーには、`RHEL_INSTANCE_HOURS_BYOL`という項目が存在し、その課金が$0になっていることがわかる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/afc41784-1f2b-72b4-ff60-be7fa55d20f1.png)

参考までに、通常IBM Cloudから購入したRHEL VSIは、以下のように`RHEL_INSTANCE_HOURS`という項目が存在し、そのライセンス料金が課金されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/d0634d13-c3fa-5e4a-a8a2-fef455c236e8.png)