lxc
ChromeOS
Chromebook
Crostini

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

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

注意点

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

前置き

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

DizhLnjU0AAPLhh.jpg
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 アプリの ES File Explorer Pro で自宅 NAS にコピーしてます。

1. termina に入る

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

Screenshot 2018-12-03 at 01.16.03.png

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 で操作できるところに移動する

ChromeOS のファイルアプリは penguin コンテナの $HOME ディレクトリにあるファイルを操作できるのですが、直接 termina VM には触れません。そこで termina VM の $LXD_CONF というディスクを penguin コンテナのデバイスに追加して penguin の $HOME に mv できるようにします。他にも手段があるのですが、ディスク容量節約になるのでオススメです。

以下の ${USER} は自分の ChromeOS のユーザ名に置き換えて実行します。2つ目のコマンドで penguin コンテナを起動し続けてコンテナにログインします。docker attach みたいな感じ。penguin からログアウトしても起動しっぱなしです。

termina
$ lxc config device add penguin lxd-conf disk source=$LXD_CONF path=/mnt/lxd_conf
Device lxd-conf added to penguin
$ run_container.sh --container_name=penguin --user=${USER} --shell
Device sshd_config added to penguin
Device ssh_host_key removed from penguin
Device ssh_authorized_keys removed from penguin
run_container: info: Started container 'penguin'
# 後略 ...

penguin 上で backup.tar.gz を $HOME に mv します。

penguin
$ mv /mnt/lxd_conf/backup.tar.gz ~/

これで ChromeOS から backup.tar.gz が操作できる状況になったはずですが、penguin コンテナを再起動したせいでファイルアプリが linux files に接続できなくなってます。ChromeOS 自体を1度再起動すると元に戻ります。

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. バックアップを ChromeOS から termina に送り込む準備

まずはファイルアプリで Linux files に backup.tar.gz を配置します。バックアップしたときと逆で penguin コンテナを経由して termina VM に送るわけです。

Chrome ブラウザのターミナルから termina VM を起動して $LXD_CONF を penguin のデバイスに追加します。バックアップのときと同じコマンドです。

termina
$ lxc config device add penguin lxd-conf disk source=$LXD_CONF path=/mnt/lxd_conf
To start your first container, try: lxc launch ubuntu:18.04

Device lxd-conf added to penguin
$ run_container.sh --container_name=penguin --user=${USER} --shell
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.

penguin コンテナにログインして backup.tar.gz と受け渡しディスクの両方に触れることを確認してみましょう。

penguin
$ ls -lh
total 1.9G
-rw-r--r-- 1 weakboson weakboson 1.9G Dec  3 18:01 backup.tar.gz

$ ls -l /mnt/lxd_conf/
total 8
-rw-r--r-- 1 weakboson weakboson 403 Dec  3 18:02 config.yml
-rw------- 1 weakboson weakboson   5 Dec  3 18:03 cookies

はい。

2. バックアップを penguin から termina に移動

backup.tar.gz を termina のディスクに mv します。かなり重い。

penguin
$ mv ~/backup.tar.gz /mnt/lxd_conf/
# 終わったら Ctrl + d で exit

3. 最初の 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 オプションは起動中コンテナを一気に破壊するために必要。

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

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

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

$ lxc init backup penguin
Creating penguin

5. バックアップ時点の 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 からログアウト

6. termina から復元に使ったバックアップを削除

忘れないうちにこのデカいバックアップを termina VM から消しておきましょう。

termina
$ ls -lh $LXD_CONF/
total 1.9G
-rw-r--r-- 1 chronos chronos 1.9G Dec  3 18:01 backup.tar.gz
-rw-r--r-- 1 chronos chronos  403 Dec  3 18:02 config.yml
-rw------- 1 chronos chronos    5 Dec  3 18:25 cookies

rm $LXD_CONF/backup.tar.gz

$ ls -lh $LXD_CONF/
total 8.0K
-rw-r--r-- 1 chronos chronos 403 Dec  3 18:02 config.yml
-rw------- 1 chronos chronos   5 Dec  3 18:25 cookies

ここでも penguin コンテナ再構築・再起動で一時的に ChromeOS のファイルアプリからは操作できなくなっているので、ChromeOS を再起動しましょう。

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

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

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

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

Screenshot 2018-12-04 at 03.45.51.png