2022年3月31日木曜日

Oracle Cloud Free Tier の Ampere A1 VM(ARM VM) に Kubernetes をインストールする

Oracle Cloudの無料枠でKubernetesクラスタを構築する(完全版) | blog.potproject.net を参考に、シングルノードクラスターを構築する。

前提

kubeadm、kubelet、kubectl のインストール

iptablesがブリッジを通過するトラフィックを処理できるようにする

sudo modprobe br_netfilter
cat | sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sudo sysctl --system

iptablesがnftablesバックエンドを使用しないようにする

sudo apt-get install -y iptables arptables ebtables

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo update-alternatives --set arptables /usr/sbin/arptables-legacy
sudo update-alternatives --set ebtables /usr/sbin/ebtables-legacy

ポート設定

sudo vi /etc/iptables/rules.v4

差分は以下の通り。

--- /etc/iptables/rules.v4      2022-03-03 19:28:40.279059992 +0000
+++ ./rules.v4  2022-03-30 23:11:47.310270150 +0000
@@ -14,8 +14,15 @@
 -A INPUT -i lo -j ACCEPT
 -A INPUT -p udp --sport 123 -j ACCEPT
 -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
--A INPUT -j REJECT --reject-with icmp-host-prohibited
--A FORWARD -j REJECT --reject-with icmp-host-prohibited
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT -m comment --comment "http"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT -m comment --comment "https"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 6443 -j ACCEPT -m comment --comment "Kubernetes API server"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 2379 -j ACCEPT -m comment --comment "etcd server client API"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 2380 -j ACCEPT -m comment --comment "etcd server client API"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 10250 -j ACCEPT -m comment --comment "Kubelet API"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 10251 -j ACCEPT -m comment --comment "kube-scheduler"
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 10252 -j ACCEPT -m comment --comment "kube-controller-manager"
+-A INPUT -p tcp -m state --state NEW -m tcp --match multiport --dports 30000:32767 -j ACCEPT -m comment --comment "NodePort Service"
 -A OUTPUT -d 169.254.0.0/16 -j InstanceServices
 -A InstanceServices -d 169.254.0.2/32 -p tcp -m owner --uid-owner 0 -m tcp --dport 3260 -m comment --comment "See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or rem
oving this rule" -j ACCEPT
 -A InstanceServices -d 169.254.2.0/24 -p tcp -m owner --uid-owner 0 -m tcp --dport 3260 -m comment --comment "See the Oracle-Provided Images section in the Oracle Cloud Infrastructure documentation for security impact of modifying or rem
oving this rule" -j ACCEPT

変更したルールを適用。

sudo iptables-restore < /etc/iptables/rules.v4

containerd のインストール

必要な設定の追加

# 必須モジュールのロード
cat | sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 必要なカーネルパラメータの設定・設定値永続化
cat | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf <<EOF
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

sudo sysctl --system

containerd パッケージインストール

# 必須パッケージをインストール
sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common

# Docker公式のGPG鍵を追加
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# Dockerのaptリポジトリの追加
sudo add-apt-repository \
    "deb [arch=arm64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

# containerdのインストール
sudo apt-get update && sudo apt-get install -y containerd

# containerdの設定
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

kubeadm、kubelet、kubectl パッケージのインストール

まだ kubernetes-focal は無いようなので kubernetes-xenial を使用する。

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

kubelet 再起動

sudo systemctl daemon-reload
sudo systemctl restart kubelet

クラスターの作成

マスターノードの初期化

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

実行結果の最後に、以下のような表示がされるのでメモしておく。 (ワーカーノードの追加時に必要)

kubeadm join 10.0.0.6:6443 --token c7qetq.em6zu48c0op2gch5 \
        --discovery-token-ca-cert-hash sha256:9d0f86ce7154975e53e52450f77b7159b98b80c507c2bcaa0a711a1ff35f1740

設定ファイル群の準備

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

マスターノード上で Pod の起動を許可する設定

セキュリティ上の理由で、デフォルトでは、コントロールプレーンノードに Pod を置かない。

今回はシングルノードでクラスターを作っているため、コントロールプレーンノード上での Pod 起動を許可する。

kubectl taint nodes --all node-role.kubernetes.io/master-

マスターノードへワーカーノードを追加する場合

マスターノードの初期化時にメモしたコマンドを実行する。 (今回は、追加のワーカーノードはないので実行しない)

sudo kubeadm join 10.0.0.6:6443 --token c7qetq.em6zu48c0op2gch5 \
        --discovery-token-ca-cert-hash sha256:9d0f86ce7154975e53e52450f77b7159b98b80c507c2bcaa0a711a1ff35f1740

flannel のインストール

Pod 間通信を実現するために、 flannel をインストールする。

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Pod の状態確認

この時点で、以下のようにすべての Pod が Running になっているはず。

ubuntu@eclipse-che:~$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE
kube-system   coredns-64897985d-n4fvq               1/1     Running   0          12m
kube-system   coredns-64897985d-xqfr4               1/1     Running   0          12m
kube-system   etcd-eclipse-che                      1/1     Running   0          13m
kube-system   kube-apiserver-eclipse-che            1/1     Running   0          13m
kube-system   kube-controller-manager-eclipse-che   1/1     Running   0          13m
kube-system   kube-flannel-ds-ffm9r                 1/1     Running   0          35s
kube-system   kube-proxy-csb5h                      1/1     Running   0          12m
kube-system   kube-scheduler-eclipse-che            1/1     Running   0          13m

動作確認

Pod のデプロイ

nginx をデプロイしてみる。

cat > ./nginx.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80
    nodePort: 30080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
EOF

kubectl apply -f ./nginx.yaml

自分自身のノードから動作確認

curl localhost:30080

NGINX のいつもの画面が表示されれば OK.

外部から(自分のPCから)動作確認

VNIC のインバウンド設定

VM 作成時に作られる VNIC は、最低限のポート開放しかしていないため、今回デプロイしたアプリケーションが使用するポート(30080) を許可する必要がある。

  1. Oracle Cloud を開く -> 右上のメニューを開く -> コンピュート -> インスタンス を選択
  2. <作成したVM> -> プライマリVNICのサブネットのリンクをクリック
  3. セキュリティ・リストDefault Security List for ... のリンクを選択
  4. イングレス・ルールイングレス・ルールの追加 ボタンを押下
    • ステートレス: チェック無し
    • ソース・タイプ: CIDR
    • ソースCIDR: 0.0.0.0/0
    • IPプロトコル: TCP
    • ソース・ポート範囲: (空)
    • 宛先ポート範囲: 30080
    • 説明: デモアプリの待ち受けポート開放

これで、外からの 30080 ポートアクセスが、ノードに届くようになる。

curl でアクセス

curl <VMの公開IPアドレス>:30080

NGINX のいつもの画面が表示されれば OK.

参考資料

2022年3月29日火曜日

monaco-editor で YAML の入力補完を行う

monaco-editor に入門する の続きから、 JSON Schema を読み込ませて YAML の入力補完を実現するところまで。

必要パッケージのインストール

monaco-yaml パッケージをインストールする。

npm install --save-dev monaco-yaml

JSON スキーマの作成

前回 と同じく、「タイトル」「URL」のプロパティを持った要素の配列を定義する。

dist/schema/Bookmarks.json

{
  "type": "array",
  "items": {
    "type": "object",
    "required": [
      "url"
    ],
    "properties": {
      "url": {
        "type": "string"
      },
      "title": {
        "type": "string"
      }
    },
    "$schema": "http://json-schema.org/schema#"
  },
  "$schema": "http://json-schema.org/schema#",
  "additionalProperties": false
}

YAML 補完機能の実装

MonacoWebpackPlugin の言語設定修正

前回のサンプルでは、 typescript, javascript, css が設定されていたが、 今回は YAML を編集したいので、 yaml のみとする。

diff --git a/js/monaco-editor/json/webpack.config.js b/js/monaco-editor/json/webpack.config.js
index 8b5ea53..1094993 100644
--- a/js/monaco-editor/json/webpack.config.js
+++ b/js/monaco-editor/json/webpack.config.js
@@ -28,7 +28,7 @@ module.exports = {
     },
     plugins: [
         new MonacoWebpackPlugin({
-            languages: ['typescript', 'javascript', 'css']
+            languages: ['json']
         })
     ]
 };

monaco-yaml のインポート

+import { setDiagnosticsOptions } from 'monaco-yaml';

スキーマファイルの読み込み

fetch で URL を指定子、ファイルの中身を取得する。

+async function init() {
+
+  const SCHEMA_URL = './schema/Bookmarks.json';
+
+  async function getJsonFromUri(jsonUri) {
+    const response = await fetch(jsonUri);
+    const json = await response.json();
+    return json;
+  }
+
+  const schema = await getJsonFromUri(SCHEMA_URL);
...(snip, 後述の YAML の Diag 設定処理)

fetch で async/await を使用するため、これ以降の処理をすべて init 関数として括る。

YAML の Diag 設定

先ほど取得した JSON スキーマの情報を使い、 Diagnostics Option の設定を行う。

...(snip, 前述のスキーマファイルの読み込み処理)
+  setDiagnosticsOptions({
+    enableSchemaRequest: true,
+    hover: true,
+    completion: true,
+    format: true,
+    validate: true,
+    schemas: [
+      {
+        uri: SCHEMA_URL,
+        fileMatch: ['*'],
+        schema: schema
+      }
+    ]
+  });
...(snip, 後述の monaco-editor の生成処理)
  • schemas.uri: JSON スキーマファイルの URI を指定
  • schemas.fileMatch: このスキーマを適用するファイル名のパターンを指定、今回は全ファイルにマッチさせたいので '*' とする
  • schemas.schema: JSON スキーマファイルの中身を指定

これで、このスキーマに則った候補の出力をしてくれるようになる。

monaco-editor の生成

   monaco.editor.create(document.getElementById('root'), {
-    value: `const foo = () => 0;`,
-    language: 'javascript',
-    theme: 'vs-dark'
+    value: "",
+    language: 'yaml',
   });
+}
  • language: エディタで開くファイルの言語、今回は YAML なので yaml を設定
  • value: エディタのテキスト初期値、今回は空にしたいので空文字を設定

init 関数を呼び出す処理追加

async/await の関係上 init 関数を作ったので、 DOMContentLoaded のタイミングでその関数を呼び出すように修正。

+document.addEventListener('DOMContentLoaded', () => {
+  init();
 });
■ index.js 全体
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { setDiagnosticsOptions } from 'monaco-yaml';

(function () {
    // create div to avoid needing a HtmlWebpackPlugin template
    const div = document.createElement('div');
    div.id = 'root';
    div.style = 'width:800px; height:600px; border:1px solid #ccc;';

    document.body.appendChild(div);
})();

async function init() {

  const SCHEMA_URL = './schema/Bookmarks.json';

  async function getJsonFromUri(jsonUri) {
    const response = await fetch(jsonUri);
    const json = await response.json();
    return json;
  }

  const schema = await getJsonFromUri(SCHEMA_URL);

  setDiagnosticsOptions({
    enableSchemaRequest: true,
    hover: true,
    completion: true,
    format: true,
    validate: true,
    schemas: [
      {
        uri: SCHEMA_URL,
        fileMatch: ['*'],
        schema: schema
      }
    ]
  });

  monaco.editor.create(document.getElementById('root'), {
    value: "",
    language: 'yaml',
  });
}

document.addEventListener('DOMContentLoaded', () => {
  init();
});

動作確認

npm start コマンドで実行し、 monaco-editor が開かれ、 <C-Space>title, url が補完されることを確認。 OK.

ここまでの修正内容を以下 Pull Request で実施しているので、必要に応じて参照してみてください。

参考資料

2022年3月28日月曜日

monaco-editor で JSON Schema の入力補完を行う

前回 の続きから、 JSON Schema を読み込ませて入力補完を実現するところまで。

JSON スキーマの作成

今回は、「タイトル」「URL」のプロパティを持った要素の配列を定義する。

dist/schema/Bookmarks.json

{
  "type": "array",
  "items": {
    "type": "object",
    "required": [
      "url"
    ],
    "properties": {
      "url": {
        "type": "string"
      },
      "title": {
        "type": "string"
      }
    },
    "$schema": "http://json-schema.org/schema#"
  },
  "$schema": "http://json-schema.org/schema#",
  "additionalProperties": false
}

JSON Schema 補完機能の実装

MonacoWebpackPlugin の言語設定修正

前回のサンプルでは、 typescript, javascript, css が設定されていたが、 今回は JSON を編集したいので、 json のみとする。

diff --git a/js/monaco-editor/json/webpack.config.js b/js/monaco-editor/json/webpack.config.js
index 8b5ea53..1094993 100644
--- a/js/monaco-editor/json/webpack.config.js
+++ b/js/monaco-editor/json/webpack.config.js
@@ -28,7 +28,7 @@ module.exports = {
     },
     plugins: [
         new MonacoWebpackPlugin({
-            languages: ['typescript', 'javascript', 'css']
+            languages: ['json']
         })
     ]
 };

スキーマファイルの読み込み

fetch で URL を指定子、ファイルの中身を取得する。

+async function init() {
+
+  const SCHEMA_URL = './schema/Bookmarks.json';
+
+  async function getJsonFromUri(jsonUri) {
+    const response = await fetch(jsonUri);
+    const json = await response.json();
+    return json;
+  }
+
+  const schemaUri = monaco.Uri.parse(SCHEMA_URL);
+  const schema = await getJsonFromUri(SCHEMA_URL);
...(snip, 後述の JSON の Diag 設定処理)

fetch で async/await を使用するため、これ以降の処理をすべて init 関数として括る。

JSON の Diag 設定

先ほど取得した JSON スキーマの情報を使い、 Diagnostics Option の設定を行う。

...(snip, 前述のスキーマファイルの読み込み処理)
+  monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
+    validate: true,
+    schemas: [
+      {
+        uri: schemaUri,
+        fileMatch: ['*'],
+        schema: schema
+      }
+    ]
+  });
...(snip, 後述の monaco-editor の生成処理)
  • uri: JSON スキーマファイルの URI を指定
  • fileMatch: このスキーマを適用するファイル名のパターンを指定、今回は全ファイルにマッチさせたいので '*' とする
  • schema: JSON スキーマファイルの中身を指定

これで、このスキーマに則った候補の出力をしてくれるようになる。

monaco-editor の生成

   monaco.editor.create(document.getElementById('root'), {
-    value: `const foo = () => 0;`,
-    language: 'javascript',
-    theme: 'vs-dark'
-    theme: 'vs-dark'
+    value: "",
+    language: 'json',
+  });
+}
  • language: エディタで開くファイルの言語、今回は JSON なので json を設定
  • value: エディタのテキスト初期値、今回は空にしたいので空文字を設定

init 関数を呼び出す処理追加

async/await の関係上 init 関数を作ったので、 DOMContentLoaded のタイミングでその関数を呼び出すように修正。

+document.addEventListener('DOMContentLoaded', () => {
+  init();
 });
■ index.js 全体
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

(function () {
    // create div to avoid needing a HtmlWebpackPlugin template
    const div = document.createElement('div');
    div.id = 'root';
    div.style = 'width:800px; height:600px; border:1px solid #ccc;';

    document.body.appendChild(div);
})();

async function init() {

  const SCHEMA_URL = './schema/Bookmarks.json';

  async function getJsonFromUri(jsonUri) {
    const response = await fetch(jsonUri);
    const json = await response.json();
    return json;
  }

  const schemaUri = monaco.Uri.parse(SCHEMA_URL);
  const schema = await getJsonFromUri(SCHEMA_URL);

  monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
    validate: true,
    schemas: [
      {
        uri: schemaUri,
        fileMatch: ['*'],
        schema: schema
      }
    ]
  });

  monaco.editor.create(document.getElementById('root'), {
    value: "",
    language: 'json',
  });
}

document.addEventListener('DOMContentLoaded', () => {
  init();
});

動作確認

npm start コマンドで実行し、 monaco-editor が開かれ、 <C-Space>{}title, url が補完されることを確認。 OK.

ここまでの修正内容を以下 commit で実施しているので、必要に応じて参照してみてください。

JSON 補完機能実装 in js/monaco-editor/json. · mikoto2000/MiscellaneousStudy@c8b5a5e

2022年3月26日土曜日

monaco-editor に入門する

monaco-editor リポジトリのサンプル「browser-esm-webpack-monaco-plugin」を実行するところまで。

monaco-edito サンプルの作成

npm プロジェクトの初期化

いつもの。

npm init

サンプルコードのコピー

monaco-editor の sample から、 browser-esm-webpack-monaco-plugin のソースコードをコピー。

monaco-editor/samples at e77825074dc019aebc7b28368428d16226d9ab8b · microsoft/monaco-editor

必要パッケージのインストール

npm install --save monaco-editor
npm install --save-dev webpack webpack-cli monaco-editor-webpack-plugin style-loader css-loader file-loader

ビルド

以下コマンドでビルドが成功することを確認。

npx webpack --mode=development

動作確認

webpack-dev-server で、ローカルサーバーを立ち上げ、動作確認する。

必要パッケージのインストール

npm install --save-dev webpack-dev-server

package.json の更新

scripts に、ビルドで使いそうなコマンドを追加。

webpack-dev-server を使うのは "start": "webpack serve" の定義部分。

また、webpack-dev-server をインストールしたので、 devDependencieswebpack-dev-server が追加されている。

diff --git a/js/monaco-editor/firststep/package.json b/js/monaco-editor/firststep/package.json
index 44eacaa..bd4660e 100644
--- a/js/monaco-editor/firststep/package.json
+++ b/js/monaco-editor/firststep/package.json
@@ -4,7 +4,11 @@
   "description": "",
   "main": "index.js",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "start": "webpack serve",
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "webpack",
+    "build:dev": "webpack --mode=development",
+    "build:pro": "webpack --mode=production"
   },
   "author": "mikoto2000",
   "license": "MIT",
@@ -17,6 +21,7 @@
     "monaco-editor-webpack-plugin": "^7.0.1",
     "style-loader": "^3.3.1",
     "webpack": "^5.70.0",
-    "webpack-cli": "^4.9.2"
+    "webpack-cli": "^4.9.2",
+    "webpack-dev-server": "^4.7.4"
   }
 }

webpack.config.json を更新

  • デフォルトのモードを development に変更
  • ./dist のパスを変数化して共有
  • devServer セクションに webpack-dev-server の設定を追加
diff --git a/js/monaco-editor/firststep/webpack.config.js b/js/monaco-editor/firststep/webpack.config.js
index 823fa44..8b5ea53 100644
--- a/js/monaco-editor/firststep/webpack.config.js
+++ b/js/monaco-editor/firststep/webpack.config.js
@@ -1,13 +1,19 @@
 const path = require('path');
 const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');

+const distPath = path.resolve(__dirname, 'dist');
+
 module.exports = {
-    mode: process.env.NODE_ENV,
+    mode: 'development',
     entry: './index.js',
     output: {
-        path: path.resolve(__dirname, 'dist'),
+        path: distPath,
         filename: '[name].bundle.js'
     },
+    devServer: {
+        static: distPath,
+        open: true
+    },
     module: {
         rules: [
             {

webpack-dev-server の設定は以下の通り。

  • devServe.static: サーバーのドキュメントルート
  • devServe.open: 起動時にブラウザを開く

動作確認

webpack-dev-server を立ち上げて動作確認。

npx webpack serve

この作業を以下 PR にまとめたので、必要があれば参照してください。

Monaco firststep by mikoto2000 · Pull Request #45 · mikoto2000/MiscellaneousStudy

参考資料

2022年3月23日水曜日

Tauri アプリ入門 3 - メインプロセスからレンダープロセスへ通信

Vanilla.js のプロジェクトを作って、「メインプロセス -> レンダープロセス」の通信を実装するところまで。

この通信のために、イベントという仕組みを利用するため、ついでに「レンダープロセス -> レンダープロセス」のイベント通信も試す。

今回は、プロジェクト作成直後から、必要な修正差分を示す形で進める。

前提

  • OS: Windows 11 Pro 21H2 ビルド 22000.556
  • Tauri の開発環境構が築済みであること
  • Vanilla.js の Tauri プロジェクトひな形が作成済みであること
    • 今回は、 tauri-app-event という名前でプロジェクトを作成した

その他、Tauri シリーズを読んでおくとよいかも。

メインプロセスからレンダープロセスへイベントを送信する

Tauri でメインプロセスからレンダープロセスへの通信を行い際には、イベントという仕組みを利用する。

今回は、以下順番で実装していく。

  1. Vanilla.js のための設定
  2. メインプロセス側にイベントを送信する関数を定義
    • イベント送信処理起動コマンドの実装
    • コマンドの登録

Vanilla.js のための設定

tauri.conf.json"withGlobalTauri": true を追加する。

diff --git a/tauri/tauri-app-event/src-tauri/tauri.conf.json b/tauri/tauri-app-event/src-tauri/tauri.conf.json
index e5699f4..8f75c29 100644
--- a/tauri/tauri-app-event/src-tauri/tauri.conf.json
+++ b/tauri/tauri-app-event/src-tauri/tauri.conf.json
@@ -7,7 +7,8 @@
     "distDir": "../dist",
     "devPath": "../dist",
     "beforeDevCommand": "",
-    "beforeBuildCommand": ""
+    "beforeBuildCommand": "",
+    "withGlobalTauri": true
   },
   "tauri": {
     "bundle": {

メインプロセス側にイベントを送信する関数を定義

イベント送信処理起動コマンドの実装

Rust 側で、 AppHandle#emit_all を実行することで、レンダープロセス側にイベントを通知できる。

第一引数にイベント名、第二引数に送信するデータを格納する。

以下では backendHello という名前で、構造体 BackendHelloEventPayload の構造をしたデータを送信している。

レンダープロセスで、イベントを listen する際に、イベント名を指定することで、同名のイベントを受け取れる。

diff --git a/tauri/tauri-app-event/src-tauri/src/command.rs b/tauri/tauri-app-event/src-tauri/src/command.rs
new file mode 100644
index 0000000..f1983a7
--- /dev/null
+++ b/tauri/tauri-app-event/src-tauri/src/command.rs
@@ -0,0 +1,42 @@
+use std::sync::mpsc::{self, TryRecvError};
+use std::thread;
+use std::time::Duration;
+
+use tauri;
+use tauri::Manager;
+
+// レンダープロセスへ送信するデータの構造を定義した構造体
+#[derive(Clone, serde::Serialize, serde::Deserialize)]
+pub struct BackendHelloEventPayload {
+  pub message: String,
+}
+
+// 初期化用関数
+// JS のイベントリスナー `DOMContentLoaded` から、初期化時に呼び出されることを想定
+#[tauri::command]
+pub fn post_setup_process_for_backend(app: tauri::AppHandle) {
+
+  let (tx, rx) = mpsc::channel();
+
+  // 新しいスレッドを作成して、 1 秒ごとにイベントを emit する処理を実装
+  tauri::async_runtime::spawn( async move {
+    loop {
+      println!("send backendHello event.");
+      thread::sleep(Duration::from_millis(1000));
+
+      // レンダープロセスへイベント送信
+      //     イベント名: backendHello
+      //     送信データ: { message: "Hello, World from backend!!!" }
+      app.emit_all("backendHello", BackendHelloEventPayload { message: "Hello, World from backend!!!".into() }).unwrap();
+
+      // emit 終了通知用チャンネルに通知が来たらループをストップ
+      match rx.try_recv() {
+          Ok(_) | Err(TryRecvError::Disconnected) => {
+              println!("stop backendHello event.");
+              break;
+          }
+          Err(TryRecvError::Empty) => {}
+      }
+    }
+  });
+
+  // 10 秒後に emit を終了(emit 終了通知用チャンネル経由でストップを通知)
+  tauri::async_runtime::spawn( async move {
+    thread::sleep(Duration::from_millis(10000));
+    let _ = tx.send(());
+  });
+}
+

コマンドの登録

先ほど作成したコマンドを、 invoke_handler で登録。

diff --git a/tauri/tauri-app-event/src-tauri/src/main.rs b/tauri/tauri-app-event/src-tauri/src/main.rs
index e994ea4..2dd091a 100644
--- a/tauri/tauri-app-event/src-tauri/src/main.rs
+++ b/tauri/tauri-app-event/src-tauri/src/main.rs
@@ -3,8 +3,13 @@
   windows_subsystem = "windows"
 )]

+mod command;
+
 fn main() {
   tauri::Builder::default()
+    // setup 中ではできない初期化処理を実行するコマンドを登録
+    // setup 中に spawn ができないので、それを行う
+    .invoke_handler(tauri::generate_handler![command::post_setup_process_for_backend])
     .run(tauri::generate_context!())
     .expect("error while running tauri application");
 }

レンダープロセス側にイベント受信処理を追加

window.__TAURI__.event.listen 関数を使って、イベント受信時に実行する関数を設定する。

diff --git a/tauri/tauri-app-event/dist/index.html b/tauri/tauri-app-event/dist/index.html
index 8da7e6c..507bb5e 100644
--- a/tauri/tauri-app-event/dist/index.html
+++ b/tauri/tauri-app-event/dist/index.html
@@ -10,11 +10,42 @@
     }

     body {
-      display: flex;
       align-items: center;
       justify-content: center;
     }
   </style>
+  <script type="text/javascript">
+    document.addEventListener('DOMContentLoaded', function() {
+        const invoke = window.__TAURI__.invoke;
+        const listen = window.__TAURI__.event.listen;
+
+        async function init() {
+            // バックエンドの初期化処理呼び出し
+            await invoke('post_setup_process_for_backend', {});
+
+            // バックエンドが emit するイベントの listen
+            //     イベント名: backendHello
+            //     受信データ: { message: "Hello, World from backend!!!" }(`event.payload` に格納されている)
+            const unlistenBackendHello = await listen('backendHello', event => {
+                console.log(event);
+                const p = document.createElement('p');
+                p.textContent = event.payload.message;
+                document.body.append(p);
+            });
+
+            // 5 秒後に listen を止める
+            setTimeout(() => {
+                console.log("timeout.");
+                unlistenBackendHello();
+                const p = document.createElement('p');
+                p.textContent = 'stop event listen.';
+                document.body.append(p);
+            }, 5000);
+        }
+
+        init();
+
+    });
+  </script>
   <body>
     <h1>tauri-app-event</h1>
   </body>

ここまで追加したのち、 npx tauri dev コマンドで実行すると、以下の挙動が確認できる。

  1. Rust 側で、 10 秒間、 1 秒ごとにイベントを発行
  2. JS 側で、起動から 5 秒間イベントを listen し、それ以後無視する

コメントが無いなど、多少差分があるが、前述の方針で実装したものを以下に格納しているので、必要に応じて確認してください。

参考資料