はじめに
Kubernetes(k8s)環境でHelmを利用してRabbitMQクラスターを稼動させています。
開発環境はDockerを利用しているのですが、マシンを変更する度に手動でQueueを設定するのは面倒になる場面が出てきたのでRest APIを利用することにしました。
公式ドキュメントにもrabbitmqadminなどのCLIツールは紹介されていますが、どうしても汎用性を持たせるためにどう使って良いか分からない、間違った設定をしてしまう、といった問題が発生しがちなので、自分のルールを強制させるためにはツールが必要です。
なおアプリケーション毎にVirtualHostを作成するなどのルールについての詳細は別の記事を参照してください。
また最後にも書いていますが、ここで説明しているコードはGitHub上で公開しています。
その他に実装のために参考にした資料は下記のとおりです。
参考資料
実行環境
- 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を利用した、慣れた方法を取ります。
source 'https://rubygems.org'
gem "httpclient"
gem "json"
.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が配置されているか、適切な内容か確認するようにしてください。
---
BUNDLE_PATH: "lib"
適当な作業用ディレクトリにこれらのファイルを配置してから、makeコマンドでライブラリを準備します。
$ 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名を指定する必要があります。
$ 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にアクセスできるかテストしてみます。
$ 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を追加するスクリプト全体は次のようになりました。
ここではまだ実際に利用することを想定しておらず、前半に変数の定義をまとめている程度にとどめて、コードは後からリファクタリングすることにします。
#!/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" を設定しています。
...
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"ユーザーへのアクセス許可を設定します。
...
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がなくても削除可能か確認してみます。
...
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を作成してアプリからもこれを利用することとします。
{
"appname":"app01",
"password":"secret",
"queues":["queue01", "queue02"]
"node":"rabbit@2478a000dddd"
}
見なれない"node"はWeb UIからログインした際に右上に表示されているCluster名です。
Queueの定義に必要なため、自動的に取得するべきか悩みましたが、とりあえず設定ファイルに記述しています。
【2023/10/19追記】Docker環境でホスト名を固定したい場合には
docker run
コマンドの引数に--hostname <your hostname>
を追加してください。
このファイルを読み込み、RabbitMQ関連のもろもろの設定を自動化するスクリプトの最終形態はGithubに登録しています。
実行例
設定ファイルを引数にコマンドを実行すると一括でUSERの作成からBindingまで完了します。
$ ./setup-rabbitmq-queues.rb setup sample-config.json
とりあえずこれで十分なのですが、しばらく使ってみて必要な変更を加えていく予定です。
以上