RabbitMQのQueue関連の設定をRest APIを利用して自動化した時のメモ

# はじめに

Kubernetes(k8s)環境でHelmを利用してRabbitMQクラスターを稼動させています。
開発環境はDockerを利用しているのですが、マシンを変更する度に手動でQueueを設定するのは面倒になる場面が出てきたのでRest APIを利用することにしました。

公式ドキュメントにもrabbitmqadminなどのCLIツールは紹介されていますが、どうしても汎用性を持たせるためにどう使って良いか分からない、間違った設定をしてしまう、といった問題が発生しがちなので、自分のルールを強制させるためにはツールが必要です。

なおアプリケーション毎にVirtualHostを作成するなどのルールについての詳細は別の記事を参照してください。

* [RabbitMQで複数アプリでの利用を想定して設定した時のメモ](https://qiita.com/YasuhiroABE/items/03ce8fd7e1fbb5585fe3)

また最後にも書いていますが、ここで説明しているコードはGitHub上で公開しています。

* [GitHub YasuhiroABE/setup-rabbitmq-queues](https://github.com/YasuhiroABE/setup-rabbitmq-queues)

その他に実装のために参考にした資料は下記のとおりです。

# 参考資料

* https://tech-lab.sios.jp/archives/7941
* [公式 API References v3.8.9](https://rawcdn.githack.com/rabbitmq/rabbitmq-management/v3.8.9/priv/www/api/index.html)
* [公式ドキュメント](https://www.rabbitmq.com/documentation.html)

# 実行環境

* Ubuntu 20.04.1 LTS amd64版
* Ruby 2.7 (deb package)
  * httpclient (Gem package, bundlerを使用)
* RabbitMQ Docker Container, rabbitmq:3.8.9-management

# APIの概要

基本となる設定をJSON形式かYAML形式で準備し、コマンドの引数に指定します。

設定したい項目は次のとおりです。

* Userの追加・削除
* Virtual Hostの追加・削除・Permission設定
* Exchangeの追加・削除・Bind設定
* Queueの追加・削除

# 開発環境のセットアップ

Rubyと標準的なhttpclientを利用することにしたので環境設定は、Gemfileとbundlerを利用した、慣れた方法を取ります。

```ruby:Gemfile
source 'https://rubygems.org'
gem "httpclient"
gem "json"
```

```makefile:Makefile
.PHONY: setup clean

setup:
        bundle config set path lib
        bundle install

clean:
        ( find . -name '*~' -type f -exec rm {} \; -print )
        rm -rf lib/ruby
        rm -rf Gemfile.lock
```

bundle config setup path libを実行すると、ホームディレクトリ下の~/.bundle/configに設定が書き込まれます。
従来は```bundle setup --path lib```のような記述をしていましたが、この場合はカレントディレクトリ下の./.bundle/configに設定が書き込まれます。
この違いは、Dockerコンテナなどを作成する際に注意が必要になる点ですので、bundle installしたライブラリが読み込めない場合には、~/.bundle/configが配置されているか、適切な内容か確認するようにしてください。

```text:~/.bundle/configファイル
---
BUNDLE_PATH: "lib"
```

適当な作業用ディレクトリにこれらのファイルを配置してから、makeコマンドでライブラリを準備します。

```bash
$ make setup
bundle config set path lib 
bundle install
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using bundler 2.1.4
Fetching httpclient 2.8.3
Installing httpclient 2.8.3
Fetching json 2.5.1
Installing json 2.5.1 with native extensions
Bundle complete! 2 Gemfile dependencies, 3 gems now installed.
Bundled gems are installed into `./lib`

$ ls
Gemfile  lib/  Makefile
```

## Dockerを利用したRabbitMQサーバーの起動

Docker公式イメージにはいくつかのTagが登録されていますが、Webブラウザから利用できるWeb Console UIを利用するためには、-managementがついているTag名を指定する必要があります。

```bash:rabbitmqコンテナの起動
$ sudo docker run -it --rm -d -p 5672:5672 -p 15672:15672 \
	--env RABBITMQ_ERLANG_COOKIE="secretcookie" \
	--env RABBITMQ_DEFAULT_USER="user" \
	--env RABBITMQ_DEFAULT_PASS="4eb4e543c5c27954" \
	--name rabbitmq  rabbitmq:3.8.9-management
```

Webブラウザからは http://localhost:15672/ 経由でアクセスできます。
オプションを指定しない時のデフォルトのID,Passwordは、guest,guest になっているので、ネットワーク接続を許す環境では RABBITMQ_DEFAULT_USER, RABBITMQ_DEFAULT_PASS で必ず変更するようにしてください。

# RabbitMQ Management APIの利用

コンテナが起動すれば、Webブラウザから操作ができますが、ここからはRest APIを利用して必要な設定を行ないます。

テストとして、curlとjqコマンドを利用してAPIにアクセスできるかテストしてみます。

```bash:curlとjqコマンドによるAPIへの接続テスト
$ curl -u user:4eb4e543c5c27954 -H "Content-Type: application/json" http://localhost:15672/api/overview | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2417  100  2417    0     0  63605      0 --:--:-- --:--:-- --:--:-- 63605
{
  "management_version": "3.8.9",
  "rates_mode": "basic",
  "sample_retention_policies": {
    "global": [
      600,
      3600,
      28800,
      86400
...
```

ここから必ずBASIC認証を行なうこと、Content-Typeにapplication/jsonを指定することが分かります。
これでRabbitMQのバージョンなどは確認できたので、userやvirtual hostの作成・削除を行なう方法から探っていきます。

## スクリプトによるUserの追加

Rubyを利用してUserを追加するスクリプト全体は次のようになりました。
ここではまだ実際に利用することを想定しておらず、前半に変数の定義をまとめている程度にとどめて、コードは後からリファクタリングすることにします。

```ruby:rabbitmq-adduser.rb全体
#!/usr/bin/ruby
# coding: utf-8

## 共通の設定項目 (前半)
$: << "./lib"
require 'bundler/setup'
require 'json'
require 'httpclient'

RQ_USER = "user"
RQ_PASS = "4eb4e543c5c27954"
RQAPI_URLBASE = "http://127.0.0.1:15672/api"

## ここから固有の処理 (後半)
client = HTTPClient.new(:force_basic_auth => true)
rqapi_url = RQAPI_URLBASE + "/users/user01"
client.set_auth(rqapi_url, RQ_USER, RQ_PASS)

req = { :password => "secret", :tags => "administrator" }
resp = client.put(rqapi_url, req.to_json, "Content-Type" => "application/json")
```

## Virtual Hostの追加

Userの追加とスクリプトの前半は同一なので、後半部分のみ掲載します。
VirtualHostには "/user01" を設定しています。

```ruby:rabbitmq-addvhost.rbの後半部分
...

client = HTTPClient.new(:force_basic_auth => true)
rqapi_url = RQAPI_URLBASE + "/vhosts/%2fuser01"
client.set_auth(rqapi_url, RQ_USER, RQ_PASS)

resp = client.put(rqapi_url, nil, "Content-Type" => "application/json")
```

## Permissionの設定

Virtual Host "/user01" に、"user01"ユーザーへのアクセス許可を設定します。

```ruby:rabbitmq-addperm.rb後半部分抜粋
...

client = HTTPClient.new(:force_basic_auth => true)
rqapi_url = RQAPI_URLBASE + "/permissions/%2fuser01/user01"
client.set_auth(rqapi_url, RQ_USER, RQ_PASS)

ret = { :configure => ".*", :write => ".*", :read => ".*" }
resp = client.put(rqapi_url, ret.to_json, "Content-Type" => "application/json")
```

## User, Virtual Host, Permissionの削除方法

基本的には"DELETE"を送信すれば良いはずです。BODYがなくても削除可能か確認してみます。

```ruby:rabbitmq-delete-all.rb後半部分抜粋
...

client = HTTPClient.new(:force_basic_auth => true)
for url in ["/permissions/%2fuser01/user01", "/users/user01", "/vhosts/%2fuser01"]
  rqapi_url = RQAPI_URLBASE + url
  client.set_auth(rqapi_url, RQ_USER, RQ_PASS)
  resp = client.delete(rqapi_url, nil, "Content-Type" => "application/json")
end
```

これで全ての定義が削除できました。
ExchangeとQueueの操作も基本的には同様だと思われるので、本番用のスクリプトを作りながら検証していきます。

## 自分ルールに沿ったRabbitMQの設定用スクリプト

まず設定に必要な項目は固有なアプリケーション名です。設定と接続に利用するUserは本来分離するべきですが、ここでは1つのAdministrationロールを持つUserを作成してアプリからもこれを利用することとします。

```json:sample-config.json
{
  "appname":"app01",
  "password":"secret",
  "queues":["queue01", "queue02"]
  "node":"rabbit@2478a000dddd"
}
```

見なれない"node"はWeb UIからログインした際に右上に表示されているCluster名です。
Queueの定義に必要なため、自動的に取得するべきか悩みましたが、とりあえず設定ファイルに記述しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78296/dac9b231-2c08-2d6d-9f16-b5081883a248.png)

> 【2023/10/19追記】Docker環境でホスト名を固定したい場合には``docker run``コマンドの引数に``--hostname <your hostname>``を追加してください。

このファイルを読み込み、RabbitMQ関連のもろもろの設定を自動化するスクリプトの[最終形態はGithubに登録して](https://github.com/YasuhiroABE/setup-rabbitmq-queues)います。

### 実行例

設定ファイルを引数にコマンドを実行すると一括でUSERの作成からBindingまで完了します。

```bash:実行例
$ ./setup-rabbitmq-queues.rb setup sample-config.json
```

とりあえずこれで十分なのですが、しばらく使ってみて必要な変更を加えていく予定です。


以上