CentOS 8でOSとカーネルのバージョンを固定する

# What's？

CentOS 8で、パッケージマネージャー（`dnf`）を使ってパッケージを更新する時に、OSとカーネルのバージョンを固定するには？ということで。

結論としては、

- `/etc/dnf/dnf.conf`に`excludepkgs`を書く
- `/etc/yum/vars/releasever`にバージョンを書いて、`/etc/yum.repos.d`ディレクトリ配下の内容を調整する

といったところですね。

# 環境

確認環境。今回はCentOS 8.3を使います。

```shell
$ cat /etc/redhat-release
CentOS Linux release 8.3.2011


$ uname -srvmpio
Linux 4.18.0-240.22.1.el8_3.x86_64 #1 SMP Thu Apr 8 19:01:30 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
```

この時点でCentOS 8.4がリリースされているので、ふつうに`dnf upgrade`しようとすると、CentOS 8.4にアップグレードされるのでこれを止めてみます。

# 未設定での振る舞い

まずは、`centos*`パッケージおよび`kernel*`パッケージの更新確認をしてみましょう。

`centos*`。

```shell
$ sudo dnf check-upgrade centos*
Last metadata expiration check: 0:04:39 ago on Mon Jun 21 13:24:20 2021.

centos-linux-release.noarch                                                               8.4-1.2105.el8                                                               baseos
```

`kernel*`。

```shell
$ sudo dnf check-upgrade kernel*
Last metadata expiration check: 0:04:56 ago on Mon Jun 21 13:24:20 2021.

kernel.x86_64                                                                         4.18.0-305.3.1.el8                                                               baseos
kernel-core.x86_64                                                                    4.18.0-305.3.1.el8                                                               baseos
kernel-modules.x86_64                                                                 4.18.0-305.3.1.el8                                                               baseos
kernel-tools.x86_64                                                                   4.18.0-305.3.1.el8                                                               baseos
kernel-tools-libs.x86_64                                                              4.18.0-305.3.1.el8                                                               baseos
```

それぞれ、OS、カーネルのアップデート対象があります。

# `centos*`パッケージおよび`kernel*`パッケージを更新対象外にする

では、これらを更新対象外にしてみましょう。`/etc/dnf/dnf.conf`を変更します。

```shell:/etc/dnf/dnf.conf
[main]
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=True
best=True
skip_if_unavailable=False
excludepkgs=centos*,kernel*
```

変更したのは、`excludepkgs`の追加です。こちらに、`,`区切りで更新対象外にしたいパッケージを記述します。

```shell
excludepkgs=centos*,kernel*
```

https://man7.org/linux/man-pages/man5/dnf.conf.5.html

確認してみましょう。

```shell
$ sudo dnf check-upgrade centos*
Last metadata expiration check: 0:07:18 ago on Mon Jun 21 13:24:20 2021.


$ sudo dnf check-upgrade kernel*
Last metadata expiration check: 0:07:26 ago on Mon Jun 21 13:24:20 2021.
```

どちらも、更新対象外になりましたね。OKです。

# 他のパッケージをインストールしようとすると？

ところがですね、たとえば`python3.8`をインストールしようとしてみると

```shell
$ sudo dnf install python3.8
Last metadata expiration check: 0:01:07 ago on Mon Jun 21 13:31:48 2021.
Dependencies resolved.
=============================================================================================================================================================================
 Package                                        Architecture                Version                                                     Repository                      Size
=============================================================================================================================================================================
Installing:
 python38                                       x86_64                      3.8.6-3.module_el8.4.0+665+abc3a503                         appstream                       79 k
Installing dependencies:
 python38-libs                                  x86_64                      3.8.6-3.module_el8.4.0+665+abc3a503                         appstream                      8.3 M
 python38-pip-wheel                             noarch                      19.3.1-1.module_el8.4.0+647+0ba99ce8                        appstream                      1.2 M
 python38-setuptools-wheel                      noarch                      41.6.0-4.module_el8.4.0+647+0ba99ce8                        appstream                      304 k
Installing weak dependencies:
 python38-pip                                   noarch                      19.3.1-1.module_el8.4.0+647+0ba99ce8                        appstream                      1.9 M
 python38-setuptools                            noarch                      41.6.0-4.module_el8.4.0+647+0ba99ce8                        appstream                      667 k
Enabling module streams:
 python38                                                                   3.8                                                                                             

Transaction Summary
=============================================================================================================================================================================
Install  6 Packages

Total download size: 12 M
Installed size: 45 M
Is this ok [y/N]: 
```

表示されるのは、CentOS 8.4のパッケージです。最新のリポジトリの情報を見ているようですね。

これはちょっと微妙ですね。8.3を向いたままにできないのでしょうか？

先ほど設定したこちらの情報は、ちょっとコメントアウトしておきます。

```shell:/etc/dnf/dnf.conf
#excludepkgs=centos*,kernel*
```

`/etc/yum/vars/releasever`にバージョンを指定。

```shell
$ sudo su -c 'echo '8.3.2011' > /etc/yum/vars/releasever'
```

これから、リポジトリの参照先を以下のどちらかに調整していきます。

http://mirror.centos.org/centos/

https://vault.centos.org/

たとえば`/etc/yum.repos.d/CentOS-Linux-AppStream.repo`であれば、以下の内容ですね。

```shell:/etc/yum.repos.d/CentOS-Linux-AppStream.repo
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
```

こちらのコメントアウトを入れ替え、`$releasever`については`/etc/yum/vars/releasever`に書き込んだ値を使ってもらいます。
※この作業が必要な理由は、後で記載します

ちなみに、他には`content`と`infra`という変数が`/etc/yum/vars`配下には格納されています。

```shell
$ cat /etc/yum/vars/contentdir
centos


$ cat /etc/yum/vars/infra
stock
```

ひとまず、現時点でのファイルをバックアップ。

```shell
$ find /etc/yum.repos.d -name '*.repo' | sed -r -e 's!(.+)!cp \1 \1.org!' | xargs -I {} sudo sh -c {}
```

一気に文字列置換します。

```shell
$ find /etc/yum.repos.d -name '*.repo' | \
  xargs sudo sed -r -i -e 's!^mirrorlist=http://mirrorlist.centos.org!#mirrorlist=http://mirrorlist.centos.org!; s!^#baseurl=http://mirror.centos.org!baseurl=http://mirror.centos.org!'
```

または、こちら。

```shell
$ find /etc/yum.repos.d -name '*.repo' | \
  xargs sudo sed -r -i -e 's!^mirrorlist=http://mirrorlist.centos.org!#mirrorlist=http://mirrorlist.centos.org!; s!^#baseurl=http://mirror.centos.org!baseurl=https://vault.centos.org!'
```

細かく見ると、`mirrorlist`をコメントアウトして

```shell
s!^mirrorlist=http://mirrorlist.centos.org!#mirrorlist=http://mirrorlist.centos.org!
```

`baseurl`のコメントアウトを解除します。

```shell
s!^#baseurl=http://mirror.centos.org!baseurl=http://mirror.centos.org!
```

もしくは、コメントアウトを解除しつつ`https://vault.centos.org`に置き換えます。

```shell
s!^#baseurl=http://mirror.centos.org!baseurl=https://vault.centos.org!
```

`$releasever`を設定しても、`http://mirrorlist.centos.org`を参照したままだと、`404`になってしまうからです…。

これで、1度`dnf`のキャッシュをクリア。

```shell
$ sudo dnf clean all
```

確認してみます。

```shell
$ sudo dnf install python3.8
CentOS Linux 8.3.2011 - AppStream                                                                                                            685 kB/s | 6.3 MB     00:09    
CentOS Linux 8.3.2011 - BaseOS                                                                                                               587 kB/s | 2.3 MB     00:03    
CentOS Linux 8.3.2011 - Extras                                                                                                                22 kB/s | 9.6 kB     00:00    
Dependencies resolved.
=============================================================================================================================================================================
 Package                                        Architecture                Version                                                     Repository                      Size
=============================================================================================================================================================================
Installing:
 python38                                       x86_64                      3.8.3-3.module_el8.3.0+468+0c52a667                         appstream                       78 k
Installing dependencies:
 python38-libs                                  x86_64                      3.8.3-3.module_el8.3.0+468+0c52a667                         appstream                      8.3 M
 python38-pip-wheel                             noarch                      19.3.1-1.module_el8.3.0+441+3b561464                        appstream                      1.2 M
 python38-setuptools-wheel                      noarch                      41.6.0-4.module_el8.3.0+441+3b561464                        appstream                      304 k
Installing weak dependencies:
 python38-pip                                   noarch                      19.3.1-1.module_el8.3.0+441+3b561464                        appstream                      1.9 M
 python38-setuptools                            noarch                      41.6.0-4.module_el8.3.0+441+3b561464                        appstream                      667 k
Enabling module streams:
 python38                                                                   3.8                                                                                             

Transaction Summary
=============================================================================================================================================================================
Install  6 Packages

Total download size: 12 M
Installed size: 45 M
Is this ok [y/N]: 
```

すると、CentOS 8.3のリポジトリの内容で表示されました。

ちなみに、現時点ではこの状態でも`centos*`や`kernel*`は更新されません。

```shell
$ sudo dnf check-upgrade centos*
Last metadata expiration check: 0:06:20 ago on Mon Jun 21 13:42:49 2021.


$ sudo dnf check-upgrade kernel*
Last metadata expiration check: 0:06:24 ago on Mon Jun 21 13:42:49 2021.
```

これは、単純に現時点でのCentOS 8.3の最新の内容だからですね。

カーネルのバージョンを完全に固定したい場合は、やはり`/etc/dnf/dnf.conf`に`excludepkgs`を書いておくのが良いでしょう。

最後に、書き換えたリポジトリ定義の変更差分を載せておきます。

```shell
$ find /etc/yum.repos.d -name '*.repo' | sed -r -e 's!(.+)!diff \1 \1.org!' | xargs -I {} sh -c {}
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
20,21c20,21
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/cr/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=cr&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/cr/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=Devel&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/Devel/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=Devel&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/Devel/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/extras/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/extras/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=fasttrack&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/fasttrack/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=fasttrack&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/fasttrack/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=HighAvailability&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/HighAvailability/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=HighAvailability&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/HighAvailability/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/centosplus/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/centosplus/$basearch/os/
13,14c13,14
< #mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=PowerTools&infra=$infra
< baseurl=https://vault.centos.org/$contentdir/$releasever/PowerTools/$basearch/os/
---
> mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=PowerTools&infra=$infra
> #baseurl=http://mirror.centos.org/$contentdir/$releasever/PowerTools/$basearch/os/
```

こうやってみると、やっぱりできる限りこういう設定はしたくないなぁ、とはちょっと思います。

## 現在のバージョンで固定する前提で、簡単に書くとすると？

現在のバージョン（`/etc/redhat-release`の値を使って）を対象に簡単に書くとしたら、こうでしょうか。

```shell
$ VER=`sed -r -e 's!^.+ ([.0-9]+)( .*)?$!\1!' /etc/redhat-release`
$ sudo su -c "echo '$VER' > /etc/yum/vars/releasever"

## http://mirror.centos.org を使う場合

$ find /etc/yum.repos.d -name '*.repo' | \
  xargs sudo sed -r -i -e 's!^mirrorlist=http://mirrorlist.centos.org!#mirrorlist=http://mirrorlist.centos.org!; s!^#baseurl=http://mirror.centos.org!baseurl=http://mirror.centos.org!'

## https://vault.centos.org を使う場合

$ find /etc/yum.repos.d -name '*.repo' | \
  xargs sudo sed -r -i -e 's!^mirrorlist=http://mirrorlist.centos.org!#mirrorlist=http://mirrorlist.centos.org!; s!^#baseurl=http://mirror.centos.org!baseurl=https://vault.centos.org!'
```

## mirrorlistとbaseurlを書き換えたのは？

`mirrorlist`で指定されたURLは、リポジトリのミラーを返す仕組みです。

たとえばこちらのURL定義に対して

```
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
```

具体的な値を入れて実際にアクセスすると、ミラーのリストが返ってきます。

```shell
$ curl 'http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=AppStream&infra=stock'
http://ftp.yz.yamagata-u.ac.jp/pub/linux/centos/8.4.2105/AppStream/x86_64/os/
http://ftp.jaist.ac.jp/pub/Linux/CentOS/8.4.2105/AppStream/x86_64/os/
http://ftp.nara.wide.ad.jp/pub/Linux/centos/8.4.2105/AppStream/x86_64/os/
http://ftp.tsukuba.wide.ad.jp/Linux/centos/8.4.2105/AppStream/x86_64/os/
http://mirrors.cat.net/centos/8.4.2105/AppStream/x86_64/os/
http://ftp-srv2.kddilabs.jp/Linux/packages/CentOS/8.4.2105/AppStream/x86_64/os/
http://ty1.mirror.newmediaexpress.com/centos/8.4.2105/AppStream/x86_64/os/
http://ftp.riken.jp/Linux/centos/8.4.2105/AppStream/x86_64/os/
http://mirror.newmediaexpress.com/centos/8.4.2105/AppStream/x86_64/os/
http://mirror.vodien.com/centos/8.4.2105/AppStream/x86_64/os/
```

ですが、マイナーバージョン等を指定しても、ミラーは返ってこなくなります。

```shell
$ curl 'http://mirrorlist.centos.org/?release=8.3.2011&arch=x86_64&repo=AppStream&infra=stock'
Invalid release/repo/arch combination
```

このため、`baseurl`で以下を参照するようにしています。

```shell
baseurl=http://mirror.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
```

もしくは、こちらですね。

```shell
baseurl=https://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
```

`https://vault.centos.org`は、アーカイブ済みのバージョンの場合に参照します。

`http://mirror.centos.org/centos/`に説明が書いていますね。

> For archived content, see Vault mirror.

http://mirror.centos.org/centos/