Crostini LXC コンテナバックアップとリストア ~ 不安定な Dev Channel でもくじけない


食べログ DevOps チームの @weakboson です。
この記事は Advent Calendar の8日目の投稿です。

2019-05 追記: Dev Channel では Chromebook 設定からバックアップ & リストアできるようになりました

Settings → Linux (Beta) → Linux → Backup & restore から操作できます。
Version 75.0.3770.19 (Official Build) dev (64-bit) にて確認。

という訳で、Dev Channel の方は以降で解説する手順は Chromebook 設定からうまく行かなかったとき試すのでよいと思います。

なお私が試してみたところ原因不明のエラーでバックアップの方は実行できませんでした。リストアは行けた。空き容量が足りなかったか、バックアップサイズが大きくなりすぎたのかもしれません。サイズの件の詳細は最後に付記します。

注意点

この手法では termina VM を起動して LXC 操作ができないとコンテナのバックアップが取れません。termina VM すらも起動しなくなってからコンテナをサルベージしてリストア……ということはできません。転ばぬ先のバックアップが必要です。

前置き

Linux on ChromeOS 使ってますか?ChromeOS は Android アプリが使えて便利なところさらに Linux も手軽に動くようになって便利ですよね。Linux に wine をインストールしたら Windows アプリまで動いてしまいます。


MagicaVoxel を動かすの図

今年の Advent Calendar は Linux on ChromeOS で typora を動かして書いています。表の編集が手打ちより断然快適。Excel からコピペして tab を置換して……とかしないで済みます。

Mac OS X もデベロッパーフレンドリーでよいのですが ChromeOS は Android アプリが動くから Kindle 読書が快適で (※) 、タッチパネル付きなので手描きでラフにスライドをつくって後から清書なんて使い方もできるのがよいところです。
ところが Dev Channel という新機能が早めに使えるモードだと Linux がとても不安定なのですよね。最近は落ち着いてきましたが Linux on ChromeOS が使えるようになった 2018 年の夏なんか毎週のように Linux が起動しなくなっていました。ChromeOS や Android 部分はオンラインバックアップが優秀なので再構築に手間はかかりませんが、Linux の再構築はちょっとめんどくさい。ところで Linux on ChromemOS は Crostini という VM 上で動いている LXC により実現されているらしいじゃないですか。するってぇとコンテナを丸ごとバックアップしておけば壊れたときもバックアップ時点の状態に復元できるのでは?と考えて調べたら reddit でもうズバリ手法が公開されているじゃないですかー

How to Backup Containers - reddit

ということでこの記事はほとんどその手法の紹介です。

※ Win/Mac 版の Kindle アプリ、使いづらいというか画面サイズ一杯に表示できませんよね?

バックアップ手順

使うのは Chrome ブラウザ上のターミナルとファイルアプリ、それにバックアップファイルをどこかに退避する手段です。最後が地味に厄介で私は ChromeOS だけで実現できてなくて、Android アプリの Cx File Explorer で自宅 NAS にコピーしてます。

1. termina に入る

LXC を動かしている VM は termina というのだそうです。

Chrome ブラウザ上で ctrl + alt + t のショートカットによりターミナルを開き
vsh termina で termina VM に入ります。成功するとこんな感じ。↓

crosh> vsh termina
(termina) chronos@localhost ~ $
crosh> vsh termina
[ERROR:vsh.cc(102)] Failed to get VM info for termina

こんな↑エラーが出たら termina VM が起動していないので vmc start termina で起動してやります。起動したら自動的に入ってくれます。

crosh> vmc start termina
(termina) chronos@localhost ~ $

ここからは termina VM と penguin コンテナを行ったり来たりして作業します。
以降の操作説明では左上の表示が termina と penguin のどちらにいるかを示します。$ はプロンプトでその後ろがコマンド、その後の $ で始まらない行は出力サンプルを表します。

2. penguin を停止する

普通は penguin というコンテナが動いてるはずです。試しに lxc list でコンテナ一覧を見てみます。

termina
$ lxc list
+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| penguin | RUNNING | 100.115.92.205 (eth0) |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

いますね。おもむろに停止させます。

termina
$ lxc stop penguin --force
$ lxc list 
+---------+---------+------+------+------------+-----------+
|  NAME   |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+------+------+------------+-----------+
| penguin | STOPPED |      |      | PERSISTENT | 0         |
+---------+---------+------+------+------------+-----------+

はい、止まった。

3. penguin をバックアップする

ここからかなり重い処理です。数分単位で時間がかかる上に処理中は Chromebook がまともに操作できなくなるくらい重くなるのでご注意を。

termina
$ lxc publish penguin --alias backup
Container published with fingerprint: 10d2c4fcc7735bc4a27e059002a2dd0957bb227dc6b903b55b4ab81ed85ecfe7
$ lxc image export backup $LXD_CONF/backup
Image exported successfully!

これで termina VM の $LXD_CONF dir に backup.tar.gz ができあがります。私の環境では /mnt/stateful/lxd_conf/backup.tar.gz でした。

termina
$ ls -lh $LXD_CONF/backup*
-rw-r--r-- 1 chronos chronos 1.9G Dec  2 16:38 /mnt/stateful/lxd_conf/backup.tar.gz

4. バックアップを ChromeOS で操作できるところに移動する

Chrome OS の Files アプリでフォルダを右クリックすると表示されるメニューから Share with Linux を選ぶと、termina では /mnt/shared/MyFiles 以下にマウントされます。
Downloads あたりを共有にして termina 上で mv します。警告が出ますが、Linux 上の mode を Chrome OS に保持できなかっただけでコピーはできてます。

termina
$ mv $LXD_CONF/backup.tar.gz /mnt/shared/MyFiles/Downloads/
mv: preserving permissions for '/mnt/shared/MyFiles/Downloads/backup.tar.gz': Operation not permitted

$ ls -l /mnt/shared/MyFiles/Downloads/backup.tar.gz 
-rw------- 1 chronos chronos 2726093289 Feb 23 07:09 /mnt/shared/MyFiles/Downloads/backup.tar.gz

5. お好きなようにどこかに保管する

ファイルアプリで backup.tar.gz をどこかに移動して保管します。私は Android アプリ ES File Explorer Pro を使って自宅の NAS にコピーしてます。ChromeOS と Android で共有マウントされてる Downloads フォルダがあるのでそこを経由するのがかんたんです。

ChromeOS にもファイルアプリを samba クライアントにするエクステンションがあってこれが動作すればよかったのですが、私の自宅のネットワーク設定では NAS が見えませんでした。

バックアップからの復元

penguin コンテナか termina VM が起動しなくなってしまったらバックアップの出番です。termina VM が起動しなくなってしまったら、一旦設定の Remove Linux Apps for Chromebook からええいままよと Linux を削除します。
その後もう1度 Linux を有効にしてまっさらな penguin ができた状態と仮定して復元を説明します。

復元も主に termina VM 操作なので Chrome ブラウザのターミナル (Ctrl + Alt + T) から実施します。

1. バックアップが termina から操作できるように共有できてるか確認

penguin を作り直したら共有が解除される……かと思ったら保持されてました。

2. 最初の penguin を削除しちゃう

同じ名前のコンテナはつくれないはずなのでさっき有効にしてほやほやの penguin コンテナを削除してしまいます。

termina
$ lxc list
+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| penguin | RUNNING | 100.115.92.204 (eth0) |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

$ lxc delete penguin --force

$ lxc list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+

はい、消えたー。--force オプションは起動中コンテナを一気に破壊するために必要。

3. バックアップから penguin という名前でコンテナを復元する

ここから2コマンドで復元完了します。バックアップのときと同様にどちゃくそ重い。

termina
$ lxc image import /mnt/shared/MyFiles/Downloads/backup.tar.gz --alias backup
Image imported with fingerprint: 10d2c4fcc7735bc4a27e059002a2dd0957bb227dc6b903b55b4ab81ed85ecfe7

$ lxc init backup penguin
Creating penguin

4. バックアップ時点の penguin コンテナに復元できたか確認

penguin コンテナを起動して中に入ってみましょう。

termina
$ lxc list
+---------+---------+------+------+------------+-----------+
|  NAME   |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+------+------+------------+-----------+
| penguin | STOPPED |      |      | PERSISTENT | 0         |
+---------+---------+------+------+------------+-----------+

$ lxc list
+---------+---------+-----------------------+------+------------+-----------+
|  NAME   |  STATE  |         IPV4          | IPV6 |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+------+------------+-----------+
| penguin | RUNNING | 100.115.92.205 (eth0) |      | PERSISTENT | 0         |
+---------+---------+-----------------------+------+------------+-----------+

$ run_container.sh --container_name=penguin --user=weakboson --shell
Last login: Mon Nov 19 15:07:31 UTC 2018 on UNKNOWN
Linux penguin 4.19.2-02071-g869138e6e829 #1 SMP PREEMPT Wed Nov 21 07:16:53 PST 2018 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

バックアップしたときの状態になっているか確認してみます。私の場合地味ですが $HOME/bin など作成してすぐの penguin にはなかったものが見えます。復元に成功したようです。

penguin
$ ls -l
total 0
drwxr-xr-x 1 weakboson weakboson 20 Oct 11 17:40 bin
drwxr-xr-x 1 weakboson weakboson 64 Dec  2 16:21 dev

# Ctrl + d で penguin からログアウト

はい以上でバックアップと復元に成功しました!これで Linux が起動しなくなっても一安心……というにはめんどくさいですね。 termina VM と penguin コンテナ、それに ChromeOS アプリを行ったり来たりする操作で自動化しづらいのがネックです。更にめっちゃ動作が重くなるところが何箇所かあってバックアップ以外できなくてだるい。

明日、9日目も私による「Nginx + Lua (Openresty) のキワモノプラクティス」です。

おまけ: LXC コンテナとネットワーク通信

termina VM で lxc list するとなにやら IP が見えてましたがこれは起動中コンテナに割り当てられているホスト内 IP で、この IP を使うと ChromeOS からネットワーク通信できます。この IP が ChromeOS 起動後の初回割当から変わるとファイルアプリから見えなくなってるんじゃないですかね。

おまけ2: backup.tar.gz が 4G を越えると手順の途中で失敗することがあるらしい

Issues With 4+ GB Images (or Anything Other Files) and Getting Them into Termina

リストアするときに 4G 以上だと1ファイルで Termina に持ってくるとき失敗することがあるとか。最近私のバックアップが 4G 超えてしまったので、失敗するかどうか再現させずに最初からリンク先の手順の通りにしてみてます。ChromeOS から Termina に送るときに split しておくとよいとか。

サイズの問題があればバックアップする (lxd publish で tar.gz を書き出す) 時点で失敗するのでは?と思うのですけれど……