1. 概要
自分用に、Hyper-V 上に Rocky Linux の VM を作る手順をまとめます。
この VM は、GitLab コンテナを動かす土台や、VSCode の DevContainer の基盤として使うことを想定しています。職場に近いコンテナ環境(Podman + docker-compose)を手元に用意しておくのが狙いです。
本記事では、仮想スイッチの作成から、VM の作成・OS インストール・NAT 設定・Podman / docker-compose の導入までを扱います(GitLab コンテナの構築自体は別記事に分けます)。
2. Hyper-V で VM を作る
なお、自動化等が可能だと思うので、コマンドで実施していますが、GUI でも設定可能な内容です。
UEFI の設定や Hyper-V の有効化など、VM 作成の根本的な部分は省きますので、ご了承ください。
2.1. 構築の準備
職場の環境は、コンテナエンジンにPodmanを使用しており、コンテナの構成管理にdocker-composeを使用し、設定内容が見える(秘伝のタレにならない)ようになっています。それに近い環境の構築を目指したいと思います。
2.1.1. イメージの入手
手元にあるのが、Rocky Linux 10.1なのでこれを使用します。
職場のディストリビューション(RHEL9系)とはやや異なりますが、大きな差分にはならないと考えています。
2.1.2. docker-compose の入手
これは手順の中で実施しますが、GitHub から取得する必要があります。
この手順はLegacyとして扱われているので、あまり良い方法ではありませんが、職場の環境を再現するために実施します。
というより、そもそもRHEL標準リポジトリではDockerは配布されていない(Podmanに置換された)ので、標準リポジトリでdocker-composeを組み合わせるにはこの方法しかなさそう?(Docker 社の外部リポジトリから導入は可能だが Red Hat のサポート外)
ということで、以下を参考に standalone の docker-compose を Podman の Docker 互換ソケット(DOCKER_HOST)に向けて使うように設定します。
2.2. VM 構築手順
以降のコマンドは 「管理者として実行」した PowerShell で行います。
2.2.1. 仮想スイッチの作成
今回の検証では、外部からの接続は行わないので、内部仮想スイッチを作ります。
$SwitchName = "InternalSwitch"
New-VMSwitch `
-Name $SwitchName `
-SwitchType Internal
# 以下は出力結果
Name SwitchType NetAdapterInterfaceDescription
---- ---------- ------------------------------
InternalSwitch Internal
仮想スイッチのIPアドレスを192.168.100.1/24設定します。
New-NetIPAddress `
-InterfaceAlias "vEthernet ($SwitchName)" `
-IPAddress 192.168.100.1 `
-PrefixLength 24
# 以下は出力結果
IPAddress : 192.168.100.1
InterfaceIndex : 38
InterfaceAlias : vEthernet (InternalSwitch)
AddressFamily : IPv4
Type : Unicast
PrefixLength : 24
PrefixOrigin : Manual
SuffixOrigin : Manual
AddressState : Tentative
ValidLifetime : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource : False
PolicyStore : ActiveStore
作成後、NIC の設定を確認してください。追加したネットワークアダプタ(仮想スイッチ)が表示されれば問題ありません。
ipconfig /all
# 以下は出力結果
# 該当部分のみ掲載
イーサネット アダプター vEthernet (InternalSwitch):
接続固有の DNS サフィックス . . . . .:
説明. . . . . . . . . . . . . . . . .: Hyper-V Virtual Ethernet Adapter #4
物理アドレス. . . . . . . . . . . . .: 00-15-5D-20-86-11
DHCP 有効 . . . . . . . . . . . . . .: いいえ
自動構成有効. . . . . . . . . . . . .: はい
リンクローカル IPv6 アドレス. . . . .: fe80::1390:7148:87d3:67fc%38(優先)
IPv4 アドレス . . . . . . . . . . . .: 192.168.100.1(優先)
サブネット マスク . . . . . . . . . .: 255.255.255.0
デフォルト ゲートウェイ . . . . . . .:
DHCPv6 IAID . . . . . . . . . . . . .: 637539677
DHCPv6 クライアント DUID. . . . . . .: 00-01-01-00-30-E6-D2-3A-34-5A-60-66-C2-A9
NetBIOS over TCP/IP . . . . . . . . .: 有効
2.2.1.1. 外部スイッチの追加(任意)
今回の検証は内部スイッチ+NAT だけで完結しますが、「VM を物理 LAN に直結して、ホスト以外の端末からも VM に触りたい」場合は、内部スイッチに加えて外部スイッチも作れます(不要な方は読み飛ばしてください)。
外部スイッチは、VM を物理 NIC 経由で物理 LAN へ直結します(ホストと同一セグメント)。ここでは ホストと NIC を共有する方針 で作ります。こうすると、物理 NIC が 1 枚でもホスト側の通信(インターネット)が維持され、ホスト ↔ VM も外部スイッチ経由でそのまま通ります。
参考(仮想スイッチの作成):Create a virtual switch for Hyper-V virtual machines
まず、バインドする物理 NIC 名を確認します。
Get-NetAdapter
外部スイッチを作成します。-NetAdapterName を指定すると種別は自動的に「外部」になり、-AllowManagementOS を省略すると既定でホストと共有になります。<NIC名> は上で確認した物理 NIC(有線を推奨)に置き換えてください。
New-VMSwitch `
-Name "ExternalSwitch" `
-NetAdapterName "<NIC名>"
外部スイッチの作成時は、その NIC のネットワーク接続が一時的に中断されます(作成後にホスト側
vEthernet (ExternalSwitch)が IP を取り直します)。
外部スイッチの使い方は、目的によって次の 2 通りに分かれます。なお、VM のアダプター操作(Add-VMNetworkAdapter / Connect-VMNetworkAdapter)は VM を作成した後(2.2.3. VM の作成 以降)に実行してください($VmName も 2.2.2.1. VMの情報 で定義します)。
(a) 内部スイッチと両立させる(VM に NIC を 2 枚持たせる)
内部スイッチ側(192.168.100.10)はホスト ↔ VM の管理用として残し、外部スイッチ側でインターネット/LAN に出ます。VM 作成後、2 枚目のアダプターを追加します。
Add-VMNetworkAdapter `
-VMName $VmName `
-SwitchName "ExternalSwitch"
VM 側では、外部側の NIC を DHCP(デフォルト経路を持つ側)、内部側の NIC を静的 IP(デフォルトゲートウェイなし)に設定します。
※デフォルトルートを 2 本持たせると経路が二重化して不安定になります。デフォルトゲートウェイは外部側の 1 本だけにしてください。
(b) 外部スイッチだけにする(内部スイッチをやめる)
VM の接続先を外部スイッチへ切り替えます。アダプター名は Get-VMNetworkAdapter -VMName $VmName で確認できます。
Connect-VMNetworkAdapter `
-VMName $VmName `
-Name "ネットワーク アダプター" `
-SwitchName "ExternalSwitch"
VM 側の IP は物理 LAN に合わせて DHCP で取り直します(本手順の静的 IP 192.168.100.10 は内部スイッチ専用なので、そのままでは通りません)。
※(a)(b) いずれも、インターネットは外部スイッチ直結で出られるので、2.4. NAT 設定 は不要です。
2.2.2. VM 設定用のシェル変数の準備
2.2.2.1. VMの情報
以降で使用するシェル変数を準備しておきます。
$VmName = "Rocky-Podman"
$ISO = "C:\ISO\RockyLinux10.1.iso" # iso ファイルがあるパスに書き換えること
$DefaultSaveFolder = (Get-VMHost).VirtualHardDiskPath
$VhdFullPath = "$DefaultSaveFolder\$VmName.vhdx"
2.2.2.2. メモリ割り当て量
VMに割り当てるメモリサイズもシェル変数として準備しておきます(2MB の倍数にしないとエラーになります)。
なお、メモリのサイズについては、Windows 11 の最低要件である4GBを残すため、$MaxVmMemory は、$TotalHostMemory から4GB引いた値を割り当てています。
また、$MinVmMemoryを小さくしすぎると、メモリひっ迫時に VM が設定値通りの最小 RAM 付近まで搾られる可能性がありますので、最小とはいえ、なるべくシステムが落ちないような値を設定しましょう。
$TotalHostMemory = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory
$MaxVmMemory = [math]::Floor(($TotalHostMemory - 4GB) / 2MB) * 2MB
$MinVmMemory = 4GB # 要件に応じて要調整
やや分かりにくいので、計算例を書いておきます(RAM: 32GB の場合)。
$MaxVmMemory: 32GB - 4GB = 28GB → ÷2MB = 14,336 個(今回は割り切れているので、Floor後も変化なし)
Floor → 14,336 個 ×2MB = 28GB
2.2.2.3. CPU コア数
VM に割り当てる CPU のコア数も割り当てます。
最近の Intel CPU は P コア/E コア混在で、どのコアを使うか手動指定できないこともあるので(私が使用しているのは Ryzen 7 5700X ですが)、全論理コアを割り当て、使用率設定でリソースを共有することで、E コアだけで動く(遅くなる)のを避けられるようにしようと思います。
$TotalHostCores = (Get-ComputerInfo).CsNumberOfLogicalProcessors
2.2.2.4. 仮想ハードディスク
仮想ハードディスクのオブジェクトを生成します。なお、動的 VHDX にするので、サイズを大きめに取っても即座に占有はしません。
$VHD = New-VHD `
-Path $VhdFullPath `
-SizeBytes 128GB `
-Dynamic
2.2.3. VM の作成
これまでに準備したシェル変数をもとに、仮想マシンを作成します。
New-VM `
-Name $VmName `
-MemoryStartupBytes 4GB `
-Generation 2 `
-SwitchName $SwitchName `
-VHDPath $VHD.Path
2.2.4. CPU 使用率の割り当て
少数コアを 100% 占有させるのではなく、使用率(優先度) で共有します。最低 50%・最大 80% を割り当てます。
Set-VMProcessor `
-VMName $VmName `
-Count $TotalHostCores `
-Reserve 50 `
-Maximum 80
※-Reserve/-Maximum は「常に占有」ではなく、スケジューリング(CPU を時間で分け合う仕組み)上の優先度の下限(必要時の最低保証)・上限を指します。
2.2.5. 動的メモリの設定
ホストと VM でメモリを効率的に融通するために、動的メモリを使用します。
Set-VMMemory `
-VMName $VmName `
-DynamicMemoryEnabled $true `
-MinimumBytes $MinVmMemory `
-MaximumBytes $MaxVmMemory `
-Buffer 30
※-Buffer 30は、VM が必要とする量に対し 30%の空きを確保するよう動的に増やすこと。大きすぎるとホストのメモリが枯渇するので注意。
2.2.6. Secure Boot の有効化
OS イメージの正規性を検証でき、起動前の不正コード実行を防ぐことが可能です。今回の検証ではあまり意味はないおと思いますが、有効化しておきます。
Linux ディストリビューションでは MicrosoftUEFICertificateAuthority テンプレートの選択が必須です。間違うと起動しません。
Set-VMFirmware `
-VMName $VmName `
-EnableSecureBoot On `
-SecureBootTemplate MicrosoftUEFICertificateAuthority
2.2.7. チェックポイント種別の設定
現状では、ゲストエージェント(hyperv-daemons)を導入しないので、ここでは標準チェックポイントを使用します。
Set-VM `
-Name $VmName `
-CheckpointType Standard
2.2.8. ISO のマウントとブート順の設定
DVD-ISO をマウントします。
Add-VMDvdDrive `
-VMName $VmName `
-Path $ISO
DVD ドライブを最優先で起動するよう、ブート順を設定します。
Set-VMFirmware `
-VMName $VmName `
-FirstBootDevice (Get-VMDvdDrive -VMName $VmName)
2.2.9. OS 起動前のエクスポート(バックアップ)
初回起動やOSのインストールに失敗した場合のため、起動前の状態(やり直しが効く状態)をエクスポートしておきます。
$ExportPath = "C:\EXAMPLE" # 任意のパスを指定してください
Export-VM `
-Name $VmName `
-Path $ExportPath
# ファイルが出力されたことを確認する
explorer $ExportPath
2.3. OS インストール手順
以下のコマンドで VM を起動します。
vmconnect.exe localhost $VmName
Start-VM -Name $VmName
2.3.1. インストーラーでの設定内容
インストーラーで次のような設定を行います。
| 設定項目 | 本手順の値 | 補足 |
|---|---|---|
| 言語 / キーボード | 日本語 or English(任意) | GUI は入らないので好みで可 |
| 時刻と日付 | アジア/東京 | — |
| ソフトウェアの選択 | 最小限のインストール(Minimal Install) | 軽量化のために GUI を入れない。アドオンも選択しない。 |
| インストール先 | 対象ディスクを選択 → 完了(自動構成) | 単一ディスクを使用 |
| KDUMP | 無効化(任意) | メモリ節約。チェックを外す |
| ネットワークとホスト名 | 手順 2.3.2. | 静的 IP |
| root アカウント | 手順 2.3.3. | root SSH は禁止 |
| ユーザーの作成 | 手順 2.3.4. | wheel(管理者)付き |
2.3.2. ネットワークとホスト名
まず、画面左下のホスト名に rocky-podman と入力して適用します。
その後、右上の Ethernet(eth0)トグルをオンにし、以降の手順でインタフェースの設定を行います。
- 設定 → IPv4 Settings タブ:
- Method:Manual
- Add で以下を入力:
- Address:192.168.100.10
- Netmask:255.255.255.0(または 24)
- Gateway:192.168.100.1 (2.2.1. 仮想スイッチの作成 で設定した IP アドレスを使用)
- DNS サーバー:8.8.8.8(NAT 経由で到達できるもの)
- Save → 左上の「完了」ボタンを押す
2.3.3. root アカウント
「root アカウントを有効化」を選び、強いパスワードを設定します(「良好」以上)。
この検証ではあまり関係ありませんが、「総当たり攻撃で root を直接狙われないようする」、ということを心がけようと思うので、root の SSH ログインは無効にします。
よって、「パスワードによる root SSH ログインを許可」のチェックは外しましょう。
2.3.4. ユーザーの作成
以下のように設定します。
- フルネーム:人間が読むための名称。任意に入力する(ユーザー名と同じでも良い)
- ユーザー名:ssh や su などで使用。英小文字・空白なし・英字始まりで、任意に入力する
- 「ユーザーアカウントに管理者権限を付与する(wheel グループに加える)」にチェックを入れる
- 2.2.3. root アカウントと同様に、強いパスワードを設定する
- 左上の「完了」ボタンを押す
2.3.5. インストール開始と再起動
設定の抜けがないか見直し、右下の「インストールの開始」を押下します。
なお、SMT(同時マルチスレッディング)に関する警告が出ても、本用途では、非本番・限定ネットワークなので無視します。
完了後、「システムの再起動」を押下してください。
また、ISO からの再起動ループを避けるため、再起動後に、以下のコマンドで ISO を外しておきます。
Set-VMDvdDrive -VMName $VmName -Path $null
2.3.6. 初回ログイン
次のような画面が表示されますので、ユーザー名とパスワードを入力し、ログインします。
rocky-podman login: # 設定したユーザー名を入力
Password: # 設定したパスワードを入力
動作確認として次のコマンドを入力します。
cat /etc/rocky-release
すると、次の内容が出力されるはずです。
Rocky Linux release 10.1 (Red Quartz)
2.3.7. ホスト(Windows)からの SSH 疎通確認
最小構成でも OpenSSH サーバーは既定で起動し、firewalld も SSH を許可しているはずですが、念のため確認しておきます。
# SSH サーバーが稼働中か
systemctl is-active sshd
# active なら稼働中
# SSH サーバーが自動起動するか
systemctl is-enabled sshd
# enabled なら自動起動
# 22番ポートを待ち受けているか
ss -tlnp | grep ':22'
# 以下は出力結果
# :22 の LISTEN 行が出れば問題なし
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* ← IPv4・全インターフェースで :22 を待ち受けている
LISTEN 0 128 [::]:22 [::]:* ← IPv6・全インターフェースで :22 を待ち受けている
# firewalld が動作しているか
sudo firewall-cmd --state
# パスワードを尋ねられたら入力
# 稼働中の場合、running が出力される
# ファイアウォールで許可(開放)されているサービスの一覧を確認する
sudo firewall-cmd --list-services
# 出力例:cockpit dhcpv6-client ssh
# 上記の出力の場合、一番左に ssh があるため、SSHが使用可能である。
# 自分のIPが 192.168.100.10 になっているか
ip -brief addr
上記で問題ないことを確認できたら、ssh を実行しましょう。
ssh <ユーザー名>@192.168.100.10
SSH 接続ができたら Hyper-V のコンソールを閉じます。
Stop-Process -Name vmconnect
2.4. NAT 設定
2.2.1. 仮想スイッチの作成 で内部スイッチとホスト側 IP(192.168.100.1)は作成済みです。しかし、内部スイッチ単体では VM はインターネットに出られません(通信できるのは「ホスト ↔ VM」だけで、物理 NIC には直接つながらない)。
そこで、ホストが VM の通信を外へ中継する(送信元を書き換えて転送する=SNAT) NAT を作成します。
これで、以下の経路でインターネットにつながるようになります。
VM → ゲートウェイ(192.168.100.1=ホスト) → NAT → 物理NIC → インターネット
参考(NAT ネットワークの考え方):Set up a NAT network
2.4.1. NAT の作成
以降のコマンドは 「管理者として実行」した PowerShell で行います。
内部ネットワークの範囲(192.168.100.0/24)に対する NAT を作成します。
New-NetNat `
-Name "HyperV-NAT" `
-InternalIPInterfaceAddressPrefix 192.168.100.0/24
作成できたか確認します。
Get-NetNat
# 以下は出力結果
# Active が True、プレフィックスが 192.168.100.0/24 であれば OK
Name : HyperV-NAT
InternalIPInterfaceAddressPrefix : 192.168.100.0/24
Store : Local
Active : True
2.4.2. 疎通確認
VM に戻り、「ホスト到達 → インターネット到達(名前解決)」の順に確認します。
まず、ゲートウェイ(ホスト)に届くか。これが通れば NAT の土台(内部スイッチ+ホスト IP)は正常です。
ping -c 3 192.168.100.1
# ホスト(ゲートウェイ)に届けば、内部スイッチ側は OK
次に、名前解決(DNS)ができるかを確認します。
ping -c 3 rockylinux.org
# 応答があれば OK
2.5. 初期設定とPodman導入
2.5.1. システム更新
インターネットに接続できることを確認できたら、システムを更新します。
sudo dnf update -y
2.5.2. Hyper-V 連携デーモン
この手順は任意です。
時刻同期・ホストからの正常シャットダウン・IP 情報の通知・(将来の)運用チェックポイントの有効化に効くので、Hyper-V 連携デーモン(hyperv-daemons)を導入します。
sudo dnf install -y hyperv-daemons
デーモンの有効化および起動を行います。
sudo systemctl enable --now hypervkvpd hypervvssd hypervfcopyd
2.2.7. チェックポイント種別の設定では、標準チェックポイントを選択しましたが、運用チェックポイントに切り替えたい場合は、PowerShell で以下のコマンドを実行します。
Set-VM `
-Name $VmName `
-CheckpointType ProductionOnly
ゲスト サービス インターフェイスも有効化します。
Get-VMIntegrationService -VMName $VmName
# 出力例
VMName Name Enabled PrimaryStatusDescription SecondaryStatusDescription
------ ---- ------- ------------------------ --------------------------
Rocky-Podman ゲスト サービス インターフェイス False OK
Rocky-Podman ハートビート True OK
Rocky-Podman キー値ペア交換 True OK
Rocky-Podman シャットダウン True OK
Rocky-Podman 時刻の同期 True OK
Rocky-Podman VSS True OK
Enable-VMIntegrationService `
-VMName $VmName `
-Name "ゲスト サービス インターフェイス"
2.5.3. 基本ツールと Podman の導入
(VM に戻り、)次のパッケージを導入します。任意に追加しても良いです。
sudo dnf install -y podman git curl tar
バージョンを確認します。
podman --version
# 表示されれば OK
2.5.4. チェックポイント作成(Podman導入完了)
ここまでの状態を保存したいので、念のため、チェックポイントを作成しておきます。PowerShell で以下のコマンドを実行してください。
Checkpoint-VM `
-Name $VmName `
-SnapshotName "Podman インストール完了"
2.5.5. Docker API 互換ソケットの作成
これは「Podman を Docker のふりをさせて、docker-compose から使えるようにする」ための手順です。
ユーザーソケットを有効化します。
systemctl --user enable --now podman.socket
# 出力例
Created symlink '/home/ユーザー名/.config/systemd/user/sockets.target.wants/podman.socket' → '/usr/lib/systemd/user/podman.socket'.
ユーザーソケットは、docker-compose が命令を送る“受け口”(差込口)となります。
--user は「そのユーザー」専用の systemd で開くことを意味します。Podman 自体は一般ユーザー(=rootless)のまま動くので、特別な"rootless化"は不要です。ここで行っているのは、docker-compose がつなぐための「Docker 互換ソケット」を、そのユーザー権限で有効化することです。
ソケット作成後、DOCKER_HOST 変数を設定します。
echo 'export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock' >> ~/.bashrc
source ~/.bashrc
実行後、状態を確認します。
systemctl --user status podman.socket
# 出力例
# Active: active (listening) になっていれば OK
podman.socket - Podman API Socket
Loaded: loaded (/usr/lib/systemd/user/podman.socket; enabled; preset: disabled)
Active: active (listening) since Sun 2026-10-25 19:40:33 JST; 7min ago
rootless のユーザーサービスは、既定ではログアウトで止まるので、ログアウト後もコンテナが動くようにします。
loginctl enable-linger $USER
2.5.6. docker-compose の導入
事情は 2.1.2. docker-compose の入手 にも書きましたが、Docker 公式の standalone インストール手順に従い、GitHub から docker-compose を入手します。
以下のコマンドを実行します。
# バージョン確認
COMPOSE_VER=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') && echo $COMPOSE_VER
# ダウンロード実行
sudo curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
# 実行権限の付与
sudo chmod +x /usr/local/bin/docker-compose
動作確認としてバージョンを確認します。
docker-compose --version
先ほど設定したソケットを使って、コンテナエンジン(Podman)に問い合わせ可能かを確認します。
echo $DOCKER_HOST
# unix:///run/user/1000/podman/podman.sock
docker-compose ls
# エラーにならず、空のプロジェクト一覧が出れば OK
# NAME STATUS CONFIG FILES
# 出力なし
2.5.7. チェックポイント作成(Docker Compose導入完了)
ここまでの状態を保存したいので、念のため、チェックポイントを作成しておきます。PowerShell で以下のコマンドを実行してください。
Checkpoint-VM `
-Name $VmName `
-SnapshotName "Docker Compose インストール完了"