Receptor (1): Receptor はじめの一歩


はじめに

Ansible Automation Platform(AAP)には、その構成の柔軟性を飛躍的に向上させる Automation Mesh と呼ばれる仕組みがあります。AWX でも Execution Node がサポートされ、より近しいことができるようになってきました(詳細は 別のエントリ で紹介しています)。

この Automation Mesh の中核 とも呼べる技術が、Receptor です。本エントリではこの Receptor を理解するはじめの一歩として、概要と基本的な使い方と動きを確認します。

Receptor は、Ansible 関連のプロジェクトではありますが、ざっくりとは 任意のコマンドをリモートノードで実行する仕組み であり、本来は Ansible とはまったく関係なく単体でも利用できる モノです。そのため本エントリでは、Automation Mesh や AWX、Ansible Runner などの Ansible 周辺との関係はいったん忘れて、まずは あくまで Receptor 単体に着目 します。

なお、エントリ中で紹介する設定ファイル類やそれを実際に動作させられる Docker 用の Compse ファイルは、GitHub のリポジトリ にも配置しています。お手元で試したい方は併せてどうぞ。

概要

Receptoransible/receptor)は、ひとことでは 任意のコマンドをリモートノードで安全かつ確実に実行する仕組み といえます。

リポジトリドキュメント を情報源にざっくりまとめると、次のようなことを実現できます。

  • Receptor が動作しているノード同士で 他ノードへの接続情報を交換しあう ことで、全体としてひとつの メッシュネットワーク が構成され、任意のノード間で相互に疎通 できる状態が作られる
  • メッシュネットワーク内では、あるノードから他のノードに対してコマンドの実行や Kubernetes クラスタへの Pod の作成を指示 できる

また、こうしたリモート実行の信頼性を高めるために、次のような工夫もされています。

  • あるノードが停止しても、メッシュネットワークの経路が再計算され、他のノードを経由して迂回できればノード間の疎通は維持される
  • リモート実行の動作中にノード間のネットワークが一時的に切断されても、リモート側の処理には影響せず、結果は復旧後に欠損なく取得できる
  • ノード間の接続に、TLS による相互認証と暗号化を利用できる
  • ノードへの処理の実行指示が改竄されていないことを、電子署名により検証できる
  • 組み込みのファイアウォールで、特定のノード間の疎通可否を制御できる

これらの特徴から、前述のとおり、Receptor は 任意のコマンドをリモートノードで安全かつ確実に実行する仕組み といえそうです。

インストールと設定

インストール

Receptor を使うためにインストールするモノには、次の二つがあります。receptorctl の導入は任意ですが、Receptor をコマンドで気軽に触る手段が現状ではこれしかないので、勉強目的ではほぼ必須です。

  • Receptor 本体(必須)
  • Receptor 専用のコマンドラインツールである receptorctl(任意)

コンテナ環境で利用する場合

上記のいずれも、Receptor のコンテナイメージ(quay.io/ansible/receptor)にはあらかじめ含まれています。コンテナ環境に慣れているなら、これがもっとも手軽な入手手段です。ただし、Receptor 経由で実行できることの範囲も当然ながらコンテナ内に閉じてしまうため、実運用では注意が必要です。

とはいえ、動作の学習には充分なので、本エントリではこれを使います。

ホストで直接動作させる場合

Receptor の本体(Go 言語製のシングルバイナリ)は、ソースコードのコンパイルか、Copr のリポジトリ上の RPM パッケージ か、前述のコンテナイメージからのコピーで導入できます。

# ソースコードをコンパイルして導入
git clone https://github.com/ansible/receptor.git
cd receptor
make receptor

# RPM パッケージで導入
dnf copr enable ansible-awx/receptor
dnf install receptor

# コンテナイメージからコピーして導入
CONTAINER_ID=$(docker create quay.io/ansible/receptor:latest)
docker cp ${CONTAINER_ID}:/usr/bin/receptor .
docker rm ${CONTAINER_ID}

ドキュメント ではソースコードからコンパイルする手段しか紹介されていないので、それ以外の手段のサポート具合は不明です。

が、RPM パッケージは AWX の Execution Node のインストーラでも使われていますし、コンテナイメージからのコピーも quay.io/ansible/awx-ee のビルドの過程 で使われているので、それなりに安心できる手段といえそうな気配はあります。RPM パッケージには Systemd 用のファイルや設定ファイルのサンプルなども含まれているので、用途によっては便利そうです。なお、充分なテストはしていませんが、ソースコードからコンパイルすれば Windows 上でも動作させられるようでした。

receptorctl も、前述の Copr のリポジトリ で RPM パッケージが公開されています。Copr を使わない場合は、PyPI から pip コマンドで導入できます。

# RPM パッケージで導入
dnf copr enable ansible-awx/receptor
dnf install receptorctl

# PyPI から導入
pip install receptorctl

設定

Receptor の設定方法は次の二通りです。

  • Receptor の起動時の引数ですべて指定する
  • 設定ファイルを作成し、Receptor の起動時に設定ファイルを指定する

設定できる範囲はどちらでも変わらず、設定ファイルに書けることはすべて起動時の引数でも渡せるようです。また、両方の併用もできそうです。

本エントリでは、一貫して設定ファイルを使った構成を行っています。設定ファイルは YAML 形式で、例えば以下のような内容です(具体的な内容は後述します)。

---
- node:
    id: demo01

- log-level:
    level: debug

- tcp-listener:
    port: 7323

このファイルのパスを起動時の引数 -c--config)で指定すると、ファイルの記述通りに構成されます。

receptor -c /etc/receptor/receptor.yml

設定できる内容は、ドキュメント--help で確認できます。

receptor --help

起動

起動すると、フォアグラウンドで動作し始めます。

$ receptor -c /etc/receptor/receptor.yml
DEBUG 2022/11/25 07:24:36 Listening on TCP [::]:7323
INFO 2022/11/25 07:24:36 Initialization complete
...

RPM パッケージを使って導入した場合は、Systemd を使った起動と停止もできます。Receptor 1.3.0 に含まれる Systemd 用のファイルはこんな感じでした。

[Unit]
Description=Receptor

[Service]
ExecStart=/usr/bin/receptor -c /etc/receptor/receptor.conf
User=receptor
Group=receptor
StandardOutput=append:/var/log/receptor/receptor.log
Restart=on-failure

[Install]
WantedBy=multi-user.target

参考: ビルトインツールの利用

バイナリ receptor は、前述のように何らかの構成を指定して Receptor のノードとして起動させるのが主ですが、組み込みのツール として、CA 証明書や証明書署名要求の作成、証明書への署名が行える機能も持っています。本エントリでは紹介は割愛します。

receptor --cert-init commonname=...
receptor --cert-makereq commonname=...
receptor --cert-signreq req=...

デモ (1): ノードの接続とコマンドの実行

実際に、リモートノードでのコマンド実行をもっともシンプルな形で実践しながら、構成の仕方や動きを確認します。

何はともあれ、まずはそもそも メッシュネットワークを構成する 必要があります。何も考えずにとにかく起動すればあとは全自動で…… というわけではなく、最終的なメッシュネットワークのトポロジはあくまで利用者の責任で設計が必要 であり、各ノードでそのための設定も必要 であることには注意が必要です。

また、コマンドのリモートノードでの実行についても、何も設定しなくても任意のノードから任意のノードで任意のコマンドを好き放題に実行できるわけではなく、実行を許可するコマンドあらかじめリモートノード側で定義する 必要があります。

構成例

基本の動きを確認したいので、まずは図の 3 ノードのシンプルな構成を考えます。

左端のノード から、自分自身右端のノード に対してコマンド cat /etc/hostname が実行できる状態を目指します。

前提: メッシュネットワークの考え方

メッシュネットワークは、各ノードで 隣接するノード同士バックエンドでの相互の接続方法を設定 することで構成されます。具体的には、隣接する二つのノードについて、一方で 接続を待ち受ける設定*-listener) を、他方で 接続しに行く設定*-peer)を行います。

ノード同士がバックエンドで接続されると、あたかもルータが経路情報を相互にアドバタイズするときのように、自身の知る隣接ノードの情報や他ノードへの経路がノード間で交換されます。最終的には 全ノードが他の全ノードへの経路情報を持つ ようになり、任意のノード同士で双方向に疎通できるメッシュネットワークが構成されます。上図では、左端のノードと右端のノードはバックエンドでは直接の接続はありません が、経路計算により、中央のノードを経由して自由に疎通できる ようになります。

バックエンド接続には方向がある ため、ノード間の経路に NAT やファイアウォールがある場合は構成に配慮が必要です。一方で、メッシュネットワーク上の通信はバックエンドの接続方向とは無関係に行える ため、ネットワーク環境側に制約がなければ、例えば上図の矢印を逆方向に構成してもまったく問題ないことになります。

なお、バックエンドの接続には、現時点では TCP か UDP か WebSocket のいずれかを利用できます。本エントリでは TCP を使います。

前提: ノードの種類

リモートノードに何らかの処理をさせるには、実行元と実行先のそれぞれのノードで構成が必要です。明確な定義はありませんが、便宜上、本エントリでは 実行元を Controller実行先を Executor として区別します。

  • Controller
    • Executor への処理の実行指示 やその進捗と結果の表示、メッシュネットワークの状態の確認 を行う役割のノード
  • Executor
    • Controller からの指示を受け て、実際に コマンドの実行 または Kubernetes クラスタの Pod の作成 を行う役割のノード

今回の構成では、左端のノードが Controller 兼 Executor で、右端のノードが Executor です。

設定ファイルの作成

今回の目的の構成を実現する、各ノードの実際の設定ファイルです。GitHub にも設定ファイルと Docker 用の Compose ファイルを配置 しています。

ノード controller01 の設定ファイル

左端のノードの設定ファイルの例です。Controller 兼 Executor として構成します。

---
- node:
    id: controller01

- log-level:
    level: debug

- tcp-peer:
    address: node02.example.internal:7323

- control-service:
    service: control
    filename: /tmp/receptor.sock

- work-command:
    worktype: gather-hostname
    command: cat
    params: /etc/hostname

以下、部分ごとの説明です。

- node:
    id: controller01

nodeid は、Receptor の世界でそのノードのユニークな ID です。TCP/IP の世界のホスト名とは 別物 で、ノードごとに localhost 以外の任意の名称を付けられます。ここでは controller01 を与えています。明示しない場合はホスト名がノード名として利用されます。

- log-level:
    level: debug

動きを追いやすくするため、ログレベルを debug にしています。

- tcp-peer:
    address: node02.example.internal:7323

メッシュネットワークを構成するための バックエンドでの接続方法 を定義する部分です。このノードは 接続しに行く側 で、バックエンドに TCP を利用するので、tcp-peer を使って接続先である 待ち受け側のホスト名とポート番号を指定 しています。今回は、中央のノードを指定しています。後述しますが、待ち受け側では tcp-listener を使って待ち受けています。

- control-service:
    service: control
    filename: /tmp/receptor.sock

control-service は、Receptor を制御するための組み込みのサービスを起動させる設定です。そのノードが 他のノードに指示を出す場合他のノードから指示を受け取る場合 の両方、つまり Controller と Executor の両方で必要 です。

かつ、そのノードを Controller にしたい場合は filename も指定する とよいでしょう。このファイルは receptorctl からの操作で発生するソケット通信で利用します。今回は、/tmp/receptor.sock としました。なお、指定したパスにファイルが存在しない場合は、起動時に自動で作成されます。

- work-command:
    worktype: gather-hostname
    command: cat
    params:  /etc/hostname

このノードに Executor として実行させる処理の定義(ワークタイプ)です。コマンドを実行させる なら work-commandKubernetes クラスタへ Pod を作成させる なら work-kubernetes を指定します。今回は前者です。

実行させるコマンド(cat)とその引数(/etc/hostname)を指定し、この組み合わせの設定に worktypegather-hostname と名前を付けています。この名前(ワークタイプ名)は後で Controller からコマンドを実行するときに利用します。

コマンドの引数や標準入力を実行時に与える手段も用意されていますが、それについては後述します。

ノード relayer01 の設定ファイル

中央のノードの設定ファイルです。このノードは Controller でも Executor でもなく、単に左右からの通信を中継するだけなので、他のノードとの接続に必要な設定のみを行っています。

---
- node:
    id: relayer01

- log-level:
    level: debug

- tcp-listener:
    port: 7323

tcp-listener が、バックエンドに TCP を利用 して 待ち受ける側 としてふるまうための構成です。ポート番号に 7323 を指定しています。前述の controller01 では、このノードのこのポートを tcp-peer で指定していました。

なお、デフォルトでは 0.0.0.0 にバインドされますが、bindaddr を指定して特定の IP アドレスでのみ待ち受けさせる構成も可能です。

ノード executor01 の設定ファイル

右端のノードの設定ファイルです。

---
- node:
    id: executor01

- log-level:
    level: debug

- tcp-peer:
    address: node02.example.internal:7323

- control-service:
    service: control

- work-command:
    worktype: gather-hostname
    command: cat
    params: /etc/hostname

このノードは、バックエンドでは 接続しにいく側 のため、tcp-peer を指定しています。接続先は中央のノードです。

また、Executor であり、Controller からのコマンド実行を受け付けられるよう、control-servicework-command を定義しています。このノード自体は Controller にはしないため、control-servicefilename は指定していません。

Docker Compose ファイル

今回の諸々の実験はコンテナ環境で行うため、次の Docker Compose ファイルを用意します。前述の設定ファイル群をコンテナにマウントし、Receptor の起動時に -c で指定しているだけの簡単なものです。コンテナ名とホスト名は、左端から順に node01node02node03 としています。

services:
  node01:
    image: ${RECEPTOR_IMAGE:?err}
    container_name: node01
    hostname: node01.example.internal
    command: "receptor -c /etc/receptor/receptor.conf"
    volumes:
      - "./conf/node01.yml:/etc/receptor/receptor.conf"

  node02:
    image: ${RECEPTOR_IMAGE:?err}
    container_name: node02
    hostname: node02.example.internal
    command: "receptor -c /etc/receptor/receptor.conf"
    volumes:
      - "./conf/node02.yml:/etc/receptor/receptor.conf"

  node03:
    image: ${RECEPTOR_IMAGE:?err}
    container_name: node03
    hostname: node03.example.internal
    command: "receptor -c /etc/receptor/receptor.conf"
    volumes:
      - "./conf/node03.yml:/etc/receptor/receptor.conf"

利用するイメージ(${RECEPTOR_IMAGE})には、次の .env ファイルを用意して今回は v1.3.0 を指定しています(追記: エントリ公開時はその時点で最新の v1.3.0.dev2 としていましたが、数日後に v1.3.0 がリリースされたので差し替えました。エントリで紹介した内容の動作は確認済みです)。

RECEPTOR_IMAGE="quay.io/ansible/receptor:v1.3.0"

実際の動き

コンテナを起動させて、動きを観察します。

docker compose up -d

メッシュネットワークの状態

起動後のログからは、バックエンド接続の初期化やノード間での経路交換、ノードの検出の様子が確認できます。node01controller01)は node03executor01)との直接のバックエンド接続を持ちませんが、node02relayer01)経由で node03executor01)の情報も把握できています。

$ docker compose logs -f node01
...
node01  | DEBUG 2022/11/25 19:51:38 Running TCP peer connection node02.example.internal:27199
node01  | WARNING 2022/11/25 19:51:38 Backend connection failed (will retry): dial tcp 172.18.0.4:27199: connect: connection refused
node01  | INFO 2022/11/25 19:51:38 Running control service control
node01  | INFO 2022/11/25 19:51:38 Initialization complete
node01  | DEBUG 2022/11/25 19:51:43 Sending initial connection message
node01  | INFO 2022/11/25 19:51:43 Connection established with relayer01
node01  | DEBUG 2022/11/25 19:51:43 Stopping initial updates
node01  | DEBUG 2022/11/25 19:51:43 Re-calculating routing table
node01  | DEBUG 2022/11/25 19:51:43 Sending routing update INHzPHRD. Connections: relayer01(1.00)
...
node01  | DEBUG 2022/11/25 19:51:43 Received routing update iArMKNrx from relayer01 via relayer01
node01  | DEBUG 2022/11/25 19:51:43 Received routing update f650xBgb from executor01 via relayer01
...
node01  | INFO 2022/11/25 19:51:43 Known Connections:
node01  | INFO 2022/11/25 19:51:43    executor01: relayer01(1.00) 
node01  | INFO 2022/11/25 19:51:43    controller01: relayer01(1.00) 
node01  | INFO 2022/11/25 19:51:43    relayer01: executor01(1.00) controller01(1.00) 
node01  | INFO 2022/11/25 19:51:43 Routing Table:
node01  | INFO 2022/11/25 19:51:43    relayer01 via relayer01
node01  | INFO 2022/11/25 19:51:43    executor01 via relayer01
...

ところで、node01 の Receptor では、Control Service をソケットファイルのパス(/tmp/receptor.sock)とともに起動させていました。このため、このノードではこのソケットファイルを介して receptorctl による Receptor の操作を行えます。

receptorctl で Receptor を操作する際は、ソケットファイルのパスを receptorctl の実行時の --socket か環境変数 RECEPTORCTL_SOCKET で指定します。今回利用しているコンテナイメージでは、デフォルトで環境変数 RECEPTORCTL_SOCKET/tmp/receptor.sock が設定されており、実際にこのパスを使うように Control Service に構成したため、--socket による明示は不要です。

以降、わかりやすいようにコンテナのシェルから操作します(docker compose exec でも同等の操作はもちろん可能です)。

$ docker compose exec -it node01 bash
[root@node01 /]# receptorctl version
receptorctl  1.3.0
receptor     1.3.0

例えば、status サブコマンドでは、メッシュネットワークの全体の情報を確認できます。以下、出力例にコメントを加えたものです。

[root@node01 /]# receptorctl status
## 自身のノード情報
Node ID: controller01
Version: 1.3.0
System CPU Count: 4
System Memory MiB: 7762

## 自身がバックエンドで接続しているノードとその経路のコスト
Connection   Cost
relayer01    1

## 自身が把握できているメッシュネットワーク内の全ノードと、各ノードのバックエンドでの接続ノードおよびそのコスト
Known Node   Known Connections
controller01 relayer01: 1 
executor01   relayer01: 1 
relayer01    controller01: 1 executor01: 1 

## 自身以外の他ノードへの経路
### executor01 へも relayer01 経由で到達できることを学習できている
Route        Via
executor01   relayer01
relayer01    relayer01

## メッシュネットワーク内で利用できるサービスの情報
### controller01 と executor01 で Control Service が起動している
Node         Service   Type       Last Seen             Tags
controller01 control   Stream     2022-11-25 19:54:13   {'type': 'Control Service'}
executor01   control   Stream     2022-11-25 19:53:43   {'type': 'Control Service'}

## メッシュネットワーク内で実行できる処理(ワークタイプ)の名称とノード
### controller01 と executor01 で gather-hostname を実行できる
Node         Work Types
controller01 gather-hostname
executor01   gather-hostname

ping サブコマンドでは、メッシュネットワーク上の任意のノードへの到達性を確認できます。executor01 とも正常に疎通できることがわかります。

[root@node01 /]# receptorctl ping relayer01
Reply from relayer01 in 317.614µs
Reply from relayer01 in 352.845µs
Reply from relayer01 in 315.205µs
Reply from relayer01 in 254.051µs

[root@node01 /]# receptorctl ping executor01
Reply from executor01 in 451.259µs
Reply from executor01 in 539.444µs
Reply from executor01 in 587.212µs
Reply from executor01 in 588.532µs

traceroute サブコマンドでは、メッシュネットワーク上の任意のノードへ到達する経路を確認できます。executor01 には relayer01 を経由して到達していることがわかります。

[root@node01 /]# receptorctl traceroute executor01
0: controller01 in 73.859µs
1: relayer01 in 429.427µs
2: executor01 in 694.976µs

コマンドの実行と結果の確認

実際に Controller から Executor に対して処理の実行を指示します。--node で対象ノードを指定し、標準入力を与えずに実行するために --no-payload-n)を付与して、実行する処理名(ワークタイプ名、ここでは gather-hotname)と共に work submit サブコマンドを実行します。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --no-payload \
  gather-hostname
Result: Job Started
Unit ID: xfaqU3sC

Receptor の世界は、このようなリモートノードでの処理を ワークWork)またはワークユニット(Work Unit)と呼んでいるようです。上記コマンドは、Receptor 目線では ワークの送信 です。これにより、リモートノードにワークが送信されます。これらワークは、内部では Workceptor と呼ばれるコンポーネントによってハンドリングされます。

デフォルトではワークは非同期的に動作するため、実行結果は実行ごとかつノードごとに付与される一意の Unit ID を使って取得する必要があります。

実行されたワークの一覧は、work list サブコマンドで取得できます。

[root@node01 /]# receptorctl work list
{
    "xfaqU3sC": {
        "Detail": "exit status 0",
        "ExtraData": {
            "Expiration": "0001-01-01T00:00:00Z",
            "LocalCancelled": false,
            "LocalReleased": false,
            "RemoteNode": "executor01",
            "RemoteParams": {},
            "RemoteStarted": true,
            "RemoteUnitID": "wDNTsHQO",
            "RemoteWorkType": "gather-hostname",
            "SignWork": false,
            "TLSClient": ""
        },
        "State": 2,
        "StateName": "Succeeded",
        "StdoutSize": 24,
        "WorkType": "remote"
    }
}

結果を取得するには、work results サブコマンドに Unit ID を渡します。

[root@node01 /]# receptorctl work results xfaqU3sC
node03.example.internal

ノード executor01 のホスト名、node03.example.internal が返ってきました。コマンドは controller01 から命令しましたが、実際の処理は executor01 で行われたことがわかります。

ワークの結果は消すまで残り続けるため、確認できたらリリース(削除)します。

[root@node01 /]# receptorctl work release xfaqU3sC
Released:
(xfaqU3sC, released)

[root@node01 /]# receptorctl work list
{}

ここまでが、コマンドの実行から結果を確認するまでの一連の流れです。

なお、結果を保持しなくてよい(結果を後から確認しない)場合は、work submit 時に --rm を付与すると、完了後に自動でワークのリリースが行われるよになります。また、--follow-f)を付与すると同期的な動作になり、ワークの実行結果が標準出力に返ってきます。気軽に実行するなら、両方を付与してしまえば、ワークも残らず結果もすぐに確認できて便利です。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --no-payload \
  gather-hostname
node03.example.internal
(hwhVXaJo, released)

デモ (2): コマンドへの引数と標準入力の連携

前述のデモでは、リモートノードで実行されるコマンドは cat /etc/hostname で固定でした。一方で、Receptor には次の機能が備わっています。

  • リモートコマンドの引数を実行時に追加する
  • リモートコマンドの標準入力に任意のデータを送る

ここでは、この二つの使い方と結果を確認します。

設定ファイルの作成

前述のデモのうち、node03executor01)の設定ファイルの work-command を次のように変更します。GitHub にも実際のファイル一式を配置 しています。

...
- work-command:
    worktype: cat-files
    command: grep
    params: "-n -H ''"
    allowruntimeparams: true

- work-command:
    worktype: echo-reply
    command: bash
    params: "-c 'while read -r line; do echo Reply from $(cat /etc/hostname): ${line^^}; done'"

ひとつめの work-commandcat-files では、allowruntimeparamstrue にしています。これにより、実行時にコマンドの引数を 追加 できるようになります。ここでは、ベースになるコマンドを grep -n -H '' としているので、つまり、実行時には grep するファイル名を与えられるということです。

ふたつめの echo-reply では、bash の中の read で標準入力を読み取り、すべてを大文字(^^)にして echo し返すようにしています。

実際の動き

コンテナを起動して、動きを確認します。以降は、先ほどと同様にコンテナのシェルで操作します。

$ docker compose up -d
$ docker compose exec -it node01 bash
[root@node01 /]#

実行時の引数の追加

work submit--param-a)で params=<追加したい引数> を指定すると、実行されるワークのコマンドに 追加の引数 を与えられます。次の例では、/etc/hostname /etc/os-release を引数として追加しました。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --no-payload \
  --param "params=/etc/hostname /etc/os-release" \
  cat-files
/etc/hostname:1:node03.example.internal
/etc/os-release:1:NAME="CentOS Stream"
/etc/os-release:2:VERSION="9"
/etc/os-release:3:ID="centos"
/etc/os-release:4:ID_LIKE="rhel fedora"
/etc/os-release:5:VERSION_ID="9"
/etc/os-release:6:PLATFORM_ID="platform:el9"
/etc/os-release:7:PRETTY_NAME="CentOS Stream 9"
/etc/os-release:8:ANSI_COLOR="0;31"
/etc/os-release:9:LOGO="fedora-logo-icon"
/etc/os-release:10:CPE_NAME="cpe:/o:centos:centos:9"
/etc/os-release:11:HOME_URL="https://centos.org/"
/etc/os-release:12:BUG_REPORT_URL="https://bugzilla.redhat.com/"
/etc/os-release:13:REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 9"
/etc/os-release:14:REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream"
(PPLqDD0a, released)

これにより、executor01 側では次のコマンドが実行されています。上記結果からも、引数で指定したファイルが grep されていることが判断できます。

grep -n -H '' /etc/hostname /etc/os-release

簡略化した方法として、追加したい引数はワークタイプ名の後にベタ書きも可能です(文字列によっては receptorctl に対するオプションと混同されてエラーになるため、-- に続けて表記するのがおすすめです)。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --no-payload \
  cat-files -- /etc/hostname /etc/os-release
/etc/hostname:1:node03.example.internal
/etc/os-release:1:NAME="CentOS Stream"
...
(WzjhPLJ0, released)

実行時の標準入力の利用

引数ではなく 標準入力 を与えることも可能です。work submit--payload-p)または --payload-literal-l)でファイルや標準入力、文字列を与えると、実行されるワークのコマンドに標準入力として渡されます。

任意のファイルの中身を渡すには、--payload-p)でファイル名を渡します。

[root@node01 /]# echo "Hello Receptor!" > /tmp/input.txt
[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --payload /tmp/input.txt \
  echo-reply
Reply from node03.example.internal: HELLO RECEPTOR!
(vfhNJ5UQ, released)

これにより、executor01 側では、次のようなコマンドに相当する処理が実行されています(実際に echo で入力が渡されているわけではないので、あくまで疑似的な表現です)。

echo "Hello Receptor!" | bash -c 'while read -r line; do echo Reply from $(cat /etc/hostname): ${line^^}; done'

receptorctl への標準入力をそのままペイロードとして渡すことも可能です。この場合、--payload- を指定します。

[root@node01 /]# echo "Hello Receptor!" | receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --payload - \
  echo-reply
Reply from node03.example.internal: HELLO RECEPTOR!
(NX9JQHC9, released)

なお、この場合、リモートノードでのワークの開始receptorctl への 標準入力へのデータストリームが閉じてから である点に注意が必要です。例えば次のように 3 秒かけて 3 行の echo を行って receptorctl に渡した場合、リモートノードでのワークは、3 秒後に 3 行のテキストがまとめてペイロードとして送られてから開始 されます。

for i in $(seq 1 3); do
  echo "Hello Receptor!"
  sleep 1
done | receptorctl ...

つまり、標準出力のストリームを開きっぱなし にして データを逐次送信するリモート側で逐次処理させる) ようなものは、Receptor は向きません

任意の文字列をベタ打ちで渡すには、--payload-literal-l)を利用します。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --rm \
  --follow \
  --payload-literal "Hello Receptor!" \
  echo-reply
Reply from node03.example.internal: HELLO RECEPTOR!
(Xs3Q5LDU, released)

デモ (3): ネットワーク障害やノード障害への耐性

ここまでで、Receptor により、リモートノードで任意のコマンドを任意のパラメータと任意のペイロードと共に実行できることを確認しました。ここでは、そうしたリモートノードでのコマンド実行がより確実に実行される仕組みとして、Receptor の耐障害性を実際に確認します。

前提: データの授受用の一時ファイル

Receptor は、技術的には、リモートノードのコマンドに 標準入力を送信 し、実行結果としてリモートノードからコマンドの 標準出力を受信 しているといえます。

ここで、標準入力と標準出力確実にノード間で授受できるようにする ため、あるワークをリモートノードで実行するときには Controller のノードと Executor のノードの 両方 で、標準出力と標準入力は次の一時ファイルとして保存されます。パスはデフォルトでは /tmp/receptor/<ノード ID>/<ユニット ID>/ 配下です。

  • stdin
    • Controller 側で入力されたペイロードのコピー
    • Executor 側にも送信され保存される
  • stdout
    • Executor 側のワークの実行結果(標準出力)のコピー
    • Controller 側にも送信され保存される
  • status
    • ワークの実行時のパラメータや状態などを記録したファイル
    • Controller 側と Executor 側のそれぞれで、自ノードでのワークの状態が保存される

ワークが完了してリリースされると、これらの一時ファイル群は削除されます。

これらの授受と保存のタイミングは、ドキュメントの図で紹介されています。引用します。

Workceptor

ユーザから work submit に与えられたペイロードは、まず Controller 側に stdin として保存されます。その後、ワークの実行命令とともにその中身が Executor 側へも送られ、Executor 側でも stdin として保存されます。ワークのコマンドが実際に Executor 側で実行されるのは、この後です。

Executor 側では、ワークのコマンドは Receptor のサブプロセスとして実行されます。このサブプロセスの標準入力には Executor 側の stdin が渡され、標準出力と標準エラー出力には同じく Executor 側の stdout が割り当てられます(技術的には Go 言語の os/execCmd.Std*std* ファイル群がそのまま渡されています)。また、ワークの実行状況は status として保存され、完了まで定期的に更新されていきます。

Controller 側は、ワークが完了するまで定期的に Executor 側に status を問い合わせ、リモートの stdout に更新があれば(報告された stdout のサイズが手元の stdout のサイズより大きければ)差分を受け取って Controller 側の statusstdout を更新します。

このように、標準入出力を一時的にファイルとして永続化することで、長時間のワークの実行中に経路や Receptor それ自体に障害が発生しても、復旧後にデータの授受を欠損なく再開できるように工夫されています。

設定ファイルの作成

経路を冗長化するため、今回は次の 4 ノードで構成します。完全な構成ファイル群は GitHub に置いています。

controller01 から executor01 への経路を 2 つにするため、Controller と Executor では tcp-peer を次のように 2 つ並べています。また、relayer01 を優先して利用させるため、経路のコストを relayer011.0relayer022.0 としました。

---
- node:
    id: controller01

...

- tcp-peer:
    address: node02.example.internal:7323
    cost: 1.0

- tcp-peer:
    address: node03.example.internal:7323
    cost: 2.0

...

relayer0* 側でも、次のように tcp-listener でコストを指定します。両端(*-peer*-listener)でコストの設定が一致していなければならない点には注意が必要です。

---
- node:
    id: relayer01

...

- tcp-listener:
    port: 7323
    cost: 1.0
---
- node:
    id: relayer02

...

- tcp-listener:
    port: 7323
    cost: 2.0

Executor の node04executor01)では、次のワークタイプを構成しました。

...
- work-command:
    worktype: echo-reply
    command: bash
    params: "-c 'while read -r line; do echo Reply from $(cat /etc/hostname): ${line^^}; done'"

- work-command:
    worktype: echo-sleep
    command: bash
    params: "-c 'for i in $(seq 1 12); do echo ${i}: $(date +%T); sleep 5; done'"

echo-reply は以前のデモで利用したものそのままで、echo-sleep は 5 秒の sleep を 12 回行うワークです。

実際の動き

コンテナを起動して、動きを確認します。

$ docker compose up -d

メッシュネットワークの状態からは、コストが認識されていることと、executor01 への経路が relayer01 になっていることが確認できます。

[root@node01 /]# receptorctl status
...
Connection   Cost
relayer01    1
relayer02    2

...
Route        Via
executor01   relayer01
relayer01    relayer01
relayer02    relayer02

[root@node01 /]# receptorctl traceroute executor01 
0: controller01 in 70.855µs
1: relayer01 in 344.99µs
2: executor01 in 330.43µs

正常ケース

ワーク echo-reply を実行して、完了を確認します。

[root@node01 /]# echo "Hello Receptor!" | receptorctl work submit \
  --node executor01 \
  --payload - \
  echo-reply
Result: Job Started
Unit ID: dcyJmLNy

[root@node01 /]# receptorctl work list --unit_id dcyJmLNy
{
    "dcyJmLNy": {
        "Detail": "exit status 0",
        "ExtraData": {
            "Expiration": "0001-01-01T00:00:00Z",
            "LocalCancelled": false,
            "LocalReleased": false,
            "RemoteNode": "executor01",
            "RemoteParams": {},
            "RemoteStarted": true,
            "RemoteUnitID": "gfxFWsHC",
            "RemoteWorkType": "echo-reply",
            "SignWork": false,
            "TLSClient": ""
        },
        "State": 2,
        "StateName": "Succeeded",
        "StdoutSize": 52,
        "WorkType": "remote"
    }
}

node01node04 で、一時ファイルの様子を確認します(以下、status は整形しています)。node04 側での Unit ID は、node01status に含まれる RemoteUnitID で確認できます。

[root@node01 /]# cat /tmp/receptor/controller01/dcyJmLNy/status
{
  "State": 2,
  "Detail": "exit status 0",
  "StdoutSize": 52,
  "WorkType": "remote",
  "ExtraData": {
    "RemoteNode": "executor01",
    "RemoteWorkType": "echo-reply",
    "RemoteParams": {},
    "RemoteUnitID": "gfxFWsHC",
    "RemoteStarted": true,
    "LocalCancelled": false,
    "LocalReleased": false,
    "SignWork": false,
    "TLSClient": "",
    "Expiration": "0001-01-01T00:00:00Z"
  }
}

[root@node01 /]# cat /tmp/receptor/controller01/dcyJmLNy/stdin
Hello Receptor!

[root@node01 /]# cat /tmp/receptor/controller01/dcyJmLNy/stdout
Reply from node04.example.internal: HELLO RECEPTOR!
[root@node04 /]# cat /tmp/receptor/executor01/gfxFWsHC/status
{
  "State": 2,
  "Detail": "exit status 0",
  "StdoutSize": 52,
  "WorkType": "echo-reply",
  "ExtraData": null
}

[root@node04 /]# cat /tmp/receptor/executor01/gfxFWsHC/stdin
Hello Receptor!

[root@node04 /]# cat /tmp/receptor/executor01/gfxFWsHC/stdout
Reply from node04.example.internal: HELLO RECEPTOR!

ワークの状況や授受された標準入出力が、両ノードで一時ファイルとして保存されていることが確認できました。ワークをリリースすると、自ノードとリモートノードの両方でこれらのファイルが削除されます。

[root@node01 /]# receptorctl work release --all
Released:
(dcyJmLNy, released)

[root@node01 /]# ls -l /tmp/receptor/controller01/
total 0

なお、何らかの理由でリモートノードと疎通できない状態でリリースしたい場合は、work release--force オプションを付与すると、自ノードのみの削除が行われます。

冗長化された経路の障害

現在の構成では、controller01executor01relayer01 経由で疎通しています。ここで、実行に 1 分かかるワーク echo-sleep の実行中に、経路の relayer01 を停止させて、ワークの動きを確認します。

work submit を実行してから、ワークが完了する前に relayer01 である node02 を停止させます。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --no-payload \
  echo-sleep
Result: Job Started
Unit ID: uocGYk24
$ docker compose stop node02
[+] Running 1/1
 ⠿ Container node02  Stopped  0.1s

node01 のログを確認すると、node02 への接続が失われ、経路の再計算が走ったことが確認できます。

$ docker compose logs -f node01
...
node01  | WARNING 2022/11/25 21:37:38 Backend connection exited (will retry)
...
node01  | DEBUG 2022/11/25 21:37:38 Re-calculating routing table
node01  | INFO 2022/11/25 21:37:38 Known Connections:
node01  | INFO 2022/11/25 21:37:38    controller01: relayer02(2.00) 
node01  | INFO 2022/11/25 21:37:38    relayer02: controller01(2.00) executor01(2.00) 
node01  | INFO 2022/11/25 21:37:38    executor01: relayer02(2.00) 
node01  | INFO 2022/11/25 21:37:38    relayer01: 
node01  | INFO 2022/11/25 21:37:38 Routing Table:
node01  | INFO 2022/11/25 21:37:38    relayer02 via relayer02
node01  | INFO 2022/11/25 21:37:38    executor01 via relayer02
...
node01  | WARNING 2022/11/25 21:37:43 Backend connection failed (will retry): dial tcp: lookup node02.example.internal on 127.0.0.11:53: no such host
...
node01  | WARNING 2022/11/25 21:37:51 Backend connection failed (will retry): dial tcp: lookup node02.example.internal on 127.0.0.11:53: no such host
...

メッシュネットワークの状態からは、executor01 への経路が relayer02 経由に切り替わったことが確認できます。

[root@node01 /]# receptorctl status
...
Connection   Cost
relayer02    2

Known Node   Known Connections
controller01 relayer02: 2 
executor01   relayer02: 2 
relayer01    
relayer02    controller01: 2 executor01: 2 

Route        Via
executor01   relayer02
relayer02    relayer02
...

[root@node01 /]# receptorctl traceroute executor01
0: controller01 in 93.198µs
1: relayer02 in 308.564µs
2: executor01 in 336.695µs

この状況でも、ワークは正常に完了しており、結果も欠損なく取得できます。

[root@node01 /]# receptorctl work list --unit_id uocGYk24
{
    "uocGYk24": {
        ...
        "StateName": "Succeeded",
        ...
    }
}

[root@node01 /]# receptorctl work results uocGYk24
1: 21:37:32
2: 21:37:37
...
11: 21:38:23
12: 21:38:28

停止させていた node02 を起動させると経路の再計算が走り、controller01 から executor01 への経路が(コスト値に従って)元の relayer01 に戻ったことがわかります。

$ docker compose start node02
[+] Running 1/1
 ⠿ Container node02  Started  0.3s
[root@node01 /]# receptorctl status
...
Connection   Cost
relayer02    2
relayer01    1

Known Node   Known Connections
controller01 relayer01: 1 relayer02: 2 
executor01   relayer01: 1 relayer02: 2 
relayer01    controller01: 1 executor01: 1 
relayer02    controller01: 2 executor01: 2 

Route        Via
executor01   relayer01
relayer01    relayer01
relayer02    relayer02
...

[root@node01 /]# receptorctl traceroute executor01
0: controller01 in 58.766µs
1: relayer01 in 305.701µs
2: executor01 in 349.324µs

経路が冗長化されている場合 は、経路の障害時 には 別の経路を使ってデータの授受が正常に続行される ことが確認できました。

ワークはリリースしておきます。

[root@node01 /]# receptorctl work release --all
Released:
(uocGYk24, released)

経路の全断

続いて、経路が全断 した場合の動きを確認します。

ワーク echo-sleep の実行中に、経路の relayer01relayer02node02node03)を両方を停止させます。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --no-payload \
  echo-sleep
Result: Job Started
Unit ID: GD8cOc0c
$ docker compose stop node02
[+] Running 1/1
 ⠿ Container node02  Stopped  0.1s

$ docker compose stop node03
[+] Running 1/1
 ⠿ Container node03  Stopped  0.1s

controller01Known Connections がなくなり、経路の表も出力されなくなりました。controller01 から executor01 への経路はすべて失われています。

[root@node01 /]# receptorctl status
...
Known Node   Known Connections
controller01 
executor01   relayer02: 2 
relayer01    
relayer02    executor01: 2 
...

ここで、controller01executor01 で、ワークの状況と一時ファイルの状況を確認します。

controller01 では、実行中に executor01 への経路が全断したため、ワークのステータスが Running のままで、stdout の中身も途中で止まっています。

[root@node01 /]# receptorctl work list --unit_id GD8cOc0c
{
    "GD8cOc0c": {
        "Detail": "Running: PID 83",
        "ExtraData": {
            ...
            "RemoteUnitID": "2Uf3Cbjj",
            ...
        },
        "State": 1,
        "StateName": "Running",
        "StdoutSize": 24,
        ...
    }
}

[root@node01 /]# cat /tmp/receptor/controller01/GD8cOc0c/status
{
  "State": 1,
  "Detail":"Running: PID 83",
  "StdoutSize":24,
  ...
  "ExtraData": {
    ...
    "RemoteUnitID": "2Uf3Cbjj",
    ...
  }
}

[root@node01 /]# cat /tmp/receptor/controller01/GD8cOc0c/stdout 
1: 11:31:18
2: 11:31:23

一方で、executor01 側では、すでに処理が完了しているようです。

[root@node04 /]# cat /tmp/receptor/executor01/2Uf3Cbjj/status
{
  "State": 2,
  "Detail": "exit status 0",
  "StdoutSize": 147,
  ...
}

[root@node04 /]# cat /tmp/receptor/executor01/2Uf3Cbjj/stdout 
1: 11:31:18
2: 11:31:23
...
11: 11:32:09
12: 11:32:14

ワークの実処理は Executor 側に閉じて行われる ため、その成否に Controller との接続性はまったく関係がありません。したがって、処理中に Controller と Executor の間の経路が全断しようとも、Executor はそのまま淡々と処理を続行し、ワークを正常に完了できます。

ここで、経路の node02 を復旧させます。

$ docker compose start node02
[+] Running 1/1
 ⠿ Container node02  Started  0.3s

Controller から Executor への経路が再計算により復活しました。ワークも Running から Succeeded になり、結果も正常に欠損なく Controller 側で取得できます。

[root@node01 /]# receptorctl status
...
Connection   Cost
relayer01    1

Known Node   Known Connections
controller01 relayer01: 1 
executor01   relayer01: 1 
relayer01    controller01: 1 executor01: 1 
relayer02    

Route        Via
executor01   relayer01
relayer01    relayer01
...

[root@node01 /]# receptorctl work list --unit_id GD8cOc0c
{
    "GD8cOc0c": {
        "Detail": "exit status 0",
        ...
        "StateName": "Succeeded",
        "StdoutSize": 147,
        ...
    }
}

[root@node01 /]# receptorctl work results GD8cOc0c
1: 11:31:18
2: 11:31:23
...
11: 11:32:09
12: 11:32:14

ワークがいちど送信できてしまえば、Controller と疎通できなくても Executor のその後の実処理には影響しない こと、Executor 側で一時ファイルに標準出力が保存されている ことで Controller 側は後からでも結果を欠損なく受領できる ことが確認できました。

node03 も復旧させれば、完全に元通りです。ワークもリリースしておきます。

$ docker compose start node03
[+] Running 1/1
 ⠿ Container node03  Started  0.3s
[root@node01 /]# receptorctl work release --all
Released:
(GD8cOc0c, released)

Controller ノードの障害

続いて、Controller ノードで障害 が発生したパタンを確認します。ワーク echo-sleep の実行中に、今度は contoller01node01)を停止させます。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --no-payload \
  echo-sleep
Result: Job Started
Unit ID: zna1urHX
$ docker compose stop node01
[+] Running 1/1
 ⠿ Container node01  Stopped  0.1s

Executor である node04 で一時ファイルの様子を確認すると、ワークは正常に完了したようです。先ほどの経路の全断の例と同様、Controller と疎通できなくても Executor の実処理には影響しない ことがわかります。

[root@node04 /]# cat /tmp/receptor/executor01/nJO9PdbS/status
{
  "State": 2,
  "Detail": "exit status 0",
  "StdoutSize": 147,
  ...
}

ここで、Controller である node01 を再開させます。

$ docker compose start node01
[+] Running 1/1
 ⠿ Container node02  Started  0.3s

Controller からジョブの結果を確認できました。正常に完了したことが認識できており、標準出力も全量が取得できているようです。

[root@node01 /]# receptorctl work list
{
    "zna1urHX": {
        "Detail": "exit status 0",
        "ExtraData": {
            ...
            "RemoteUnitID": "nJO9PdbS",
            ...
        },
        "State": 2,
        "StateName": "Succeeded",
        "StdoutSize": 147,
        ...
    }
}

ワークの状態や経過が Controller 側でも一時ファイルとして永続化される ことで、Controller の Receptor が再起動 しても ワークを見失うことなく復旧される ことが確認できました。

ワークはリリースしておきます。

[root@node01 /]# receptorctl work release --all
Released:
(zna1urHX, released)

Executor ノードの障害

最後に、Executor ノードで障害が発生したパタンを確認します。ワーク echo-sleep の実行中に、executor01node04)を停止させます。

[root@node01 /]# receptorctl work submit \
  --node executor01 \
  --no-payload \
  echo-sleep
Result: Job Started
Unit ID: 5lIdpCah
$ docker compose stop node04
[+] Running 1/1
 ⠿ Container node01  Stopped  0.1s

Controller 側では、ワークはまだ Running のままです。標準出力も途中までしか取得できていません。

[root@node01 /]# receptorctl work list
{
    "5lIdpCah": {
        "Detail": "Running: PID 182",
        "ExtraData": {
            ...
            "RemoteUnitID": "62ESTRMU",
            ...
        },
        "State": 1,
        "StateName": "Running",
        "StdoutSize": 96,
        ...
    }
}

node04 を再開させても、状態は変わりません。node04 側の status ファイルも Running のままです。

$ docker compose start node04
[+] Running 1/1
 ⠿ Container node04  Started  0.3s
[root@node01 /]# receptorctl work list
{
    "5lIdpCah": {
        "Detail": "Running: PID 182",
        "ExtraData": {
            ...
            "RemoteUnitID": "62ESTRMU",
            ...
        },
        "State": 1,
        "StateName": "Running",
        "StdoutSize": 96,
        ...
    }
}
[root@node04 /]# cat /tmp/receptor/executor01/62ESTRMU/status
{
  "State": 1,
  "Detail": "Running: PID 182",
  ...
}

今回は、node04 をコンテナごと停止させたので、ワークの実処理として起動された bash も止まっており、PID 182 はもう存在しません。Receptor だけが停止 したのであれば状況の継続的な追跡ができるようですが、ワークの実処理ごと停止 した場合は、追跡できなくなるようです。

とはいえ、ワークのコマンドの冪等性は当然ながら Receptor 側からは知りえない ので、むやみに再実行されるよりは、何もしないで止まったままの方が安全 ではあります。

ワークをリリースします。

[root@node01 /]# receptorctl work release --all
Released:
(5lIdpCah, released)

個人的には、このパタンではワーク自体が Failed で落ちたほうが素直な気もしていますが、ソースコードをみても、少なくとも現時点の実装だとこれが仕様といえそうです。気になるので Issue を作って PR も送っています。

デモ (4): 複雑なトポロジの例

これまでのデモの構成は非常にシンプルでしたが、*-listerner*-peer複数回かつ両方 記述できるため、1:NN:1N:N の関係も自由に定義できます。次のような複雑なトポロジでも、必要なことはあくまで隣接するノードの *-listerner*-peer の定義だけです。実際のファイル群は GitHub に配置 しています。

経路情報の交換が完了すれば、全ノードが他の全ノードと双方向に疎通できるようになります。

[root@wc01 /]# receptorctl status
Node ID: west-controller01
Version: 1.3.0
System CPU Count: 4
System Memory MiB: 7762

Connection        Cost
west-relayer03    1

Known Node        Known Connections
controller01      east-relayer01: 1 east-relayer02: 1 west-relayer01: 1 west-relayer02: 1 
east-executor01   east-relayer01: 1 east-relayer02: 1 
east-relayer01    controller01: 1 east-executor01: 1 
east-relayer02    controller01: 1 east-executor01: 1 
west-controller01 west-relayer03: 1 
west-executor01   west-relayer03: 1 
west-executor02   west-relayer01: 1 west-relayer02: 1 
west-relayer01    controller01: 1 west-executor02: 1 west-relayer03: 1 
west-relayer02    controller01: 1 west-executor02: 1 
west-relayer03    west-controller01: 1 west-executor01: 1 west-relayer01: 1 

Route             Via
controller01      west-relayer03
east-executor01   west-relayer03
east-relayer01    west-relayer03
east-relayer02    west-relayer03
west-executor01   west-relayer03
west-executor02   west-relayer03
west-relayer01    west-relayer03
west-relayer02    west-relayer03
west-relayer03    west-relayer03

Node              Service   Type       Last Seen             Tags
west-controller01 control   Stream     2022-11-26 20:12:30   {'type': 'Control Service'}
east-executor01   control   Stream     2022-11-26 20:11:35   {'type': 'Control Service'}
west-executor01   control   Stream     2022-11-26 20:11:36   {'type': 'Control Service'}
west-executor02   control   Stream     2022-11-26 20:11:36   {'type': 'Control Service'}
controller01      control   Stream     2022-11-26 20:11:36   {'type': 'Control Service'}

Node              Work Types
east-executor01   gather-hostname
west-executor01   gather-hostname
west-executor02   gather-hostname

左上の Controller wc01 から右端の Executor ee01 への疎通を確認し、ワークを送っている例です。

[root@wc01 /]# receptorctl ping east-executor01
Reply from east-executor01 in 1.414839ms
Reply from east-executor01 in 1.977049ms
Reply from east-executor01 in 1.366458ms
Reply from east-executor01 in 1.9261ms

[root@wc01 /]# receptorctl traceroute east-executor01
0: west-controller01 in 96.69µs
1: west-relayer03 in 317.221µs
2: west-relayer01 in 742.736µs
3: controller01 in 839.477µs
4: east-relayer02 in 1.204882ms
5: east-executor01 in 806.648µs
[root@wc01 /]# receptorctl work submit \
  --node east-executor01 \
  --rm \
  --follow \
  --no-payload \
  gather-hostname
ee01.example.internal
(7W9RAP7A, released)

まとめ

Automation Mesh のコア要素である Receptor の基本的な使い方と動きを確認しました。

Receptor にはこれ以外にも Kubernetes クラスタへの Pod 作成暗号化や組み込みのファイアウォール など、さまざまな機能があります。後続のエントリで順次簡単に紹介しています。

Receptor 関連エントリ

@kurokobo

くろいです。ギターアンサンブルやら音響やらがフィールドの IT やさんなアルトギター弾き。たまこう 48 期ぎたさん、SFC '07 おんぞう、新日本ギターアンサンブル、Rubinetto。今は野良気味。

コメントを残す

メールアドレスが公開されることはありません。