Skip to content

Instantly share code, notes, and snippets.

@tomotake-koike
Last active July 1, 2020 00:37
Show Gist options
  • Select an option

  • Save tomotake-koike/fe746203ca4441d36f7431bdad40e1fa to your computer and use it in GitHub Desktop.

Select an option

Save tomotake-koike/fe746203ca4441d36f7431bdad40e1fa to your computer and use it in GitHub Desktop.
ETCD VersionのDowngrade

ETCDのVersionをDowngradeする

ETCDではdb保護のためUpgradeはサポート(世代に依る)しているが、Downgradeは対応していない。(少なくとも現状のv3.4.x以前のVersionでは)
このため一旦新しいVersionを試した後、旧Versionに戻したいと思っても、起動時に以下のClusterとのVersionの整合性とれない旨のエラーとなり起動しない。

cluster cannot be downgraded (current version: 3.4.5 is lower than determined cluster version: 3.5).

この辺の処理でガードを掛けている。)

ETCDは新しいVersionのBinaryで起動すると自動的にClusterを新しいVersionへ移行する。(参考:Upgrade procedure
すべてのNodeにおいて新しいVersionのBinaryで起動するまでは切り戻しは可能であるが、すべてのNodeが新しいVersionで起動するとそのClusterのVersionをもとに戻すことができなくなる。
本番環境では考えられないが、もしシングルNode構成下で(Snapshotも取らずに)早まってBuggyな新しいVersionのBinaryを動かしてしまうと、一発でこの事象に陥る。

本稿はCDKのCharmed K8s環境で構築したシングル構成時におけるDowngradeの方法を示す。(冗長構成下においては一旦シングルNode構成にしてから実施する必要があるだろう。)
ただしDowngradeした後の状態にKVSが正しく機能しているかはサポート外であるため、それぞれの環境で確認する必要がある。

事前準備

Master Nodeのバックアップ(VMイメージのcloneやsnapshot等)を取得する。
SystemdによりRespawnを繰り返しているはずなので、これを停止する。

$ sudo systemctl stop snap.etcd.etcd.service

Downgrade手順

手順の流れとしてはbootstrap起動することで新しいClusterを構成した状態を作り、そこにDBファイルを戻す。

1. Data directoryの退避

snapwal(Write-Ahead Logging)が格納されているmemberディレクトリをrenameする。

$ sudo mv /var/snap/etcd/current/member  /var/snap/etcd/current/member_BK

2. Bootstrap起動するように設定変更する

ETCDはClusterが存在していない初期状態で起動されるとCluster環境を構築する。
設定のinitial-cluster-stateを変更してこのCluster初期起動状態を変更する。

$ sudo sed -i 's/^\(initial-cluster-state: \)existing/\1new/' /var/snap/etcd/common/etcd.conf.yml

3. bootstrap起動してCluster環境を構築する

ETCDをbootstrap起動することによりmemberディレクトリ配下が生成される。

$ sudo systemctl start snap.etcd.etcd.service

正常に起動しているかを確認する。
以下はsystemdによりrespawnされていないかsystemctl statusによりActiveしてから十分な時間(30秒程度以上)が経過していることを見ている。

$ sudo systemctl status snap.etcd.etcd.service| grep Active
   Active: active (running) since Thu 2020-05-14 21:24:13 UTC; 14min ago

snapおよびwalディレクトリが生成されていることを確認する。

$ sudo ls -al /var/snap/etcd/current/member

ETCDを停止する。

$ sudo systemctl stop snap.etcd.etcd.service

4. DBファイルを戻す

退避しておいたsnapからDBファイルを戻した状態でETCDを起動する。

$ sudo cp /var/snap/etcd/current/member_BK/snap/db /var/snap/etcd/current/member/snap/db
$ sudo systemctl start snap.etcd.etcd.service

5. Cluster構成下で起動するように設定変更する

設定のinitial-cluster-stateを変更してCluster構成済みの状態で起動するようにする。

$ sudo sed -i 's/^\(initial-cluster-state: \)new/\1existing/' /var/snap/etcd/common/etcd.conf.yml

6. ETCDを再起動する

$ sudo systemctl restart snap.etcd.etcd.service

正常に起動しているかを確認する。
以下はsystemdによりrespawnされていないかsystemctl statusによりActiveしてから十分な時間(30秒程度以上)が経過していることを見ている。

$ sudo systemctl status snap.etcd.etcd.service| grep Active
   Active: active (running) since Thu 2020-05-14 21:50:41 UTC; 31s ago

この状態でkube-apiserverが動作するはずなので、kubectlコマンド等で正常性を確認する。

$ kubectl get node
NAME            STATUS   ROLES    AGE   VERSION
juju-5ddd4a-1   Ready    <none>   21d   v1.18.2
juju-5ddd4a-2   Ready    <none>   21d   v1.18.2
juju-5ddd4a-4   Ready    <none>   21d   v1.18.2

Troubleshooting

ReadOnly状態 or T.O.が発生する

上述の手順後を行った後、Readは出来るものののWriteが出来ない状況(kubectlの場合delete等がT.O.となる状況)に陥った場合、Snapshotを生成してRestoreすることで復旧する。

ETCDにWriteが可能であるかの確認

正常にWriteが出来れば以下のようにctdctl putが成功する。

$ export ETCDCTL_API=3
$ etcdctl --endpoints=http://127.0.0.1:4001 put /TEST/date "$(date)"
OK
$ etcdctl --endpoints=http://127.0.0.1:4001 get /TEST/date
/TEST/date
Fri May 15 22:46:10 UTC 2020
$ etcdctl --endpoints=http://127.0.0.1:4001 del /TEST/date
1

ETCDのSnapshotの取得と復元

Readだけが出来る状態であれば起動中のETCDからSnapshotファイルを生成し新たなdata-dirを生成(restore)する。

$ mdkir etcd-restore ; cd etcd-restore
$ etcdctl --endpoints=http://127.0.0.1:4001 snapshot save etcd-snap.dat
$ URLS=$(grep ^initial-advertise-peer-urls /var/snap/etcd/common/etcd.conf.yml | cut -d\  -f2)
$ etcdctl snapshot restore etcd-snap.dat --data-dir=data-dir --initial-advertise-peer-urls="${URLS}" --initial-cluster="default=${URLS}"

dbファイルからでもHashチェックをスキップすることで新たなdata-dirを生成(restore)することが出来る。

$ mdkir etcd-restore-from-db ; cd  etcd-restore-from-db
$ sudo cp /var/snap/etcd/current/member_BK/snap/db ./
$ URLS=$(grep ^initial-advertise-peer-urls /var/snap/etcd/common/etcd.conf.yml | cut -d\  -f2)
$ etcdctl snapshot restore db --data-dir=data-dir --initial-advertise-peer-urls="${URLS}" --initial-cluster="default=${URLS}" --skip-hash-check=true

生成したdata-dirを適用する。

$ sudo systemctl stop snap.etcd.etcd.service
$ sudo rm -rf /var/snap/etcd/current/member
$ sudo cp -a data-dir/member /var/snap/etcd/current/
$ sudo sed -i 's/^\(initial-cluster-state: \)existing/\1new/' /var/snap/etcd/common/etcd.conf.yml
$ sudo systemctl start snap.etcd.etcd.service
$ sudo sed -i 's/^\(initial-cluster-state: \)new/\1existing/' /var/snap/etcd/common/etcd.conf.yml

Kuberntetes Workerとの状態不一致の解消

ETCDが暫くの間存在していない状態であったため、この間に発生しているWorker Node側の状態変化についての追従が出来ていない。
復旧後、一定時間経過しても状態不一致と思われる挙動が発生した場合は、Worker Nodeの落とし上げを実施する。(kubelet, kube-proxyだけの再起動では復旧しないケースがある模様。)

なぜCharmed K8sのETCDがVersion Upしていたのか?

2020-07-01追記:

以下etcd−snapのBugであった。
https://bugs.launchpad.net/etcd-snaps/+bug/1874890
こんな感じに発生していた。(本件をシェアした時のスライドから。) ETCDが止まった流れ


以下は一旦v3.5で起動したため、ClusterのVersionが上がってしまい発生した。

cluster cannot be downgraded (current version: 3.4.5 is lower than determined cluster version: 3.5).

JujuはETCDをsnapでインストールをするが、今回ETCDのjuju config kubernetes-masterで以下のbundle設定にしているためv3.5に上がることはない想定。

  etcd:
    charm: cs:~containers/etcd
    num_units: 1
    options:
      channel: 3.4/stable

snap info etcdを見ても

name:      etcd
summary:   Resilient key-value store by CoreOS
publisher: Canonical✓
store-url: https://snapcraft.io/etcd
contact:   snaps@canonical.com
license:   Other Open Source
description: |
  Etcd is a high availability key-value store, implementing the RAFT
  algorithm to deal with failover within the etcd cluster.  Popular
  in the Docker community as a shared store of small but important
  data in a distributed application.
snap-id: TKebVGcPeDKoOqAmNmczU2oWLtsojKD5
channels:
  latest/stable:    3.2.10                   2018-09-17 (202) 12MB -
  latest/candidate: 3.2.10                   2018-09-17 (202) 12MB -
  latest/beta:      3.2.10                   2018-09-17 (202) 12MB -
  latest/edge:      3.3.0-rc.0-1709-g3f725e1 2018-07-23 (194) 13MB -
  ingest/stable:    ingest-0.5               2017-05-17  (12)  4kB classic
  ingest/candidate: ↑
  ingest/beta:      ↑
  ingest/edge:      ↑
  3.4/stable:       3.4.5                    2020-04-30 (230) 22MB -
  3.4/candidate:    3.4.5                    2020-04-30 (230) 22MB -
  3.4/beta:         3.4.5                    2020-04-30 (230) 22MB -
  3.4/edge:         3.4.5                    2020-04-30 (230) 22MB -
  3.3/stable:       3.3.15                   2019-12-02 (217) 13MB -
  3.3/candidate:    3.3.15                   2019-12-02 (217) 13MB -
  3.3/beta:         3.3.15                   2019-12-02 (217) 13MB -
  3.3/edge:         3.3.19                   2020-04-07 (229) 21MB -
  3.2/stable:       3.2.10                   2018-09-17 (202) 12MB -
  3.2/candidate:    3.2.17                   2018-09-18 (208) 12MB -
  3.2/beta:         3.2.17                   2018-09-18 (208) 12MB -
  3.2/edge:         3.2.17                   2018-09-18 (208) 12MB -
  3.1/stable:       3.1.10                   2017-10-03  (81) 10MB -
  3.1/candidate:    3.1.10                   2017-10-02  (81) 10MB -
  3.1/beta:         3.1.10                   2017-10-03  (81) 10MB -
  3.1/edge:         3.1.10                   2017-10-03  (81) 10MB -
  3.0/stable:       3.0.17                   2017-10-03  (85) 10MB -
  3.0/candidate:    3.0.17                   2017-10-03  (85) 10MB -
  3.0/beta:         3.0.17                   2017-10-03  (85) 10MB -
  3.0/edge:         3.0.17                   2017-10-03  (85) 10MB -
  2.3/stable:       2.3.8                    2017-05-17  (55)  5MB -
  2.3/candidate:    2.3.8                    2017-10-03  (55)  5MB -
  2.3/beta:         2.3.8                    2017-10-03  (55)  5MB -
  2.3/edge:         2.3.8                    2017-10-03  (55)  5MB -

となっていて、v3.5はこの時点でリリースされていないようであるが、なぜこの事象に陥ったのか?

根本原因

Charmed Kubernetesは各machineに対してETCDをsnapでをインストールしている。
snapのパッケージ・アップデートは基本的にNWに接続されている状況下においては自動的に行われる。(現状、snap機能内でこの自動アップデートを完全に止めることは出来ない。)
上記環境のETCDのインストールはchannel=3.4/stableを指定しており、v3.4内のバージョン管理を行うことを想定していた。
しかし、ETCDバージョン3.4.0の筈であった、snap revisionの218で配布されたETCDバイナリは3.5.0-preのバージョンとなっており、この後snapdによる自動アプデートが走ったため、この事象に陥っていた。

$ ls -al /snap/etcd/218/bin/etcd
-rwxr-xr-x 1 root root 32647643 Sep 10  2019 /snap/etcd/218/bin/etcd

$ /snap/etcd/218/bin/etcd --version                                                                                                        
etcd Version: 3.5.0-pre
Git SHA: Not provided (use ./build instead of go build)
Go Version: go1.12.9
Go OS/Arch: linux/amd64

$ /snap/etcd/218/bin/etcdctl version
etcdctl version: 3.5.0-pre
API version: 3.5

環境戻して確認してみたが、やはりこの状態。

$ sudo snap revert etcd --revision=218
etcd reverted to 3.4.0
$ snap changes etcd --abs-time 
ID   Status  Spawn                 Ready                 Summary
381  Done    2020-05-23T05:42:46Z  2020-05-25T05:54:04Z  Auto-refresh 10 snaps
386  Done    2020-05-25T06:10:18Z  2020-05-25T06:10:19Z  Revert "etcd" snap
$ snap info etcd --abs-time  | grep -e ^tracking -e ^installed
tracking:     3.4/stable
installed:          3.4.0                                         (218) 22MB -
$ etcdctl version
etcdctl version: 3.5.0-pre
API version: 3.5
$ /snap/etcd/current/bin/etcd --version
etcd Version: 3.5.0-pre
Git SHA: Not provided (use ./build instead of go build)
Go Version: go1.12.9
Go OS/Arch: linux/amd64

おそらく、この3.4.0のsnapパッケージは当時(インストールしたのは2020年2月頃)のMaster branchからビルドされたものと思われる。この結果、ETCDのClusterバージョン管理の仕様からすると本来ありえないDowngradeなマイグレーションとなってしまった模様。
GitHub Repogitoryをみるとtagv3.4.0は正しいVersionの3.4.0となっている。
https://github.com/etcd-io/etcd/commit/898bd1351fcf6e0ebce8d7c8bbbbf3f3e42aa42c

commit 898bd1351fcf6e0ebce8d7c8bbbbf3f3e42aa42c (HEAD -> v3.4.0, tag: v3.4.0)
Author: Gyuho Lee <leegyuho@amazon.com>
Date:   Fri Aug 30 08:09:55 2019 -0700

    version: 3.4.0
    
    Signed-off-by: Gyuho Lee <leegyuho@amazon.com>

以下はmaster branchのv3.5.0-preが登場したcommit。
https://github.com/etcd-io/etcd/commit/7fbbb9c8bf840a09121ecc34a1c61c637ccbafe3#diff-22d6d38cd847736f6c99f3133279067c

commit 7fbbb9c8bf840a09121ecc34a1c61c637ccbafe3 (HEAD -> v3.5.0-pre)
Author: Gyuho Lee <leegyuho@amazon.com>
Date:   Fri Aug 2 15:27:54 2019 -0700

    *: add 3.5 capability for 3.5 dev tree
    
    Signed-off-by: Gyuho Lee <leegyuho@amazon.com>

Workaround

今回はそもそもsnapのパッケージ管理に問題があったが、snapdによる自動アップデート(snap refresh)を止めてしまうことが得策である。しかし、snapの管理ポリシーのためか完全に停止することは出来ない。このため、以下何れのWorkaroundで対応することが推奨される。
(参考:Snapcraft Docs:Managing updates

[WA#1] snapにおけるauto refreshの期間を遅らせる

$ sudo snap set system refresh.hold="$(date --date="JST 2021-05-23" +%Y-%m-%dT%H:%M:%S%:z)"
$ snap refresh --time --abs-time 
timer: sun2
last: 2020-05-22T22:56:00Z
hold: 2020-07-21T22:56:00Z
next: 2020-06-14T00:00:00Z (but held)

[WA#2] 従量課金の接続場合にはrefreshされない設定をしrefreshされないようにする

以下設定を行い、 NetworkManagerのRestrict background data usageオプションを設定する。

$ sudo snap set system refresh.metered=hold

[WA#3] snapへのパッケージ管理repositoryの通信が通らないようにする

以下のhost設定を/etc/hostsに設定することで通信を滞らせる。

127.0.0.1 api.snapcraft.io

[WA#4] snapdを停止/非活性化する

snapdだけを止めてもseededにより再起動してしまうため、snapd.seededも止める必要がある。(影響については要確認)

$ sudo systemctl stop snapd.service snapd.socket snapd.seeded.service
$ sudo systemctl disable snapd.service snapd.socket snapd.seeded.service
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment