自宅サーバ(Ubuntu)上にKVM+cockpitで検証・学習環境を立てた話

## 1.はじめに：学習環境としてのKVM
普段はUbuntuを中心に触っている私ですが、業務ではRHELやCentOSなどのRedHat系ディストロについて触れる機会がなにかと多いものです。[^9]
また資格(LinuC)の取得にあたり、`rpm`や`yum`のオプションに精通しておく必要もありました。
[^9]:CentOSまわりのもろもろでこの辺の事情も変わってくるのかもしれません。

そんなわけで、KVMによる仮想マシンをサーバ機内で立ち上げ、そこにAlmaLinux9を導入して学習用環境にすることを考えました。 

今回使用する自宅サーバ機のスペックは以下の通り。

- CPU:Ryzen 3 4300G
- RAM:DDR4-3200 8GB
- OS:Ubuntu 22.04 LTS Server

現状はSambaでのバックアップとRustdeskのサーバとして使用しており、今回流用する形としました。
OSはUbuntu 22.04 LTS Serverです。[^1]IPアドレスの固定化設定が済んでいることを前提とします。

[^1]:なお新規に専用で構築するのであれば、｢インストール時の選択次第でCockpitが標準で導入される｣｢標準でネットワーク管理がNetworkManagerになっている｣の二点においてRHEL派生ディストロのほうが楽ではあります。
 
また、クライアントPC側は、SSH接続が可能な環境とWebブラウザがあり、サーバ側があればよいです。
(最近は標準のWindowsからでもいけるらしいですね……)

## 2.Cockpitの導入
Cockpitはサーバ管理用のWebコンソールです。
RHEL派生ディストロの一部環境では、起動時やログイン時に下記のような表示が出てくるのを見たことがあるかもしれません。
それです。
```
Activate the web console with : systemctl enable --now cockpit.socket
```

Ubuntuでもリポジトリに入っており、簡単にインストールすることが可能です。


なお、以下特記なき限りコマンドはサーバ側のホストにSSH接続して入力します。
```
$ sudo apt update
$ sudo apt install cockpit cockpit-machines
```
`cockpit`パッケージがcockpitの本体、`cockpit-machines`パッケージがcockpitからKVMを操作するのに必要なパッケージです。
`cockpit`のパッケージは管理したい機能ごとに切り出されていて、他にも下記のような関連パッケージがある[^3]ので、必要に応じて導入すると良いでしょう。
[^3]:図には、cockpitへの依存関係で勝手に入るものも含んでいます。
<details><summary>関連パッケージ一覧</summary>

- Ubuntu 22.04 LTSの場合
```
$ dpkg -l cockpit-*
要望=(U)不明/(I)インストール/(R)削除/(P)完全削除/(H)保持
| 状態=(N)無/(I)インストール済/(C)設定/(U)展開/(F)設定失敗/(H)半インストール/(W)トリガ待ち/(T)トリガ保留
|/ エラー?=(空欄)無/(R)要再インストール (状態,エラーの大文字=異常)
||/ 名前                   バージョン           アーキテクチ 説明
+++-======================-====================-============-===========================================
ii  cockpit-bridge         264-1ubuntu0.22.04.1 amd64        Cockpit bridge server-side component
un  cockpit-dashboard      <なし>               <なし>       (説明 (description) がありません)
un  cockpit-doc            <なし>               <なし>       (説明 (description) がありません)
ii  cockpit-machines       265-1                all          Cockpit user interface for virtual machines
ii  cockpit-networkmanager 264-1ubuntu0.22.04.1 all          Cockpit user interface for networking
ii  cockpit-packagekit     264-1ubuntu0.22.04.1 all          Cockpit user interface for packages
un  cockpit-pcp            <なし>               <なし>       (説明 (description) がありません)
un  cockpit-shell          <なし>               <なし>       (説明 (description) がありません)
un  cockpit-sosreport      <なし>               <なし>       (説明 (description) がありません)
un  cockpit-ssh            <なし>               <なし>       (説明 (description) がありません)
ii  cockpit-storaged       264-1ubuntu0.22.04.1 all          Cockpit user interface for storage
ii  cockpit-system         264-1ubuntu0.22.04.1 all          Cockpit admin interface for a system
un  cockpit-systemd        <なし>               <なし>       (説明 (description) がありません)
un  cockpit-tuned          <なし>               <なし>       (説明 (description) がありません)
un  cockpit-users          <なし>               <なし>       (説明 (description) がありません)
ii  cockpit-ws             264-1ubuntu0.22.04.1 amd64        Cockpit Web Service
```
- AlmaLinux OS 9 (≒RHEL9)の場合
``` 
$ yum search cockpit
メタデータの期限切れの最終確認: 0:00:13 前の 2024年10月04日 16時07分14秒 に実施しました。
============================ 名前 完全一致: cockpit ============================
cockpit.x86_64 : Web Console for Linux servers
========================== 名前 & 概要 一致: cockpit ===========================
cockpit-bridge.x86_64 : Cockpit bridge server-side component
cockpit-composer.noarch : Composer GUI for use with Cockpit
cockpit-doc.noarch : Cockpit deployment and developer guide
cockpit-machines.noarch : Cockpit user interface for virtual machines
cockpit-ostree.noarch : Cockpit user interface for rpm-ostree
cockpit-packagekit.noarch : Cockpit user interface for packages
cockpit-pcp.x86_64 : Cockpit PCP integration
cockpit-podman.noarch : Cockpit component for Podman containers
cockpit-session-recording.noarch : Cockpit Session Recording
cockpit-storaged.noarch : Cockpit user interface for storage, using udisks
cockpit-system.noarch : Cockpit admin interface package for configuring and
                      : troubleshooting a system
cockpit-ws.x86_64 : Cockpit Web Service
```
</details>

## 3.Cockpitの起動〜KVM仮想マシン作成まで
cockpitがインストールできたら、下記コマンドでcockpitを起動します。
```
$ sudo systemctl start cockpit
$ sudo systemctl enable cockpit
```
この段階で`https://(サーバのipアドレスorHOSTNAME):9090/`にアクセスすれば、Cockpitに接続することができます。

:::note warn
9090番ポートが既に他で使われていたり、ファイアウォールで塞がれていると起動できません。
標準状態で使用ポートがCockpitと干渉するものとしてはprometheusなどがあるようで、どちらかの使用ポートを変更する必要があります。
Cockpit側では`/usr/lib/systemd/system/cockpit.socket`に使用ポートの指定がおかれています。
:::

このとき証明書の設定がされていない状態なので、下記のような画面が表示されます。
以下左がFirefox、右がVivaldi[^4]での表示例です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/86338490-5f72-6ef2-d7c2-6b4ff1224f85.png" width="50%" alt="Firefoxでの「潜在的なセキュリティリスクあり」のエラー画面。「戻る」と「詳細に進む」の2つのボタンが表示されている。"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/3628646f-41a9-a0a4-0e9b-cc5b5448748f.png" width="50%" alt="Vivaldiでの「この接続ではプライバシーが保護されません」のエラー画面。「詳細」と「セキュリティの保護されたページに戻る」の2つのボタンが表示されている。">

[^4]:VivaldiというかChromium系は全部こんな感じだと思います。ChromeやEdgeに関しては私用PCに入れていないので試せていませんが。

この時点では正常な挙動なので、詳細へ進む→危険性を承知の上で使用(Firefox)、詳細→〇〇へアクセスする(Vivaldi)とします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/fdba6c4e-544a-e99a-ac63-4c16f48f77fc.png" width="50%" alt="Firefoxで詳細へ進むをクリックした画面。「戻る」と危険性を承知の上で使用の２つの選択肢が提供される"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/0c55d528-dc09-db67-7a51-35e047e99bb1.png" width="50%" alt ="vivaldiでの同様の画面。新たに「アクセスする(安全ではありません)」の選択肢が提供される">

下記ログイン画面が表示されたら、サーバで普段使っているユーザアカウントのユーザ名・パスワードを入れてログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/9e525166-282a-cc28-0191-a0271c506474.png)
このようなダッシュボードが表示され、左側に`仮想マシン`の項目があれば導入成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/e851df82-5750-86ba-005a-6c8235ae5546.png)

なお、標準状態では「制限付きアクセス」(≒一般ユーザ権限)になっており、一部の設定確認や設定変更が制限されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/57660340-1292-910a-0c9a-6bd7bf2ea082.png)
右上の「制限付きアクセス」をクリックし、ユーザパスワードを再度入力することで、管理者アクセスに切り替えることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/2e317400-ee4c-c01d-76ba-a90225b8029e.png)

こののち行う作業には、管理者権限が必要です。

## 4.仮想マシンを立ててみる

:::note w
このあと実施するリソース割当の設定量よりも、OSインストール作業中は多めに割り当てられる事象を確認しています。
他用途にも使用しているサーバの場合、下記手順はリソースに余裕がある時に実施してください。
:::

左側にある`仮想マシン`をクリックすると、下記の画面が表示されます。「仮想マシンの作成」があるので押してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/089c2035-a7e1-82f0-bc4d-8dd647fec4a0.png)

出てきたウィンドウに必要事項を入れていきます。
メモリ割当はサーバのリソースと適宜相談して決めることになります。過大に割り当てるとサーバの本来の用途を圧迫するため、学習用途なら搭載メモリの1/4程度で十分かと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/c250d946-6cc4-7ea5-b98a-e6c835a56876.png)

Cockpitを使う利点のひとつとして、OSのダウンロード・インストールが自動化可能というのが有ります。
`インストールタイプ`が標準で「OSをダウンロードします」になっているので、下の`オペレーティングシステム`のドロップダウンから導入したいOSを選択しましょう。今回はAlmaLinux9を選択しています。
<details><summary>インストール選択肢にあるディストリビューション</summary>

サーバ･開発等用途でのメジャーどころは粗方カバーしている印象。
```
- AlmaLinux 9 (CentOS後継系ディストロ)
- CentOS Stream 8 / 9
- CentOS 7
- Circle Linux 8.4 / 8.5 (CentOS後継系ディストロその2)
- Debian 10 / 11 / 12 / testing
- EuroLinux9 (CentOS後継系ディストロその3)
- Fedora Rawhide
- Fedora Linux 37 / 38 / 39 / 40
- Fedora Silverblue 37 / 38 / 39 / 40
- openSUSE Tumbleweed
- openSUSE Leap 15.4 / 15.5
- Rocky Linux 8 / 9 (CentOS後継系ディストロその4)
- Ubuntu 16.04 LTS / 18.04 LTS / 20.04 LTS
```
</details>

自動インストール機能の使用も選択可能です。
チェックを入れると最低限の必要事項を入力する欄が開きます。今回は有効にて進めました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/6f039151-9a43-b1b2-cc6e-c018e0d575cf.png)

「作成」をクリックして、状態のところが「実行中」表示になるまでしばらく待ちます。

「実行中」表示となると名前のところがクリック可能になるので、開いてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/b992b258-a9c9-7683-94b9-d08e1d1c4a75.png)

すると下記の管理画面になり、インストールの進捗が見えます。
この画面ではほかに自動起動の設定や、仮想マシンの操作、リソース状況やデバイス構成の確認等ができます。
なお自動インストールを選択しなかった場合は、「VNCコンソール」の画面からインストーラを操作してインストールすることになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/b54b3693-f9bb-0675-b5fe-681552641f30.png)

しばらくして、VNCコンソール上にログイン画面が表示されたらインストール成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/422d5907-5941-31bb-aaa0-a71ce7330471.png)
下部にネットワークインターフェースの設定欄があるので、ここに載っているIPアドレスを使ってssh接続しましょう。
下記では仮想マシンをインストールしたサーバを踏み台にして、そこから接続しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/93e8c509-12d0-5e67-3ec2-b3cc4a83f0f5.png)
```
user@deskmeetserver:~$ ssh 192.168.122.95
The authenticity of host '192.168.122.95 (192.168.122.95)' can't be established.
ED25519 key fingerprint is SHA256:b8RWlFEaS0FVlhRX87KLlSVSEpO1xtAzjH+Y6SNQQeY.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.122.95' (ED25519) to the list of known hosts.
user@192.168.122.95's password: 
Activate the web console with: systemctl enable --now cockpit.socket

[user@AlmaLinux9-00 ~]$ 
```

他方で、サーバと同じネットワークにある別PCからssh接続しようとしても、接続が確立されません。
```
$ ssh 192.168.122.95
ssh: connect to host 192.168.122.95 port 22: Connection timed out

$ 
```

## 5.ネットワーク接続設定

これを解消するにはサーバのホスト側でネットワーク設定を変更し、ブリッジネットワークを作成する必要があります。
しかし、現在(17.10以降)のUbuntu Serverでは[^7]Netplanがネットワークを管理しています。
Cockpitのネットワーク管理機能ははNetworkManagerを前提としているため、標準状態のUbuntuホストではネットワーク設定変更が行えないようです。
[^7]:"Ubuntu Serverでは"というのが重要で、デスクトップ版イメージでインストールした環境では標準でNetworkManagerが使われるようです。

### netplan設定変更
まず、`network-manager`と`cockpit-networkmanager`がインストールされているか確認してください。
```
$ dpkg -l network-manager cockpit-networkmanager
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                   Version         Architecture Description
+++-======================-===============-============-=========================================================
un  cockpit-networkmanager <none>          <none>       (no description available)
ii  network-manager        1.36.6-0ubuntu2 amd64        network management framework (daemon and userspace tools)
```
今回は`network-manager`はインストールされているものの、`cockpit-networkmanager`はインストールされていませんでした。
aptでインストールしておきます。
```
$ sudo apt install cockpit-networkmanager 
(出力略)
```

`/etc/netplan`以下に、netplanの設定ファイル(YAML形式)が保存されています。
```
$ pwd
/etc/netplan

$ ll
total 20
drwxr-xr-x   2 root root  4096 Jun  6  2023 ./
drwxr-xr-x 124 root root 12288 Oct 18 06:06 ../
-rw-r--r--   1 root root   322 Jun  6  2023 00-installer-config.yaml

$ cat 00-installer-config.yaml 
# This is the network config written by 'subiquity'
network:
  ethernets:
    enp2s0:
      addresses:
      - 192.168.0.55/24
      nameservers:
        addresses:
        - 192.168.0.1
        - 192.168.0.1
        - 192.168.0.1
        search: []
      routes:
      - to: default
        via: 192.168.0.1
  version: 2
```
設定ファイルは複数存在でき、ファイル名昇順に読み込まれていくようです。
初期状態では上記のように`00-installer-config.yaml`のほか、環境により`50-cloud-init.yaml`などのファイルが存在する可能性があります。
ただし、これらを直接書き換えるのは一定のリスクがあり、(それなりに横行しているものの)お行儀が良くないとされています。[^8]
参考：[【Ubuntu】 /etc/netplan/50-cloud-init.yamlを編集するの止めろ #Ubuntu - Qiita](https://qiita.com/yas-nyan/items/9033fb1d1037dcf9dba5)
[^8]:リンク先にもありますが例えば`50-cloud-init.yaml`に直接書き加えた設定は、何らかの理由で`cloud-init`が再実行された際に上書きされて飛びます。今回のように`00-installer-config.yaml`の場合、`subiquity`が再実行される状況は考えにくく、直接変更でも実害はないのかもしれませんが……

そこで既存のファイルよりも名前が後ろに来るファイルを作成し、そちらの設定で上書きされるようにします。

```
$ sudo cp 00-installer-config.yaml 99-manual-config.yaml

$ ll
total 24
drwxr-xr-x   2 root root  4096 Oct 19 02:35 ./
drwxr-xr-x 124 root root 12288 Oct 18 06:06 ../
-rw-------   1 root root   322 Jun  6  2023 00-installer-config.yaml
-rw-------   1 root root   297 Oct 19 02:35 99-manual-config.yaml

$ sudo vi 99-manual-config.yaml 

$ cat 99-manual-config.yaml 
network:
  renderer: NetworkManager
  ethernets:
    enp2s0:
      addresses:
      - 192.168.0.55/24
      nameservers:
        addresses:
        - 192.168.0.1
        - 192.168.0.1
        - 192.168.0.1
        search: []
      routes:
      - to: default
        via: 192.168.0.1
  version: 2

```

上記では、`renderer: NetworkManager`の一行を追加しています。

### netplan設定適用

下記コマンドで、netplanの設定を120秒間だけ一時的に適用します。
この間に設定が正しく適用され、サーバに引き続き接続可能であることを確認してください。
```
$ sudo netplan try
WARNING:root:Cannot call Open vSwitch: ovsdb-server.service is not running.

Do you want to keep these settings?


Press ENTER before the timeout to accept the new configuration
```
正常であることを確認できたら、ENTERキーを押下して設定を適用させます。

このような方法を取るのは、構文エラーや設定の誤り等でSSHを含むネットワーク接続ができなくなるリスクを最小限とするためです。

設定変更が完了したら、cockpitを再起動させておきます。
```
user@deskmeetserver:/etc/netplan$ sudo systemctl restart cockpit
```
cockpitに再ログインし、下記二点を確認できれば一連の設定変更は成功となります。
- 「ネットワーキング」タブが増えていること。
- 「ネットワーキング」タブを開くと、ネットワークの概要が表示されること。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/10c109f6-5a3e-6c82-c42c-beb0412ff5f4.png)

### 仮想マシンをLAN内に公開する
ネットワーキング画面右端の「ブリッジの追加」をクリックすると、下記のようなポップアップが開きます。

ここで「ポート」から、先程固定IPを設定したNIC(今回の例では「enp2s0」)を選択して適用することで、ブリッジを作成することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/64de59d4-3f1e-77ef-2659-1f4da0fb3501.png)

再度、作成した仮想マシンの設定画面に戻ります。
下の方にスクロールしていくと「ネットワークインターフェース」の項目があるので、右側の「編集」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/5142ed2c-f770-8b8f-6be8-4c7c27505eac.png)

開いたポップアップで、「インターフェース形式」を「Bridge to LAN」に、「ソース」を先程作成したブリッジ(変えていないのなら「bridge0」)に、それぞれ指定して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/9c38fd33-07fc-114e-d433-08709ad745f0.png)

:::note info
仮想マシンが起動中だった場合は、設定反映のために仮想マシンのシャットダウンが必要です。

:::

仮想マシンの起動後、再度「ネットワークインターフェース」欄を覗いて、ipアドレスを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/7867625a-4696-437c-2323-04661e8bfa63.png)

手元のマシンからこのアドレス宛に、ssh接続してみると…
```
$ ssh 192.168.0.13
The authenticity of host '192.168.0.13 (192.168.0.13)' can't be established.
ED25519 key fingerprint is SHA256:b8RWlFEaS0FVlhRX87KLlSVSEpO1xtAzjH+Y6SNQQeY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.0.13' (ED25519) to the list of known hosts.
user@192.168.0.13's password: 
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Wed Nov 13 11:11:21 2024
[user@AlmaLinux9-00 ~]$ 
```
無事ログインすることができました!!

今回ブリッジネットワークにより、ゲストOSはホスト(サーバ機)や手元端末などと同じネットワークに、擬似的につながった状態となっています。
これにより手軽にSSHログインできるようになり、またネットワークを介して利用するサービスを仮想マシン内でホストしても接続可能になりました。

## 6.ゲストOSの固定ip化
ただし、いま仮想マシンに割りあたっているipアドレスはDHCPによる自動割当なので、変わってしまう可能性があります。
cockpitの管理画面まで行けば確認できますが手間なので、コレも固定してしまいましょう。

ところでcockpit自体もまた、ネットワークを介して利用するサービスです。
そして、今インストールしたAlmaLinuxのイメージには、最初からcockpitが含まれています。

そういうわけで、ログイン時に示される下記コマンドを実行すれば……
```
[user@AlmaLinux9-00 ~]$ sudo systemctl enable --now cockpit.socket
[sudo] password for kazuto: 
[user@AlmaLinux9-00 ~]$ 
```
**cockpitで作った仮想マシンの中のcockpit**を利用することもできちゃうんですね。
(セキュリティ警告など、ログインまでの手順はさっきログインしたときと同じです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/167f1abc-ee8c-e70b-e468-9eb130cc0cb4.png)

これにより仮想マシンのipアドレスを固定化するところまで、cockpitで完結させることができます。

右上の「制限付きアクセス」をクリックして管理アクセスに切り替え後、ネットワーキング→IPv4右の「編集」をクリックすると、IPv4の設定画面が開きます。

ここで「アドレス」右の「+」をクリックするだけで、入力欄が開き固定IPアドレスの付与が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3767939/343101e2-4f75-30db-86bf-496bd880343b.png)


## 7.まとめ
気軽に触れて気軽に壊せるLinux環境が無限に手に入るようになりました。
技術力の向上に繋げてゆきたいです。

ここまで、慣れている人ならインストール待ち時間抜きで30分程度、不慣れでも半日もあればできました。
設定ファイル変更の必要も一箇所(NetworkManager有効化)だけで、1回実施すれば何個でも仮想マシンを立てられます[^15]。
環境を壊したら仮想マシンを作り直すのも簡単ですし、ダッシュボード経由で操作できるのでとっつきやすく、管理も簡単ですね。
[^15]:物理的なリソースが許す限り。

<font color="888888">え、一般人は手元環境がWindowsだからWSLがあれば充分だって?ハイ、ごもっともでございます……</font>

## 参考

https://pc.watch.impress.co.jp/docs/column/ubuntu/1576889.html

https://qiita.com/ku_suke/items/269dfecec398187aed77