おうちサーバー構築報告：AnsibleでProxmoxVEのゲストマシン4台それぞれ/etcをSubversion管理

## 能書き

[おうちサーバー構築報告：予告](https://qiita.com/katz_engineer/items/471408eda1d09507c451)からのおうちサーバー構築の続きです。

[Intel NUC のマシン NUC13ANHI5000 に Proxmox VE をインストール](https://qiita.com/katz_engineer/items/ee594cab0300398131f7)して、[コンテナの構築方法を調査](https://qiita.com/katz_engineer/items/3cf9a545d4c8b7c77206)し、[仮想マシンの構築方法も調査](https://qiita.com/katz_engineer/items/932640f6c209a4ffddbb)し、さあこれからだ！と言う訳ですが。その前に`/etc`のバージョン管理を仕込みます。但し私の個人的な好みで、GitではなくSubversionを使います。その為etckeeperは使いません。

[既に何度か同じ内容の記事を書いています](https://qiita.com/katz_engineer/items/be501fa7e4625cdcce3b)が、今回は作業手順をAnsibleで実行する形にしてみました。Ansibleを使うのは初めてです。

### 目標

- ProxmoxVE上にゲストマシンを構築
    - コンテナを3台（マシン名: network, mail, proxy）
    - 仮想マシンを1台（マシン名: docker）
- それぞれSubversionでバージョン管理できるように設定し、最初のコミットを実行
    - その手順をAnsibleで各マシンにおいて実行

### 参考文献

`/etc`をSubversion管理する過去記事です。

- [私的サーバー構築日誌：Subversionをインストールして /etc をバージョン管理・Ubuntu 24.04 LTS in MicroServer N54L](https://qiita.com/katz_engineer/items/be501fa7e4625cdcce3b)
- [私的サーバー構築日誌：Subversionをインストールして /etc をバージョン管理・Ubuntu 22.04](https://qiita.com/katz_engineer/items/3e743263a878df2b8aef)

`/etc`のバージョン管理とは関係ありませんが、もし`xmllist`コマンドがあればSubversionのログを自由に操作できます。

- [確認 - 俺様サーバー構築記 - Subversionサーバーの構築@デスクトップパソコン - Qiita](https://qiita.com/katz_engineer/items/b893316b4d1bb7731e87#%E7%A2%BA%E8%AA%8D)
- [確認 - 俺様サーバー構築記 - Subversionをインストールして /etc をバージョン管理@全機 - Qiita](https://qiita.com/katz_engineer/items/29bb4672f69e70944a80#%E7%A2%BA%E8%AA%8D)

Ansibleについての参考文献と言えば、筆頭はやはり公式でしょうか。

- [Ansible community documentation](https://docs.ansible.com/)

Ansible関連での諸々。

- [How to Install Ansible on Ubuntu 24.04 LTS Noble Numbat](https://www.ansiblepilot.com/articles/how-to-install-ansible-in-ubuntu-24.04-ansible-install/)
- [AnsibleでのSSH接続ユーザー指定方法 - Qiita](https://qiita.com/NewGyu/items/6a6ba033588514dbca9a)
- [[Ansible] 認証情報の変数名は ansible_user、ansible_password に統一したほうがよさそう](https://tekunabe.hatenablog.jp/entry/2019/02/24/ansible_auth_variables)
- [Ansibleでリモートセットアップしようとしたら "Missing sudo password" と言われたときに見る記事 - Hatena Blog](https://ytooyama.hatenadiary.jp/entry/2021/07/05/180949)

Ansibleの構成管理ではない使い方という事で、こんなQiita記事がありました。

- [Ansibleならローカルのシェルスクリプトをリモートホストで実行できて便利 - Qiita](https://qiita.com/isobecky74/items/c5af4f0e289dd2df4c6f)

その他。

- [おうちサーバー構築報告：コンテナ on Proxmox VE with ZFS](https://qiita.com/katz_engineer/items/3cf9a545d4c8b7c77206)
- [おうちサーバー構築報告：仮想マシンとDocker on Proxmox VE with ZFS](https://qiita.com/katz_engineer/items/932640f6c209a4ffddbb)
- [秘密鍵と公開鍵を生成 - 私的サーバー構築日誌：SSH公開鍵認証・公開鍵を集中管理 - Qiita](https://qiita.com/katz_engineer/items/97db541d0bee758a2ebe#%E7%A7%98%E5%AF%86%E9%8D%B5%E3%81%A8%E5%85%AC%E9%96%8B%E9%8D%B5%E3%82%92%E7%94%9F%E6%88%90)
- [ssh-copy-idコマンドを試してみた - Qiita](https://qiita.com/gama1234/items/5c97b1c106e3f0d093c0)
- [3. useraddコマンドでユーザーを追加（オプション有り） - Linuxのユーザー追加、useraddコマンドの使い方 - Qiita](https://qiita.com/yasushi-jp/items/78d5965c1624b1529ff6#3-useradd%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E6%9C%89%E3%82%8A)
- [Linuxでsudo権限の付与と削除を行う方法を完全解説 - ITtrip](https://ittrip.xyz/soft/linux/linux-sudo-permission-handling)
- [Linuxコマンド - ユーザ・グループの確認/追加/削除 - Qiita](https://qiita.com/shin4488/items/eb5cd6db669380fae774)

## ゲストマシン構築

[コンテナ](https://qiita.com/katz_engineer/items/3cf9a545d4c8b7c77206)と[仮想マシン](https://qiita.com/katz_engineer/items/932640f6c209a4ffddbb)の構築方法は以前やりましたので、ProxmoxVE操作の詳細はそちらを参照してください。画面キャプチャを撮って貼り付けるのは大変なんです…

ゲストマシンへの接続は、Ansibleコントロールノードについては普段使いのWindowsマシンから公開鍵方式でssh接続しようと思います。ProxmoxVEのコンソールではコマンドのコピペとか出来なさそうですし。

Windowsにおいても下記のコマンドで公開鍵ペアを生成できます。暗号の種類は、昨今の情勢を鑑みて楕円曲線暗号Ed25519にします。既に公開鍵があればそれを再利用しても良いです。

```console:クライアントマシン:Windows
ssh-keygen -t ed25519 -P ""
```

鍵ペアは`%HOMEPATH%/.ssh`ディレクトリに作成されて、それに関して問合せがありますので、何も入力せずにEnterキーを押しましょう。

そうして作成した鍵ペアのうち公開鍵の方（ファイル名:`id_ed25519.pub`、拡張子`.pub`の付いている方）を適当なテキストエディタで開いて、もしくは下記コマンドを実行して、内容をコピーします。

```console:クライアントマシン:Windows
type %HOMEPATH%\.ssh\id_ed25519.pub
```

### コントロールノードとしてコンテナ1台

#### 構築

- ホスト名: constructor
- リソースプール: all
- パスワード: （適当なパスワードを設定）
- SSH公開鍵: （コピーした公開鍵を貼り付け）
- テンプレート: ubuntu-24.04-standard_24.04-2_amd64.tar.zst
- ディスクサイズ: 8GiB
- CPUコア: 1
- メモリ: 512MiB
- スワップ: 512MiB
- IPv4/CIDR: 172.16.1.100/16 （我が家のIPアドレス体系に沿って決定）
- ゲートウェイ: 172.16.2.1 （我が家のゲートウェイアドレス）

そして作成したコンテナを開始。

設定したパスワードはコンソールで`root`ログインする際に使用します。GUIで確認します。

普段使いのWindowsマシンからssh接続を試みます。パスワードを入力せずに接続できます。

```console:クライアントマシン:Windows
ssh root@172.16.1.100
```

#### 準備

Ansibleを実行するユーザーを作成します。`root`でこういう操作はセキュリティ的にまずいので。

```console:コントロールノードconstructor:ユーザーroot
useradd -m -s /bin/bash ansible
passwd ansible
```

このコントロールノードから各ターゲットノードにansibleユーザーで公開鍵ssh接続しますので、それ用の鍵を作成しておきます。

```console:コントロールノードconstructor:ユーザーroot
su - ansible
```

```console:コントロールノードconstructor:ユーザーansible
ssh-keygen -t ed25519 -P ""
```

作成できたら公開鍵を表示。

```console:コントロールノードconstructor:ユーザーansible
cat .ssh/id_ed25519.pub
```

ターゲットノードを作成する際にはこの公開鍵をコピペします。

### ターゲットノードとしてコンテナ3台

1台目は諸々雑多なサーバーソフトを集めます。何となくネットワーク関連に見えなくもないので、マシン名は network とします。あまり動かない筈なので超非力でも十分です。多分。

- ホスト名: network
- リソースプール: all
- パスワード: （適当なパスワードを設定）
- SSH公開鍵: （コントロールノード`constructor`の公開鍵をコピーして貼り付け）
- テンプレート: ubuntu-24.04-standard_24.04-2_amd64.tar.zst
- ディスクサイズ: 8GiB
- CPUコア: 1
- メモリ: 512MiB
- スワップ: 512MiB
- IPv4/CIDR: 172.16.1.101/16 （我が家のIPアドレス体系に沿って決定）
- ゲートウェイ: 172.16.2.1 （我が家のゲートウェイアドレス）

2台目は電子メールサーバーです。内部専用で滅多に動かないので超非力にしましたが、ディスクサイズだけ念の為にちょっと付け足しました。

- ホスト名: mail
- リソースプール: all
- パスワード: （適当なパスワードを設定）
- SSH公開鍵: （コントロールノード`constructor`の公開鍵をコピーして貼り付け）
- テンプレート: ubuntu-24.04-standard_24.04-2_amd64.tar.zst
- ディスクサイズ: 16GiB
- CPUコア: 1
- メモリ: 512MiB
- スワップ: 512MiB
- IPv4/CIDR: 172.16.1.102/16 （我が家のIPアドレス体系に沿って決定）
- ゲートウェイ: 172.16.2.1 （我が家のゲートウェイアドレス）

3台目はリバースプロキシです。これも超非力で大丈夫かな。念の為にメモリだけちょっと付け足しましょうか。

- ホスト名: proxy
- リソースプール: all
- パスワード: （適当なパスワードを設定）
- SSH公開鍵: （コントロールノード`constructor`の公開鍵をコピーして貼り付け）
- テンプレート: ubuntu-24.04-standard_24.04-2_amd64.tar.zst
- ディスクサイズ: 8GiB
- CPUコア: 1
- メモリ: 1024MiB
- スワップ: 1024MiB
- IPv4/CIDR: 172.16.1.103/16 （我が家のIPアドレス体系に沿って決定）
- ゲートウェイ: 172.16.2.1 （我が家のゲートウェイアドレス）

3台構築できたらすべて開始しておきます。
開始したらコントロールノード`constructor`から公開鍵で（パスワード無しで）ssh接続できる事を確認します。

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.101
```

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.102
```

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.103
```

### ターゲットノードとして仮想マシン1台

仮想マシンはDockerサーバーです。それなりにパワーを付与します。現在の予定ではGitLabとMattermostを動かそうと考えています。趣味で開発を始めたら GitLab CI/CD とか試したい。

- 名前: docker
- リソースプール: all
- ブート時に起動: チェックを入れる
- CD/DVDイメージファイル(iso)を使用
    - ISOイメージ: ubuntu-24.04-live-server-amd64.iso
- ディスクサイズ: 512GiB
- CPUコア: 4
- メモリ: 16384MiB
- 最小メモリ量: 8192MiB

そしてUbuntuインストール。

- Installer update available
    - Continue without updating
- keyboard configuration
    - Layout: Japanese
    - Variant: Japanese
- Choose the type of installation
    - Ubuntu Server
- Network configuration
    - IPv4 Method: Manual
    - Subnet: 172.16.0.0/16
    - Address: 172.16.1.104
    - Gateway: 172.16.2.1
    - Name servers: 172.16.2.1
    - Search domains: local
- Proxy configuration
    - Proxy address: （空欄のまま）
- Ubuntu archive mirror configuration
    - （特に何もせずDone）
- Guided storage configuration
    - Use an entire disk
        - Set up this disk as an LVM group
- Storage configuration
    - （特に何もせずDone）
    - Confirm destructive action
        - このままインストールするとデータが消えるとの警告  
- Profile configuration
    - Your name: administrator （適当に名前を決める）
    - Your servers name: docker
    - Pick a username: administrator （適当に名前を決める）
    - Choose a password: （適当なパスワードを設定）
    - Confirm your password: （パスワードをもう一度）
- Upgrade to Ubuntu Pro
    - Skip for now
- SSH configuration
    - Install OpenSSH server: チェックを入れる
- Featured server snaps
    - dockerを選択

最初はパスワードでログインします。

```console:クライアントマシン:Windows
ssh administrator@172.16.1.104
```

確認できたらすぐに`exit`。そして公開鍵を設定します。
[公開鍵を転送して設定するのは`ssh-copy-id`という便利なコマンドがあるそうです](https://qiita.com/gama1234/items/5c97b1c106e3f0d093c0)が、Windowsには無いらしいです。残念。

```console:クライアントマシン:Windows
ssh administrator@172.16.1.104 "cat >.ssh/authorized_keys" <.ssh\id_ed25519.pub
```

今度はパスワード無しでログインできます。

```console:クライアントマシン:Windows
ssh administrator@172.16.1.104
```

また、コントロールノード`constructor`からの公開鍵ssh接続を設定します。Ubuntuなので[`ssh-copy-id`](https://qiita.com/gama1234/items/5c97b1c106e3f0d093c0)が使えます。

```console:コントロールノードconstructor:ユーザーansible
ssh-copy-id administrator@172.16.1.104
```

コントロールノード`constructor`からもパスワード無しでログインできるようになりました。

```console:コントロールノードconstructor:ユーザーansible
ssh administrator@172.16.1.104
```

最後に一つ細工を。仮想マシン`docker`に於いて、`administrator`ユーザーが`sudo`で昇格する際はパスワード無しにします。Ansibleで必要なので。

```console:ターゲットノードdocker
echo administrator$'\t'ALL=\(ALL\) NOPASSWD: ALL | sudo tee -a /etc/sudoers.d/npconf >/dev/null
```

余談ですが、`/etc/sudoers.d`の中に置くファイルは拡張子があったらダメのようです。実際に試行錯誤して掴んだ情報です。

再起動で反映します。実際にやってみると再起動しなくても反映しているようですが。

```console:ターゲットノードdocker:ユーザーadministrator
sudo reboot
```

## Ansibleインストール

コントロールノード`constructor`にAnsibleをインストールします。[Ubuntu24.04へのAnsibleインストールは、単に`apt install`で良いみたいです](https://www.ansiblepilot.com/articles/how-to-install-ansible-in-ubuntu-24.04-ansible-install/)。

```console:コントロールノードconstructor:ユーザーroot
apt update
apt upgrade -y
```

```console:コントロールノードconstructor:ユーザーroot
apt install -y ansible
```

確認。

```console:コントロールノードconstructor:ユーザーroot
ansible --version
```

## Ansible設定と実行

`ansible`ユーザーになります。

```console:コントロールノードconstructor:ユーザーroot
su - ansible
```

インベントリを定義します。

```console:コントロールノードconstructor:ユーザーansible
cd
mkdir svn_etc
cd svn_etc
cat <<___ >inventory.ini
[servers]
172.16.1.101
172.16.1.102
172.16.1.103
172.16.1.104 ansible_user=administrator

[servers:vars]
ansible_user=root
___
```

インベントリの確認の為に`ping`してみましょう。

```console:コントロールノードconstructor:ユーザーansible
ansible all -m ping -i inventory.ini
```

`playbook.yaml`を作ります。内容は`/etc`のSubversion管理で、[以前](https://qiita.com/katz_engineer/items/be501fa7e4625cdcce3b)の手順を脚本化したものです。おお、「Playbook化した」と書くよりカッコいい気がしますな。

残念ながら冪等性はありません。2回流すと`rm /etc/ssl/certs/NetLock*`の所でエラーが出て停止します。正確なファイル名を書けないので仕方がありません。

```console:コントロールノードconstructor:ユーザーansible
cat <<___ >playbook.yaml
- name: Play test
  hosts: servers

  tasks:
  - name: apt update
    apt:
      update_cache: yes
    become: yes
  - name: apt upgrade
    apt:
      upgrade: yes
    become: yes
  - name: install subversion
    apt:
      name: subversion
    become: yes
  - name: create the directory of a repository
    file:
      path: /etc/.svn_repo
      state: directory
    become: yes
  - name: initialize the repository
    shell:
      cmd: svnadmin create /etc/.svn_repo/
      creates: /etc/.svn_repo/README.txt
    become: yes
  - name: create the temporary directory of /etc
    file:
      path: /tmp/etc
      state: directory
    become: yes
  - name: svn-import the temporary directory
    shell:
      cmd: svn import /tmp/etc file:///etc/.svn_repo/ -m "create a project"
      creates: /etc/.svn_repo
    become: yes
  - name: remove the temporary directory
    file:
      path: /tmp/etc
      state: absent
    become: yes
  - name: remove an illegal ssl certificate (1)
    shell: bash -c "ls /etc/ssl/certs/*.0 | while read f; do echo -n $f$'\t'; readlink $f; done | grep NetLock | cut -f 1 | xargs -r rm"
    become: yes
  - name: remove an illegal ssl certificate (2)
    shell: rm /etc/ssl/certs/NetLock*
    become: yes
  - name: svn-checkout the directory /etc
    shell:
      cmd: svn co file:///etc/.svn_repo/ /etc
      creates: /etc/.svn
    become: yes
  - name: svn-add all the directory /etc
    shell: svn add *
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/1
    become: yes
  - name: svn-revert some files
    shell: ls *- | xargs -r svn revert
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/1
    become: yes
  - name: svn-commit /etc
    shell: svn ci -m "first commit"
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/1
    become: yes
  - name: svn-update /etc for first commit
    shell: svn up /etc
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/2
    become: yes
  - name: svn-ignore some files
    shell: bash -c "svn propset svn:ignore -F <(svn st | cut -b9-) /etc"
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/2
    become: yes
  - name: svn-commit ignored files
    shell: svn ci -m "setting svn:ignore"
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/2
    become: yes
  - name: svn-update /etc for setting svn:ignore
    shell: svn up
    args:
      chdir: /etc
      creates: /etc/.svn_repo/db/revs/0/3
    become: yes
___
```

まずは文法チェック。

```console:コントロールノードconstructor:ユーザーansible
ansible-playbook -i inventory.ini playbook.yaml --syntax-check
```

問題無ければ実行しましょう。

```console:コントロールノードconstructor:ユーザーansible
ansible-playbook -i inventory.ini playbook.yaml
```

## 確認

各ゲストマシンにログインして、それぞれログを表示させてみます。

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.101 svn log /etc
```

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.102 svn log /etc
```

```console:コントロールノードconstructor:ユーザーansible
ssh root@172.16.1.103 svn log /etc
```

```console:コントロールノードconstructor:ユーザーansible
ssh administrator@172.16.1.104 sudo svn log /etc
```

それぞれこんな感じで表示されればOKです。

```console:コントロールノードconstructor:ユーザーansible
$ ssh root@172.16.1.101 svn log /etc
------------------------------------------------------------------------
r2 | root | 2024-12-22 09:25:56 +0000 (Sun, 22 Dec 2024) | 1 line

setting svn:ignore
------------------------------------------------------------------------
r1 | root | 2024-12-22 09:20:06 +0000 (Sun, 22 Dec 2024) | 1 line

first commit
------------------------------------------------------------------------
```

## 仕舞い

これで`/etc`をバージョン管理できるようになりました。その手順をAnsibleで自動化し、同じ手作業を4台で繰り返さなくて済むようにしました。

ProxmoxVEでコンテナと仮想マシンの混在環境をAnsibleでまとめて初期設定という事例は、ネット記事でも（日本語では）見かけないようです。皆様の参考になれば幸いです。