pdfファイルのサムネイルが表示されない

Ubuntu 24.04のファイルマネージャーNautilusにてpdfファイルのサムネイルとして、1枚目の内容ではなく汎用のpdfアイコンが表示されて困った。 特に電子書籍を大量に所有しているのでサムネイルとして表紙を描画して欲しい。

サムネイル描画のためだけに追加の設定等は不要なはず?と思ったらどうやらキャッシュの影響らしい。

https://www.reddit.com/r/Ubuntu/comments/1cf3vgz/there_is_no_thumbnail_of_the_first_page_for_pdf/

rm -r ~/.cache/thumbnails/* を実行してキャッシュを削除した上でF5ボタンで表示を更新したところ、サムネイルが描画されるようになった。

CloudflareのAPIトークンにおいて複数ゾーンリソースを管理する

TerraformにてCloudflareのゾーンリソースを管理したい。 特に単一のゾーンではなく複数のゾーンを扱いたい。

Terraformのcloudflare providerには cloudflare_zoneリソースがあるのでこれを利用すればよい。 Cloudflareとの認証にはAPI_TOKENを利用すればよい。

developers.cloudflare.com

単一のゾーンを扱うときは上記操作で問題なかったが、2つ目のゾーンを追加したところ認証エラーが発生するようになった。

$ terraform plan
...
╷
│ Error: error finding Zone "xxxxxxxxxxxxxxxxxxxxxxx": Unauthorized to access requested resource (9109)
│ 
│ 
╵
Operation failed: failed running terraform plan (exit 1)

原因はAPIトークンの有効範囲として1つ目のゾーンに制限していたこと。 これはデフォルトの設定でこのようになっているのか自分が制限したのかは定かではないが、APIトークンの対象ゾーンリソースとして特定のゾーンのみが選択されていた。

このリソースの範囲をすべてのゾーンに変更すると正常にterraformがAPI経由でリソースにアクセスできるようになった。

kubernetesクラスタにおけるetcdコンポーネントの動作検証

Kubernetesクラスタにおけるデータストアとしてetcdがある。 Kubernets The Hard Wayでetcdのセットアップを行ったので、その設定詳細について確認する。

また、Kubernetesにおける etcdクラスタについては以下のドキュメントが参考になる。

kubernetes.io

ユニットファイル

etcdの起動はsystemdで管理しており、そのunitファイルがリポジトリに格納されている これらの内容は以下の通り。詳細なオプション説明については以下が参考になる。

etcd.io

ところで、etcd v3.4までTo start etcd automatically using custom settings at startup in Linux, using a systemd unit is highly recommended.と説明記載あったが、v3.5からは削除されたことが気になる。 また、各種オプションの説明についてもv3.4までの方がより詳細な説明が記載されており、v3.5は情報量が落ちている。これらの説明がv3.5では他に移動したのか、意図して削除したのか気になる。基本的には利用バージョンのドキュメントを参照しつつも、自分のように新しく学ぶ段階ではv3.4のドキュメントも参照するとよさそう。

name
etcdメンバを識別するためのHuman-readableな名前。Kubernetes The Hard Wayではcontrollerと設定されている。 このオプションはinitial-clusterオプションで利用する。

特に複数のetcdメンバーから構成されるetcdクラスタを構築するときに重要になる。 etcdクラスタの構築方法がstatic bootstrappingであってもdiscovery bootstrappingであっても、クラスタ内でユニークな名前となるように設定した方がよさそう。シングルメンバetcdクラスタの場合はおそらくnameは重要ではない。

initial-advertise-peer-urls
etcdがクラスタに接続するときに利用するURL。Kubernetes The Hard Wayではシングルノードetcdで動作するので利用しない。 プロダクションレディなKubernetesではコントロールプレーンの全ノードに対するドメイン名になるのだろうか。

listen-peer-urls
etcdクラスタの接続を許可するURL。Kubernetes The Hard Wayではシングルノードetcdで動作するので利用しないし、 http://127.0.0.1:2380 の設定なので他ノードから接続できない。

initial-advertise-peer-urls とは異なり、どのアドレスでリッスンするかを指定するので、ドメイン名は指定できない。全許可なら http://0.0.0.0:2380 のような設定になるし、コントロールプレーン用のネットワークが 192.168.128.0/24 だったりしたら http://192.168.128.0:2480 のような設定になるはず? v3.4までのドキュメントには invalid exampleやv3.5以降より具体的な説明が記載されていたのでわかりやすかった。

listen-client-urls
etcdクライアントが接続するURL。Kubernetesにおいてetcdのclientとは kube-apiserverのことで、kube-apiserver以外のコンポーネントのユニットファイルにはetcdの接続URLは記載されていないし、Kubernetesコンポーネント図にはetcdとkube-apiserverのみ接続線が図示されている。

Kubernetes The Hard Wayの設定においては http://127.0.0.1:2379 が指定されているので、コントロールプレーンノードにおいてetcdとkube-apiserverが必ず動作し、kube-apiserverはローカルetcdに接続することが想定されていると理解できる。

advertise-client-urls
etcdクライアント(kube-apiserver)がetcdクラスタに接続するときに利用するURL。Kubernetes The Hard Wayにおいては http://127.0.0.1:2379の設定になっており、kube-apiserverのローカルでetcdが動作すればよいという設計になっていることがわかる。

listen-client-urlsadvertise-client-urlsの関係は listen-peer-urlsinitial-advertise-peer-urlsの関係に等しく、etcdがどのURLで接続許可するかとkube-apiserverがどのURLで接続するのかを設定できる。ただ、Kubernetesにおいて、etcdとkube-apiserverが同居しないコントロールプレーンの設計が存在しうるのか気になるところ。

initial-cluster-token
etcdクラスタを識別するためのトークン。Kubernetes The Hard Wayでは複数のクラスタを同時に構築することは想定していないので etcd-cluster-0 で固定。 複数クラスタを同じ設定で構築する場合もinitial-cluster-tokenはそれぞれユニークな値を指定することで、クラスタを識別できるようになるしetcdメンバが混在すること防ぐことができる。

initial-cluster
etcdクラスタにおける初期メンバ一覧。Kubernetes The Hard Wayにおいてはシングルメンバのetcdクラスタなのであまり意味はない。

マルチメンバかつstatic bootstrappingの場合は事前に全メンバがわかっているので、メンバの名前と接続用のURLを記載できる。 ドキュメントには --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380のような記載例が提示されておりイメージしやすい。

etcdクラスタメンバが動的に構成される場合はこのようにメンバのIP一覧を事前に列挙することはできないので、Discoveryの仕組みを利用する。

initial-cluster-state
etcdクラスタの初期構築(new)か、既存のetcdクラスタへの追加(existing)なのかを指定する。

もし間違った値を指定した場合もetcdクラスタを壊さず安全に停止してくれるらしいが、この値の指定がどのような意味を持つのかよくわかっていない。とりあえず既存クラスタにjoinするように動作し、joinできなかったら新規クラスタを構築するような動作はできないのだろうか。

バックアップ・リストア

Kuberneteのetcdクラスタ運用において、バックアッププランはちゃんと作成しておけよと注意書きがあるのでここだけ検証しておく。

Kubernetes The Hard Wayでは --data-dir=/var/lib/etcd を指定しているのでこのディレクトリをバックアップしてもよい(volume snapshot)し、etcdctl snapshot save snapshot.dbのコマンドでスナップショットを作成してもよい(built-in snapshot)。
Kubernetesのドキュメントには --endpointの指定もあるが、これは指定するなら --endpints=http://127.0.0.1:2379 のようになる。これがデフォルト値なので変更なしなら指定しなくてよい。運用系の操作なので2380ポートかと思ったが、etcdctlからの操作はクライアント接続でありgRPCプロトコルなので2379ポートらしい。また、クライアント接続なので --listen-client-urlsで指定したURLからでないと接続できない。

root@khw-server:~# etcdctl snapshot save snapshot.db
{"level":"info","ts":1726459074.9597218,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"snapshot.db.part"}
{"level":"info","ts":"2024-09-16T12:57:54.988563+0900","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":1726459074.99662,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"127.0.0.1:2379"}
{"level":"info","ts":"2024-09-16T12:57:55.494366+0900","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"}
{"level":"info","ts":1726459075.562186,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"127.0.0.1:2379","size":"1.2 MB","took":0.594269111}
{"level":"info","ts":1726459075.573733,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"snapshot.db"}
Snapshot saved at snapshot.db
root@khw-server:~# etcdctl  --write-out=table snapshot status snapshot.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| 45051425 |   163644 |        927 |     1.2 MB |
+----------+----------+------------+------------+

このファイルを別サーバに転送してリストアする。 etcdctlのスナップショットリストアはetcdメンバにバックアップデータを投入するのではなく、データディレクトリに展開する役目を持つ。このため、リストアする際はetcdメンバを停止させ、データディレクトリを空にした上で実行する。その上でetcdを起動すればリストアできる。

$ etcdctl --data-dir /tmp/etcd/ snapshot restore snapshot.db
Deprecated: Use `etcdutl snapshot restore` instead.

2024-09-16T13:18:39+09:00   info    snapshot/v3_snapshot.go:265 restoring snapshot  {"path": "snapshot.db", "wal-dir": "/tmp/etcd/member/wal", "data-dir": "/tmp/etcd/", "snap-dir": "/tmp/etcd/member/snap", "initial-memory-map-size": 0}
2024-09-16T13:18:39+09:00   info    membership/store.go:141 Trimming membership information from the backend...
2024-09-16T13:18:39+09:00   info    membership/cluster.go:421   added member    {"cluster-id": "cdf818194e3a8c32", "local-member-id": "0", "added-peer-id": "8e9e05c52164694d", "added-peer-peer-urls": ["http://localhost:2380"]}
2024-09-16T13:18:39+09:00   info    snapshot/v3_snapshot.go:293 restored snapshot   {"path": "snapshot.db", "wal-dir": "/tmp/etcd/member/wal", "data-dir": "/tmp/etcd/", "snap-dir": "/tmp/etcd/member/snap", "initial-memory-map-size": 0}

# etcdを別ターミナルで起動する

$ etcdctl get /registry/secrets/default/kubernetes-the-hard-way | hexdump -C
00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 6b 75 62 65 72 6e  |s/default/kubern|
00000020  65 74 65 73 2d 74 68 65  2d 68 61 72 64 2d 77 61  |etes-the-hard-wa|
00000030  79 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |y.k8s:enc:aescbc|
00000040  3a 76 31 3a 6b 65 79 31  3a b8 f6 3b 4f 4d 76 b4  |:v1:key1:..;OMv.|
00000050  ea 1d 0f 0f c4 f5 0d ad  19 bb 4e ef 97 bd 35 df  |..........N...5.|
00000060  50 ef db a6 23 29 58 f1  5a 73 d7 b9 83 a3 e0 b1  |P...#)X.Zs......|
00000070  a0 e4 24 3c 7d 5b ab de  5d 62 a6 e1 59 48 84 f2  |..$<}[..]b..YH..|
00000080  40 58 1f ec 5a 9b e2 a5  15 c8 58 b3 78 36 2f eb  |@X..Z.....X.x6/.|
00000090  90 0e 31 86 53 c5 1d 17  78 43 4d 10 69 55 61 42  |..1.S...xCM.iUaB|
000000a0  df 5c 76 70 64 3f 47 db  09 14 a4 bb ca 7e 26 0e  |.\vpd?G......~&.|
000000b0  c7 2b c6 97 54 68 4d 35  54 11 96 38 25 81 d6 98  |.+..ThM5T..8%...|
000000c0  6a 46 77 0b e6 12 32 db  cf 84 98 11 01 5b cf 49  |jFw...2......[.I|
000000d0  7a 75 92 48 54 57 e0 6b  14 9f 48 05 2a 3b d0 23  |zu.HTW.k..H.*;.#|
000000e0  ed a0 09 56 37 0b fa 48  5c 87 99 bd 34 e3 41 e0  |...V7..H\...4.A.|
000000f0  76 29 87 35 eb 8c fc 09  ff e8 ad 8e ac 6e 57 c5  |v).5.........nW.|
00000100  7b 3e cf ea 93 31 c5 4d  48 88 b9 d8 b3 7f 5c 85  |{>...1.MH.....\.|
00000110  a1 97 a6 0b 65 52 3f b1  35 4b 26 eb b9 de 3d 42  |....eR?.5K&...=B|
00000120  73 e3 93 c8 74 08 37 7b  74 6b bf 91 e8 8e 21 1a  |s...t.7{tk....!.|
00000130  8d 76 33 2c 15 13 76 cf  2f 1e 38 24 eb 91 9a dc  |.v3,..v./.8$....|
00000140  98 0f a8 5d 53 36 83 e4  58 22 9f 82 8c 2b 2e 3e  |...]S6..X"...+.>|
00000150  3c 79 98 ab 12 d2 44 19  fb 0a                    |<y....D...|
0000015a

envsubstで環境変数をテンプレートに展開する

Kubernetes The Hard Way を進めていたら、見知らぬ envsubstコマンドに遭遇した。

$ export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
$ envsubst < configs/encryption-config.yaml \
  > encryption-config.yaml

これは環境変数を展開して埋め込むコマンド。manによると substitutes environment variables in shell format stringsと説明がある。 上記の例ではconfigs/encryption-cnofig.yaml環境変数 ENCRYPTION_KEY を参照しており、これを環境変数の値で埋めて encryption-config.yamlとして生成し直している。 このようにシェルで簡単にテンプレートの仕組みが実現できる。

ちなみに、envsubstはDebian系では gettext-baseパッケージに含まれており、大抵の場合は最初からインストールされている。

$ dpkg -S /usr/bin/envsubst 
gettext-base: /usr/bin/envsubst

packages.debian.org

参考:

qiita.com

LinuxのWeb管理コンソール: Cockpit

前回記事の検証のため、Fedorasshしたところ見慣れないメッセージとしてwebコンソールの案内が表示された。

$ ssh 192.168.10.124
Web console: https://localhost:9090/ or https://192.168.10.124:9090/
...

実際にこのURLにアクセスしたところ、該当サーバのWeb管理コンソールが表示された。

確認したところ、これはCockpitというソフトウェアでFedoraRHEL系ではデフォルトで有効になっている。 また、DebianUbuntuもサポートされておりインストール可能。

cockpit-project.org

そういえば以前CoreOSなどのコンテナOSに対する管理GUIとして登場した記憶がある。 最近RHEL系を利用していなかったのでCockpitの存在やデフォルトで有効なことを知らなかった。

Debianにおける127.0.1.1の取り扱い

Kubernetes The Hard Wayを進めていたら、クラスタノードのホスト名の設定において /etc/hosts 書き換えるというものがあった。 この中で、 127.0.1.1 のエントリを書き換えるという操作があった。

while read IP FQDN HOST SUBNET; do 
    CMD="sed -i 's/^127.0.1.1.*/127.0.1.1\t${FQDN} ${HOST}/' /etc/hosts"
    ssh -n root@${IP} "$CMD"
    ssh -n root@${IP} hostnamectl hostname ${HOST}
done < machines.txt

この 127.0.1.1 が何なのかぱっとわからなかったので確認してみた。 調べてみたら以下のQiita記事が見付かり、そこに全部書いてあった。

qiita.com

www.debian.org

すなわち、歴史的経緯により ホスト名を逆引きできるIPアドレスが欲しかった、127.0.0.1だとlocalhostになってしまうので127.0.1.1がホスト名を返すように /etc/hosts にエントリを追加している。

ドメイン名を設定する場合はドメイン名をエントリに含める必要がある。 Kubernetes The Hard Wayではドメインkubernetes.local の設定はDebianのインストール後に行う想定なのでこのような書き換え作業が生じている。

クラスタ構築に利用したDebian12 (bookworm) だけでなく、Ubuntu 22.04 および Ubuntu 24.04 でも同様に /etc/hosts127.0.1.1のエントリが存在した。 一方で、Debian系ではないFedora 40で確認したところ127.0.1.1に関するエントリは存在しなかった

# Fedora40
$ cat /etc/hosts
# Loopback entries; do not change.
# For historical reasons, localhost precedes localhost.localdomain:
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
# See hosts(5) for proper format and other examples:
# 192.168.1.10 foo.example.org foo
# 192.168.1.13 bar.example.org bar

kindでは構築するクラスタノードのリソースは制御できない

ローカル環境においてKubernetesクラスタの動作検証するときの手段の1つとしてkindがある。 kindはdocker in dockerの仕組みを利用して簡単にkubernetesクラスタを構築・利用することができる。

クラスタノードを制御する

kindはマルチノードクラスタにも対応しており、複数のノードで構成されたクラスタを構築することができる。 また、ノードごとにKubernetesバージョンを指定したり、ディスクマウントやポートマッピングを設定したり、ノードへのラベル付けやkubeadmに追加のパラメータを渡すこともできる。 これによりある程度複雑なクラスタを構成することができる。

kind.sigs.k8s.io

ノードのリソースは制御できない

kindでクラスタを構築するときに制御できない要素としてノードリソースがある。 例えば、CPU、メモリ、ストレージなどはkindを実行するホストのリソースがすべて渡される。8GBメモリのVM上で3つのノードで構成されるクラスタを構築するとき、ノード毎のメモリを2GBに制限するみたいなことはできない。すべてのノードがホストリソースを共有することになる。

github.com

kubeletのオプションとして system-reservedなどを指定する方法も提案されているが、これはあくまでシステム(OS)向けの設定なのでクラスタ上に作成するpodの割り当てを制御することはできない。

github.com

また、kindは複数ホストで分散して実行することはスコープ外としている。 このため、ノードごとに必要なリソースをVMで作成して複数VMクラスタを構築する、ということはkindでは実現できない。

github.com

こういった課題を解決するため、kubeletに新しいオプションを定義してリソースを制限できるようにすることが提案されている。 こういったオプションが実装されて始めてホストリソース以外のリソース制限を適用できるようになる。

github.com