💮

Fuchsia エミュレータを GCE で実行し、ローカルで画面表示する

2021/12/22に公開

はじめに

本記事は、「Fuchsia 開発環境を GCE 上に構築する」シリーズの一編です。

関連記事:

本記事では、Fuchsia エミュレータを GCE で実行し、ローカルで画面表示する方法を紹介します。

fx emu-remote

Fuchsia ではリモートでエミュレータを実行し、ローカルのブラウザで画面表示・操作できる仕組み fx emu-remote があります。
プロトコルは WebRTC で、通信路は SSH のポートフォワーディングを利用します。

fx emu-remote

一連の流れは次の通りです。

  1. ローカルで Fuchsia のコード取得
    fx emu-remote コマンドのために必要
  2. GCE インスタンスで Fuchsia のコード取得
  3. ローカルから GCE インスタンスへ、ビルドとエミュレータ実行を指示 fx emu-remote --stream GCEインスタンス
    → GCE インスタンスでビルド fx build、エミュレータ実行 fx emu
  4. ローカルでブラウザ表示(WebRTC over SSH 経由)

事前準備

Fuchsia を GCE でビルドしてみた を参考に GCE インスタンス作成。
Fuchsia エミュレータを GCE のネストされた仮想化上で動かす を参考に GCE インスタンス上で KVM を有効にする。

ローカルですること

  • Fuchsia コードを取得

    $ curl -s "https://fuchsia.googlesource.com/fuchsia/+/HEAD/scripts/bootstrap?format=TEXT" | base64 --decode | bash
    環境変数 PATH の編集
    
    • fx コマンドを使えるようにするため、PATH を編集
  • fuchsia/tools/devshell/emu-remote を編集

     ssh_args=(
    -  -6 # We want ipv6 binds for the port forwards.
    +  # -6 # We want ipv6 binds for the port forwards.
       # Requests to the WebRTC service address locally go to the workstation.
       -L "${local_port}:localhost:${remote_port}"
       -o ExitOnForwardFailure=yes
    
    • IPv6 を決め打ちで指定していたため、コメントアウトして無効にします
  • ビルド対象を指定

    cd fuchsia/
    fx set workstation.qemu-x64 --release
    
  • SSH コマンドで GCE インスタンスに接続できるようにする

    gcloud compute instances start インスタンス名
    gcloud compute config-ssh
    
    • ~/.ssh/config にエントリが作成されます

    • 今回のインスタンスでは外部 IP アドレスが毎回変わるので、起動ごとに gcloud compute config-ssh を実行します。

      インスタンス起動とconfig-sshをまとめて実行する自作スクリプトを利用しています

リモート(GCE)ですること

  • Fuchsia コードを取得

    cd ~/
    curl -s "https://fuchsia.googlesource.com/fuchsia/+/HEAD/scripts/bootstrap?format=TEXT" | base64 --decode | bash
    環境変数 PATH の編集
    
    • ~/fuchsia に配置
    • fx コマンドを使えるようにするため、PATH を編集
  • パッケージのインストール

    sudo apt install xvfb x11-xserver-utils

    • fx emu-remotefx emu で使用するパッケージをインストール
  • KVM の準備

    sudo usermod -a -G kvm ユーザ名

  • エミュレータからネットワークに接続したい場合(fx emu-N をつける場合)

    sudo ip tuntap add dev qemu mode tap user ユーザ名 && sudo ip link set qemu up

    • インスタンス起動ごとに実行が必要です。~/.profile などに書いておくとよいかもしれません

実行

事前に GCE インスタンスを立ち上げて、gcloud compute config-ssh を実行しておきます。
作業はローカルでのみ行います。

fx emu-remote --stream --no-open --no-turn instance-test.us-west1-b.gce-fuchsia ~/fuchsia -- -N -p touch
  • --stream

    • リモートでエミュレータを起動する場合に指定する
    • このオプションを指定しない場合、リモートでビルドした成果物をローカルにコピーし、ローカルでエミュレータを起動する。
      emu-remote という名前なんだから、デフォルトで --stream を指定した動作をしてほしい・・・
  • --no-open

    • 手動でブラウザを立ち上げる場合に指定する
    • このオプションを指定しない場合、エミュレータ起動を 1 秒おきに監視し、起動確認後、自動的にブラウザを立ち上げようとする。
      画面出力が煩わしいので、無効にしている
  • -- -N -p touch

    • -- 以降は、fx emu へ渡すオプション
    • -N
      エミュレータからネットワークにアクセスするときに必要(ネットワークを使わない場合は不要)
    • -p touch
      入力デバイスにマウスではなくタッチパネルを使用する。ブラウザから操作するには、これが必要

参考:

備考:

  • 実際の用途

    • ビルドは GCE に SSH ログインして手動で行う
    • エミュレータ起動は、fx emu-remote --no-build ... として、ビルドなしで起動させる
  • --no-turnは、TURN サーバーを使用しない場合に指定する

    今回は SSH トンネル経由でブラウザから直接、エミュレータに接続するので(NAT 越えしないので)TURN サーバーは不要と考えたが、--no-turn を指定すると、ブラウザで画面を開くことができなかった

ブラウザで開く

https://web-femu.appspot.com/?port=8080 をブラウザで開きます。

  • web-femu.appspot.com が WebRTC クライアントだと思います
  • ローカルホストのポート 8080 を使用します
    • ローカルホストのポート 8080 は、GCE のポート 8080(fx emu が使用するポート)に SSH トンネルで接続します

fx emu

ネットワーク転送量と費用

30 秒間、画面を操作しない場合

開始
$ date && ss -tni 'dport == 8080'
Wed 22 Dec 2021 04:10:05 PM JST
State                           Recv-Q                           Send-Q                                                      Local Address:Port                                                        Peer Address:Port
ESTAB                           0                                0                                                                   [::1]:37200                                                              [::1]:8080
         cubic wscale:7,7 rto:201 rtt:0.123/0.081 ato:40 mss:32768 pmtu:65536 rcvmss:542 advmss:65464 cwnd:10 bytes_sent:133229 bytes_acked:133230 bytes_received:98051 segs_out:209 segs_in:340 data_segs_out:168 data_segs_in:171 send 21312.5Mbps lastsnd:1359 lastrcv:1377 lastack:1359 pacing_rate 42495.5Mbps delivery_rate 8192.0Mbps delivered:169 app_limited busy:343ms rcv_rtt:480385 rcv_space:66217 rcv_ssthresh:129432 minrtt:0.032
終了
$ date && ss -tni 'dport == 8080'
Wed 22 Dec 2021 04:10:35 PM JST
State                           Recv-Q                           Send-Q                                                      Local Address:Port                                                        Peer Address:Port
ESTAB                           0                                0                                                                   [::1]:37200                                                              [::1]:8080
         cubic wscale:7,7 rto:201 rtt:0.113/0.03 ato:40 mss:32768 pmtu:65536 rcvmss:542 advmss:65464 cwnd:10 bytes_sent:137094 bytes_acked:137095 bytes_received:100761 segs_out:214 segs_in:350 data_segs_out:173 data_segs_in:176 send 23198.6Mbps lastsnd:4012 lastrcv:4040 lastack:4012 pacing_rate 46091.3Mbps delivery_rate 8192.0Mbps delivered:174 app_limited busy:343ms rcv_rtt:480385 rcv_space:66217 rcv_ssthresh:134852 minrtt:0.032
開始時(バイト) 終了時(バイト) 転送量(バイト)
98,051 100,761 2,710
  • bytes_recieved フィールドを参照
  • 2,710 B / 30 sec

30 秒間、連続して画面を操作する場合

操作前
$ date && ss -tni 'dport == 8080'
Wed 22 Dec 2021 04:13:08 PM JST
State                           Recv-Q                           Send-Q                                                      Local Address:Port                                                        Peer Address:Port
ESTAB                           0                                0                                                                   [::1]:37200                                                              [::1]:8080
         cubic wscale:7,7 rto:201 rtt:0.075/0.021 ato:40 mss:32768 pmtu:65536 rcvmss:542 advmss:65464 cwnd:10 bytes_sent:160284 bytes_acked:160285 bytes_received:117021 segs_out:246 segs_in:412 data_segs_out:203 data_segs_in:208 send 34952.5Mbps lastsnd:1062 lastrcv:1108 lastack:1062 pacing_rate 69327.3Mbps delivery_rate 8192.0Mbps delivered:204 app_limited busy:346ms rcv_rtt:480385 rcv_space:66217 rcv_ssthresh:167372 minrtt:0.032
操作後
$ date && ss -tni 'dport == 8080'
Wed 22 Dec 2021 04:13:39 PM JST
State                           Recv-Q                           Send-Q                                                      Local Address:Port                                                        Peer Address:Port
ESTAB                           0                                0                                                                   [::1]:37200                                                              [::1]:8080
         cubic wscale:7,7 rto:201 rtt:0.076/0.011 ato:40 mss:32768 pmtu:65536 rcvmss:542 advmss:65464 cwnd:10 bytes_sent:164922 bytes_acked:164923 bytes_received:120273 segs_out:253 segs_in:424 data_segs_out:209 data_segs_in:214 send 34492.6Mbps lastsnd:275 lastrcv:404 lastack:275 pacing_rate 68311.1Mbps delivery_rate 8192.0Mbps delivered:210 app_limited busy:346ms rcv_rtt:480385 rcv_space:66217 rcv_ssthresh:173876 minrtt:0.032
操作前(バイト) 操作後(バイト) 転送量(バイト)
117,021 120,273 3,252
  • bytes_recieved フィールドを参照
  • 3,252 B / 31 sec

ネットワーク転送量

以上より、30 秒の画面転送で約 3 KB かかると想定します。

  • 3 KB / 30 sec = 6 KB / min = 360 KB / hour

GCE ネットワーク費用

月間使用量が 0 ~ 1 TB の場合 0.12 ドル/GB、1 ドル 110 円で約 13.2 円/GB。
インターネット下り料金 | Google Cloud

1 時間、fx emu-remote 画面を転送する場合、4.752 円。
(0.12 * 110 * 360 / 1000 = 4.752)

付録

fx emu-remote の動作詳細

fx emu-remote --stream --no-open instance-test.us-west1-b.gce-fuchsia ~/fuchsia -- -N -p touch 実行時の主要な動作です。

  1. GCE に SSH でログインし、ビルドを行う

    ssh instance-test.us-west1-b.gce-fuchsia \
      -S "~/.ssh/control-fuchsia-fx-remote" \
      -o ControlMaster=auto \
      "cd ~/fuchsia && ./.jiri_root/bin/fx build multiboot.bin fuchsia.zbi obj/build/images/fvm.blk"
    
  2. ローカルホストのポート 8080 からリモートホストのポート 8080 へトンネルを掘ります。fx emu でエミュレータを起動します

    ssh instance-test.us-west1-b.gce-fuchsia \
      -S "~/.ssh/control-fuchsia-fx-remote" \
      -o ControlMaster=auto
      -L 8080:localhost:8080 \
      -o ExitOnForwardFailure=yes \
      "cd ~/fuchsia && xvfb-run ./.jiri_root/bin/fx emu -x 8080 -t \"curl -s -X POST https://networktraversal.googleapis.com/v1alpha/iceconfig?key=...\" -N -p touch"
    
    • エミュレータの画面は Xvfb に出力
    • エミュレータはポート 8080 で WebRTC を待ち受け
    • curl は、TURN サーバーの設定に使用

ソースコード:emu-remote - Fuchsia

Discussion