【Rust製構成管理ツール】JetPorch とは【次世代Ansible？】

本記事は [KMC Advent Calendar 2023](https://adventar.org/calendars/8840) と [KLab Engineer Advent Calendar 2023](https://qiita.com/advent-calendar/2023/klab) の 19 日目の記事です.

## TL;DR

JetPorch とは...

- Ansible の開発者である Michael DeHaan さんによって開発された新しい構成管理ツール
- Rust で記述されていて速い
- Ansible Playbook と似た記法 (YAML)
- 単純明快で直感的な仕様 (を目指している)
- 最低限の機能は揃っているがまだまだ開発段階

**追記 2024/01/22**

2023年末から2024年初めにかけて開発体制/管理方針が変わったようで現在JetPorchのサイトやブログ, ソースコードページなどが不安定になっているようです ( ; ∀ ; ) (公開になったり非公開になったり) (詳しく分かり次第追記します.)

## 本記事の目的

最近 JetPorch という構成管理ツールがリリースされたのを知り, 個人的に使っている Ansible の移行先として興味を持ったので調べてみました.

https://x.com/polleninjp/status/1711442891883807138

> Jet
> Ansible のソースが Any 型ばかりで読みにくく泣きそうだった中見つけた
> ちょっと追っていきたい

## JetPorch とは

JetPorch とは **Ansible の開発者である [Michael DeHaan](https://github.com/mpdehaan)** さんによる新しい**構成管理ツール**です.

基本的なコンセプトは Ansible とよく似ていますが, 昨今の複雑しつつある自動化に対応するため Ansible では足りない部分・実現困難な部分を Michael DeHaan さん自らが見直し改善しています.

また, Ansible が Python で書かれているのに対し JetPorch はより強い型システムや強力な並列性をサポートする **Rust** で書かれています. より学習しやすくユーザーにシンプルに映ること[^jetporch_goal_simplicity][^save_user_time_to_do_other_things]も目的の一つとされています.

[^jetporch_first_blog]: [A New IT Automation Project? Moving Beyond Ansible And Keeping The Spirit - An Invitation.](https://laserllama.substack.com/p/a-new-it-automation-project-moving)
[^jetporch_goal_simplicity]:
    > I would like to see a move back towards minimalism, grocery-list style simplicity [^jetporch_first_blog]

[^save_user_time_to_do_other_things]:
    > The goal is to deploy or automate things and get you more time to do something else. [^jetporch_dev_blog_1]

2023 年 10 月 に v0.1.0 として初リリースされ, SourceHut にてソースの公開・管理が行われています[^development_on_sourcehut].

[^development_on_sourcehut]: [SourceHut - JetPorch Documentation](https://www.jetporch.com/community/sourcehut)

- <https://www.jetporch.com/>
- <https://git.sr.ht/~mpdehaan/jetporch>

![Image from Gyazo](https://i.gyazo.com/c3b8c62d6bc96bb32c08ae6f71789542.png)

## 基本コンセプト

基本的なイメージは Ansible と同じです. JetPorch を実行するコントロールマシンから操作対象のマネージドノードに対して, **SSH**[^more_strong_style_over_ssh]で並列に命令を送り, 数台から数千台以上の規模[^more_strong_style_over_ssh]のサーバーを一括で操作・構成管理することが目的です.

![Image from Gyazo](https://i.gyazo.com/5be5145a979c5a8ff7192bd4468c656d.png)

## Ansible と比較した特徴

同様の既存ツールである Ansible と比較すると以下のような特徴を持っています.

|                                                          | Ansible                                                 | **JetPorch**                                             |
| :------------------------------------------------------- | :------------------------------------------------------ | :------------------------------------------------------- |
| **開発言語**                                             | Python                                                  | **Rust**                                                 |
| **マネージドノード[^managed_node_control_node]への要件** | Python が必要                                           | **無し**                                                 |
| 実行速度                                                 | 遅い                                                    | **速い**                                                 |
| **単純明快さ**                                           | 悪い                                                    | **良** くなるように慎重に実装[^jetporch_goal_simplicity] |
| 命令の仕方                                               | Push 型 ( / Pull 型[^ansible_pull])                     | Push 型 ( / Pull 型(実装予定))                           |
| 命令の送信方法                                           | SSH                                                     | SSH ( + α[^more_strong_style_over_ssh] (実装予定) )      |
| Playbook の記述                                          | YAML                                                    | YAML                                                     |
| 標準モジュール                                           | 多い                                                    | 少ない (追加実装予定)                                    |
| 外部モジュールの言語                                     | 何でも[^module_language]                                | 何でも[^module_language][^jetporch_external_module]      |
| 3rd party モジュール                                     | 多数 ([Ansible Galaxy 等](https://galaxy.ansible.com/)) | 今後の広がりに期待                                       |

[^managed_node_control_node]: SSH 元のマシンをコントロールノード, SSH 先のマシンをマネージドノードと呼ぶ.
[^module_language]: 対応した JSON での入出力が可能な形態になっていればモジュール化可能. [^jetporch_dev_blog_1]
[^more_strong_style_over_ssh]:
    > Support local connectivity, SSH, and new message bus deployment architectures supporting potentially 50k-100k systems at once.
    > [A New IT Automation Project? Moving Beyond Ansible And Keeping The Spirit - An Invitation](https://laserllama.substack.com/p/a-new-it-automation-project-moving)

[^ansible_pull]: [ansible-pull — Ansible Documentation](https://docs.ansible.com/ansible/latest/cli/ansible-pull.html)

### 開発言語: Rust

まず個人的に一番うれしい点は **Rust** によって実装されていることです.

Rust は強力な型システムや所有権を持つため安全性の高い並列性・並行性のあるプログラミングを可能にしており, 実行速度の向上をもたらします.

最近の Python プロジェクトでは広く型ヒントが導入されつつありますが Ansible にはまだ無く, ソースコードを読む際には労力がかかります. その点においても Rust の型システムは開発者の理解を助けてくれます. 所有権による強力なコンパイラチェックも高い安全性を保証してくれます.

### マネージドノードへの要件

Ansible ではマネージドノードに Python を必要としますが JetPorch にはそのような要件はありません. そのため Python がインストールされていない環境であっても JetPorch であれば利用することができます.

> I’m excited about this part - Core modules should work by streaming shell commands and not require any Python on the remote machines or anything like that.
> (訳[^github_copilot_well_done]) この部分についてはとてもワクワクしています. Core モジュールはシェルコマンドをストリーミングすることで動作し, リモートマシンには Python などのインストールは必要ありません.
> ...
> This should be particularly nice because bash just keeps working and there is nothing to keep updating to deal with different python versions.
> I look forward to refining by bash skills.
> これは特に素晴らしいことで, bash はそのまま動作し, 異なる Python のバージョンに対応するために更新する必要があるものは何もありません.
> bash のスキルを磨くことができるのを楽しみにしています.

※ 個人的な解釈としては JetPorch のコア機能自体にはそこまで複雑な機能を乗せるためのものではなく, 複雑なことは外部モジュールに任せる前提で設計を行えているからこそ Bash を選択できているのではないかと考えます.

### 単純明快さ

今や Ansible は様々な機能を持っていますが, その分仕様が複雑になり学習コストが高くなりがちです.

その点, JetPorch は直感的な仕様・単純明快さを重視[^jetporch_goal_simplicity]しており, あるケースに置いて便利であっても非直感的になり得る機能の導入には慎重な態度をとっています[^jetporch_dev_blog_external_modules_preview].

> I would like to see a move back towards minimalism, grocery-list style simplicity, and looking at things through a more systems-programming / hard-engineering type lens.
> 私は最小限主義, 食料品リストのようなシンプルさ, よりシステムプログラミング / ハードエンジニアリングのようなレンズを通して物事を見ることを望んでいます.
> ...
> Simply put, things should be rock-solid, lightening fast, well-engineered, and readable and auditable by people who have never even seen the content before.
> 単純に言えば, 事柄は堅牢で, 高速で, よく設計されており, これまでにその内容を見たこともない人によって読みやすく監査可能であるべきです.

また, JetPorch をそれのみでで完結させるのではなく, 様々なツールと連携させる上ではこのシンプルさが不可欠です[^jetporch_dev_blog_1].

> It should be simple and easy to understand and be easy to call from other tools.
> Adapt the Unix philosophy basically.
> ...
> それはシンプルで理解しやすく, 他のツールから呼び出しやすいことが望ましいです.
> 基本的には Unix の哲学を採用しています.

## JetPorch の基本

ここからは簡単なサンプルを用いて JetPorch の使い方を紹介します. Ansible を使ったことがない人に対してもわかるように説明しますが, Ansible を使ったことがある人は殆ど変わらぬ使用感で使うことができると思います.

利用する際の基本要素は以下の２つから成り立っています.

- **Inventory** ... マネージドノード[^managed_node_control_node] (操作対象のサーバー) の情報を記述したファイル群
- **Playbook** ... 実行するタスクを記述したファイル群

このように処理と処理対象を分けて記述することで, 例えば Inventory だけを切り替えることで同じ処理を異なるサーバーに対して実行することができます.

**"Playbook"** の仕様は以下のようになっており, **"Play"** と呼ばれる単位のリストで与えられます.

```yaml
- name: Play name
  hosts:
    - some_host_or_host_group # マネージドノードのリスト
  tasks: # "Task" の配列
    - !echo
      name: Some task
      msg: "ready to go!"
- name: Play name 2
  hosts: ...
  tasks: [...]
```

**"Play"** はどのマネージドノードに対して, どのような処理を行うかを記述する単位です.

**"Play"** が持つ `tasks` には処理の単位である **"Task"** の配列が渡されています.

一つ一つの **"Task"** では 例えばファイルを作成する, パッケージをインストールする, ファイルの内容を変更するなどの抽象化された処理 (**"Module"**) を呼び出すことで記述できます.

この **Module** ごとの処理に **冪等性** をもたせることで構成管理ツールとしての真価を発揮しますが, 冪等性に関する説明は他の Ansible 関連の記事を読んでみてください.

## とりあえず実行してみる

JetPorch を触ってみたい人向けに本当に初歩的なハンズオンをしてみます.
マネージドノードに cowsay package をインストールし実行した cowsay をファイルに書き出すという処理を行います.

環境

- コントロールノード: Ubuntu 22.04
- マネージドノード: Ubuntu 22.04
  - `node1.example.com`
  - `node2.example.com`

### インストール

以下のうちのどちらの方法でもインストール可能です.

- [Installing From Packages](https://www.jetporch.com/basics/installing-from-packages)
  - `$ cargo install jetp`
  - <https://crates.io/crates/jetp>
- [Installing From Source](https://www.jetporch.com/basics/installing-from-source)
  - リンク先の説明では make していますが, 現状の実装はただ cargo build しかしていない[^jetporch_cargo_build]ので, そのまま cargo build しても問題ありません.

[^jetporch_cargo_build]: <https://git.sr.ht/~mpdehaan/jetporch/tree/main/item/Makefile#L4-5>

### 準備

- ssh-agent を起動しておき, マネージドノードの ssh 鍵を登録しておいてください (ssh-add).
- また, 今回の例ではマネージドノードにはパスワード無しで sudo できるユーザーを ssh 対象とする必要があります[^sudo_with_password_not_implemented].

以下のように inventory, playbook を配置します. (実際はディレクトリをここまで階層的に分けるルールは無いため, プロジェクトの規模に応じて適宜変更してください.)

```txt
zsh ❯ tree --charset ascii
.
|-- inventory
|   `-- dev
|       `-- groups
|           |-- some_host_group1
|           `-- some_host_group2
`-- playbooks
    `-- debug
        `-- debug.yml
```

`some_host_group1` (拡張子なし)

```yaml
hosts:
  - node1.example.com
```

`some_host_group2` (拡張子なし)

```yaml
hosts:
  - node2.example.com
```

`debug.yml`

```yaml
- name: Sample
  groups:
    - all # all は すべてのホストに対して実行する
    # - some_host_group2 # 個別にグループやホストも指定可能
  tasks:
    - !echo
      name: Echo
      msg: "ready to go!"
    - !apt
      package: cowsay
      with:
        sudo: root
    - !shell
      name: Cowsay hello world
      cmd: |-
        cowsay "hello world" > /tmp/cowsay_hello_world.txt
```

- `echo` module は msg を標準出力に出力するだけの module です.
- `apt` module は apt を使ってパッケージをインストールする module です.
- `shell` module は `cmd` に与えられたコマンドを実行する module です.

### 実行

```sh
jetp ssh \
  --playbook "./playbooks/debug/debug.yml" \
  --inventory "./inventory/dev" \
  --threads 2
```

ログ

```txt
----------------------------------------------------------
> playbook start: /home/.../jetporch/playbooks/debug/debug.yml
----------------------------------------------------------
> play: Sample
----------------------------------------------------------
> batch 1/1, 2 hosts
----------------------------------------------------------
> begin task: Echo
… node1.example.com => running
  ..... node1.example.com : ready to go!
… node2.example.com => running
  ..... node2.example.com : ready to go!
----------------------------------------------------------
> begin task: apt
… node1.example.com => running
… node2.example.com => running
✓ node1.example.com => matched
✓ node2.example.com => matched
----------------------------------------------------------
> begin task: Cowsay hello world
… node2.example.com => running
… node1.example.com => running
✓ node1.example.com => complete
✓ node2.example.com => complete
----------------------------------------------------------
> play complete: Sample
----------------------------------------------------------

┌─────────┬─────┬─────┐
│Results  │Items│Hosts│
├─────────┼─────┼─────┤
│Roles    │0    │     │
│Tasks    │3    │2    │
├─────────┼─────┼─────┤
│Matched  │2    │2    │
│Created  │0    │0    │
│Modified │0    │0    │
│Removed  │0    │0    │
│Executed │2    │2    │
│Passive  │2    │2    │
│Skipped  │0    │0    │
├─────────┼─────┼─────┤
│Unchanged│4    │0    │
│Changed  │2    │2    │
│Failed   │0    │0    │
└─────────┴─────┴─────┘

(✓) Actions were applied.
```

ここのマネージドノードにそれぞれ ssh して `cat /tmp/cowsay_hello_world.txt` すると以下のような結果になっているはずです.

```txt
 _____________
< hello world >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

### check mode

`jetp ssh` の箇所を `jetp check-ssh` に変えると Check Mode として実行されます. このモードでは実際には実行して変化を及ぼさず, どのような処理が行われるかを事前に確認することができます. Ansible の Check Mode と似たものだと思われます.

## 感想

今回は JetPorch の基本的な事項や Dev ブログなどで公開されている思想などをお伝えしてきました.

個人で軽く触ってみた感想ですが Ansible と比べると標準で使えるモジュールが非常に少なく, 移行しようにも不足感が拭えません. とりあえずは `!shell` 等でどうにか凌いで行く必要がありそうです.

一方で, 最近外部モジュールのサポート[^jetporch_dev_blog_external_modules_preview] が入ったため, JSON 入出力が可能であれば自由なモジュールを開発し利用することができるようです. 冪等性のあるモジュールの開発は大変そうですが容易に外部モジュールを取り込めることは JetPorch の可能性を広げてくれると思います.

今後の JetPorch の発展に期待し, 今後どのようなツールと連携した使い方をなされていくのかが楽しみです.

## おわりに

あと関係ないですが, Michael DeHaan さんはどうやら clippy のスタイルの一部を嫌っており独自のフォーマットで記述しているようです. 自分は Trailing Spaces などがあるとムズムズするのでソースコードを見ていると一気にフォーマットをかけたくなります.

## 参考

- <https://www.jetporch.com/>
- <https://git.sr.ht/~mpdehaan/jetporch>
- [JetPorch Dev Blog #1 - by laserllama](https://jetporch.substack.com/p/jetporch-dev-blog-1)
- <https://www.jetporch.com/modules/external-modules>
- <https://jetporch.substack.com/p/external-modules-preview>

[^jetporch_dev_blog_1]: [JetPorch Dev Blog #1 - by laserllama](https://jetporch.substack.com/p/jetporch-dev-blog-1)
[^jetporch_external_module]: <https://www.jetporch.com/modules/external-modules>
[^jetporch_dev_blog_external_modules_preview]:
    > Popular Questions: What About Role Dependencies?
    > ...
    > The answer is they intentionally cannot, because we want things to be very straightforward to read, audit and develop.
    > <https://jetporch.substack.com/p/external-modules-preview>

[^github_copilot_well_done]: 訳すのが面倒だったため英語だけ載せようとしたら GitHub Copilot くんが提案してくれたので脳死で Tab 押しました.
[^sudo_with_password_not_implemented]: パスワードで sudo に入る方法はまだ実装されていません. `jetp local` であれば interactive にパスワードを聞かれるので入力すれば実行できそうです.
    > https://www.jetporch.com/appendix/security-approach#sudo-and-other-access-control-tools
    > Sudo access with sudo passwords will be added in the Tech Preview 2 release