はじめに
以前、PowerShellからHyper-VのVM作成を行う記事を書いた。 ただ、例で使ったAlmaLinuxの場合、VMを作成した後にGUIからポチポチとインストールを行う必要がある。検証用のVMを何度も作る場合、面倒だ。
今回は、Red Hat系で使われる自動インストール機能であるKickstartを使って、 AlmaLinuxのインストールを自動化したので、その備忘録を残す。
実施環境
- ホストOS: Windows 11
- ゲストOS: AlmaLinux 10.1
- PowerShellバージョン: 5.1
前提
この記事の手順は管理者権限で起動したPowerShellで実行する。
Kickstartの設定ファイルを作成する
Kickstartでインストールするためには、ks.cfgファイルを作成する必要がある。
PowerShellから以下コマンドで、ks.cfgファイルをHyper-VのVM用フォルダに作成する。
# VMの名前(好きな名前でOK)
$vmName = "AlmaLinux10-1-sandbox"
# 作成したVMに関連するファイル(チェックポイント等)の保存先(好きなパスでOK)
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
# 今回はKickstart関連ファイルの保存先(好きなパスでOK)
$ksDir = "$vmPath\kickstart"
# `ks.cfg`ファイルのファイル名と保存先
$ksFile = "$ksDir\ks.cfg"
# Kickstart関連ファイルの保存先を作成する
New-Item -ItemType Directory -Force -Path $ksDir | Out-Null
# `ks.cfg`ファイルを作成する
$ksContent = @'
# GUIではなく、テキストモードでインストールする
text
# インストール完了後にシャットダウンする
# PowerShell側でVM停止を検知し、ISOとKickstart用VHDXを外すため
shutdown
# インストール元としてCD/DVDメディアを使う
cdrom
# 言語、キーボード、タイムゾーン
# キーボードを日本語配列にしたい場合は、keyboard --xlayouts='jp'にする
lang en_US.UTF-8
keyboard --xlayouts='us'
timezone Asia/Tokyo --utc
# DHCPでネットワークを有効化し、ホスト名を「almalinux.local」にする
network --bootproto=dhcp --device=link --activate --hostname=almalinux.local
# rootのパスワードを指定する
rootpw --plaintext example_root_password
# root以外のユーザを作成し、wheelグループに入れる
user --name=example_user --password=example_user_password --plaintext --groups=wheel
# SSHを許可し、FirewallとSELinuxは有効にする
firewall --enabled --service=ssh
selinux --enforcing
# ブートローダー設定
bootloader --location=mbr
# インストール対象は1つ目のディスクだけにする
ignoredisk --only-use=sda
# 既存パーティションがあれば削除し、自動パーティション作成する
zerombr
clearpart --all --initlabel --drives=sda
autopart --type=lvm
# SSHサーバを入れておく
%packages
@^minimal-environment
openssh-server
%end
# インストール後にSSHを自動起動し、日本語ロケールを設定する
# dnfでインストールしておきたいものがあれば、ここに書く
%post
systemctl enable sshd
dnf install -y glibc-langpack-ja
echo 'LANG=ja_JP.UTF-8' > /etc/locale.conf
%end
'@
# BOMなしUTF-8で保存する
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($ksFile, $ksContent, $utf8NoBom)
Kickstart用VHDXを作成する
Red Hat系では、OEMDRVというラベルのボリューム直下にks.cfgがあると、自動でKickstartファイルとして読み込む。そのため、今回はOEMDRVラベルのVHDXを作成し、Windowsにマウントした後、ks.cfgを配置するようにする。
以下コマンドを実行する。冒頭にも書いたとおり、管理者権限で実行すること。
# Kickstart用VHDXの保存先
$ksVhdxPath = "$vmPath\kickstart-oemdrv.vhdx"
# Kickstart用VHDXを作成する
New-VHD -Path $ksVhdxPath -SizeBytes 64MB -Dynamic | Out-Null
# マウントするパス
$mountPath = "$vmPath\kickstart-mount"
# マウント先フォルダを作成する
New-Item -ItemType Directory -Force -Path $mountPath | Out-Null
# フォルダにマウントしたかどうかのフラグ用変数
$accessPathAdded = $false
try {
# VHDXをWindowsにマウントする
$vhd = Mount-VHD -Path $ksVhdxPath -PassThru
# マウントしたVHDXに対応するディスク情報を取得する
$disk = $vhd | Get-Disk
# 初期化してFAT32でフォーマットする
# ラベルはKickstart自動検出用にOEMDRVにする
Initialize-Disk -Number $disk.Number -PartitionStyle MBR
# VHDXを使ってパーティションを作成する
$partition = New-Partition `
-DiskNumber $disk.Number `
-UseMaximumSize
# 自動でドライブ文字を割り当てないようにする
Set-Partition `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-NoDefaultDriveLetter $true
# 作成したパーティション情報を再取得する
$partition = Get-Partition `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber
# 念のため、ドライブ文字が割り当てられていた場合は削除する
if ($partition.DriveLetter) {
Remove-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath "$($partition.DriveLetter):\"
}
# パーティションをFAT32でフォーマットし、ラベルをOEMDRVにする
Format-Volume `
-Partition $partition `
-FileSystem FAT32 `
-NewFileSystemLabel OEMDRV `
-Confirm:$false | Out-Null
# ドライブ文字ではなく、フォルダにマウントする
Add-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath $mountPath
$accessPathAdded = $true
# KickstartファイルをVHDXのルート直下にks.cfgとしてコピーする
Copy-Item `
-LiteralPath $ksFile `
-Destination "$mountPath\ks.cfg" `
-Force
}
finally {
# フォルダにマウントしている場合、マウントを解除する
if ($accessPathAdded) {
Remove-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath $mountPath `
-ErrorAction SilentlyContinue
}
# 処理中にエラーが発生しても、必ずVHDXをアンマウントする
Dismount-VHD -Path $ksVhdxPath -ErrorAction SilentlyContinue
}
Kickstartを使ってVMを作成する
前回の記事を参考にVMを作成する。ただし、今回はks.cfgがあるVHDXを接続して作成する。
ダウンロードしたAlmaLinuxのISOファイルは、前回と同様、%USERPROFILE%\Downloads\に置くことにする。
まず、前回と同様に以下コマンドでVMを作成する。
# AlmaLinux VMの.vhdxファイルの保存先
$vhdPath = "$vmPath\$vmName.vhdx"
# ダウンロードしたAlmaLinux ISOの保存先
$isoPath = "$env:USERPROFILE\Downloads\AlmaLinux-10.1-x86_64-minimal.iso"
# 接続する仮想スイッチ名
$switchName = "Default Switch"
# 動的メモリを使用するかどうか
$dynamicMemoryEnabled = $true
# VM起動時に割り当てるメモリサイズ
$memoryStartupBytes = 4GB
# 最小メモリ
$minimumBytes = 2GB
# 最大メモリ
$maximumBytes = 8GB
# VMの仮想ストレージの最大サイズ
$vhdSizeBytes = 40GB
# VMに割り当てる仮想CPUコア数
$processorCount = 2
# 仮想マシンの世代
$generation = 2
# VMを作成する
New-VM `
-Name $vmName `
-Path $vmPath `
-NewVHDPath $vhdPath `
-SwitchName $switchName `
-NewVHDSizeBytes $vhdSizeBytes `
-Generation $generation
# CPUの設定を行う
Set-VMProcessor -VMName $vmName -Count $processorCount
# メモリの設定を行う
Set-VMMemory `
-VMName $vmName `
-DynamicMemoryEnabled $dynamicMemoryEnabled `
-StartupBytes $memoryStartupBytes `
-MinimumBytes $minimumBytes `
-MaximumBytes $maximumBytes
# チェックポイントを無効にする
Set-VM -Name $vmName -CheckpointType Disabled
# DVDドライブにAlmaLinux ISOを接続
Add-VMDvdDrive -VMName $vmName -Path $isoPath
# Secure Bootを有効にする
Set-VMFirmware -VMName $vmName -EnableSecureBoot On -SecureBootTemplate MicrosoftUEFICertificateAuthority
# AlmaLinux ISOから起動するようにする
$dvdDrive = Get-VMDvdDrive -VMName $vmName | Where-Object { $_.Path -eq $isoPath }
Set-VMFirmware -VMName $vmName -FirstBootDevice $dvdDrive
ここまでは前回とほぼ同じだが、今回はKickstart用のOEMDRV VHDXを接続するようにする。
# Kickstart用のOEMDRV VHDXを接続する
Add-VMHardDiskDrive -VMName $vmName -Path $ksVhdxPath
接続した状態で、VMを起動させインストールを開始させる。インストールが完了し、Kickstartに書いたshutdownによりVMが停止するまで待つ。
インストール完了後、AlmaLinux ISOとKickstart用VHDXを外すようにして、VMを起動させる。
以下のコマンドをまとめて実行すると、インストール完了後にVMを起動し、vmconnect.exeで接続するところまで行える。VM接続画面が表示されれば、インストール後の起動まで完了したことが分かる。
# VMを起動してインストール開始
Start-VM -Name $vmName
# インストール完了後、KickstartのshutdownによりVMが停止するまで待つ
$timeoutMinutes = 60
$deadline = (Get-Date).AddMinutes($timeoutMinutes)
while ((Get-VM -Name $vmName).State -ne "Off") {
if ((Get-Date) -gt $deadline) {
throw "インストール完了待ちがタイムアウトしました。VMの画面を確認してください。"
}
Start-Sleep -Seconds 10
}
# インストールISOを外す
Get-VMDvdDrive -VMName $vmName |
Set-VMDvdDrive -Path $null
# Kickstart用VHDXを外す
Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $ksVhdxPath } |
Remove-VMHardDiskDrive
# 本体VHDXから起動するようにする
$osDisk = Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $vhdPath }
Set-VMFirmware -VMName $vmName -FirstBootDevice $osDisk
# インストール済みAlmaLinuxを起動する
Start-VM -Name $vmName
# 接続する
vmconnect.exe localhost $vmName
これでVMに接続し、ログインできればOK。
VM作成から接続までのスクリプト
前回と同様に、VM作成からOSインストール、接続までの手順をまとめたスクリプトを貼る。実行後はインストール完了まで待つだけなので便利だ。冒頭にも書いたとおり、管理者権限が必要なことに注意。ps1スクリプトとして使うなら、先頭に#Requires -RunAsAdministratorを書いた方がいいかもしれない。
$vmName = "AlmaLinux10-1-sandbox"
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
$ksDir = "$vmPath\kickstart"
$ksFile = "$ksDir\ks.cfg"
New-Item -ItemType Directory -Force -Path $ksDir | Out-Null
$ksContent = @'
# GUIではなく、テキストモードでインストールする
text
# インストール完了後にシャットダウンする
# PowerShell側でVM停止を検知し、ISOとKickstart用VHDXを外すため
shutdown
# インストール元としてCD/DVDメディアを使う
cdrom
# 言語、キーボード、タイムゾーン
# キーボードを日本語配列にしたい場合は、keyboard --xlayouts='jp'にする
lang en_US.UTF-8
keyboard --xlayouts='us'
timezone Asia/Tokyo --utc
# DHCPでネットワークを有効化し、ホスト名を「almalinux.local」にする
network --bootproto=dhcp --device=link --activate --hostname=almalinux.local
# rootのパスワードを指定する
rootpw --plaintext example_root_password
# root以外のユーザを作成し、wheelグループに入れる
user --name=example_user --password=example_user_password --plaintext --groups=wheel
# SSHを許可し、FirewallとSELinuxは有効にする
firewall --enabled --service=ssh
selinux --enforcing
# ブートローダー設定
bootloader --location=mbr
# インストール対象は1つ目のディスクだけにする
ignoredisk --only-use=sda
# 既存パーティションがあれば削除し、自動パーティション作成する
zerombr
clearpart --all --initlabel --drives=sda
autopart --type=lvm
# SSHサーバを入れておく
%packages
@^minimal-environment
openssh-server
%end
# インストール後にSSHを自動起動し、日本語ロケールを設定する
%post
systemctl enable sshd
dnf install -y glibc-langpack-ja
echo 'LANG=ja_JP.UTF-8' > /etc/locale.conf
%end
'@
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($ksFile, $ksContent, $utf8NoBom)
$ksVhdxPath = "$vmPath\kickstart-oemdrv.vhdx"
New-VHD -Path $ksVhdxPath -SizeBytes 64MB -Dynamic | Out-Null
$mountPath = "$vmPath\kickstart-mount"
New-Item -ItemType Directory -Force -Path $mountPath | Out-Null
$accessPathAdded = $false
try {
$vhd = Mount-VHD -Path $ksVhdxPath -PassThru
$disk = $vhd | Get-Disk
Initialize-Disk -Number $disk.Number -PartitionStyle MBR
$partition = New-Partition `
-DiskNumber $disk.Number `
-UseMaximumSize
Set-Partition `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-NoDefaultDriveLetter $true
$partition = Get-Partition `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber
if ($partition.DriveLetter) {
Remove-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath "$($partition.DriveLetter):\"
}
Format-Volume `
-Partition $partition `
-FileSystem FAT32 `
-NewFileSystemLabel OEMDRV `
-Confirm:$false | Out-Null
Add-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath $mountPath
$accessPathAdded = $true
# KickstartファイルをVHDXのルート直下にks.cfgとしてコピーする
Copy-Item `
-LiteralPath $ksFile `
-Destination "$mountPath\ks.cfg" `
-Force
}
finally {
# フォルダにマウントしている場合、マウントを解除する
if ($accessPathAdded) {
Remove-PartitionAccessPath `
-DiskNumber $disk.Number `
-PartitionNumber $partition.PartitionNumber `
-AccessPath $mountPath `
-ErrorAction SilentlyContinue
}
Dismount-VHD -Path $ksVhdxPath -ErrorAction SilentlyContinue
}
$vhdPath = "$vmPath\$vmName.vhdx"
$isoPath = "$env:USERPROFILE\Downloads\AlmaLinux-10.1-x86_64-minimal.iso"
$switchName = "Default Switch"
$dynamicMemoryEnabled = $true
$memoryStartupBytes = 4GB
$minimumBytes = 2GB
$maximumBytes = 8GB
$vhdSizeBytes = 40GB
$processorCount = 2
$generation = 2
New-VM `
-Name $vmName `
-Path $vmPath `
-NewVHDPath $vhdPath `
-SwitchName $switchName `
-NewVHDSizeBytes $vhdSizeBytes `
-Generation $generation
Set-VMProcessor -VMName $vmName -Count $processorCount
Set-VMMemory `
-VMName $vmName `
-DynamicMemoryEnabled $dynamicMemoryEnabled `
-StartupBytes $memoryStartupBytes `
-MinimumBytes $minimumBytes `
-MaximumBytes $maximumBytes
Set-VM -Name $vmName -CheckpointType Disabled
Add-VMDvdDrive -VMName $vmName -Path $isoPath
Set-VMFirmware -VMName $vmName -EnableSecureBoot On -SecureBootTemplate MicrosoftUEFICertificateAuthority
$dvdDrive = Get-VMDvdDrive -VMName $vmName | Where-Object { $_.Path -eq $isoPath }
Set-VMFirmware -VMName $vmName -FirstBootDevice $dvdDrive
Add-VMHardDiskDrive -VMName $vmName -Path $ksVhdxPath
Start-VM -Name $vmName
$timeoutMinutes = 60
$deadline = (Get-Date).AddMinutes($timeoutMinutes)
while ((Get-VM -Name $vmName).State -ne "Off") {
if ((Get-Date) -gt $deadline) {
throw "インストール完了待ちがタイムアウトしました。VMの画面を確認してください。"
}
Start-Sleep -Seconds 10
}
Get-VMDvdDrive -VMName $vmName |
Set-VMDvdDrive -Path $null
Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $ksVhdxPath } |
Remove-VMHardDiskDrive
$osDisk = Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $vhdPath }
Set-VMFirmware -VMName $vmName -FirstBootDevice $osDisk
Start-VM -Name $vmName
vmconnect.exe localhost $vmName
VMの削除
以下コマンドでVMやその関連ファイル、Kickstart関連ファイルを削除できる。
# 作成したVMの名前
$vmName = "AlmaLinux10-1-sandbox"
# VM関連ファイルのパス
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
# Kickstart関連ファイル・ディレクトリ
$ksVhdxPath = "$vmPath\kickstart-oemdrv.vhdx"
# VMを取得する
$vm = Get-VM -Name $vmName -ErrorAction SilentlyContinue
if ($null -ne $vm) {
# VMを停止する
if ($vm.State -ne 'Off') {
Stop-VM -Name $vmName -TurnOff -Force
while ((Get-VM -Name $vmName).State -ne 'Off') {
Start-Sleep -Seconds 1
}
}
# VMを削除する
Remove-VM -Name $vmName -Force
}
# Kickstart用VHDXがマウントされたままならアンマウントする
Dismount-VHD -Path $ksVhdxPath -ErrorAction SilentlyContinue
# VM用ディレクトリごと削除する
if (Test-Path -LiteralPath $vmPath) {
Remove-Item -LiteralPath $vmPath -Recurse -Force
}
おわりに
Red Hat系で/rootにあるanaconda-ks.cfgって何者なんだろうとこれまで思っていたが、今回でKickstart用のファイルだということが分かった。
Ubuntuではautoinstallというもので、Kickstartと同様のことが出来るようだ。いずれ、試してみたいと思う。
コメント