えんでぃの技術ブログ

えんでぃの技術ブログ

ネットワークエンジニアの視点で、IT系のお役立ち情報を提供する技術ブログです。

systemd-resolvedの特徴と、使い方紹介

systemd-light

前の記事

systemd-resolvedがデフォルトのDNSクライアントとなる前後の/etc/nsswitch.confを比較しました。
また、興味のある方向けに詳細まで踏み込んで紹介しました。
忙しい方はサマリだけでもご覧ください。

endy-tech.hatenablog.jp

お伝えしたいこと

Fedora33以降で (/etc/hostsmulticast DNSを除けば) デフォルトのDNSクライアントとなったnss-resolve (systemd-resolved)について概要を紹介します。
従来のDNSであるnss-dns (/etc/resolv.conf)との違いに着目しつつ、systemd-resolvedの基本構成や便利機能 (per-link DNS)、使い方について書きました。

設定ファイルである/etc/systemd/resolved.conf の書きっぷりについては深く触れていませんが、基本動作にてDNSサーバ選出時にどう関わるかのみ紹介しています。
DNSサーバを使い分けない場合は/etc/systemd/resolved.confのみに記載するのがシンプルですが、per-link DNSを使う場合は寧ろNetworkManagerのみでほぼ全てを制御するのが良いと思います。
いずれにしても設定箇所をあまり分散せず、可能な限り一つにまとめるのがわかりやすくて良いでしょう。

systemd-resolved とは

systemd-resolvedは、Linux においてDNSクライアントとして動作します。
多くのLinuxディストリビューションでinitプロセスを管理しているsystemdの1コンポーネントでもあります。

systemd-resolvedの主要な構成要素は、下表の通りです。

要素 説明
systemd-resolved.service systemd に管理されているサービス。
systemctlによって起動・停止などを制御する
/etc/systemd/resolved.conf systemd-resolvedの設定ファイル
/run/systemd/resolve/stub-resolv.conf Fedora33以降では、/etc/resolv.confがこのファイルへのシンボリックリンクになっている。
digなど/etc/resolv.confを固定的に参照するプログラムとの互換性のために、systemd-resolved.service/run/systemd/resolve/stub-resolv.confを自動的に更新する
resolvectl systemd-resolvedコマンドラインツール。
systemd-resolvedの動作を確認したり、リアルタイムに変更したりできる

systemd-resolved のAPI

man systemd-resolved、またはArch Wiki によると、systemd-resolvedを使う方法 (API: Application Programming Interface) は3種類あります。
いずれのAPIもローカル専用で、ネットワーク越しに外部ホストからアクセスすることはできません。

man systemd-resolvedによると、サポートされる機能の多さとしては(1) > (2) >> (3)という関係になっており、systemdの開発者としては(3)よりも(1), (2) のAPIを使うことをより推奨しているようです。
(3) は、古いプログラムへの互換性を保つために残されています。

次のセクションでこれらのAPIがどのような使われ方をするのか、より具体的に紹介します。

# API 説明
1 D-Bus (Desktop Bus) プロセス間通信機能を提供するAPI
2 NSS (Name System Switch) /etc/nsswitch.confを介してアクセスする方式。
GNU C ライブラリによって実装されている
3 Local DNS Stub Listener TCP/UDP127.0.0.53:53 でリッスンしている
/etc/resolv.confを直接参照するプログラムが利用する

(1) D-Bus (Desktop Bus)

プロセス間で各種設定情報や命令を直接やり取りするための仕組みです (IPC: InterProcess Communication = プロセス間通信)
基本的には同一OS内のローカルのやり取りのみをサポートします。
ネットワーク越しに外部ホストとメッセージをやり取りするような使い方は、サポートされていないとのことです。

標準入力や標準出力、シェルなどのユーザー操作を介さずにプロセス間で直接やり取りすることで、ユーザーに意識させず、プロセス間でイイカンジに情報連携するための仕組みと理解しています。
アプリケーション開発者がこういった内部処理を作り込むことで、複数プロセスが矛盾なく連携し、粗結合なアーキテクチャで高度な機能や使い勝手を実現できるのでしょう。

D-Busの概要は、下記サイトにとてもわかりやすく解説されていました。

www.silex.jp

D-Busを利用している身近な例としては、私の知る限り2つあります。
どちらもNetworkManager関連です。

1つ目は、nmcliです。
nmcliで設定する際、裏ではNetworkManager.serviceD-Bus API を叩いて設定変更を反映しているようです。
nmtuiなど、その他のコマンドも恐らく同様です

以下の出力からも何となく察せると思います。

nmcli conn up eno1
# Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5)

2つ目は、NetworkManagerからsystemd-resolvedへの設定情報連携です。
Fedora33以降の構成で、/etc/nsswitch.conf (resolveが選出され、DNSクライアントとしてsystemd-resolvedが選択される) や、/etc/resolv.conf (システムのDNSサーバとして127.0.0.53を参照する。結果としてsystemd-resolvedがスタブリゾルバとして問い合わせを代行する) だけを見ていると、NetworkManager のDNS設定が挙動に影響しない...と考えたくなるのですが、実は影響を与えます。

それは、NetworkManagerD-Bus APIによってsystemd-resolvedに設定情報を連携しているためです。
man NetworkManager.confに書いてありますが、/etc/NetworkManager/NetworkManager.confsystemd-resolvedという設定項目のデフォルト値がtrueになっています。
これにより、D-Bus API越しの情報連携が有効になります。

この挙動に馴染みのない方は、ご注意を...。
NetworkManagersystemd-resolvedの連携については、記事の後半で具体的な設定手順と共に紹介します。

(2) NSS (Name Service Switch)

DNSクライアントが名前解決をする際、/etc/nsswitch.confhosts:行を参照してどの方式で名前解決するかを決定します。

Fedora33以降の/etc/nsswitch.confは、以下のようになっています。

hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns

# 参考情報として、Fedora32 <span style="color: #0000cc">(恐らくRHEL8と同じ)</span> の/etc/nsswitch.confも載せておきます
#hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname

詳細は前回の記事に譲りますが、多くの場合は/etc/hostsに記載がなければsystemd-resolvedによって外部DNSサーバに問い合わせするのが基本の動きです。

(3) Local DNS Stub Listener

Local DNS Stub Listener とは、TCP/UDPでリッスンしている 127.0.0.53:53のことです。

Fedora33以降では/etc/resolv.conf/run/systemd/resolve/stub-resolv.confへのシンボリックリンクとなっており、/run/systemd/resolve/stub-resolv.confsystemd-resolvedが自動更新するという構造になっています。
結果として、/etc/resolv.confを参照すると、システムのDNSサーバが127.0.0.53、つまりsystemd-resolvedに設定されていることになります。

digコマンドやhostコマンドのように、/etc/nsswitch.confではなく/etc/resolv.confを直接参照するプログラムにも互換性を持たせるため、こういったアクセスの仕方も用意されています。

systemd-resolved の基本機能

前回の記事で紹介しましたが、名前解決に使われるNSS (/etc/nsswitch.confの右側に列挙されているもののこと) には様々なものがあります。
systemd-resolved(=nss-resolve) は、既存のNSSの多くを置き換える機能性を持っています。
systemd-resolvedの場合はキャッシュが効くため、systemdのマニュアルでは従来のfilesmyhostnameよりも推奨されています (→ 前回の記事)。

既存のNSS名 既存NSSの機能 systemd-resolvedでの対応
files /etc/hostsを読み取る デフォルトで有効
mdns4_minimal mDNS機能 (あまり使わない) デフォルト無効。
/etc/systemd/resolved.confを書き換えて有効化できる
dns 外部DNSサーバにクエリを投げる) デフォルト有効。
従来のdns/etc/resolv.confによるシンプルな制御だった。
resolveでは、/etc/systemd/resolved.confでより高度な制御ができる
myhostname 自身のホスト名、*.localhostlocalhostなどの名前解決
(直接は使わないが、この機能を利用するプログラムもある)
デフォルト有効

Fedora33のデフォルトの/etc/nsswitch.conffilesが最初に検索されるため、/etc/hostsの検索にsystemd-resolvedが利用されることはありません。
/etc/nsswitch.confの順序を入れ替えてresolvefilesよりも左に配置することで、systemd-resolved/etc/hostsを読み取るよう設定することも可能です。

このようにsystemd-resolvedは従来のNSSの大半を置き換える機能性を持ちます。
後続のセクションでは、これらの機能の中でも従来のdnsに相当する、外部DNSサーバへの問い合わせの挙動について紹介します。

systemd-resolved と従来型DNSの違い

systemd-resolved (nss-resolve)と従来型DNS (nss-dns)の違いを示します。

設定ファイルの違い

従来のDNS/etc/resolv.confを参照していました。

一方で、systemd-resolved/etc/systemd/resolved.confを参照します。
それぞれの設定ファイルの仕様を見比べると、両者の違いがより詳細に見えてきます。

search (ドメイン自動補完)

まずは、search機能についておさらいします。
例えばsearch に xxx.com を指定していたとします。
ping Aを実行すると、Aが名前解決できなくてもxxx.comを自動補完してA.xxx.compingを実行してくれます。

従来のDNSでは、/etc/resolv.confndotsというオプションによって「ピリオドをいくつまで含んでいるときに自動補完の対象とするか」を制御できました (man resolv.conf)。

systemd-resolvedの場合はndotsを敢えて実装しておらず、単一ラベル (つまりピリオドを含まない) の場合のみドメインを補完します(man systemd-resolved)。
systemd-resolvedndotsを実装していない理由については、man systemd-resolvedで言及されています。

DNSサーバの複数指定

従来のDNSでは、最大3台までDNSサーバを指定できます。
基本的には1台目のみにクエリを投げます。
1台目のDNSサーバへのクエリがタイムアウトしたら、次のDNSサーバに問い合わせます。

systemd-resolvedでは、Routing という仕組みによってDNSサーバが選択されます。
ネットワークのIP Routing とは若干関係ありますが、完全に別物です。
これがper-link DNSに繋がります。
詳細は次のセクションで紹介します。

基本動作

systemd-resolvedは、ネットワークインターフェースごとにDNSサーバを指定できます。
その上で、Routing という仕組みによって、クエリごとにDNSサーバを使い分けます。

具体的には以下の動きになります (man systemd-resolvedPROTOCOLS AND ROUTINGより)
※グレーにした部分はあまり重要ではないので、無視してください
※5. についてはコマンド実行ログは掲載していませんが、私の手元で検証済みです

# 詳細
1 /etc/hostsや自身のホスト名、localhostlocalhost.localdomainXXX.localhostなどは、ネットワークにクエリを出すことなくローカルのみで名前解決する
2 単一ラベル (ピリオドを含まない) 場合は、Unicast DNS (通常のUDP#53のクエリ) は使われず、LLMNR (mDNSのように同一ネットワーク内の名前解決をするサービス) で名前解決する。
ただし、AレコードやAAAAレコードの場合は search domains によってドメイン名が補完され、Unicast DNS でクエリされる
3 *.localという名前はmDNSとして扱われるため、Unicast DNSとしては動作しない
4 複数ラベルA, AAAA, PTRレコードの場合 (ピリオドを含む場合) は、以下のロジックで最適なDNSサーバが選出される (man resolved.confDomains=も併せて参照) (※1)(※2)
  • アクセス先のFQDNが search domains やroute-only domainsと重なる場合は、そのインターフェースと紐づくDNSサーバが優先される
  • 複数の search domains やroute-only domainsに該当する場合は、ラベル数 (※3) の多い search domains と紐づくDNSサーバが優先される (best matching)
  • best matchingDNSサーバが複数あった場合、それらのDNSサーバ全てに対してクエリされる。クエリ結果が複数返ってきた場合は先に受信した方が優先される
  • ~.を含めてsearch domains に全く一致しなかった場合、5 に進む
5 4 のper-link DNSのsearch domains に一致しなかった場合、以下の挙動となる。
  • グローバルなDNSサーバ (/etc/systemd/resolved.confDNS) が設定されていれば、そこに問い合わせる
  • グローバルなDNSサーバが設定されていなかった場合、IPルーティングのデフォルトゲートウェイと紐づくper-link DNSサーバ (resolvectlコマンド出力で+DefaultRouteフラグがついているDNS) に問い合わせる
  • グローバルなDNSサーバ、IPデフォルトゲートウェイのいずれも設定がない場合、全てのper-link DNSとして設定されたDNSサーバに問い合わせ、最初に返ってきた応答を採用する

(※1) search domains に ~. を指定することで、どのFQDNにもマッチする指定が可能です (DNS Default Route)。best matching の観点では、最も優先順位が低いです。~をつけるとRouting Domain の扱いとなり、「DNS Routingの評価対象になるが単一ラベルに対するドメイン補完には利用されない」挙動になります。.はルートドメインを意味し、全てのドメインにマッチします

(※2) グローバルなDNS設定 (/etc/systemd/resolved.confDNSDomains) もper-link DNSと同様に評価されます。グローバルなDNSサーバがbest matching となれば、グローバルなDNSサーバのみにクエリされます。グローバルなDNSサーバとper-link DNSサーバの両方がbest matching となった場合は、どちらにもクエリされます

(※3) 例えば、www.google.comというFQDNにはwwwgooglecomという3つのラベルが含まれます

基本動作の要点

DNS Routing の挙動が非常にややこしいですが、上表の4と5をまとめると、A, AAAA, PTR レコードのクエリ先のDNSサーバは、以下の順序で評価されるようです。
あらゆる状況に対処できるように、systemd-resolvedには細かいルールが設定されていますが、基本的には1か2で確定できるように設計するのがわかりやすくてオススメです。

  1. ~.を含め、search domains に一致したDNS (best matching)。per-link DNS もグローバルなDNSも特に差はなく、同列に評価される
  2. グローバルなDNSが設定されていれば、そこにクエリする
  3. IP Routing の世界でデフォルトルートと紐づくNICにper-link DNSサーバが設定されていれば、そこにクエリする
  4. 全てのper-link DNSサーバにクエリする

設定方法

man resolved.confによると、/etc/systemd/resolved.confではシステム全体でクエリ先のDNSサーバを設定できますが、 search domains によって優先順位付けするような指定方法 (per-link DNS) はできません。

per-link DNSの設定はsystemd-networkd、または外部アプリケーション (例えばD-Bus連携しているNetworkManager) からの同期によって読み込まれます。
次のセクションで具体的な設定例を紹介しますが、設定例は使い慣れた NetworkManager (nmcli)にて紹介します。

どんな時に役に立つか?

Linux端末がVPNに接続している場合、VPN越しにアクセスするDNSサーバと、通常の物理ネットワーク越しにアクセスするDNSサーバで使い分けたいケースがあると思います。
社内向けドメインVPN越しのDNSサーバに、その他のgoogle.comなどのドメインは物理ネットワーク越しのDNSサーバに名前解決をしたいときに、per-link DNSが役に立つと思います。

(参考) 具体例

実際にDNS設定がどうなるか、色々な例で試してみました。

systemd-resolvedで/etc/hostsを認識させる

/etc/nsswitch.confを変更して、systemd-resolvedしか使わないように変更します。
これは検証のための設定変更であり、本来はこのようにすべきではないのでご注意ください。

#hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns
hosts:      resolve [!UNAVAIL=return]

また、/etc/hostsに試験用のエントリを追加します。

8.8.8.8     google-hosts

この状態でgoogle-hostspingしたところ、名前解決できました。
確かに、systemd-resolved/etc/hostsを読み込むようです。

ping google-hosts
# PING google-hosts (8.8.8.8) 56(84) bytes of data.
# 64 bytes from google-hosts (8.8.8.8): icmp_seq=1 ttl=116 time=2.88 ms
# (以下略)

ちなみに、自身のホスト名へのpingも通りました (nss-myhostname相当)。

ping pc
# PING pc.internal1 (192.168.100.155) 56(84) bytes of data.
# 64 bytes from pc (192.168.100.155): icmp_seq=1 ttl=64 time=0.252 ms
# (以下略)

変更した/etc/nsswitch.confを元に戻しておきます。

hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns

search domains にヒットする方を優先する

こちらの構成で検証します。

dns1

DNS1, DNS2 という2台のDNSサーバを用意し、PCからそれぞれにクエリを投げて名前解決する構成を組みました。
PCには、DNS1と紐づけてendy1.testを、DNS2と紐づけてendy2.testを search domains (ipv4.dns-search) に設定しています。
systemd-resolvedにおいて、問い合わせ先のDNSサーバは search domains に一致するものが優先される仕様です。
rr.endy1.testはDNS1に、rr.endy2.testはDNS2に問い合わせる構成となります。

PCのNetworkManager設定は、以下のようになっています。

# DNS1向け
nmcli connection add \
con-name dnstest1 \
ifname enp7s0 \
type ethernet \
ipv4.method manual \
ipv4.addresses 192.168.100.100/24 \
ipv4.dns 192.168.100.2 \
ipv4.dns-search endy1.test  # ★search domains

nmcli connection up dnstest1

# DNS2向け
nmcli connection add \
con-name dnstest2 \
ifname enp1s0 \
type ethernet \
ipv4.method manual \
ipv4.addresses 192.168.122.100/24 \
ipv4.dns 192.168.122.3 \
ipv4.dns-search endy2.test  # ★search domains

nmcli connection up dnstest2

DNS1には、 endy1.test.zoneendy2.test.zoneには、それぞれ以下の行を記載しました。
rr.endy1.testrr.endy2.testに、それぞれ192.168.100.2 (DNS1のIPアドレス) を紐づけました。
ゾーンファイルを2つ作ったのは、ドメイン名を区別するためです。

rr    A   192.168.100.2

DNS2には、 endy1.test.zoneendy2.test.zoneには、それぞれ以下の行を記載しました。
rr.endy1.testrr.endy2.testに、それぞれ192.168.100.2 (DNS2のIPアドレス) を紐づけました。
DNS1とは異なるアドレスを返すため、どちらにPCからDNS1とDNS2のどちらに問い合わせたかわかるようになっています。

rr    A   192.168.122.3

ここで、PCから2箇所にdigを実行してみます。
endy1.testはDNS1に問い合わせており、endy2.testはDNS2に問い合わせていることがわかります。
この挙動から、 search domains と紐づくDNSサーバに優先して問い合わせることがわかります。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       6779    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       6758    IN  A   192.168.122.3

resolvectl domainsystemd-resolvedが選択するリンクと search domains の関係を確認すると、以下のようになります。

resolvectl domain
# Global:
# Link 2 (enp1s0): endy2.test
# Link 3 (enp7s0): endy1.test

best matching が優先される (longest match)

黄色の部分のみ構成を変更しました。

dns2

rr.endy1.testrr.endy2.test共に、.testドメインに属します。
ただ、上図の構成においては、仕様上以下の動作になります。

  • rr.endy1.testはDNS1とDNS2の両方の search domains に該当するが、best matching の考え方に基づいて2階層分のドメイン名に一致するDNS1が優先される
  • rr.endy2.testは、DNS2の search domains にしか一致しないため、DNS2が優先される

黄色部分の変更差分のNetworkManager設定は、以下のようになります。

sudo nmcli connection modify dnstest2 ipv4.dns-search test
sudo nmcli connection up dnstest2

ここで、PCから2箇所にdigを実行してみます。
endy1.testはDNS1に問い合わせており、endy2.testはDNS2に問い合わせていることがわかります。
この挙動から事前の予想通り、より多くの label (ピリオドで区切られた文字列) に一致するDNSサーバが優先されることがわかりました。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       4800    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.122.3

resolvectl domainsystemd-resolvedが選択するリンクと search domains の関係を確認すると、以下のようになります。

resolvectl domain
# Global:
# Link 2 (enp1s0): test
# Link 3 (enp7s0): endy1.test

best matching なDNSサーバが複数存在する場合、全てのDNSサーバにクエリする

次は以下の黄色部分を変更しました。

dns3

search domains の条件を両方のリンクで揃えました。
search domains のbest matching でも同等な場合は、それらのDNSサーバ全てに問い合わせる仕様です。
そして、最初にDNS応答を受信した経路が採用されます。

NetworkManagerの設定差分は、以下のようになります。

sudo nmcli connection modify dnstest1 ipv4.dns-search endy1.test,endy2.test
sudo nmcli connection up dnstest1

sudo nmcli connection modify dnstest2 ipv4.dns-search endy1.test,endy2.test
sudo nmcli connection up dnstest2

dig で確認します。
DNSキャッシュをクリアしながら実行していると、途中で結果が変わっていることがわかります。
DNS1とDNS2のうち、先に受信した応答が表示されています。
どちらもほぼ同時に応答を受信するため、結果が不定になっています。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       6554    IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.100.2

resolvectl flush-caches

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       86400   IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.122.3

最後に、resolvectlで設定を確認します。
両方の search domains にbest matching となっていることがわかります。

resolvectl domain
# Global:
# Link 2 (enp1s0): endy1.test endy2.test
# Link 3 (enp7s0): endy1.test endy2.test

DNSデフォルトルート

DNSデフォルトルートの挙動を確認します。
DNS1と紐づくリンクに、デフォルトゲートウェイを設定します。
また、 search domains を空欄にすることで一致しないようにします。

dns4

search domains に具体的なキーワードが全く該当しない場合、デフォルトルートが評価されます。
以下の2通りのいずれかの条件に該当するリンクは、デフォルトルートとして扱われます。

(※) .は、ルートドメインなので全てのドメインに一致します。また、ドメイン名の先頭に~を付けると、「ホスト名にドメイン名を補う」効果はなく、「best matchingなDNSサーバの選択」のみに寄与するという意味になります (routing domain)。

即ち、今回の場合は全てのクエリがDNS1を向くようになります。

NetworkManager上は、以下の変更差分となります。

sudo nmcli connection modify dnstest1 ipv4.gateway 192.168.100.1
sudo nmcli connection modify dnstest1 ipv4.dns-search ""
sudo nmcli connection up dnstest1

sudo nmcli connection modify dnstest2 ipv4.dns-search ""
sudo nmcli connection up dnstest2

digを実行してみると、実際に全てのクエリがDNS1を向くようになります。

dig rr.endy1.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy1.test.       86400   IN  A   192.168.100.2

dig rr.endy2.test | sed -ne '/ANSWER SECTION/,/^$/p'
# ;; ANSWER SECTION:
# rr.endy2.test.       86400   IN  A   192.168.100.2

resolvectl domainからは、両リンクで search domains が空っぽであることがわかります。
resolvectl dnsの出力では、実質的にDNS1にしか問い合わせが向いていないことを反映して、DNS1のみが表示されます。
resolvectlからは、IPルーティングのdefault routeが反映されて、DefaultRouteのフラグが片方のリンクで有効 (+) になっていることがわかります。

resolvectl domain
# Global:
# Link 2 (enp1s0):
# Link 3 (enp7s0):

resolvectl dns
# Global:
# Link 2 (enp1s0):
# Link 3 (enp7s0): 192.168.100.2

resolvectl
# Link 2 (enp1s0)
#      Protocols: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported

# Link 3 (enp7s0)
#          Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
# Current DNS Server: 192.168.100.2                                               

長くなるのでコマンド実行結果は省きますが、いくつか追加で補足します。

DNS2の search domains に~.を追加すると、IP Routing のデフォルトゲートウェイよりも優先されてDNS2のみに問い合わせるようになります。
NetworkManagerの設定コマンドとしては、以下のようになります。

sudo nmcli connection modify dnstest2 ipv4.dns-search "~."
sudo nmcli connection up dnstest2

更にDNS1の search domains に~.を追加すると、DNS1とDNS2の両方にクエリを出すようになりました。
search domains に~.が設定されたリンク同士であれば、ipv4.gatewayの設定有無が優先順位に影響することは無いようです。

現在のDNS設定の確認方法

systemd-resolvedは、/etc/systemd/resolved.confやデフォルトルート、他デーモンからのD-Bus連携によって挙動が変わるため、非常に複雑です。
resolvectlコマンドを使えば、systemd-resolvedの設定情報を確認できるため便利です。

resolvectlをそのまま実行すると、最も詳細な情報が出てきます。
resolvectl domainによって、per-link DNSと関係する search domains を確認できます。
resolvectl dnsによって、各リンクと紐づくDNSサーバを確認できます。

具体的な出力例は、(参考) 具体例を参照してください。

(参考) D-Bus関連コマンド

D-Bus周りでそれっぽいコマンドを実行してみました。
D-Busに対応しているプロセスであれば、CLIGUIだけでなくD-Bus APIでもステータスを確認できます。
--xmlオプションをつければ、XMLで出力することも可能です。

オブジェクトパス (/org/freedesktop/NetworkManager/DnsManager) はインターネットで「NetworkManager D-Bus」で調べても良いですが、bash-completion の働きでタブ補完することでも簡単に確認できました。

D-Bus オブジェクトにはプロパティとメソッドが紐付いて定義されており、コマンドラインで参照できます。
中々使う機会はないのですが、こういったものも存在することを知っておくとたまに便利かもしれません。

# オブジェクトを指定して、メソッドやプロパティの名前や型情報、持っている値を参照できる
gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager/DnsManager

# node /org/freedesktop/NetworkManager/DnsManager {
# (中略)
#   interface org.freedesktop.NetworkManager.DnsManager {
#     methods:
#     signals:
#     properties:
#       readonly s Mode = 'systemd-resolved';
#       readonly s RcManager = 'symlink';
#       readonly aa{sv} Configuration = [{'nameservers': <['192.168.1.1']>, 'interface': <'eno1'>, 'priority': <100>, 'vpn': <false>}];
#   };
# };

まとめ

具体例を入れたら非常に長くなってしまいましたが、systemd-resolvedの挙動についてまとめました。

D-Busの働きにより、NetworkManagerの設定変更によりsystemd-resolvedの設定を変更できるので、使い勝手は従来と大きく変わりません。

一方でVPNを利用している場合など、宛先ドメインによってDNSサーバを使い分けたいときにはsystemd-resolvedの per-link DNS 機能が役に立ちそうです。

/etc/nsswitch.conf を少し深く理解する (systemd-resolved対応)

multiple_switch

お伝えしたいこと

前回の記事で、Fedora33の変更点を紹介しました。
その中で、デフォルトのDNSクライアントがnss-dns (/etc/resolv.confベースで動作) からsystemd-resolved (systemdの一部。より高度かつ幅広い機能を持つ) に変わると紹介しました。

endy-tech.hatenablog.jp

それに伴い、/etc/nsswitch.confの中身もFedora33以降で変わっています。

本記事では、/etc/nsswitch.confを細かく読み込んでみます。
個々の名前解決サービスについては、「そもそもどんな機能を持っていて、どのように動くのか」の概要のみ紹介します。
systemd-resolvedの基本動作については、後続の記事で扱います。

サマリ

先に要点だけ紹介します。
詳細は後半のセクションで紹介しますが、ややマニアックなので興味のある方だけ見ていただくのが良いかもしれません。

Fedora33以降の動作

多くのプログラムは、最初に/etc/nsswitch.confを参照します。
nss-files (/etc/hosts)で名前解決できない場合は、nss-resolve (systemd-resolved)に処理が移ります。
そしてsystemd-resolved/etc/systemd/resolved.confの設定に従って、外部DNSサーバへの問い合わせなど処理を継続します。

↓こちらが、Fedora33の/etc/nsswitch.confです。

hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns

dighostなど一部のプログラムは、/etc/nsswitch.confを読まず、/etc/resolv.confのみを参照します (情報元:man digman host)。

systemd-resolvedは、digのようなプログラムとも互換性を保つために/etc/resolv.confシンボリックリンク越しに自動更新します。
デフォルトでは/etc/resolv.conf/run/systemd/resolve/stub-resolv.confへのシンボリックリンクとなっており、システムのDNSサーバは127.0.0.53に設定されています。
127.0.0.53:53 (TCP/UDP)は、systemd-resolvedがリッスンしているソケットであり、結果的にdigsystemd-resolvedを参照することになります。

Fedora32以前の動作

多くのプログラムは、最初に/etc/nsswitch.confを参照します。
nss-files (/etc/hosts)で名前解決できない場合は、nss-dnsの制御を受けます。
そしてnss-dns/etc/resolv.confを参照します。

/etc/resolv.confは、シンボリックリンクではなくテキストファイルです。
/etc/resolv.confがテキストファイルの場合、原則としてNetworkManagerがこのファイルを自動更新します。
NetworkManagerを無効化したり、/etc/resolv.conf/run/NetworkManager/resolv.conf以外を宛先とするシンボリックリンクの場合はNetworkManagerの制御対象外となり、/etc/resolv.confを手動更新できるようになります (RHEL6時代からの流れでそういった構成の現場も一部あると思います)。

ちなみに比較的新しいCentOS8, RHEL8においてはsystemd-resolved.serviceはデフォルトで有効化されていますが、/etc/nsswitch.confresolveが書かれていないので実質ほぼ使われません。

↓こちらが、Fedora32以前の/etc/nsswitch.confです。RHEL8も同様だと思います。

hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname

digなど一部のプログラムは、/etc/nsswitch.confを読まず、/etc/resolv.confのみを参照します (情報元:man dig)。
/etc/resolv.confは上述の通り、基本的にはNetworkManagerが自動更新します。
DNSの設定を変えたい場合は、nmcliコマンドなどでNetworkManagerから制御します。

/etc/nsswitch.conf の読み込み

概要

Fedora33の/etc/nsswitch.confは以下のようになっていました。

hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns

# Fedora32以前はresolveが存在せず、代わりにdnsが上位にいた (RHELやCentOSもこちらがデフォルトのはず)
# hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname

man nsswitch.confを参考に、上行の意味を読み取ります。

左側に書いてある hosts がデータベース名、右側に書いてあるfiles, mdns4_minimal, resolve, myhostname, dnsがサービス名です。
データベースごとに、どのサービスから情報を引いてくるかの優先順位を決めているのが/etc/nsswitch.confであり、NSS (Name System Switch) の役割です。
名前解決をしたい場合は、hostsデータベースを参照します。

サービスは左に書いてあるものほど優先順位が高く、Fedora33以降では従来使われていたdnsよりもresolve (systemd-resolved) の方が優先順位が高い構成となっています。
サービスの実体はnss-<サービス名>という共有ライブラリです。
例えばresolveの実体はnss-resolveであり、/lib64/libnss_resolve.so.2に存在します。

[NOTFOUND=return]!UNAVAIL=returnは、[STATE=ACTION]の形式で動作を一部変更します。
[STATE=ACTION]は、その左に書いてあるサービスに作用します。
上記例ではmdns4_minimal [NOTFOUND=return]で1セット になります。
詳細な動作は後述します。

以降のセクションで、filesから順に各サービスの意味を読み取っていきます。

files

filesでは、/etc/hostsから名前を検索します。
検索にヒットしなければ次のnss (mdns4_minimal) に進みます。

files/etc/hostsと対応するという情報は、man nsswitch.confに書いてありました。

mdns4_minimal [NOTFOUND=return]

mdns4_minimalの名前解決は基本的に行われず、そのままスキップされて次へ進みます。
...以降は参考情報です。

mdns4_minimalが名前解決をする対象は、名前が.localで終わる場合か、IPアドレス169.254.x.xの場合のみです (つまりリンクローカル専用のサービスです)。
従ってこの部分の処理は大抵「対象外」としてスキップされ、次のresolveへ進みます。

NOTFOUND=returnは、実際にmdns4_minimalのサービスでクエリが行われた場合のみ関係します。
言い換えると、名前が*.localか、IPアドレス169.254.x.xの場合しか関係しません。
実際にクエリを行って、「該当するレコードが見つからない (NOTFOUND)」という結果だった場合、/etc/nsswitch.confの走査はこの時点で終了して結果を返します (return)。
つまり、resolve以降には進まず、この場で「その名前は存在しない」という応答を返すことになります。

余談ですが、テスト用のドメイン.localとしてDNSサーバに登録した時、この仕様に引っかかってDNSサーバに問い合わせが行かないと思われます。
/etc/nsswitch.confにてresolvednsよりも先にmdns4_minimalの評価対象となり、mdns4_minimalで名前解決しようとするも見つからず、[NOTFOUND=return]によって名前解決を終了してしまうためです。

.localは思わぬ挙動をすることがあるので、mDNSを使う意図がなければ不用意に使わないようにしましょう。

(参考) mDNSの概要

気になる方向けにリンクを貼ります。
パケットキャプチャも含めてわかりやすい解説が書いてあり、とてもありがたい記事でした。

john-rama01.hatenablog.com

概要としては、以下のとおりです。

resolve [!UNAVAIL=return]

resolveは、systemd-resolved.serviceを指します。
例えば、systemctl status systemd-resolvedで状態を確認できます。

systemd-resolved.serviceの動作については次の記事で紹介しますが、基本的にはこのサービスによって問い合わせ先のリモートDNSサーバが決まり、実際に問い合わせが行われます。

[!UNAVAIL=return]により、systemd-resolved.serviceが正しく動作していれば、以降のmyhostnamednsのクエリは実施されない設定になっています。
!UNAVAILは「Unavailableではない」、つまり「正しく動作している限りは」という意味になります。

(参考) myhostname

そうそう使うことはないと思いますが、折角の機会なのでmyhostnameについても書きます。

基本的にはresolve/etc/nsswitch.confの走査が完了するので、nss-myhostnameが発動することは基本ありません。
ただman nss-myhostnameman nss-resolveを見比べるとわかりますが、nss-resolvenss-myhostname相当の機能も内包しています。
従って、myhostname相当の名前解決は、systemd-resolvedメインの構成になったとしても引き続き可能です。
具体的にはman systemd-resolvedの「SYNTHETIC RECORDS」を見ればわかりますが、以下に示す名前が解決されます。

  • 自身のホスト名 (私の環境では、pc)。
    • NICに設定されたIPアドレスを一通り返す
    • scope 順にソートされる
      • scope の意味はman ip-addressを参照
      • 平たく言うと、通常のIPより localhost の方が下に表示される
    • IPが未設定だった場合は、127.0.0.2 (ローカルループバックアドレス) と ::1 (local host) を返す
  • localhostlocalhost.localdomain*.localhost*.localhost.localdomain
    • 127.0.0.1::1を返す
  • _gateway

上記リストを見たら同じことを考えると思うのですが、私達がシェル上でコマンドを実行してこれらの名前解決を利用することはそうそうありません。
「一部のプログラムが機能を果たすため、内部的に使われることがある」機能だと思います。

ただせっかくなので、実験として実際に名前解決してみました。

(参考) myhostname の名前解決を試してみる

getent ahosts <name>で調べると、/etc/nsswitch.confに従って名前を表示してくれます。
今回の構成では、systemd-resolvedが応答を返してくれます。

[stopendy@pc ~]$ getent ahosts pc
fe80::1e69:7aff:fe6a:24f0%2 STREAM pc
fe80::1e69:7aff:fe6a:24f0%2 DGRAM  
fe80::1e69:7aff:fe6a:24f0%2 RAW    
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 STREAM 
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 DGRAM  
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 RAW    
192.168.1.110   STREAM 
192.168.1.110   DGRAM  
192.168.1.110   RAW    
192.168.122.1   STREAM 
192.168.122.1   DGRAM  
192.168.122.1   RAW  

[stopendy@pc ~]$ getent ahosts TEKITO.localhost
::1             STREAM localhost
::1             DGRAM  
::1             RAW    
127.0.0.1       STREAM 
127.0.0.1       DGRAM  
127.0.0.1       RAW

[stopendy@pc ~]$ getent ahosts _gateway
fe80::1%2     STREAM _gateway
fe80::1%2     DGRAM  
fe80::1%2     RAW    
192.168.1.1     STREAM 
192.168.1.1     DGRAM  
192.168.1.1     RAW 

digの場合は、/etc/resolv.confを参照します (man digより)。
/etc/nsswitch.confは見てくれません。
host コマンドも同様の結果になります

今回digで名前解決できているのは、/etc/resolv.conf127.0.0.53がServerとして指定されており、systemd-resolvedが名前解決しているためです。
digのようなプログラムも互換性を保つため、基本的には/etc/resolv.conf../run/systemd/resolve/stub-resolv.confなどへのシンボリンクリンクになっています。
そして、/run/systemd/resolve/stub-resolv.confsystemd-resolvedが自動更新するという作りになっています。

[stopendy@pc ~]$ dig pc | sed -ne '/ANSWER SECTION/,/^$/p'
;; ANSWER SECTION:
pc.         0   IN  A   192.168.1.110
pc.         0   IN  A   192.168.122.1

[stopendy@pc ~]$ dig TEKITO.localhost | sed -ne '/ANSWER SECTION/,/^$/p'
;; ANSWER SECTION:
TEKITO.localhost.   0   IN  A   127.0.0.1

[stopendy@pc ~]$ dig _gateway | sed -ne '/ANSWER SECTION/,/^$/p'
;; ANSWER SECTION:
_gateway.       0   IN  A   192.168.1.1

[stopendy@pc ~]$ 

もう一つ実験として、以下のコマンドでsystemd-resolvedを停止してみます。
これにより、myhostnameにも出番が回ってくるようになります。

sudo systemctl stop systemd-resolved

もう一度同じコマンドを試してみます。
getent ahosts は、変わらず応答を返します。
しかし応答を返しているのはmyhostnameです。

※ちなみに、getent ahosts pc --service myhostname と、明確にmyhostnameを指定することも可能です

[stopendy@pc ~]$ getent ahosts pc
fe80::1e69:7aff:fe6a:24f0%2 STREAM pc
fe80::1e69:7aff:fe6a:24f0%2 DGRAM  
fe80::1e69:7aff:fe6a:24f0%2 RAW    
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 STREAM 
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 DGRAM  
240d:1a:75d:1200:1e69:7aff:fe6a:24f0 RAW    
192.168.1.110   STREAM 
192.168.1.110   DGRAM  
192.168.1.110   RAW    
192.168.122.1   STREAM 
192.168.122.1   DGRAM  
192.168.122.1   RAW    

一方で、digコマンドは応答を返しません。
これはdigが常に/etc/resolv.confに従って127.0.0.53、つまりsystemd-resolvedにクエリを投げているためです。
systemd-resolvedを停止している間は、digがまともに動作しなくなってしまいます。

[stopendy@pc ~]$ dig pc
^C[stopendy@pc ~]$ 

dns

nss-dns は、従来通りのDNSサービスです。
/etc/resolv.confに従ってDNSサーバを決定します。

/etc/resolv.confの中身を見ればコメントで書かれているのでわかりますが、/etc/resolv.confは大抵他のサービスによって自動更新される構成になっています。
どのサービスが自動更新するかはディストリビューションのバージョンによって変わりますが、いずれにしても手動で/etc/resolv.confを編集することは原則推奨されていません。

Fedora32以前のnss-dnsがメインだった頃は、NetworkManagerが/etc/resolv.confを直接自動更新していました。
nmcli connection modify ens192 ipv4.dns '8.8.8.8, 8.8.4.4' といったコマンドで挙動を制御できます。

Fedora33以降のnss-resolveメインになった後は、/etc/resolv.confsystemd-resolved管理の別ファイルへのシンボリックリンクに変わっています。
そしてsystemd-resolvedシンボリックリンク先を自動で書き換えることで、/etc/resolv.confが自動更新されています。
なお、デフォルトの構成ではsystemd-resolvedの設定ファイルである/etc/systemd/resolved.confには外部DNSサーバのIPアドレスが指定されていません。
/etc/systemd/resolved.confにシステム全体のデフォルトのDNSサーバを設定することができますが、ここが空白でも per-link DNS を設定していれば名前解決が可能です。
このあたりの動きについては、次の記事で詳細を紹介したいと思います。

(参考) /etc/nsswitch.conf の順序は書き換えるべき?

未検証ではありますが、man nss-resolveに興味深いことが書いてありました。

  • nss-resolve単体で外部DNSへの問い合わせだけでなく、/etc/hostsの読み込みと自身のホスト名の名前解決なども可能
    • 言い換えると、nss-resolveは、nss-dnsだけでなく、nss-filesnss-myhostname相当の機能も内包している
  • systemd-resolved内部では、/etc/hostsや自身のホスト名は、外部DNSクエリよりも先に評価される (man systemd-resolvedのPROTOCOLS AND ROUTINGを参照)
  • systemd-resolvedによるnss-filesnss-myhostname相当の名前解決は、結果がキャッシュされる
  • /etc/nsswitch.confにおいて、resolvefiles, nss-dns, myhostname よりも優先されるように左に書くべきだ (処理効率や推奨動作の観点で)
  • 逆に言うと、resolveの右にfilesnss-dnsmyhostnameを残しておくべき。systemd-resolvedがDownしているときも名前解決を継続できるようにするため
  • /etc/nsswitch.confにおいてmymachines (VMやコンテナの名前解決) を利用する場合は、resolveよりも手前に配置する。nss-resolveはこの機能を内包していないため
  • また、man systemd-resolvedman resolved.confによると、resolvemdns相当の機能も有効化できる
    • resolvectl mdnsコマンドの出力によると、デフォルトではsystemd-resolvedのmdns機能は無効化されていた
    • 有効化するためには、/etc/systemd/resolved.confMulticastDNS=を変更する必要がある

将来的にsystemd-resolvedをメインで扱うようになったら、上述の設定チューニングも視野に入れても良いのかもしれませんね。
上記を踏まえて/etc/nsswitch.confを書き換えるとしたら、このようになります。

# files をresolveの後に持ってくる
hosts:      mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] files myhostname dns

# resolved.conf でMulticastDNS=true, またはMulticastDNS=resolveに設定した場合は、mdns4_minimal も右に移動
# hosts:      resolve [!UNAVAIL=return] files mdns4_minimal [NOTFOUND=return] myhostname dns

まとめ

/etc/nsswitch.confの中身をガッツリ見てみました。

Fedora33以降になって、最近普及し始めてきたresolve (systemd-resolved.service) が上位に移動してきました。
systemd-resolved/etc/hosts読み取り機能やDNSサーバ問い合わせ機能を持っており、基本的に従来のDNSクライアント機能全てを置き換え可能です。
ただし、systemd-resolved.serviceがダウンしてしまうと上記の機能が全て使えなくなってしまうので、従来のサービスも/etc/nsswitch.confの下位には残しておくことが推奨されています。

次の記事

systemd-resolved.serviceの基本機能と設定方法について、より踏み込んで紹介します。

systemd-resolved.serviceはper-link DNSに対応しており、より柔軟なDNSサーバ選択が可能です。
特にVPN配下の環境においては、物理リンクの先にあるインターネットDNSサーバとVPNの先にある社内向けDNSサーバをFQDNによって使い分けることができます。
Cisco Anyconnect VPN Client のSplit DNSと同様な機能だと思いますが、システム構成によってはこの機能が重要になってくると思います。

endy-tech.hatenablog.jp

Fedora33の変更点

fedora-logo

お伝えしたいこと

Fedora 33の変更点を今更ながら紹介します。
私が気になるポイントのみなので、量は少なめです。

公式情報

Fedora 33の変更点は、Release Notes の System Administrators セクション配下にまとまっています。

主な変更点

個人的に気になった箇所だけピックアップします。

デフォルトのエディタがvimからnanoに変更

Fedora 33以降は、デフォルトのエディタがvimからnanoに変更されました。
echo $EDITOR を実行すると、nano が指定されています。

この設定は、/etc/profile.d/nano-default-editor.shに書いてあります。
気になる方は、~/.bashrcに以下の行を足すことでデフォルトのエディタをvimに戻せます。

export EDITOR=/usr/bin/vim

Fedora Workstation のデフォルトのファイルシステムがBtrFSに

公式情報

Fedora Workstation をこれからインストールする方のみ該当します。
インストール画面におけるデフォルトのFile System が BtrFSになります (元は恐らくXFSでしょうか)。

既にFedoraをインストール済みの方には関係ありません。
また、Fedora Server などには該当しません。

systemd-resolvedがデフォルトで有効化されるように

公式情報

タイトルの通り、systemd-resolved.serviceがデフォルトで有効化されるようになりました。
また、/etc/nsswitch.confも書き換えられ、名前解決時の検索の優先順位が変更されました。

# Fedora32 以前では、dns (nss-dns) が主なDNSクライアントだった
hosts:      files mdns4_minimal [NOTFOUND=return] dns myhostname

# Fedora33以降では、resolve(nss-resolve) に置き換えられた
hosts:      files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] myhostname dns

/etc/nsswitch.confは左から優先的に評価されます。
Fedora33以降では、files (/etc/hosts) > mdns4_minimal (ほぼ使わない) > resolve (systemd-resolved) > ... という優先順位になり、systemd-resolvedが実質的にメインのDNSクライアントになりました。

デフォルトの構成であれば、従来通りNetworkManagerでDNSクライアントの挙動は変更できます。
systemd-resolvedを本格的に使う場合は、このあたりを確認すると良いと思います。

man コマンド 内容
man systemd-resolved systemd-resolvedの機能と動作の説明
man resolvectl systemd-resolvedのステータス確認コマンド
man resolved.conf systemd-resolved.service の設定ファイル

今回の挙動変更については、Fedora Wiki に影響の詳細がわかりやすく記載されていました。

fedoraproject.org

またsystemd-resolvedについては、この後2記事に分けて詳細を紹介予定です。

まとめ

Fedora 33 の変更点のうち、気になった部分のみまとめました。

  • デフォルトのエディタがvimからnanoに変更
  • Fedora Workstation インストール時、デフォルトで選択されるファイルシステムEXT4からBtrFSに変更
  • デフォルトのDNSクライアントがnss-dnsからsystemd-resolvedに変更

いつかRHELもこうなるかもしれません。

次の記事

2記事に分けて紹介予定です。

1記事目では、/etc/nsswitch.conf のFedora33以降の変更点に触れつつ、少し深めに読み方を紹介します。

endy-tech.hatenablog.jp

2記事目では、systemd-resolved の特徴と基本的な使い方について紹介します。

endy-tech.hatenablog.jp

FedoraのOSバージョンアップ手順

fedora-logo

お伝えしたいこと

Fedora 32 から Fedora 33 にバージョンアップしました。
公式手順通りに実行しただけですが、実際にそのまま試してうまくいったということで記録に残します。
バージョンアップ手順は、公式サイトを都度ご確認ください。
手順が更新されている可能性もありますし、複数メジャーバージョンをまたいでバージョンアップするときはアップグレードパスが異なる場合もあります (※)。

(※) Fedora公式ページに書いてあります。Fedora20以前の場合は、Fedora21近辺を経由してから最新版にバージョンアップします。また、Fedora20以前からのバージョンアップ手順は異なります。

バージョンアップ手順の前に、なぜバージョンアップすべきなのか知りたい方は、先に(参考) バージョンアップすべき理由をご覧ください。

バージョンアップ手順

公式情報

  1. Fedora Quick Docs - Upgrading to a new release of Fedora
  2. Fedora Quick Docs - DNF System Upgrade

1つ目の記事に、Fedoraのバージョンアップ手順が複数パターン書いてあります。
公式で推奨されているDNF System Upgrade を利用した手順にて対応しました (2つ目の記事)。

事前バックアップ

公式手順にはさらっと書いてありますが、事前にバックアップを取得します。
今回はrsyncによるバックアップを取得しました。
Timeshift による定期バックアップももちろんセットしていますが、下記記事に記載の通り今回のケースにおいては3つの理由からrsyncの方が向いています。

  • ハードリンクを保持できる
  • システム不具合のリスクが高い作業である
  • バックアップ取得したいタイミングが明確である (バックアップ定期実行ではなく、手動実行でもよい)

rsyncによるバックアップ方法とメリットは、こちらの記事で紹介しています。Timeshiftのメリットにも触れています

endy-tech.hatenablog.jp

↓Timeshiftとは何か、Timeshiftの使い方についてはこちらの記事で紹介しています (基本GUIが必要です)。

endy-tech.hatenablog.jp

バージョンアップの実施

では、下記公式情報に従ってバージョンアップします。
Fedora Quick Docs - DNF System Upgrade

パッケージ更新、再起動

まずは、既存のrpmパッケージを全て最新にします。
--refreshを付けることで、ローカルのメタデータのキャッシュを「期限切れ」扱いにします。
要するにこのタイミングでリポジトリから改めてパッケージ一覧をダウンロードすることで、現時点の最新版のパッケージを確実にオンラインからダウンロードし、インストールするということをやっています。

sudo dnf upgrade --refresh

そしてコンピュータを再起動します (重要プロセスやカーネルの更新が入っている可能性もあるためだと思います)。

sudo reboot

dnf-plugin-system-upgrade のインストール

DNF によって Fedora をバージョンアップするために、DNF System Upgrade Plugin をインストールします。
既にインストール済みでしたらこの手順は不要ですが、とりあえず実行しちゃっても害はありません。

sudo dnf install dnf-plugin-system-upgrade

Fedora 新バージョン用のパッケージをダウンロード

ここは要注意です。
コピペする前にコマンドをよく確認してください。

dnf system-upgrade downloadは、バージョンアップに必要なパッケージをダウンロードするコマンドです (man dnf-system-upgrade)。
--refreshオプションは、メタデータのキャッシュを利用せず、必ずダウンロードするオプションです (man dnf)。

さて、ここが重要です。
--releaseverで、アップグレード先のFedoraバージョンを指定します。
基本的には最新の安定版を指定します (今回は33)。
最新の安定版は、Fedoraダウンロードサイトで確認できます (例えば初期パッケージが最小限のFedora Serverはコチラ)。
その他アグレッシブなバージョン (rawhide版や、現時点では安定版ではない34など) のインストール方法はまた異なりますので、公式サイトにてご確認ください。
https://docs.fedoraproject.org/en-US/quick-docs/dnf-system-upgrade/

ダウンロードには以下のコマンドを実行します。
大量のダウンロードが発生しますが、この時点ではまだ影響はありません。

sudo dnf system-upgrade download --refresh --releasever=33

もし従来バージョンでは提供されていたパッケージが、バージョンアップ後のリポジトリに存在しない場合にはエラーになることがあります。
その際は、--allowerasingオプションを追加することで存在しないパッケージについてはインストールを諦めることができます。
このオプションをつけて実行した場合、実際に削除されるパッケージ名を確認できるはずです (未検証ですが、downloadコマンドなので悪影響はないはずです)。
このオプションを追加して、「実際に消えても問題ないパッケージか」、「代替品を探しておくべきか」、「新バージョンのリポジトリに追加される見込みがあれば待つべきか」など検討した上で、更新作業を進めてよいかご判断ください。
非公式のリポジトリを追加しているとこのような事態になりうるとのことですが、幸いなことに今回のケースでは何も起こりませんでした。

Fedoraバージョンアップ

以下のコマンドを実行した直後、OS再起動してバージョンアップ処理が実行されます。

sudo dnf system-upgrade reboot

バージョンアップされたことの確認

以下のようなコマンドで確認可能です。

cat /etc/os-release 
# NAME=Fedora
# VERSION="33 (Server Edition)"
# (以下略)

dnf list
# (fc33など、新しいバージョンに対応したパッケージが表示される)

RHEL系の場合は以下コマンドでも確認できます。

cat /etc/redhat-release
# Fedora release 33 (Thirty Three)

以上でバージョンアップ作業は完了です。
お疲れ様でした。

(参考) バージョンアップすべき理由

1つ目の理由は、古いOSバージョンでは最新のパッケージが使えないことがあるためです。
OSがサポート期間中であっても、最新のOSバージョンに紐づく公式リポジトリでしかパッケージが更新されないことがあります。

今回についても、GNS3クライアントをFedora に導入しようとしたら、最新版のGNS3クライアントが新しいバージョンのFedora公式リポジトリでしか提供されていなかったのがバージョンアップのきっかけになりました。
※GNS3サーバとGNS3クライアントは、同じバージョンでないと動きません。今回はGNS3サーバが最新だったので、クライアントも最新にしたかったのです

2つ目の理由は、OSがサポート切れ (End of Life) になると色々と困るためです。
これは言わずもがなですね。
サポートが切れると、パッケージが更新されなくなったり、各種製品の動作要件から外れたり、新しいツールが動作しなくなったりします。
ツールのバージョンを古いまま保ったとしても外部サービスと連携する時に互換性がなくなり、不具合が起こるリスクもあります。

3つ目の理由は、あまりにも古いバージョンのままにしておくと、アップグレードパスが複雑になるためです。
Fedora公式のアップグレード手順にも記載がありますが、End of Life のOSからのアップグレードはサポートされないか、良くても複雑な手順を踏むことになります。
中間のバージョンを経由しつつ複数のバージョンアップ作業が必要になることもザラです。
常に最新に保つ必要まではないですが、あまり最新版とかけ離れないよう意識するのが大事かなと思います。
特にFedoraのようにリリースサイクルの短いディストリビューションではなおさらです。

まとめ

Fedoraのバージョンアップ手順を書きました。
公式通りの手順でうまく行きました。
基本は公式ページに従って操作するのが良いと思いますが、ざっくり予習するのに本記事が少しでもお役に立てれば幸いです。

rsync+TimeshiftによるLinuxシステム・データバックアップ方法の紹介

sync

Icons made by Freepik from www.flaticon.com

前の記事

Linux のシステムバックアップツールとして、Timeshift を紹介しました。

Timeshift の特徴と使い方
endy-tech.hatenablog.jp

Timeshift の詳細
endy-tech.hatenablog.jp

お伝えしたいこと

Timeshift は優秀なシステムバックアップ手段ですが、2つの弱点があります。

  • Timeshift がシステムバックアップに特化しており、データバックアップには別製品を利用するのが推奨されていること
  • Timeshiftが発行するrsyncコマンドは -H が指定されていないため、コピー元のハードリンクの関係性を保持しないこと

それらの弱点を補強する手段として、rsyncコマンドの使い方を紹介します。

なお、Timeshift に全く興味のない方でもrsyncのオプションは参考にしていただけると思いますので是非ご覧ください!

今回実現したいバックアップ構成

今回は、2種類のバックアップを使い分けます。
それぞれ長所と短所があるのですが、両方実行することで補えるようになっています。
ここでは概要のみ示します。
詳細については、(参考) Timeshift と rsync の違いの詳細で紹介します。

比較軸 方式1 方式2
バックアップツール Timeshift (GUI) rsync (コマンド)
実行間隔 定期自動実行 不定期手動実行
世代管理 する しない
バックアップ先 同一ディスクの別パーティション (/home) 外付けHDD
コピー元のハードリンク 保持する 保持しない

方式1 のTimeshiftは、システムバックアップを定期的かつ高頻度に実行します。
不意のシステム不具合が発生した時に、ロールバックするために使います。
ロールバック先が比較的最近のデータになるので、ロールバック時の影響範囲が少なくて済むのが利点です。

方式2 のrsyncは、システムバックアップとデータバックアップの両方を不定期かつ低頻度に実行します。
外付けHDDにバックアップを取得するので、ディスク故障時の復旧にも対応できます。
またハードリンクを保持してのバックアップが可能なので、システム復旧時においてもTimeshiftより理想的な復旧手段となります。
ただし、バックアップ頻度が少ないため、データ損失の範囲が大きくなりがちな点はTimeshift とトレードオフになります。

なお、以下の条件を両方満たす方は方式2のrsyncのみに振り切って、rsyncコマンドをanacronで定期実行する方式も良いと思います。

  • 常時マウント可能な外部ディスクを持っている (例えばNASNFSストレージを持っているか、SSHでリモートサーバにrsyncできる環境など)
  • 複数世代のバックアップが必ずしも必要ではない (複数世代の自力実装は作り込みに労力がかかります)

(方式1) Timeshift による定期システムバックアップ

Timeshift の使い方については、前回の記事を参照してください。

endy-tech.hatenablog.jp

(方式2) rsync による不定期全体バックアップ

バックアップ取得コマンド

以下のコマンドでバックアップを実行します。

sudo rsync -a -ADHRSX -vi --delete --force --stats --delete-excluded \
--exclude-from=/home/stopendy/Documents/backup/exclude.list \
/ \
/run/media/stopendy/toshiba_ext/pc/

以下、補足です。

  • 今回はシステムバックアップとデータバックアップを同時に取得しています
  • データのみバックアップを取りたい場合はコピー元ディレクトリを / から /home に変更します
  • コピー先の/run/media/stopendy/toshiba_ext/pc/は、外付けHDDを表しています

オプションの意味

Timeshift が発行しているコマンドをベースに、以下のアレンジを加えました。

  • ファイル属性保持のオプション (-AHX) の追加
  • 画面出力の簡素化 (-ii-i)
  • ログ出力オプション (--log-file) の削除
  • 世代管理用のオプション (--link-dest) の削除

各オプションの意味は、以下の通りです。

オプション 意味
-a, --archive -rlptgoD と同等。
-A, -H, -X は含まない
-r, --recursive ディレクトリ配下もコピーする
-l, --links シンボリックリンクをそのままコピーする
一方で-L, --copy-linksを指定すると、リンクを追跡してファイルの実体をコピーする
-p, --perms パーミッションを保持する
-t, --times 更新時刻を保持する。
ファイルのタイムスタンプが同じだった場合にコピーをスキップさせたい場合は、必須のオプション
-g, --group 所有グループを保持する
-o, --owner オーナーユーザーを保持する
-A, --acls ACLを保持する
-D --devices --specials と同義
--devices バイスファイル (/dev/*) も送れるようにする
--specials 特殊ファイル (named sockets, fifo など) も送れるようにする
-H, --hard-links ハードリンクを保持する (コピー元でinodeが等しいファイルの組み合わせを見つけたら、コピー先でも同じinodeになるようにする)
-R, --relative 送信元として指定したディレクトリ階層を保持してコピーする。例えばrsync a/b cを実行すると、c/bにファイルが生成するが、rsync -R a/b cを実行すると、c/a/bにファイルが生成する。今回はコピー元のパスが/なので意味はない
-S, --sparse スパースファイルを認識し、スパースファイルとしてコピーする
-X, --xattrs 拡張ファイル属性を保持する
v, --verbose より詳細に画面/ログ出力する
-i, --itemize-changes 変更点のあったファイルを一覧表示する。
-iiのように繰り返すと、変更されていないファイルも一覧表示する
--delete バックアップ先のディレクトリ内のファイルで、対応するバックアップ元のディレクトリに存在しないものがあった場合、そのファイルを削除する
この挙動はバックアップ元のパスにディレクトリを指定した場合のみ有効となる
--force バックアップ先の空でないディレクトリ名とバックアップ元のファイル名が同じだった場合、ディレクトリを削除してファイルをコピーする
--stats コピー完了後に表示される統計値をより詳細にする
--delete-excluded --excluded--exclude-fromなどによってコピー対象から除外されたファイルも、コピー先に存在した場合は削除する
--exclude-from=<FILE> コピー対象から除外するファイル・ディレクトリの一覧を<FILE>から読み取る。
コマンドラインに直接除外条件を書く場合は、--exclude=<PATTERN>を使う

除外リスト

除外リストの中身

--exclude-from オプションに指定したexclude.listは、以下のような中身になっています。
リストの書きっぷりは環境によって若干変わります。
/home 配下のユーザー名を変えたり、条件を足し引きしたり、一部をワイルドカード (*) で置き換えることで、他の環境にもマッチする形にアレンジできると思います。

#####################
# For System Backup (From Timeshift)
#####################
- /dev/*
- /proc/*
- /sys/*
- /media/*
- /mnt/*
- /tmp/*
- /run/*
- /var/run/*
- /var/lock/*
- /var/lib/docker/*
- /var/lib/schroot/*
- /lost+found
- /timeshift/*
- /timeshift-btrfs/*
- /data/*
- /DATA/*
- /cdrom/*
- /sdcard/*
- /system/*
- /etc/timeshift.json
- /var/log/timeshift/*
- /var/log/timeshift-btrfs/*
- /swapfile
- /snap/*
- /root/.thumbnails
- /root/.cache
- /root/.dbus
- /root/.gvfs
- /root/.local/share/[Tt]rash
- /home/*/.thumbnails
- /home/*/.cache
- /home/*/.dbus
- /home/*/.gvfs
- /home/*/.local/share/[Tt]rash
- /root/.mozilla/firefox/*.default/Cache
- /root/.mozilla/firefox/*.default/OfflineCache
- /root/.opera/cache
- /root/.kde/share/apps/kio_http/cache
- /root/.kde/share/cache/http
- /home/*/.mozilla/firefox/*.default/Cache
- /home/*/.mozilla/firefox/*.default/OfflineCache
- /home/*/.opera/cache
- /home/*/.kde/share/apps/kio_http/cache
- /home/*/.kde/share/cache/http
- /var/cache/apt/archives/*
- /var/cache/pacman/pkg/*
- /var/cache/yum/*
- /var/cache/dnf/*
- /var/cache/eopkg/*
- /var/cache/xbps/*
- /var/cache/zypp/*
- /var/cache/edb/*
#- /home/stopendy/**
#- /root/**
#- /home/*/**

#####################
# /home/
#####################
# /home/stopendy/, /home/*/.*
+ /home/stopendy/Applications/
+ /home/stopendy/Desktop/
+ /home/stopendy/Documents/
+ /home/stopendy/Music/
+ /home/stopendy/Pictures/
+ /home/stopendy/Videos/

+ /home/*/.config
+ /home/*/.dir_colors
+ /home/*/.local/
+ /home/*/.local/bin/***
- /home/*/.local/*
+ /home/*/.ssh/

- /home/stopendy/*
- /home/*/.*

- /home/*/.config/biscuit/
- /home/*/.config/Code/
- /home/*/.config/google-chrome/

# /home/**/*.log
+ /home/*/Documents/**/*.log
- /home/**/*.log

# /home/timeshift/
- /home/timeshift/

#####################
# /root/
#####################
+ /root/.config
+ /root/.dir_colors
+ /root/.local/
+ /root/.local/bin/***
- /root/.local/*
+ /root/.ssh/

- /root/*
- /root/.*

- /root/.config/biscuit/
- /root/.config/Code/
- /root/.config/google-chrome/

除外リストの設計思想

前半のシステムバックアップに関係する部分は、Timeshift で使っているexclude.list をほぼ流用しています。

後半のデータバックアップに関係する部分は、ホームディレクトリ直下の隠しフォルダ配下のバックアップ対象を厳選しています。
特に、ブラウザやVSCodeのキャッシュファイルが大量にあって邪魔だったので明示的に除外しています (biscuit, Code, google-chrome)。

除外リストの文法

除外リストの文法を簡単にまとめました。
公式情報は、man rsync の「FILTER RULES」セクションに書いてあります。

  • コピー対象から除外するディレクトリを列挙する
  • 先頭に+を記載すると、「このファイルを含める」という意味になる (プラス記号の後に半角スペースが必要)
  • 先頭に-を記載すると、「このファイルを除外する」という意味になる。この記号は省略しても同じ意味 (マイナス記号の後に半角スペースが必要)
  • 先頭に#を記載すると、コメント行扱いとなって無視される
  • *は1文字以上 (スラッシュを含まない) にマッチする
    • /a/*は、/a/bにマッチするが、/a/a/b/cにはマッチしない
  • **は1文字以上 (スラッシュを含んで良い) にマッチする
    • /a/**は、/a/b/a/b/cにマッチするが、/aにはマッチしない
  • ***は0文字以上 (スラッシュを含んで良い) にマッチする
    • /a/***は、/a/a/b/a/b/cの全てにマッチする
  • 除外条件と含める条件の評価順は、--exclude--includeと同じ考え方となる
    • 例えば、a/b/cにあるファイルcは、aa/ba/b/cの全てが除外扱いではない場合のみコピーされる
    • 除外リストの複数条件に一致する場合、リストの上に書いてある方が優先される
    • 詳細はてっく煮ブログが参考になる

除外リストの動作確認

exclude.listを更新する際は、テスト実行モード (-n, --dry-run) とバックアップ対象ファイルの全表示 (-ii) も一緒に指定しつつrsyncを繰り返し実行すると便利です。
出力されたバックアップ対象ファイルをチェックすることで、exclude.listの定義にミスがないかを事前に確認することができます。
今回の例では、以下がテストコマンドになります。

sudo rsync -n -a -ADHRSX -vii --delete --force --stats --delete-excluded \
--exclude-from=/home/stopendy/Documents/backup/exclude.list \
/ \
/run/media/stopendy/toshiba_ext/pc/

rsync バックアップからの復旧

注意事項

復旧手順は、以下の条件で検証しました。

  • KVM上の仮想マシンFedora 32 + Cinnamon Desktop など、模擬的にパソコン環境を再現
  • rsync でシステム・データバックアップ
  • Fedora 33 にバージョンアップ
  • データ復旧確認
    • ファイルを1つ削除して、データ復旧コマンドを実行
    • 再ログインし、最低限の動作確認を実施
  • システム復旧確認
    • システム復旧コマンドを実行し、OS再起動
    • カーネルやパッケージのバージョンが元に戻ったことを確認
    • 最低限の動作確認を実施

ご覧の通り、擬似環境で最低限の確認しかしていません。
情報としては参考になると思いますが、実際に使う前に必ず手元のVMなどでも検証するようにしてくださいね。

復旧コマンドの基本的な考え方

以降のセクションでシステム復旧、データ復旧、システム+データ復旧の3つの例を紹介します。

3つの例に共通して、復旧の際はバックアップ取得コマンドを部分的に変更して実行します。
基本的には引数を入れ替えて、--delete-excluded-R を外します。
単純にコピー元とコピー先の引数を入れ替えただけで実行すると、大事故に繋がる可能性がありますのでご注意ください。
必ず検証してくださいね。

(1) システムの復旧

システム復旧コマンドは、バックアップコマンドをベースに以下のように作りました。

  • --delete-excluded-Rを外す
  • コピー元とコピー先の引数を入れ替える
  • --exclude-fromより手前に--exclude=/homeも入れることでデータフォルダを除外する

検証したところ、OSをISOファイルから起動するレスキューモードからシステム復旧する必要は必ずしもありませんでした。
実際にシステムディスクがマウントされた状態で復旧してみたら、/var配下の1ファイルがエラーになっただけでしたので、このやり方でも問題ないようです (環境にもよるかもしれません)。
とはいえ、「やはり念のためレスキューモードから実行したい」と思った場合は、(参考) レスキューモードからの起動を参考にしてみてください。

システムをリストアした後は、必ずOS再起動してください。
大抵のファイルはrsyncで巻き戻されますが、メモリや/procなどの情報はそのままです。
システム復旧は、OS再起動まで実行して初めて完了するとご認識ください。

# システム復旧
sudo rsync -a -ADHSX -vi --delete --force --stats \
--exclude=/home  \
--exclude-from=/home/stopendy/Documents/backup/exclude.list \
/run/media/stopendy/toshiba_ext/pc/ \
/

(2) データの復旧

データ復旧コマンドは、バックアップコマンドをベースに以下のように作りました。

  • --delete-excluded-Rを外す
  • コピー元とコピー先の引数を入れ替えつつ、対象がデータディレクトリとなるように変更
  • 念のため--deleteを除外した (必要なら再度追加してもよい)
  • コピー先が/homeである場合、コピー元はxxx/home/のように末尾に/を入れること!
    • rsyncはコピー元に/を入れると、ディレクトリ内の全ファイルをコピー対象とする
    • コピー元に/が入っていない場合、ディレクトリ自体をコピー対象とする
    • /の有無でコピー対象の考え方が変わるので、-nでテストしつつ慎重に判断すること

データのみ復旧する場合は、システム復旧時とは異なりシングルユーザーモードで実行する必要はありません。
ただ設定ファイルを強制的に書き換えることで、ユーザープロセスがクラッシュすることがあります。
例えば、私の環境では日本語入力を管理するiBusデーモンがクラッシュし、一時的に日本語入力ができなくなりました。
これらのプロセスを復旧するためにも、/home配下のデータ復旧後は最低でもユーザーの再ログインは実施してください。

# データ復旧
sudo rsync -a -ADHSX -vi --force --stats  \
--exclude-from=/home/stopendy/Documents/backup/exclude.list \
/run/media/stopendy/toshiba_ext/pc/home/ \
/home

(3) システムとデータの復旧

基本的には、上述の(1) システムの復旧(2) データの復旧をそれぞれ実行してください。
同時に実行することはおすすめしません。
システム復旧とデータ復旧は、実行したい場面も目的もコマンドラインオプションも色々異なります。
たまたまそれら全てが一致することもあるかもしれませんが、手順がややこしくなるので辞めておいたほうが良いと思います。

以上で3記事に渡るTimeshift と rsync によるバックアップ構成の紹介は終了となります。
ご覧いただき、ありがとうございました。

以下に補足情報が続きます。

(参考) Timeshift と rsync の違いの詳細

Timeshift と rsync を使い分けているのは、それぞれの短所を補い合うためです。
概要は上述の今回実現したいバックアップ構成にて紹介しましたが、本セクションではより詳細に紹介します。

実行間隔

Timeshift は定期的にバックアップを取得します (日次や週次など)。
一方で、rsync不定期 (恐らく3ヶ月〜半年に1度程度) でバックアップを取得する想定です。

不意にシステムが起動しなくなってしまったときは、Timeshift により最近のバックアップデータから復旧させることでデータ損失を最小限にできます

なお、rsyncのバックアップ実行タイミングは以下の想定です。

  • システムに問題が発生するリスクの高い作業の直前
    • OSバージョンアップ
    • パッケージ一括更新 (sudo dnf upgrade)
  • ディスク故障に備えたユーザーデータのバックアップ (半年に一度程度)

世代管理

先に一般論を書きます。
バックアップの定期実行は、ある程度頻度を高くしないと復旧時のデータ損失が大きくなりますが、一方でデータの保管期間をある程度長くしないと復旧が間に合わずに手遅れになるリスクが高まります。

ここでツールの話に戻りますが、Timeshift はバックアップを複数世代持ちます。
rsync はバックアップを1世代のみ保管し、バックアップの度に同一ディレクトリに差分ファイルを追加・削除する動きです。

Timeshift は差分バックアップを複数世代持つことで、バックアップ取得間隔を短く保ちつつも、バックアップの保管期間を長くすることができます。
これは定期的にバックアップする用途として、重要な特徴です。

例えばバックアップを日次・週次で実行し、日次バックアップを3世代、週次バックアップを2世代保管する設定にしていたとします。
その場合は、基本的には当日・前日・前々日のデータに戻すことで最低限の損失でシステムを復旧させることができます。
対応が遅れてしまった場合でも、1、2週間前のバックアップから復旧させれば、多くの場合は不具合が発生する前まで戻せます。

普段遣いのLinux PC は「作業の度に手動バックアップ、作業後に正常性確認 (または常時監視)、異常を検知したらすぐに復旧」とは中々いかないと思います。
こういった定期バックアップ + 世代管理の仕組みがあることで、より快適にLinux を扱えるようになります。

バックアップ対象

一般的には、システムバックアップとデータバックアップは分けるべきです。
例えばTimeshift によってユーザーデータも含めてバックアップを取得してしまうと、システムを復旧したい時にユーザーデータも巻き添えを食って古いデータで上書きされてしまいます。
従って、Timeshift はシステムの (/homeを除いた) バックアップのみ取得する構成としています。
これはTimeshift 公式でも推奨されている使い方です。

一方で、データバックアップの取得はrsyncで行います。
今回の構成では事情により、rsyncでシステムとデータ両方のバックアップを取得しています。
しかしrsyncの場合は、コマンドや除外リストを少し弄ることでシステムのみ復旧、データのみ復旧といった操作も容易にできるので、特に問題はありません。

rsync でシステムとデータの両方のバックアップを取得していることは、rsyncが外付けHDDにバックアップを取得していることや、ハードリンクを保持できることに関係します。
これらの事情については、この後のセクションで紹介します。

バックアップ先 (同一ディスク vs 外部ディスク)

バックアップを実行する際、当然ながらバックアップ先のファイルシステムはマウントされている必要があります。

Timeshift は定期的にバックアップを取得します。
その性質から、Timeshift のマウント先は常時マウントされていることが望ましいです。
私の環境ではPCの内蔵ディスクはメインの1本しかありませんし、NASNFSも持っていません。
常時マウントされているディスクは、コピー元と同じメインのディスクしかない構成です。
Timeshift はシステムのみ (/homeを除外する) のバックアップですので、バックアップ先として/homeを指定します。

バックアップ元とバックアップ先が同一のディスクだと、設定ミスやファイル破損には対処できますが、ディスク故障でデータが失われてしまいます。
そこで、外付けHDDの出番です。
データバックアップは、外付けHDDを接続してrsyncコマンドを実行することで不定期に取得します。
これにより、内蔵ディスク自体が故障しても外付けHDDから復旧できます。

コピー元のハードリンクを保持するか否か

Timeshift はシステムバックアップを取得しますが、実は完璧ではありません。
Timeshift が内部的に実行しているrsyncでは、-AHXが指定されていません。
これにより、Timeshift のバックアップから復旧した場合、以下のような制限を伴います。

  • バックアップ元のACLを保持しない (-Aの指定がないため)
  • バックアップ元のハードリンクを保持せず、複数ファイルのコピーとして扱われる (-Hの指定がないため)
  • バックアップ元の拡張ファイル属性を保持しない (-Xの指定がないため)

特に-Hの指定がないことが影響を大きくしています。
ハードリンクの関係性にあるファイル群は、本来それらのうちどれか1つを更新すれば、他のハードリンクにも変更が反映されます。
しかし、単純なファイルコピーではそうも行かず、全てのファイルをそれぞれ同様に更新する必要が出てきてしまいます。
Timeshiftユーザーでこのことが問題になっている話は聞いたことがありませんが、システムファイルではハードリンクが少なからず使われているので、この影響は小さくないと思います。

一方でrsyncは、当然ながらどのオプションを付与するか自分で選択できます。
rsyncコマンドを実行する際、-AHXオプションを指定することで、上記の問題を回避できます。

Timeshiftよりも低頻度ではありますが、不定期にrsyncでシステムバックアップも取得することで、ハードリンクに関わる問題を回避することがrsyncを使う目的の一つです。

(参考) rsync実行時のフラグの意味

rsync実行時、-i-iiを付与して実行すると変更対象のファイル一覧の左にフラグが付きます。
例えば.f.d などのフラグです。

ここではフラグの読み方を紹介します。
正式な情報は、man rsync--itemize-changes, -iセクション配下を参照してください。

フラグの基本的な書式は、YXcstpoguax です。

Y (1文字目) は、更新方法を表します(update types)。
Yが最も重要です。

フラグ 意味
. 更新なし
> ローカルへのファイルコピー
< リモートへのファイルコピー (SSHなどによって外部ホストにファイルコピーしたときのみこのフラグが付く)
c ファイル・ディレクトリの生成
h ハードリンクとして生成 (-H, --hard-linksオプション利用時のみ)
* *MESSAGE と表記され、その他の変更を表す。主に*deletingでファイルの削除を表す

X (2文字目) は、コピーされるファイルの分類です。

フラグ 意味
f ファイル
d ディレクト
L シンボリックリンク
D バイスファイル
S special file (named socket や fifo など)

cstpoguax (3文字目〜11文字目) は、変更の理由として検知されたファイル属性です。

フラグ 意味
c チェックサム
s サイズ
t,T t: 更新時刻 (要:-t, --times)
T: 更新時刻 (-t, --timesを指定しなかった場合のみ表示される)
p パーミッション (要:-p, --perms)
o オーナー (要:-o, --owner。要:特権付き (sudoなど) でのrsync実行)
g グループ (要:-g, --group。要:特権付き (sudoなど) でのrsync実行)
u,n,b u: アクセス時刻 (use time。要:--atimes)
n: 作成時刻 (要:crtimes)
b: アクセス時刻と作成時刻の両方(both)
a ACL
x 拡張ファイル属性 (extended attribute)

cstpoguax (3文字目〜11文字目) は、以下の文字で表現されることもあります。

フラグ 意味
. 変更なし
+ 全フラグ変更あり (大抵、ファイルやディレクトリ生成になる)
? 不明 (宛先のrsyncのバージョンが古い場合に表示されることがある)
※スペース 全て変更なし (.) の場合は、スペースで省略表記される

最後に具体例を示します。

フラグ 意味
.f.d 何もしない (差分なし)
>f..t...... ファイルコピー (ファイルの更新時間のみ差分)
.d..t...... 何もしない (ディレクトリの更新時間のみ差分)
*deleting ファイル削除 (--deleteオプションなどが指定されており、対応するファイルがコピー先に存在しないため)
>f.st.....x ファイルコピー (更新時刻とファイルサイズと拡張ファイル属性の差分)
cd+++++++++ ディレクトリ生成 (全部差分あり)

(参考) レスキューモードからの起動

RHEL8のドキュメントによると、レスキューモードとはsystemdの機能で提供されるものとLinux起動イメージファイルを利用するものの2種類ありますが、ここでは後者のイメージファイルを利用したレスキューモードのお話をします。

手順はFedoraをベースに紹介します。
Debian系など他のディストリビューションでは手順が異なる可能性もありますが、ご了承ください。

レスキューモードでは、OSインストール用のISOファイルやUSBを利用してOS起動します。
ルートファイルシステム上でプロセスが動作していない状態で各種操作ができます。

rsyncによるシステム復旧の文脈においてどの程度の意味があるかはわかりませんが、ご参考までに手順を記載しておきます。

まず、起動順序を通常のHDDよりもUSB、またはCDドライブなどを優先するようにBIOS/UEFI (VMの場合は仮想マシンのオプション) を設定しておきます。
その上で、起動用USBを物理サーバに挿すか、仮想サーバの場合はCDROMにOSインストール用のISOファイルを紐づけます。

その状態でOSを起動すると、コンソール画面に以下のようなメニューが表示されるので、Troubleshooting -->を選択します。

rescue1

そして Rescue a Fedora systemを選択します。

rescue2

レスキューモードの起動方法の選択肢が出てくるので、基本的には1) Continueを選択します。
ハードディスクのマウントやネットワークの起動など実施してくれるので色々と便利です。

もしこれらの自動起動箇所に不具合があるなどの理由で1. を選択できない場合には、3) Skip to shellを選択します。
このオプションは何もせずISOファイルの中身だけを使ってシェルを起動するので、基本トラブルが発生することはありません。

rescue3

1) Continue を選択した場合

このモードを選んだ場合、ディスク上にインストールされたLinuxを検知し、自動的に/mnt/sysrootにマウントします。
マウントオプションはrwで、読み書き可能です (ちなみに2を選んだ場合はroになります)。
また、NetworkManager.serviceも起動し、ネットワークも使用可能になります。

このモードを選んだら、後は簡単です。
mnt/sysrootchrootして、リストア作業を続行するのみです。
chrootコマンドは以下のコマンドで実行できます。
また、su -コマンドにより、必要に応じてrootユーザのログイン処理を読み込むことも可能です (プロンプト文字列が変わると思います)。
またこのモードではデフォルトでUSキーボードになっているので、お好みに応じてキーボードの言語をchroot前に変えておきます。

localectl set-keymap jp
chroot /mnt/sysroot
su -

外付けHDDからリストアしたい場合は、外付けHDDを手動でマウントする必要があります。
まずはマウントポイントを作成します。
私の環境では、以下のようになります。

sudo mkdir -p /run/media/stopendy/toshiba_ext/

続いて、マウントしたい外付けHDDのデバイスファイルを特定します。
fdisk -lで詳細情報を表示し、ディスクサイズなどをヒントにデバイスファイルを特定します。
今回は/dev/sda2でした。

特定が難しい場合は、usb-devicesコマンドも試してみましょう。
ManufacturerSerial Numberを元にusb-devicesの表示の中から目的のUSBデバイスは容易に特定できます。
そのUSBデバイスには、Product=External USB 3.0と書かれていました。
一方でfdisk -lにもDisk model: External USB 3.0と同様に書かれていましたので、これらを見比べれば確実に紐づけできます。

usb-devices
sudo fdisk -l

最後に、手動でマウントします。

sudo mount /dev/sda2 /run/media/stopendy/toshiba_ext/

以降は、(1) システムの復旧に従ってリストアを続行します。
リストアが完了したら、exitで抜けます。
するとOSが自動が再起動し、うまくいけば復旧しています。

仮想マシンの場合はshutdown -h nowで一度シャットダウンしてから、ISOファイルの取り外しや起動順序の設定戻しなどを対応します。

3) Skip to shell を選択した場合

自動マウントやネットワーク起動などの処理が一切行われず、直接シェルが起動します。
まずは、Linux Root Filesystem を手動で /mnt/sysroot にマウントします。

まずは、LVMを使える状態にします。
LVMをそもそも利用していない場合は、この工程をスキップして問題ありません。
lvdisplayコマンドでLogical Volume を表示します。
以下の情報から、Volume Group の名前がfedora_pcであることと、LV StatusがNOT availableであることがわかります。

$ lvdisplay
# 一部のみ表示
  LV Path                /dev/fedora_pc/root
  LV Name                home
  VG Name                fedora_pc
  LV Status              NOT available

lvchangeコマンドにより、LVを利用可能にします。
--activate yは、対象をAvailableにするという意味です。
その後の引数には、LV PathVG Nameを指定します。
VG Nameを指定すると、対象のVolume Group に紐づくLogical Volume に対して一括変更できて便利なので、今回はこちらを指定します。
再びlvdisplayを実行し、LV Statusavailableとなっていることを確認します。

lvchange --activate y fedora_pc
lvdisplay

次に、システムディレクトリをマウントします。
マウントディレクトリを作成し、マウントします。
mountコマンドに指定するデバイスファイルには、先ほどのlvdisplayで表示されたLV Pathを指定します。
最初にRoot File Systemからマウントしますが、他にもファイルシステムがある場合は同じ手順を繰り返します。

mkdir /mnt/sysroot
mount /dev/fedora_pc/root /mnt/sysroot

そしてchrootします。

chroot /mnt/sysroot
su -

あとは1) Continue を選択した場合と同じ手順です。

Linux PC関連まとめ

endy-tech.hatenablog.jp

まとめ

Timeshiftの弱点を補強するシステム・データバックアップ方法として、rsyncコマンドについてまとめました。
本来であればTimeshift が-AHXオプションに対応するようプルリクエストを送るか、自力でrsync定期実行 & 差分管理スクリプトを組むのが理想かもしれません。
しかし、自身のスキルでは理想を追うのが難しいため、現時点で実現できる仕組みとして今回のやり方を考えました。

皆さまにとっても参考になれば幸いです。

Timeshift の仕組みを調べてわかったこと

timeshift_black_bold
引用元:timeshift_black_bold.png

前の記事

Timeshift の機能や特徴、基本的な使い方を紹介しました。

endy-tech.hatenablog.jp

お伝えしたいこと

Timeshift の内部動作に踏み込んで確認し、わかったことをまとめます。
Timeshift を普通に使う上では不要な知識かもしれませんが、以下の内容が気になった方はぜひ読んでみてください。

  • Timeshift が内部的にどのようにcronと連携しているのか
  • そもそもcrondとanacronは何が違うのか
  • Timeshift はrsyncコマンドをどのオプションをつけて実行しているのか

特にrsyncコマンドはオプションが多いため、既成品が使っているオプションを見るだけでも参考になると思います。

ソースコードにはほぼ触れず、Timeshift の実行ログやLinuxの設定ファイルを確認することで考察します。

バックアップ処理の定期実行の仕組み

Timeshift 定期実行の概要

後述のシステム起動からTimeshift トリガーまでの動きを追ってみるに書いてあるとおり、 Timeshift は crond によって毎時バックアップ処理をトリガーされています。
そして、Timeshiftのソースコードによると、バックアップが最後に実行された時刻を内部的に管理し、設定したとおりの実行周期 (日次や週次) となるようにバックアップ処理をスキップしたり実行したりしています。
Timeshift のこういった動きは、crondよりも寧ろanacronに近いです。

cron と anacron の違い

さて、anacron は cron と同様にインターバルを置きながら処理を繰り返したい場合に使われますが、cronと比較していくつかの違いがあります。
以下の2枚のスライドで、その要点を示します。

赤字部分が重要なポイントです。
また前のセクションで記載の通り、Timeshift は anacron に近い性質を持っているので、anacron の性質には特にご注目ください。

cron_vs_anacron_summary

cron_vs_anacron_example

crond は指定した時刻にPCを起動していないと日次ジョブ実行されません。
一方、anacronであれば1時間ほどPCを起動していれば起動時刻が何時であっても日次ジョブが実行されます。
パソコン用途としては、anacronの方が扱いやすい印象を受けます。

次のセクションでcronとanacronの違いについて、詳細を補足します。

(参考) cron の詳細

cronの設定ファイルである /etc/crontabは、以下のような構造になっています。
何曜、何月、何日、何時、何分に実行するかを明確に記述し、定刻に処理を実行する作りであることが読み取れると思います。
また、設定ファイルに記述できる最小単位がminuteであることから、最小実行間隔は「毎分」です。

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

man crondによると、crondの設定ファイルは、以下の3箇所のいずれかに配置可能です。

  • /etc/crontab
  • /etc/cron.d/*
  • /var/spool/cron/<username>

各ファイルは、デフォルトでrootユーザのみ編集可能となるようパーミッションが設定されています。
ただし、/var/spool/cron/<username>だけは例外で、crontab -eコマンドで一般ユーザーの権限で編集できます。

anacron の詳細

anacronの設定ファイルである /etc/anacrontabは、以下のような構造になっています。
anacronは1日を最小単位として、一定時間間隔でコマンド実行する作りであることがわかると思います。

#period in days   delay in minutes   job-identifier   command
1  5  cron.daily      nice run-parts /etc/cron.daily
7  25 cron.weekly     nice run-parts /etc/cron.weekly
@monthly 45    cron.monthly        nice run-parts /etc/cron.monthly

システム起動からTimeshift トリガーまでの動きを追ってみる

コマンドの出力を順に貼りつつ紹介します。
出力は一部のみ抜き出しています。

最初に、システム起動の過程でcrond.serviceが起動します。
そこで、systemctl cat crondコマンドによって crond.service の中身を見てみました。
/etc/sysconfig/crondによると、$CRONDARGSの中身は空っぽでした。
つまり、サービス起動時に実行しているコマンドは /usr/sbin/crond -n です。
-n はフォアグラウンド実行という意味で、サービスから起動する際にしばしば指定するオプションのようです。

$ systemctl cat crond
# /usr/lib/systemd/system/crond.service
# (略)
[Service]
EnvironmentFile=/etc/sysconfig/crond
ExecStart=/usr/sbin/crond -n $CRONDARGS
# (略)

man crondによると、crondが起動した時には以下の3ファイルを読み込みます。

  • /etc/crontab
  • /etc/cron.d/*
  • /var/spool/cron/<username>

/etc/cron.d/timeshift-hourlytimeshift --check --scriptedコマンドを毎時0分 (hh:00) に発行し、Timeshiftの定期実行をトリガーしていました。
このコマンドを発行したタイミングが Timeshift に設定した定期バックアップの間隔以降であれば、バックアップを取得します。
それ以外のタイミングでトリガーされた場合は、処理がスキップされます。
ちなみに、このファイルは毎日/毎週/毎月/N日のスケジュール実行を指定している場合のみ生成されます。

$ cat /etc/cron.d/timeshift-hourly
# (略)
0 * * * * root timeshift --check --scripted

また、「OS起動時」のスケジュール実行を指定している場合は、/etc/cron.d/timeshift-bootが生成します。

@rebootによって「OS起動時」を表現しています (man 5 crontab参照)。

$ cat /etc/cron.d/timeshift-boot
# (略)
@reboot root sleep 10m && timeshift --create --scripted --tags B

(参考) 続き

以下は Timeshift とは関係ありませんが、参考情報としてcronの動きの続きを追ってみます。
/etc/cron.d/0hourly/etc/cron.hourly/ディレクトリ配下の実行ファイルを毎時の定刻 (hh:01) に実行していました。
run-partsとは、指定したディレクトリ配下の実行ファイルを全て実行するコマンドです。

$ cat /etc/cron.d/0hourly
# (略)
01 * * * * root run-parts /etc/cron.hourly

/etc/cron.hourly/0anacron が一時間ごとにanacron コマンドをトリガーしています。

$ cat /etc/cron.hourly/0anacron
# (略)
/usr/sbin/anacron -s

anacronコマンドは、/etc/anacrontabを読み取って実行します。
/etc/anacrontabから、/etc/cron.daily/etc/cron.weekly/etc/cron.monthlyを実行しています。
つまり、cronの日次実行、週次実行、月次実行はanacronによって実行されています。
昔はこれらの実行が/etc/crontabに書かれていたのですが、ある時点でanacronが利用されるようになりました (man run-partsにそう書かれていました)。

$ cat /etc/anacrontab
# (略)
#period in days   delay in minutes   job-identifier   command
1  5  cron.daily      nice run-parts /etc/cron.daily
7  25 cron.weekly     nice run-parts /etc/cron.weekly
@monthly 45    cron.monthly        nice run-parts /etc/cron.monthly

Timeshift が実行しているrsyncコマンド

コマンドの確認方法

Timeshift のログから、Timeshift が実行しているrsyncコマンドを確認できます。
※バックアップ実行中にps auxで確認するという手も恐らくあります

Timeshift を起動し、下図の赤枠部分 (Menu > View TimeShift Logs) を選択することでログを開けます。

timeshift_logs

2種類のログファイルが存在します。

  • yyyy-mm-dd_hh-mm-ss_backup.log
    • anacron により、1時間に1度timeshift --check --scriptedコマンドが実行される度に生成する
    • 定期バックアップ処理がスキップされても実行されてもこのファイルが生成する
    • rsync のオプションを確認できるのは、定期バックアップ処理が実行された時のログファイルのみ
  • yyyy-mm-dd_hh-mm-ss_gui.log
    • GUI操作のログ
    • バックアップの手動実行の際のログも含まれる

コマンドの中身

Timeshift が実行していたコマンドを貼り付けます。
以下のコマンドでは、コピー元が / で、コピー先が/run/timeshift/backup/timeshift/snapshots/2020-11-07_17-00-02/localhost/ です。

rsync \
-aii \
--recursive \
--verbose \
--delete \
--force \
--stats \
--sparse \
--delete-excluded \
--link-dest='/run/timeshift/backup/timeshift/snapshots/2020-11-02_23-00-01/localhost/' \
--log-file='/run/timeshift/backup/timeshift/snapshots/2020-11-07_17-00-02/rsync-log' \
--exclude-from='/run/timeshift/backup/timeshift/snapshots/2020-11-07_17-00-02/exclude.list' \
--delete-excluded \
'/' \
'/run/timeshift/backup/timeshift/snapshots/2020-11-07_17-00-02/localhost/'

オプションの意味は以下の通りです (man rsync より)。
-a, --delete, --exclude-from, --link-destが特に重要です。

オプション 意味
-a, --archive -rlptgoD と同等。
-A, -H, -X は含まない
-r, --recursive ディレクトリ配下もコピーする
-l, --links シンボリックリンクをそのままコピーする
一方で-L, --copy-linksを指定すると、リンクを追跡してファイルの実体をコピーする
-p, --perms パーミッションを保持する
-t, --times 更新時刻を保持する。
ファイルのタイムスタンプが同じだった場合にコピーをスキップさせたい場合は、必須のオプション
-g, --group 所有グループを保持する
-o, --owner オーナーユーザーを保持する
-D --devices --specials と同義
--devices バイスファイル (/dev/*) も送れるようにする
--specials 特殊ファイル (named sockets, fifo など) も送れるようにする
-i, --itemize-changes 変更点のあったファイルを一覧表示する。
-iiのように繰り返すと、変更されていないファイルも一覧表示する
v, --verbose より詳細に画面/ログ出力する
--delete バックアップ先のディレクトリ内のファイルで、対応するバックアップ元のディレクトリに存在しないものがあった場合、そのファイルを削除する
この挙動はバックアップ元のパスにディレクトリを指定した場合のみ有効となる
--force バックアップ先の空でないディレクトリ名とバックアップ元のファイル名が同じだった場合、ディレクトリを削除してファイルをコピーする
--stats コピー完了後に表示される統計値をより詳細にする
-S, --sparse スパースファイルを認識し、スパースファイルとしてコピーする
--delete-excluded --excluded--exclude-fromなどによってコピー対象から除外されたファイルも、コピー先に存在した場合は削除する
--link-dest=<DIR> 差分バックアップをしたいときに指定する。
--link-destを指定しなかった場合は、コピー先に既にコピー元と同じファイルが存在していると認識された場合はコピーをスキップする。
--link-destを指定した場合は、スキップする代わりに<DIR>からのハードリンクを生成する。
Timeshiftは、<DIR>に前回のスナップショットを指定することで、ハードリンクによる差分バックアップを実現している
--log-file=<FILE> rsyncの実行ログを<FILE>に出力する
--exclude-from=<FILE> コピー対象から除外するファイル・ディレクトリの一覧を<FILE>から読み取る。
コマンドラインに除外条件を直接書く場合は、--exclude=<PATTERN>を使う

--exclude-fromに指定されていたファイルは、TimeshiftのSettngs > Filters > Summary と同じ内容が書かれていました。

(考察) Timeshiftはハードリンクを保持しない

Timeshiftでは -A, -H, -X は指定されていませんでした。
従って、以下の挙動となります。

  • -A, --acls指定なし → ACLを保持しない
  • -X, --xattrs指定なし → 拡張ファイル属性を保持しない
  • -H, --hard-links指定なし → コピー元のハードリンクを認識せず、ハードリンクについても別ファイルとしてそれぞれコピーする

特に気になるのは、-Hが指定されていない点です。
Linux のシステムディレクトリでは、ハードリンクを使用している箇所が多数あります。
ハードリンクの関係にあるファイル群のうち一つを編集すると他のハードリンクにも反映されるという特徴は、ハードリンクファイルを編集する手順に影響する重要な要素だと思います。
実際に Timeshift を利用しているユーザーから特に問題提起がされていないことから、実はそれほど大きな問題ではない可能性もあります。

しかし、どうしても気になる場合は、Timeshift と rsync を使い分けるのがおすすめです。
Timeshift の定期実行・差分管理と、rsyncのハードリンク保持で手軽にバックアップ周期とハードリンク保持のメリットを享受できます。
また、rsync は Timeshift のサポート範囲外であるデータバックアップにも使うことができます。
詳細は、次の記事で紹介します。

/run/timeshift/backup

理由は不明ですが、Timeshiftはスナップショット保存先のディレクトリを/run/timeshift/backup/ にマウントする仕様のようです。
このことはmountコマンドや、Timeshiftの実行ログから確認できます。

私はスナップショットの保存先に/homeを指定しているのですが、/run/timeshift/backup/配下にもバックアップが生成していました。
初めて気づいたときは二重にバックアップを取得していると思って焦ったのですが、そんなことはなく、問題ありませんでした。

$ mount | grep timeshift
/dev/mapper/fedora_pc-home on /run/timeshift/backup type ext4 (rw,relatime,seclabel)

Linux PC関連まとめ

endy-tech.hatenablog.jp

次の記事

Timeshift と rsync を併用し、システムバックアップとデータバックアップに両対応したバックアップ方式を紹介します。

endy-tech.hatenablog.jp

まとめ

Timeshift に関連して気になった点をメモしました。
最初は興味だけで調べていたのですが、cronとanacronの連携、Timeshiftが rsync -AHXを指定していないことがわかり、私としては役に立つ情報が得られました。
結果として、Timeshift と rsync を併用するバックアップ方式を考案できたのは大きかったです。

皆様にとっても参考になる情報があれば幸いです。

TimeshiftによるLinuxシステムバックアップ

timeshift_black_bold
引用元:timeshift_black_bold.png

お伝えしたいこと

Linux のお手軽なシステムバックアップツールであるTimeshiftについて紹介します。

Timeshift の公式情報は、基本的にGitHubのREADMEに全て書いてあります。
https://github.com/teejee2008/timeshift

紹介しているコマンドは、Fedoraを基準にしています。
他のディストリビューションをお使いの方は、上記READMEを参照しつつ、主にインストールコマンドの部分を適宜読み替えてください。

Timeshift とは

Timeshift は、GUI操作により定期・不定期のバックアップを取得し、世代管理するツールです。
/etc, /usr, /boot などのディレクトリに含まれる、システム用途の設定ファイル、バイナリなどのバックアップを取得します。
Linuxは基本的に全てをファイルで表現しますので、システム関連のファイルを復元すれば、システムに対する変更を巻き戻すことができます。
例えばパッケージやカーネルのバージョンアップ、ブートローダー周りの設定変更といったものも基本的には復元できます。

Timeshift の魅力は、マニュアルを読まずとも使いこなせるシンプルなGUIです。
慣れていれば10分以内に初期設定を完了し、1クリックでバックアップし、3クリックで復元できます。
また、バックアップ・復元の動作も非常に高速で、ストレスをほとんど感じません。

一方で、Timeshiftは/home などに保存されたドキュメントや画像などのユーザーデータの保管に使用することは推奨されていません。
私はコマンドラインrsyncをデータバックアップに利用し、Timeshift と相補的に使い分けています。
rsyncの使い方については、別の記事にて紹介します。

基本機能

バックアップ方式

バックアップ方式は以下の2通りから選べます。
BtrFS (B-Tree File System) を使っている方以外は、Rsyncを選択することになります。

Rsync

rsyncコマンドにより、バックアップ先にファイルをコピーする方式です。
事前にコピー先のファイル名やタイムスタンプなどを確認して更新があったファイルのみコピーするため、2度目以降のバックアップが特に高速です。

バックアップの世代管理には--link-destオプションを利用しています。
--link-destを利用することで、以下のような動きになります。

  • 前回バックアップから更新のないファイルは、前回のバックアップファイルからのハードリンクを生成する
  • 前回バックアップから更新のあったファイルは、バックアップ元からコピーを生成する

ハードリンクはファイルの実体 (inode) を共有した複数のファイルを作るという特性を持ちます。
ハードリンクによる差分バックアップは、以下の強力なメリットを持ちます。

  • ハードリンクの作成はディスク容量をほとんど消費しない
  • ハードリンクの作成は非常に高速
  • 世代間に依存関係がないため、各世代は順不同で高速に削除できる
  • ハードリンクは日々使っている基本機能なので、不具合が発生する懸念は少ない

ハードリンクの仕組みについては、以下のリンクが参考になります。

qiita.com

最後に、rsyncはハードリンクの仕組みを利用していることから、当然ながらファイルシステム自体がハードリンクに対応している必要があります。
大抵は問題ないのですが、sshfsはハードリンクに対応していないとのことですのでご注意ください。

BtrFS

BtrFSについては詳しくないので、簡潔に記載します。

  • BtrFSのスナップショット機能を使ってバックアップを取る
  • バックアップ元がBtrFSを利用していることが前提
  • その他、SubVolume構成にも条件がある
  • 詳細はTimeshiftのREADMEを参照

定期バックアップ

バックアップ間隔と保管する世代数を指定できます。
裏ではcrond が関係しています。

バックアップタイミングは起動時、日次、週次、月次、年次、任意の日数、または手動実行から選ぶことができます。
保管する世代数は、バックアップタイミングごとに指定できます。
例えば日次バックアップは7世代保管、月次バックアップは2世代、といった指定が可能です。

インストール

以下のコマンドでTimeshiftをインストールします。

sudo dnf install timeshift

初期設定

デスクトップ環境のメニューから timeshift と検索して、Timeshiftを起動します (またはsudo timeshift-gtkコマンド)。

Timeshift は裏で設定内容に応じて /etc/cron.d 配下を編集するため、rootレベルの特権が必要です。
従って、GUIからの起動時に毎回sudoと同様のパスワードを求められます。
このあたりの制御はPolkitで実装されています (/usr/share/polkit-1/actions/in.teejeetech.pkexec.timeshift.policy)。
Polkit とは、特権が必要な操作を非特権プロセスから実行するための仕組みで、sudoコマンドと似たような役割です。
GUIツールで特権操作を実装する時に、Polkitがしばしば利用されるようです。

timeshift_authentication

パスワード認証が通ったら、初期設定を行います。
以下の画面で赤枠のSettings を開きます。
または、隣のWizard を開いてもほぼ同じです。
なお、Timeshift の設定反映のタイミングは、設定ウィンドウの「OK」を選択したタイミングではなく、Timeshift のウィンドウを全て閉じた時になりますのでご注意ください。

timeshift_settings

以降、Settingsの各タブの設定手順を順に紹介します。

Type

バックアップの方式を指定します。
RsyncかBtrFSの二択ですが、バックアップ元でBtrFSを使っていない場合はRsyncしか使えません。

私の環境は LVM + EXT4を利用しているので、無条件でRsyncになります。

1_type

Location

スナップショットの保存場所 (バックアップ先) のデバイスファイルを指定します。

前提として、私の環境は以下のパーティション構成です。

  • /boot: 1 GiB
  • /: 50 GiB
  • /home: 865 GiB

基本方針としては、システムパーティション (/boot, /) のバックアップをデータパーティション (/home) に保存します。
従って、バックアップ先としては/home に対応するデバイスファイルを指定します。

バイスファイルはdfコマンドで特定できます。
LVMの場合は、dfで表示されたファイルに対し、更にls -lfileコマンドを実行してシンボリックリンク先を調べることで特定できます。

$ sudo df -h | grep /home
/dev/mapper/fedora_pc-home  865G  174G  647G  22% /home

$ file /dev/mapper/fedora_pc-home
/dev/mapper/fedora_pc-home: symbolic link to ../dm-1

上記出力から、今回はdm-1を指定すれば良いことがわかります (容量からも自明ですが...)。

2_location

Timeshiftでは、具体的なバックアップ先のパスは指定できません。
バックアップ先は常に<mount-point>/timeshift/ 配下になります。
今回の設定では、/home/timeshift/配下にバックアップデータが蓄積されます。

Schedule

バックアップの取得間隔と保存世代数を指定します。
このあたりの設定はフィーリングです。
一旦適当に設定して、後から必要に応じて設定変更・手動のスナップショット削除などを実施する形でも問題ないと思います。

いずれにしても、リスクのある作業 (OSやパッケージの更新など) の直前に手動でバックアップを取ると思いますし、最低限それだけあれば何とかなります。
定期バックアップは、その上で実施する追加の安全策という位置づけです。

私の場合は、日次バックアップを5回分、週次バックアップを3回分保存しています。
結構長い期間の差分バックアップを保管するため、容量も大きくなる傾向があります。
しかし、日次バックアップでロールバックすればデータ損失を最小限に抑えられますし、対処が遅れて日次バックアップが自動削除されてしまった場合も週次バックアップで何とかなります。
日次バックアップと週次バックアップを数世代ずつ保存しておけば、概ねバランスが良いのではないかと思います。

3_schedule

Users

各ユーザのホームディレクトリをバックアップ対象に含めるか否かを指定します。
デフォルトでは、全てのホームディレクトリはバックアップ対象から除外されます。

繰り返しになりますが、Timeshift はシステムデータのバックアップ用ツールです。
ユーザーデータのバックアップをTimeshiftで実施することは非推奨になります (可能ですが)。

私の環境では、全てのホームディレクトリをバックアップ対象から除外しています。
ユーザーデータのバックアップは、rsyncコマンドにて別途手動で実行しています。
手動実行としているのは、バックアップ先が外付けHDDで、バックアップするときのみ接続するためです。

4_users

Filters

ホームディレクトリ以外に、細かくバックアップ対象・対象外のファイル/ディレクトリを指定します。
Users タブで設定した内容は、この画面にも自動で表示されます。

私の環境では、特に何の設定追加もしていません。

5-1_filters

画面下部のSummary を選択すると、バックアップ対象外のファイル一覧を確認できます。
この画面で指定されているホームディレクトリ以外にも、/dev/*, /proc/*, /sys/*, /home/*/** などのディレクトリが自動的にバックアップ対象外として指定されています。
この中で例外的にバックアップを取得したいディレクトリ / ファイルがあれば、バックアップ対象として別途指定する (+フラグをつける) 必要があります。

なお、rsyncの仕様として --include, --excludeオプションは先に指定したほうが優先されます。
従って、例えば/home/*は除外したいけど、/home/somefileだけはバックアップを取りたいという場合、/home/somefileの追加ルールを/home/*の除外ルールよりも上に書く必要があります。

rsyncのフィルタについては、他にも複雑なルールがいくつかあります。
詳しくはこちらのブログが参考になります。

tech.nitoyon.com

あまり複雑なフィルタを書くとハマります。
フィルタは可能な限りはシンプルに保つことをおすすめします。

5-2_exclude_list

Misc

バックアップを実行するたびに、バックアップ先に世代ごとのディレクトリが追加されていきます。
ここでは世代ごとのディレクトリ名に使用されるタイムスタンプの形式を指定します。

私はデフォルトのまま変更しませんでした。

6_misc

設定完了

基本設定が終わったら、Timeshiftのウィンドウを閉じれば完了です。
後はLinuxが起動している間に、crond/anacron がジョブを開始し、自動的にバックアップを取得します。

繰り返しになりますが、Timeshift の全てのウィンドウを閉じたタイミングで設定が反映されます。
設定画面の「OK」をクリックしたタイミングではないので、ご注意ください。

バックアップ手動実行

手動でバックアップを取得する際は、Create を選択します。
パッケージ一括更新やOS更新など、リスクの高い作業を実施する直前に実行する使い方が基本かなと思います。

timeshift_create

過去の状態への復元

過去の状態に戻したい場合は、スナップショットを選択の上、Restoreを選択します。
現在のファイルを全てバックアップ時のファイルに書き換えた上で、OSを再起動します。
うまく行けば、元の状態に復旧します。

timeshift_restore

(参考) Timeshift によるデータバックアップが推奨されない理由

TimeshiftのREADME には、以下のように書かれています。

Timeshift is similar to applications like rsnapshot, BackInTime and TimeVault but with different goals. It is designed to protect only system files and settings. User files such as documents, pictures and music are excluded. This ensures that your files remains unchanged when you restore your system to an earlier date. If you need a tool to backup your documents and files please take a look at the excellent BackInTime application which is more configurable and provides options for saving user files.

GitHub - teejee2008/timeshift

要約すると、以下のようになります。

  • Timeshift をシステムバックアップ用途に限定することで、復元したときもユーザーデータが変更されないようにできる
  • ユーザーデータのバックアップを取得するなら、BackInTime や TimeVaultのような他のツールを使ったほうが良い
    • (えんでぃ追記) BackInTimeを実際に使ってみたら実行速度が非常に遅かった
    • (えんでぃ追記) TimeVault は開発がストップしていた
    • (えんでぃ追記) 結局、rsyncコマンドを使うことにした。詳細は次の記事を参照

確かに上に書いてある通りの理由で、システムバックアップとデータバックアップで分けて管理すべきだと思います。
それぞれのバックアップは取得タイミングも復元タイミングも全く別になるはずです。

Timeshiftをシステムバックアップ用、rsyncコマンドをデータバックアップ用で分けて使うことで、上記のような使い分けを容易に実現できます。

(参考) CLIによる操作

Timeshift はCLIからの操作にも一部対応しています。
timeshift コマンドによってスナップショットの一覧表示や、スナップショットの即時取得、特定スナップショットへのリストアが可能です。
使い方の詳細はman timeshift を参照してください。
timeshiftコマンド自体は、結構お手軽に使えそうです。

しかし、スケジュールやオプションの設定はtimeshiftコマンドではできず、やはりGUIからの操作が基本となるようです。
GUIで設定した内容は/etc/timeshift.jsonに保管されているようなので、CLI環境においてはこのjsonファイルを編集することで挙動を変えることもできそうです。

ただ、jsonを書き換える利用法は本来の利用方法と異なりますし、何らかの副作用を生む可能性もあります。
CLIで定期バックアップを取得したい場合は、自前でcrondにrsyncベースのスクリプトを登録することをご検討いただくのが良いと個人的には思います。

Timeshift と rsync の違い

詳細は次の記事に譲りますが、Timeshift が内部的に発行しているrsync コマンドを確認すると、-AHX オプションが付与されていません。
また2021年1月現在、GUIからこれらのオプションを有効化/無効化する機能が実装されていません。
従って、以下の機能が必要な場合は、残念ながらTimeshiftよりもrsyncコマンドの方が向いています。

  • ACL (Access Control List) の保持 (-A, --acls)
  • ハードリンクの関係性の保持 (-H, --hard-links)
    • Linux のシステムディレクトリではもちろんハードリンクのファイルも存在する
    • この関係性をリストア後も維持するなら、-Hオプションは必須となる
  • 拡張ファイル属性の保持 (-X, --xattrs)

差分同期のスケジュール実行も含めて全て自力で実装しようとすると難易度が高いですが、以下のバランスであれば比較的簡単に実装できると思います。
このあたりの細かい実装方法は、後続の記事にまとめます。

  • バックアップのスケジュール実行はTimeshiftに任せる (-AHXの部分は妥協する)
  • OSバージョンアップなどリスクの高い作業前のみ、rsyncコマンドで-AHXも含めたバックアップを取得する

参考にした動画

Timeshift の概要を知るのに参考になりました。
Chris Titus 氏の動画は、Linux / Windows / Mac 関連の情報を普段遣いの観点で調べるのにいつも重宝しています。

まとめ

システムバックアップのツールとして、Timeshiftを紹介しました。

カーネルディストリビューションをアップグレードするときは、デスクトップやOSそのものが起動しなくなるリスクを伴います。
過信は禁物ですが、Timeshiftでバックアップを取得してから作業することで、メンテナンス作業のリスクを手軽に、しかし大幅に軽減できます。

Linux PC関連まとめ

endy-tech.hatenablog.jp

次の記事

Timeshift の動作について詳細を確認してまとめました。
以下の内容に触れています。

  • Timeshift はanacron 的な動作をすること、
  • cronとanacronの違い、そして
  • Timeshift が内部的に発行しているrsyncコマンドラインオプション

endy-tech.hatenablog.jp

Timeshift とセットでコマンドラインrsyncでシステム・データバックアップを取得する方法については、以下の記事でまとめています。
rsyncコマンドに-Hを指定することで、ハードリンクの関係性を維持したシステムバックアップも取得可能です。
rsyncはTimeshiftを置き換えるものではなく、両方を使い分けます。
本文では、これらを使い分けるべき理由についても触れています。

endy-tech.hatenablog.jp