dnf コマンドでパッケージとモジュールを混在させてみて、両者の挙動について迫った。

dnf コマンドでパッケージ形式とモジュール形式、二つのインストール形式を混在させようとした場合どうなるのか検証を行った結果、両者の違いについて理解を深められたので得られた知見について共有します。

# 背景
CentOS8 ではパッケージ管理に大きな変更がありました。
RedHat からリリースされるコンテンツは、 BaseOS と Appstream の二つのリポジトリから配信されるようになります。
特に Appstream では、従来のパッケージ形式のソフトウェア管理に加わる形で、新たにモジュールという概念が追加されました。
端的に言えばモジュールとは、サイズの小さなリポジトリの集合から構成されるもので、RPM をさらに拡張させた新たなパッケージ管理の仕組みです。
yum コマンドの使用感をそのままに新たな概念が導入された結果、dnf コマンドにはパッケージ形式とモジュール形式という二つのインストール形式が共存することになりました。

## どっちも使うとどうなる？
ここでふと素朴な疑問が浮かびます。
パッケージとモジュール、両方から同じソフトウェアをインストールすると意図していない動作が起きたりするんじゃ……？

実際に検証を行い、dnf コマンドの挙動を確認しました。

#### 検証項目

1. **先にパッケージ**でインストールした後に……

    1-a. **違うバージョン**をモジュールでインストールする。
    1-b. **同じバージョン**をモジュールでインストールする。
    1-c. **モジュールのアンインストール**を実行する。

2. **先にモジュール**でインストールした後に……

    2-a. **違うバージョン**をパッケージでインストールする。
    2-b. **同じバージョン**をパッケージでインストールする。
    2-c. **パッケージのアンインストール**を実行する。

# 1. 先にパッケージでインストールした後に……
nginx 1.14 をパッケージインストールします。
"モジュールプロファイルのインストール"、"モジュールストリームの有効化"という見慣れない項目が yum から増えていますね。
検証 1-a, 1-b, 1-c では以下をインストールした状態をスタートとして、検証を行います。

```bash
$ sudo dnf -y install nginx
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                                      アーキテクチャー                           バージョン                                                                   リポジトリー                                  サイズ
=============================================================================================================================================================================================================================================
group/moduleパッケージをインストール:
 nginx                                                           x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                     570 k
 nginx-all-modules                                               noarch                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      23 k
 nginx-filesystem                                                noarch                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      24 k
 nginx-mod-http-image-filter                                     x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      35 k
 nginx-mod-http-perl                                             x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      45 k
 nginx-mod-http-xslt-filter                                      x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      33 k
 nginx-mod-mail                                                  x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      64 k
 nginx-mod-stream                                                x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      85 k
モジュールプロファイルのインストール:
 nginx/common
モジュールストリームの有効化:
 nginx                                                                                                      1.14
```

## 1-a. 違うバージョンをモジュールでインストールする。

1.16 をモジュールとしてインストールしようとすると……できませんでした。

```bash
$ sudo dnf module install nginx:1.16/common
メタデータの期限切れの最終確認: 0:33:39 時間前の 2020年05月22日 02時29分55秒 に実施しました。
依存関係が解決しました。
オペレーションは、モジュール 'nginx' ストリーム '1.14' を ストリーム '1.16' へと切り替える結果となります
エラー: 有効化されたモジュールのストリームの切り替えはできません。
モジュールからすべてのインストールされたコンテンツを削除し、'dnf module reset <module_name>' コマンドを使用してモジュールをリセットすることを推奨します。モジュールのリセット後に、別のストリームをインストールできます。
```

パッケージとして nginx をインストールした際、1.14 のモジュールストリームが有効化されたためです。

```bash
モジュールストリームの有効化:
 nginx                                                                                                      1.14
```

こうしてシステムにインストールされているバージョンを管理し、異なるバージョンが混在しないよう管理しているわけですね。
しかし、エラーメッセージを読む限り ```dnf module reset``` コマンドで nginx モジュールをリセットすることによってモジュールの切り替えを行えるようです。 
1.14 をインストールしたままモジュールストリームをリセットして 1.16 をインストールしようとしてみます。

```bash
$ sudo dnf module reset nginx
メタデータの期限切れの最終確認: 1:43:20 時間前の 2020年05月28日 08時18分52秒 に実施しました。
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                               アーキテクチャー                                        バージョン                                                  リポジトリー                                             サイズ
=============================================================================================================================================================================================================================================
モジュールの再設定:
 nginx

トランザクションの概要
=============================================================================================================================================================================================================================================

これでよろしいですか? [y/N]: y
完了しました!

$ sudo dnf module install nginx:1.16/common
メタデータの期限切れの最終確認: 1:43:32 時間前の 2020年05月28日 08時18分52秒 に実施しました。
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                                      アーキテクチャー                           バージョン                                                                   リポジトリー                                  サイズ
=============================================================================================================================================================================================================================================
アップグレード:
 nginx                                                           x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                     581 k
 nginx-all-modules                                               noarch                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      24 k
 nginx-filesystem                                                noarch                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      25 k
 nginx-mod-http-image-filter                                     x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      35 k
 nginx-mod-http-perl                                             x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      46 k
 nginx-mod-http-xslt-filter                                      x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      34 k
 nginx-mod-mail                                                  x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      64 k
 nginx-mod-stream                                                x86_64                                     1:1.16.1-1.module_el8.1.0+250+351caf85                                       AppStream                                      91 k
モジュールプロファイルのインストール:
 nginx/common
モジュールストリームの有効化:
 nginx                                                                                                      1.16

トランザクションの概要
=============================================================================================================================================================================================================================================
アップグレード  8 パッケージ

ダウンロードサイズの合計: 900 k
これでよろしいですか? [y/N]:
```

nginx 1.16 へのアップグレードと、 1.16 ストリームの有効化が行われる結果になりました。なるほど、これなら不整合は起きません。

## 1-b. **同じバージョン**をモジュールでインストールする。
さて、マシンの状態を nginx 1.14 パッケージインストール直後まで戻して今度は同じバージョンをモジュールインストールしようとしてみましょう。

```bash
$ sudo dnf module install nginx
CentOS-8 - AppStream                                                                                                                                                                                         6.2 kB/s | 4.3 kB     00:00
CentOS-8 - Base                                                                                                                                                                                               10 kB/s | 3.9 kB     00:00
CentOS-8 - Extras                                                                                                                                                                                            3.9 kB/s | 1.5 kB     00:00
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                               アーキテクチャー                                        バージョン                                                  リポジトリー                                             サイズ
=============================================================================================================================================================================================================================================
モジュールプロファイルのインストール:
 nginx/common

トランザクションの概要
=============================================================================================================================================================================================================================================

これでよろしいですか? [y/N]:
```

rpm パッケージのダウンロードは行わず、モジュールプロファイルのインストールのみ行われるようです。
モジュール独自の概念として、ストリームの中には更にプロファイルという細かな区分が存在します。続々と用語が増えていきますがあまり難しいものではありません！
例えば、postgresql にはどんなプロファイルがあるのか見てみましょう。

```bash:postgresqlの場合
$ sudo dnf module list postgresql
メタデータの期限切れの最終確認: 0:17:37 時間前の 2020年05月28日 10時22分19秒 に実施しました。
CentOS-8 - AppStream
Name                       Stream                       Profiles                       Summary
postgresql                 9.6                          client, server [d]             PostgreSQL server and client module
postgresql                 10 [d][e]                    client, server [d] [i]         PostgreSQL server and client module
postgresql                 12                           client, server                 PostgreSQL server and client module
```

nginx には common プロファイルしかありませんでしたが、postgresql には client と server の2通りのプロファイルがあることが確認できます。同バージョン内の別々の機能を持つ rpm 群をプロファイルによって分けて提供しているわけですね。

話を nginx に戻します。```dnf module -y install nginx```コマンドの実行前と実行後における ```dnf module list nginx``` コマンドの出力結果について比較してみます。

```bash:実行前
$ sudo dnf module list nginx
メタデータの期限切れの最終確認: 0:00:56 時間前の 2020年05月28日 10時22分19秒 に実施しました。
CentOS-8 - AppStream
Name                       Stream                       Profiles                       Summary
nginx                      1.14 [d][e]                  common [d]                     nginx webserver
nginx                      1.16                         common                         nginx webserver

ヒント: [d]efault, [e]nabled, [x]disabled, [i]nstalled
```

```bash:実行後
$ sudo dnf module list nginx
メタデータの期限切れの最終確認: 0:11:19 時間前の 2020年05月28日 10時22分19秒 に実施しました。
CentOS-8 - AppStream
Name                       Stream                       Profiles                       Summary
nginx                      1.14 [d][e]                  common [d] [i]                 nginx webserver
nginx                      1.16                         common                         nginx webserver

ヒント: [d]efault, [e]nabled, [x]disabled, [i]nstalled
```

Profiles 列の １行目、common に変化が見られます。実行前は ```common[d]``` 、実行後は ```common[d][i]```  となっています。
```[i]``` は現在システムにインストールされているプロファイルを示すマークです。モジュールとして nginx 1.14 をインストールしたことによって、 **nginx** モジュールの **1.14** ストリーム中の **common** プロファイルがインストールされたとシステムに認識されたようです。

## 1-c. モジュールのアンインストールを実行する。
パッケージインストールを行った後にモジュールのアンインストールコマンドを実行するとどうなるか確認してみましょう。

```bash
$ sudo dnf module remove nginx
メタデータの期限切れの最終確認: 0:10:29 時間前の 2020年05月28日 11時21分52秒 に 実施しました。
引数 nginx でプロファイルを一致できません
依存関係が解決しました。
行うべきことはありません。
完了しました!

$ rpm -qa | grep nginx
nginx-mod-http-perl-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
nginx-all-modules-1.14.1-9.module_el8.0.0+184+e34fea82.noarch
nginx-mod-mail-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
nginx-mod-stream-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
nginx-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
nginx-mod-http-image-filter-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
nginx-filesystem-1.14.1-9.module_el8.0.0+184+e34fea82.noarch
nginx-mod-http-xslt-filter-1.14.1-9.module_el8.0.0+184+e34fea82.x86_64
```

おや、どうやらモジュールとしてインストールされている認識ではないのでこの方法では何もしてくれないようです。
```dnf remove``` ならアンインストールしてくれますね。

```bash
$ sudo dnf remove nginx
依存関係が解決しました。
================================================================================
 パッケージ      Arch   バージョン                             Repo       サイズ
================================================================================
削除中:
 nginx           x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream 1.7 M
未使用の依存関係の削除:
 nginx-all-modules
                 noarch 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream   0
 nginx-filesystem
                 noarch 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream   0
 nginx-mod-http-image-filter
                 x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream  29 k
 nginx-mod-http-perl
                 x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream  61 k
 nginx-mod-http-xslt-filter
                 x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream  25 k
 nginx-mod-mail  x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream 113 k
 nginx-mod-stream
                 x86_64 1:1.14.1-9.module_el8.0.0+184+e34fea82 @AppStream 171 k

トランザクションの概要
================================================================================
削除  8 パッケージ

解放された容量: 2.0 M
これでよろしいですか? [y/N]:
```

# 2. 先にモジュールでインストールした後に……
検証 2-a では nginx の代わりに postgresql を使用します。（詳細は後述）
検証 2-b, 2-c では nginx 1.14 をインストールした状態をスタートとして検証を行います。

```bash
$ sudo dnf module -y install nginx
メタデータの期限切れの最終確認: 0:29:16 時間前の 2020年05月28日 11時21分52秒 に実施しました。
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                                      アーキテクチャー                           バージョン                                                                   リポジトリー                                  サイズ
=============================================================================================================================================================================================================================================
group/moduleパッケージをインストール:
 nginx                                                           x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                     570 k
 nginx-all-modules                                               noarch                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      23 k
 nginx-filesystem                                                noarch                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      24 k
 nginx-mod-http-image-filter                                     x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      35 k
 nginx-mod-http-perl                                             x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      45 k
 nginx-mod-http-xslt-filter                                      x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      33 k
 nginx-mod-mail                                                  x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      64 k
 nginx-mod-stream                                                x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                       AppStream                                      85 k
モジュールプロファイルのインストール:
 nginx/common
モジュールストリームの有効化:
 nginx                                                                                                      1.14
```

## 2-a. 違うバージョンをパッケージでインストールする。
1-a に対応した検証を行うのであれば nginx 1.14 をモジュールインストールした状態で nginx 1.16 をパッケージインストールするのが望ましいところですが、 nginx 1.16 をパッケージインストールできませんでした。そのため、代わりに postgresql 9.6 をモジュールインストールした後に postgresql 10 する形で検証を行います。

```bash
# 9.6 をモジュールインストールする前に、10 がパッケージインストールされることを確認。
$ sudo dnf install postgresql
メタデータの期限切れの最終確認: 0:14:16 時間前の 2020年06月05日 02時08分54秒 に                                                                                                                                                              実施しました。
依存関係が解決しました。
================================================================================
 パッケージ   Arch     バージョン                             Repo        サイズ
================================================================================
インストール:
 postgresql   x86_64   10.6-1.module_el8.0.0+15+f57f353b      AppStream   1.5 M
依存関係のインストール:
 libpq        x86_64   12.1-3.el8                             AppStream   195 k
モジュールストリームの有効化:
 postgresql            10

トランザクションの概要
================================================================================
インストール  2 パッケージ

ダウンロードサイズの合計: 1.7 M
インストール済みのサイズ: 6.4 M
これでよろしいですか? [y/N]: N

$ sudo dnf module -y install postgresql:9.6
(省略)

$ sudo dnf install postgresql
メタデータの期限切れの最終確認: 0:04:46 時間前の 2020年06月05日 02時08分54秒 に                                                                                                                                                              実施しました。
パッケージ postgresql-9.6.10-1.module_el8.0.0+16+7a9f6089.x86_64 は既にインスト                                                                                                                                                              ールされています。
依存関係が解決しました。
行うべきことはありません。
完了しました!
```

モジュールインストールを行う前と行った後で、 ```dnf install postgresql``` でインストールされるバージョンが異なっています。
どうやらストリームが有効化されると、それに準じたバージョンがインストールされるようです。
ストリームが有効化されていない場合は ```[d]``` マークで示されていたデフォルトストリームに倣うことになります。

ついでに、 nginx 1.16 をモジュールインストールした後に nginx 1.14 をパッケージインストールしてみます。

```bash
$ sudo dnf install nginx
メタデータの期限切れの最終確認: 0:43:27 時間前の 2020年05月28日 11時21分52秒 に実施しました。
パッケージ nginx-1:1.16.1-1.module_el8.1.0+250+351caf85.x86_64 は既にインストールされています。
依存関係が解決しました。
行うべきことはありません。
完了しました!
```

今までの ```dnf install nginx``` コマンドでは 1.14 がインストールされていましたが、今度は 1.16 のインストールを試みています。
有効化されているストリームに従ってパッケージをインストールしていることが確認できました。

## 2-b. 同じバージョンをパッケージでインストールする。
今度は nginx 1.14 をモジュールインストールした状態で、nginx パッケージをインストールしてみます。

```bash
$ sudo dnf install nginx
メタデータの期限切れの最終確認: 0:57:13 時間前の 2020年05月28日 11時21分52秒 に実施しました。
依存関係が解決しました。
行うべきことはありません。
完了しました!
```

まあそうでしょうね、という結果です。
逆の時はモジュールプロファイルの更新がありましたが今度は何もありません。

## 2-c. パッケージのアンインストールを実行する。
nginx 1.14 モジュールを ```dnf remove nginx``` でアンインストールします。

```bash
$ sudo dnf remove nginx
依存関係が解決しました。
=============================================================================================================================================================================================================================================
 パッケージ                                                      アーキテクチャー                           バージョン                                                                  リポジトリー                                   サイズ
=============================================================================================================================================================================================================================================
削除中:
 nginx                                                           x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                     1.7 M
依存関係パッケージの削除:
 nginx-all-modules                                               noarch                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                       0
未使用の依存関係の削除:
 nginx-mod-http-image-filter                                     x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                      29 k
 nginx-mod-http-perl                                             x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                      61 k
 nginx-mod-http-xslt-filter                                      x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                      25 k
 nginx-mod-mail                                                  x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                     113 k
 nginx-mod-stream                                                x86_64                                     1:1.14.1-9.module_el8.0.0+184+e34fea82                                      @AppStream                                     171 k

トランザクションの概要
=============================================================================================================================================================================================================================================
削除  7 パッケージ

解放された容量: 2.0 M
これでよろしいですか? [y/N]:y
```

アレ？ **1 パッケージ足りない**ですね。そのままアンインストールを実行して何が残ったか調べてみましょう。

```bash
$ rpm -qa | grep nginx
nginx-filesystem-1.14.1-9.module_el8.0.0+184+e34fea82.noarch
```

```nginx-filesystem``` が残ってしまっています。
確かに nginx の依存パッケージの一つのはずですが、異なるアンインストール方法で削除してしまうとこのようなことも起こりえるようです。
ところで、 ```dnf module remove``` はモジュールとしてインストールされている認識がシステムにない時、アンインストールを行わないことを確認しました。
この状態で ```dnf module reset nginx``` するともしかして……

```bash
$ sudo dnf module -y reset nginx
メタデータの期限切れの最終確認: 1:46:13 時間前の 2020年05月28日 14時24分53秒 に 実施しました。
依存関係が解決しました。
================================================================================
 パッケージ        アーキテクチャー バージョン          リポジトリー      サイズ
================================================================================
モジュールプロファイルの無効化:
 nginx/common
モジュールの再設定:
 nginx

トランザクションの概要
================================================================================

完了しました!

$ sudo dnf module remove nginx
メタデータの期限切れの最終確認: 2:13:00 時間前の 2020年05月28日 11時21分52秒 に実施しました。
引数 nginx でプロファイルを一致できません
依存関係が解決しました。
行うべきことはありません。
完了しました!
$ rpm -qa | grep nginx
nginx-filesystem-1.14.1-9.module_el8.0.0+184+e34fea82.noarch
```

```nginx-filesystem``` を nginx の依存パッケージとしてアンインストールすることができなくなってしまいました。
```dnf remove nginx-filesystem``` で個別に削除することはできますが、パッケージのアンインストールを行う前に ```dnf module list``` でどちらの方式でインストールを行っているか確認したほうが無難そうです。

# まとめ
- CentOS8 はストリームによって、異なるバージョンのソフトウェアがシステム内に混在しないよう管理している。
- CentOS8 はプロファイルによって、「**モジュールとしてインストールされたか**」「**されている場合はどのプロファイルか**」を認識している。
- パッケージインストールを行った後にモジュールインストールを行うと、メタ情報の更新が差分として行われる。
- **モジュールインストールしたものに対してパッケージのアンインストールを行うと、依存関係を正常に辿れない場合がある。**

# おわりに
- **モジュールでインストールしたら、モジュールでアンインストールしよう！！**

弊社 web サイトではコンテナ、クラウド、構成管理ツール、パッケージマネージャなど、 IT 基盤構築に関する様々な技術情報の公開を行っています。興味がありましたら覗いてみてください。
http://c.itdo.jp/category/technical-information/