Netlify で定期的に JSON をジェネレートして通知する

PWA のデプロイに利用した Netlfy。

blog.kondoumh.com

JSON を生成するプログラムをデプロイすれば JSON を置いて定期更新する用途にも使えるということで、Tumblr API で取得したデータから JSON を生成するだけの Node.js アプリを書いて Netlify にデプロイしました。

github.com

デプロイした JSON を別サイトの PWA から参照するために CORS の設定が必要です。_header ファイルを作り読み取りアクセスを許可するようにしておきました。

Netlify のビルドトリガーは GItHub へのコミットの他に Webhook を作成して経由で外部から起動することもできます。

f:id:kondoumh:20190620230902p:plain

あとは外部のサーバーから curl 叩く crontab が仕掛けられればいいだけです。それだけのために CircleCI とかでジョブを作るのもオーバーなので GCP の Cloud Scheduler で1日1回起動するようにしてみました。

f:id:kondoumh:20190620231612p:plain

Webhook URL と HTTP メソッド (POST)、unix-cron 形式で時刻と頻度を指定するだけです。

Netlify からはメールや Slack で通知が可能です。Slack のプライベートチャンネルに Webhook を作って、Netlify の Build & deploy の Deploy notifications でビルド開始、ビルド成功、ビルド失敗 のタイミングで通知するように設定しました。

f:id:kondoumh:20190620232113p:plain

全体像はこんな感じです (図にするほどのものでもありませんが)。

f:id:kondoumh:20190625005307p:plain

これで日々更新される JSON データが Web で取得可能になりましたので表示用の PWA アプリを作ってみました。

github.com

Vue の table コンポーネントにデータバインディングさせています。

f:id:kondoumh:20190625005913p:plain

ひとまず自分用に雑に作ったので、ちょいちょい手を入れていきたいと思います。

Windows Terminal (Preview) を試す

Windows Terminal の Preview が Microsoft Store に出ているというので見に行きました。

f:id:kondoumh:20190623225342p:plain

確かに出てましたが、Windows 10 バージョンが 1803 のため動作要件を満たしてません。

f:id:kondoumh:20190623225812p:plain

とりあえず Windows Update を地道にしてたら May 2019 Update 1903 がロールアウトで降ってきました。

f:id:kondoumh:20190623232539p:plain

そのままバージョンアップ。Windows Terminal も無事にインストールできました。

f:id:kondoumh:20190623230423p:plain

WSL を導入してない状態だと PowerShell と cmd がタブで起動できます。PowerShell がデフォルトになっているようです。

タブに設定のアイコンがあるので押してみると設定の JSON ファイルがシェル起動するようになっていて、デフォルトのエディタがないため関連付けの画面が出てきました。きっと正式リリースの時は何らか GUI 画面が提供されるのでしょう。

f:id:kondoumh:20190623234702p:plain

とりあえず JSON ファイルをメモ帳で開き編集。profiles のプロパティをいじって PowerShell の透明度を変更したりしてみました。

        {
            "acrylicOpacity" : 0.10000001639127731,
            "background" : "#012456",
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "powershell.exe",
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 10,
            "guid" : "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
            "historySize" : 9001,
            "icon" : "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
            "name" : "Windows PowerShell",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "startingDirectory" : "%USERPROFILE%",
            "useAcrylic" : true
        },

f:id:kondoumh:20190623234022p:plain

Windows 10 のおなじみの壁紙がかなりポップになっていますね。

次に Git for Windwos の bash の追加に挑戦。icon は png のみのようです。

        {
            "acrylicOpacity" : 0.44999998807907104,
            "closeOnExit" : true,
            "colorScheme" : "Campbell",
            "commandline" : "C:\\Program Files\\Git\\bin\\bash.exe",
            "cursorColor" : "#FFFFFF",
            "cursorShape" : "bar",
            "fontFace" : "Consolas",
            "fontSize" : 12,
            "guid" : "{c0a23c69-addf-41c8-a214-dc4af501d7ec}",
            "historySize" : 9001,
            "icon" : "C:\\Program Files\\Git\\git-bash.png",
            "name" : "bash",
            "padding" : "0, 0, 0, 0",
            "snapOnInput" : true,
            "startingDirectory" : "%USERPROFILE%",
            "useAcrylic" : true
        }

ファイルをセーブするとメニューは出ました。

f:id:kondoumh:20190624001619p:plain

bash の起動には Windows Terminal 自体の再起動が必要でした。

f:id:kondoumh:20190624002339p:plain

Git の Text UI tig も動きました。

f:id:kondoumh:20190624002900p:plain

ターミナル上の日本語の表示はできましたが、インラインの日本語入力はできず、文字消去もバイト単位。Vim では日本語表示ができていません。Git for Windows の mintty なみというわけにはまだ行かないようです。

それにしてもタブ付きのターミナルが標準で搭載されるというのは素晴らしいです。正式リリースが期待されます。

AWS での作業用に Cloud9 を活用する

GCP には Cloud Shell という Web ベースのターミナルが使えるので、ちょっと作業したい時ブラウザだけで完結できます。

cloud.google.com

AWS には 標準で使える ターミナルはありません。そのため、AWS CLI をローカルマシンにインストールして(そのため Python 環境をインストールして) 環境を作ったり、作業用の EC2 インスタンス作ったりしてました。

blog.kondoumh.com

以前、Cloud IDE を比較したとき初めて使った AWS Cloud9、今は Node.js のコードを書いたりするのに便利に使っています。

aws.amazon.com

IDE のサービスという認識のためターミナルをメインに使うという発想がありませんでした。考えてみたら SSH 不要なので可用性が高いです。環境構築も非常に簡単です。Cloud9 のポータルから Create environment をクリックして開始します。

f:id:kondoumh:20190615182458p:plain

EC2 インスタンスを利用して構築することがほとんどだと思います。Java 開発用の IDE として使うとかでなければ、t2.nano でも十分でしょう。Platform は CentOS ベースの Amazon Linux でも Ubuntu でもお好みで。30分後にインスタンス停止するのがデフォルトオプションなので安心です。

f:id:kondoumh:20190615191742p:plain

EC2 ではなく自前のサーバーに構築することも可能です。このオプションは Cloud 9 を定額の VPS で構築したい場合などに使えそうです。SSH の接続情報を指定することになります。

f:id:kondoumh:20190615192623p:plain

EC2 の場合は確認画面が出ますので構成を確認して Create Environment をクリックします。

f:id:kondoumh:20190615192728p:plain

構築が開始されます。

f:id:kondoumh:20190615203808p:plain

この時、裏では CloudFormation で Stack が作成され始めています。

aws.amazon.com

CloudFormation のスタックを確認してみます。EC2 のセキュリティグループとインスタンスが構築されていっていることが分かります。

f:id:kondoumh:20190615204908p:plain

構築はすぐに終わり IDE が起動します。

f:id:kondoumh:20190615211359p:plain

この時、EC2 管理画面では、t2.micro のインスタンスが起動しているのが確認できます。

f:id:kondoumh:20190615211645p:plain

AWS CLI も最初から使えます。

f:id:kondoumh:20190615205047p:plain

IDE を利用する場合はこれで完了ですが、EKS の CLI で作業をする場合などは、Cloud9 のテンポラリなセッションキーを消して Credential を書き換える必要があります。利用したい IAM ユーザーでターミナルを使用するために設定画面の AWS Settings の AWS managed temporary credentials をオフにします。

f:id:kondoumh:20190615213945p:plain

aws configure を実行して利用したい IAM のアクセスキー ID と秘密アクセスキーを入力します。

masa:~/environment $ aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: text

Cloud9 は短時間で環境が作れて、課金も微量で、ブラウザだけで作業が完結するので SSH 設定で消耗する必要がなくなりました。

Kubernetes ベースの機械学習基盤 Kubeflow をラップトップにデプロイする

機械学習を業務に適用するには、データの収集、機械学習モデルの作成、機械学習モデルを利用する API 開発、API を利用するアプリ開発、プロダクションへの適用、運用時の経年変化によるモデルの修正 といったプロセスで進めることになります。

Kubeflow は機械学習モデル作成、API 開発とデプロイまでをカバーする基盤ソフトウェアです。その実体は数多くの OSS の 集合体 (best-of-breed *1 ) となっています。

www.kubeflow.org

Kubeflow はその名の通り Kubernetes ネイティブなアプリなので Kubernetes クラスターにデプロイして使用します。GCP / EKS / AKS といったメジャーなマネージド Kubernetes はもちろん、ローカルの Minikube、kind *2 などにデプロイ可能です。

残念ながら現状、Docker for Desktop は Kubernetes のバージョンが 1.10.11 とやや古いので、最新版の Kubeflow はデプロイできません。Minikube だと VirtualBox 仮想マシンの分のオーバーヘッドがあるので Docker for Desktop のバージョンアップが待たれます。

MiniKF を使ったデプロイ

Kubeflow には MiniKF という Minikube ベースのディストリビューションが提供されており vagrant up するだけで試すことができます。Windows でも Mac でも Linux でも OK ですが、起動するだけでメモリは 12GB 以上必要です。

www.kubeflow.org

VirtualBox と Vagrant をインストールしておきます。

www.vagrantup.com

www.virtualbox.org

次に適当なディレクトリで vagrant init / up します。

$ vagrant init arrikto/minikf
$ vagrant up

VirtualBox は headless mode で動きますので Window は出ません。完了したら、次のようなメッセージが表示されます。

f:id:kondoumh:20190614131617p:plain

ブラウザで 10.10.10.10 を開くと次のような画面が出ます。

f:id:kondoumh:20190614131941p:plain

Web の画面にターミナルが出ていて、このまま Kubernetes クラスターの起動と Kubeflow のデプロイ作業を続けることになります。

指示通りブラウザで作業を継続してもいいですが、インストール対象のマシンがリモートの Linux だったりして SSH で作業している場合などは、仮想マシンをポートフォワードしてもターミナルとの通信でエラーになってしまいます。その場合、仮想マシンに SSH 接続して作業を続行できます。vagrant ssh でマシンに入って minikf コマンドを叩きます。

$ vagrant ssh

Last login: Wed Jun 12 13:42:56 2019 from 10.0.2.2


Welcome to MiniKF! Type "minikf" to ensure
everything is up and running.

$ minikf

f:id:kondoumh:20190614132347p:plain

初回は10分以上かかります。完了すると Web 画面がこのようになります。

f:id:kondoumh:20190614140904p:plain

10.10.10.10:8080 で Kubeflow のダッシュボードが開きます。

f:id:kondoumh:20190614141130p:plain

デプロイされた Kubeflow 環境に別マシンから接続したい場合、VirtualBox の設定でポートフォワーディングすれば OK です。

f:id:kondoumh:20190614141403p:plain

名前 プロトコル ホスト IP ホストポート ゲスト IP ゲストポート
任意 TCP 0.0.0.0 空きポート 10.10.10.10 8080

Vagrantfile に以下のように記述することも可能です。

Vagrant.configure("2") do |config|
  :
  config.vm.network "forwarded_port", guest: 8080, host: 8080, host_ip: "0.0.0.0"
  :
end

起動した状態で kubernetes の Service や Pod を見てみます。

vagrant@minikf:~$ kubectl -n kubeflow get svc

NAME                                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
ambassador                               ClusterIP   10.101.145.42    <none>        80/TCP              17d
ambassador-admin                         ClusterIP   10.101.77.15     <none>        8877/TCP            17d
argo-ui                                  NodePort    10.111.146.214   <none>        80:32457/TCP        17d
centraldashboard                         ClusterIP   10.106.93.62     <none>        80/TCP              17d
jupyter-0                                ClusterIP   None             <none>        8000/TCP            17d
jupyter-lb                               ClusterIP   10.107.106.12    <none>        80/TCP              17d
jupyter-web-app                          ClusterIP   10.105.85.161    <none>        80/TCP              17d
katib-ui                                 ClusterIP   10.108.218.43    <none>        80/TCP              17d
kf-controller                            ClusterIP   10.103.123.244   <none>        80/TCP              17d
minio-service                            ClusterIP   10.111.170.236   <none>        9000/TCP            17d
ml-pipeline                              ClusterIP   10.104.125.126   <none>        8888/TCP,8887/TCP   17d
ml-pipeline-tensorboard-ui               ClusterIP   10.97.28.36      <none>        80/TCP              17d
ml-pipeline-ui                           ClusterIP   10.99.46.227     <none>        80/TCP              17d
mysql                                    ClusterIP   10.99.108.198    <none>        3306/TCP            17d
notebooks-controller                     ClusterIP   10.98.198.91     <none>        443/TCP             17d
profiles                                 ClusterIP   10.104.136.216   <none>        443/TCP             17d
studyjob-controller                      ClusterIP   10.109.150.25    <none>        443/TCP             17d
tf-job-dashboard                         ClusterIP   10.99.61.120     <none>        80/TCP              17d
vizier-core                              NodePort    10.108.253.190   <none>        6789:31426/TCP      17d
vizier-core-rest                         ClusterIP   10.107.7.132     <none>        80/TCP              17d
vizier-db                                ClusterIP   10.100.73.125    <none>        3306/TCP            17d
vizier-suggestion-bayesianoptimization   ClusterIP   10.110.135.67    <none>        6789/TCP            17d
vizier-suggestion-grid                   ClusterIP   10.108.29.137    <none>        6789/TCP            17d
vizier-suggestion-hyperband              ClusterIP   10.100.113.40    <none>        6789/TCP            17d
vizier-suggestion-random                 ClusterIP   10.102.11.44     <none>        6789/TCP            17d


vagrant@minikf:~$ kubectl -n kubeflow get pod
NAME                                                        READY   STATUS    RESTARTS   AGE
ambassador-6776b669f8-hx252                                 1/1     Running   6          17d
argo-ui-5dd54b58dd-zt8b8                                    1/1     Running   5          17d
centraldashboard-655cc9b87d-nfqvw                           1/1     Running   5          17d
jupyter-0                                                   1/1     Running   5          17d
jupyter-web-app-54c87dc6bd-jv474                            1/1     Running   5          17d
katib-ui-555897fdf7-zk6p8                                   1/1     Running   5          17d
kf-controller-bbc79fdbb-2pq6v                               1/1     Running   5          17d
metacontroller-0                                            1/1     Running   5          17d
minio-6f647575fb-97xzm                                      1/1     Running   4          17d
ml-pipeline-77ff6b76d8-wt9g5                                1/1     Running   5          17d
ml-pipeline-persistenceagent-6d4788b46f-8t9rz               1/1     Running   9          17d
ml-pipeline-scheduledworkflow-5cfd76dd45-2hhdb              1/1     Running   5          17d
ml-pipeline-ui-6586fc8cd9-mztfn                             1/1     Running   5          17d
ml-pipeline-viewer-controller-deployment-67cfc45b8c-kd78p   1/1     Running   9          17d
mysql-6ff979f77f-wldk4                                      1/1     Running   4          17d
notebooks-controller-68585fc8b6-r7j5m                       1/1     Running   8          17d
profiles-5bb5b5dc8-zkktm                                    1/1     Running   8          17d
pytorch-operator-55759ffb8d-kdjgn                           1/1     Running   5          17d
spartakus-volunteer-bbb4d6d7b-567pz                         1/1     Running   5          17d
studyjob-controller-fcd7fbcd6-m4dvc                         1/1     Running   8          17d
tf-job-dashboard-6ff5599fb-qfmnt                            1/1     Running   5          17d
tf-job-operator-79f967f447-f8skb                            1/1     Running   5          17d
vizier-core-76b54db47f-gdlwp                                1/1     Running   23         17d
vizier-core-rest-569dc8b789-v6hdn                           1/1     Running   5          17d
vizier-db-f5f495b5c-vs7l7                                   1/1     Running   4          17d
vizier-suggestion-bayesianoptimization-fbfd657b9-p9v72      1/1     Running   5          17d
vizier-suggestion-grid-66b858dfc5-rp2x9                     1/1     Running   5          17d
vizier-suggestion-hyperband-97cbfd98c-vqwt7                 1/1     Running   5          17d
vizier-suggestion-random-66c6f8889-fhrkb                    1/1     Running   5          17d
workflow-controller-c59cc89bc-z72tv                         1/1     Running   7          17d

25の Service、 29の Pod が起動してます。

vagrant@minikf:~$ docker ps | grep kubeflow | wc -l
60

コンテナの数にして60! かなりの規模ですね。試しに MiniKF を使わず素の Minikube 環境を作ってデプロイしてみたところ、起動するだけで 15GB 以上メモリを使ってしまいました。これでは様々なタスク用の Pod を起動する余裕はありません。MiniKF は起動直後は 12-3 GB ぐらいでちょっと余裕があるので、かなりチューニングされている模様です。

Jupyterlab、 Tensorflow、Pipeline などの機能が統合されています。argo-ui という Service が上がっています。argo は Kubernetes native なワークフローエンジンで CI/CD パイプラインの基盤にもなっています。

argoproj.github.io

JupyterLab の起動

JupyterLab を使ってみます。

Notebooks メニューで Notebook Servers メニューを開いて New Server ボタンをクリック。

f:id:kondoumh:20190614152413p:plain

Server 名を入力します。CPU やメモリ、ディスクなどのリソース割り当ても可能です。入力したら SPAWN をクリックしてサーバーを起動します。

f:id:kondoumh:20190614152613p:plain

しばらくするとサーバーが構築されます。

f:id:kondoumh:20190614152933p:plain

CONNECT をクリックすると JupyterLab が起動します。

f:id:kondoumh:20190614153127p:plain

普通に使えます。

f:id:kondoumh:20190614153152p:plain

この状態で、Pod を見てみると、JupyterLab 用の Pod が1つ増えていました。コンテナも2つ増加。

vagrant@minikf:~$ kubectl -n kubeflow get pod
NAME                                                        READY   STATUS    RESTARTS   AGE
  :
hoge-0                                                      1/1     Running   0          26m
jupyter-0                                                   1/1     Running   5          17d
jupyter-web-app-54c87dc6bd-jv474                            1/1     Running   5          17d
   :

vagrant@minikf:~$ docker ps | grep kubeflow | wc -l
62

Kubernetes のリソースとして JupyterLab の Server や Notebook がデプロイされサービスとして提供されていることが分かります。

Pipelines を動かしてみる

Pipelines も少し見てみます。

サンプルのパイプラインが最初からいくつかデプロイされてます。

f:id:kondoumh:20190615112216p:plain

コインを投げて結果を出力するサンプル。Pipeline の DSL である Condition (条件分岐) のデモのようです。

f:id:kondoumh:20190615112711p:plain

コインをトスする部分は Python のワンライナーをシェルで実行して、ファイルと標準出力に結果を出しています。

f:id:kondoumh:20190615113456p:plain

Condition DSL はこのように記述するようです。

f:id:kondoumh:20190615113702p:plain

実行した結果。condition-4 と condition-6 が発動して print-4 が実行されたみたいです。

f:id:kondoumh:20190615113915p:plain

ログ出力結果もパイプラインのノードごとに見ることができます。

f:id:kondoumh:20190615114308p:plain

kubectl で Pod の状態を見るとパイプライン用の Pod がデプロイ・実行されて完了状態になっています。

vagrant@minikf:~$ kubectl -n kubeflow get pod
NAME                                                            READY   STATUS              RESTARTS   AGE
  :
conditional-execution-pipeline-lk5qp-1114233284                 0/2     Completed           0          88m
  :

以上は、超簡単なサンプルでしたが、本格的な機械学習のパイプラインサンプルも入っています。TensorFlow を用いて、データの検証、変換、モデルの学習、モデルを使った予測、モデルの評価、モデルの Serving などを実行できるようです。

f:id:kondoumh:20190615115341p:plain

実行してみましたが、Pod の状態が、ContainerCreating のまま進みません。

vagrant@minikf:~$ kubectl -n kubeflow get pods

tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047   0/2     ContainerCreating   0          148m

Pod の詳細を describe してみたら、GCP の credentials ボリュームのマウントに失敗しているということで、ローカルの Minikube で動かすのは難しそうです。

vagrant@minikf:~$ kubectl -n kubeflow describe pod tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047
Name:               tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047
    :
Events:
  Type     Reason       Age                    From               Message
  ----     ------       ----                   ----               -------
  Warning  FailedMount  8m44s (x62 over 146m)  kubelet, minikube  Unable to mount volumes for pod "tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047_kubeflow(54dd9cc1-8f0c-11e9-97d8-0800271ba290)": timeout expired waiting for volumes to attach or mount for pod "kubeflow"/"tfx-taxi-cab-classification-pipeline-example-5fq7b-4292043047". list of unmounted volumes=[gcp-credentials]. list of unattached volumes=[podmetadata docker-sock gcp-credentials mlpipeline-minio-artifact pipeline-runner-token-8t2qs]
  Warning  FailedMount  2m27s (x80 over 149m)  kubelet, minikube  MountVolume.SetUp failed for volume "gcp-credentials" : secret "user-gcp-sa" not found

おわりに

Kubeflow はこのように、分析環境やワークフローなどをまとめて面倒見てくれる統合環境ですが、使いこなす上ではデータサイエンティストと Kubernetes に精通したエンジニアが協力し合うチーム体制が必要になりそうです。

*1:開発元に拘らず複数のソフトウェアを組み合わせて構成されたソリューション。Suites と対比される。

*2:https://github.com/kubernetes-sigs/kind コンテナ上に Kubernetes 環境を構築するツール

Windows ラップトップをメイン環境に (MacBook Pro はサーバーに)

最近仕事で使うようになった Kubernetes 自宅でも触ってます。AWS や GCP 以外の検証環境としては発売から4年以上経つ MacBook Pro 13 2015 (16GB RAM / Core i7) が唯一戦えるマシンになっていて Minikube (Virtual Box を利用) や Docker for Desktop の Kubernetes を切り替えてやってます。

ちょっと大きめのアプリを動かすとメモリ 16GB ではかつかつで GUI アプリを使う作業は厳しくなり、サブ機の Let's note CF-SZ6 (8GB RAM / Core i5) をメインにしてみました。

macOS の共有設定で、リモートログインを有効にして SSH で入るようにしました。

f:id:kondoumh:20190609012744p:plain:w300

画面がオフになってもスリープしない設定も入れておきます。

f:id:kondoumh:20190615093128p:plain

Mac には Git for Windows の mintty で接続しています。Windows 10 は 徐々に UI 改善されているので Emacs ライクなキーバインドがないことを除けばけっこう快適です。もうすぐ Windows Terminal も リニューアルされるし、VS Code で Remote Development も使えるようになったし Mac じゃなくてもいい作業は増えていくことでしょう。

marketplace.visualstudio.com

メモリ32GB以上のパワフルマシンを調達したい気持ちもありますが、しばらく手持ちのマシン + クラウドで凌ごうかと。

Netlify と PWA で JAMstack を目指す

静的サイトをホスティングできる Netlify 流行ってます。Git リポジトリを指定するだけでビルド・デプロイでき、CDN でリソースをキャッシュしてくれるので、PWA のホスティングにも向いています。

PWA を作る方法は色々とありますが Vue CLI で簡単にプロジェクト生成できるのでちょっと試してみました。

プロジェクト作成時にマニュアル選択にします。

f:id:kondoumh:20190526084153p:plain:w330

フィーチャー選択で PWA Support を選択します。

f:id:kondoumh:20190526084212p:plain

あとは好みで選択してプロジェクトを生成。

PWA アプリは前に作ったトイ・ダイアグラムエディタを流用し GitHub にリポジトリを作りました。

blog.kondoumh.com

Netlify に GitHub アカウントでサインアップして GitHub のリポジトリをピックアップ。

f:id:kondoumh:20190526094434p:plain

ビルド設定をして Deploy site ボタンを押してしばらく待つとデプロイが完了します。

f:id:kondoumh:20190526094317p:plain

あっけなくデプロイが終わりました。Deploy log も Web UI で確認できるので、エラー原因の特定も簡単です。

f:id:kondoumh:20190526095201p:plain

デプロイされたサイトにアクセスして、PWA アプリをインストールしてみました。

f:id:kondoumh:20190526095431p:plain

ちゃんと動いてます。

iPad の Safari や Android の Chrome からも PWA としてインストールできました。

このサンプルはクライアントサイドだけで完結するので、Tumbr API を叩いて簡単な画面描画をするアプリを PWA にしてデプロイしてみました。

blog.kondoumh.com

こちらも問題なく動作。

外部 API を叩いてちょっとしたタスクをこなすための PWA は手軽に作れるようになりました。

主にクライアント側の JavaScript と API で動的な処理を行い、HTML や CSS はデプロイ時に pre build するというアプリケーションアーキテクチャを JAMstack というそうです。

jamstack.org

Netlify ではデプロイ時にプロジェクトのビルドスクリプトを実行して成果物をデプロイしてくれます。

  • 静的サイトジェネレータでサイトをビルド -> 静的サイト公開
  • Vue CLI などのアプリビルド -> PWA 公開
  • ビルド時にJSON 生成 -> JSON データ公開

のように。他の API やサイトのスクレイピング結果などをアグリゲートするような用途にマッチします。

さらに、Functions を使えば AWS Lambda をラップしたサーバーレスな API も作ることができます。

www.netlify.com

JAMstack なアプリ開発を支援する機能どんどん実装されている感じがします。

以前 Web IDE である CodeSandbox を使ってみましたが、CodeSandbox に Netlify へのデプロイ機能が統合された模様です。

blog.kondoumh.com

www.netlify.com

サーバーレスで、ブラウザだけでアプリの開発からデプロイまで完結してしまう・・。そんな時代になりました。

Electron WebView のデバッグで DevTools を使う

Electron での開発で自分で作ったアセット (HTML / JavaScript / CSS) を WebView で表示することはあまりないと思いますが、アプリの構造上 WebView を使う必要がある場合、困るのは Main Wndow 用の DevTools では WebView コンテンツの JavaScript デバッグとか DOM のインスペクトができないことです。

WebView には openDevTools メソッドがあり専用の DevTools を起動できます。WebView のdom-ready イベントで使うと別ウインドウで上がってきます。

const webview = document.querySelector('webview');
webview.addEventListener('dom-ready', () => {
  webview.openDevTools();
});

開発時 (Electron アプリがパッケージングされていない時) だけ DevTools を起動したい場合、App モジュールの isPackaged を使って判定ができます。DEBUG モードのように使えるわけです。App モジュールは main process で使うもので renderer process では require しても取れませんが Remote モジュール経由で使えます。

const {app} = require("electron").remote;

const webview = document.querySelector('webview');
webview.addEventListener('dom-ready', () => {
  if (!app.isPackaged) {
    webview.openDevTools();
  }
});

f:id:kondoumh:20190517204717p:plain

これで DevTools が使えるようになりましたが、毎回オープンするのはウザいので開発時だけ表示するメニューを用意して、そこから起動できるようにします。

main process では メニュー構築用のテンプレートにパッケージングされていない場合のみ Debug メニューを追加します。このメニューには強制リロードと Main Window 用のサブメニューも role で追加しておくと便利です。

  const template = [
    {
      // snip
    }
  ];

  if (!app.isPackaged) {
    template.unshift({
      label: "Debug",
      submenu: [
        { role: "forceReload"},
        { role: "toggledevtools" },
        { 
          label: "open devTools for WebView",
          click () {
            mainWindow.webContents.send("openDevTools");
          }
        }
      ]
    });
  }

f:id:kondoumh:20190517204817p:plain

renderer process では コマンドに応じて WebView の openDevTools を呼びます。

ipcRenderer.on("openDevTools", () => {
  const webview = document.querySelector('webview');
  webview.openDevTools();
});

これで必要時だけ DevTools を起動できて Remote モジュール使わなくてよくなりました。