Contents
Mastodonが持つ AWS S3 との連携機能
分散SNSソフトウェアであるMastodonには、クラウドストレージとの連携機能が存在する。AWS S3のオブジェクトストレージとRESTful APIを使って画像や動画データの格納とキャッシングをさせるというもので、概要としては以下の通りになる。

Cephが持つS3互換ストレージインタフェース
つい先日MastodonインスタンスをRBD(Rados Block Device)+NFSで構成する共有ストレージに配置したばかりなんだけれども、以前もそうだったんだけどCephでちゃんと使ったのってRBDしかないなぁ・・と言うことに気づいた。そこで、せめてこれぐらいは扱ってないと駄目だろうなぁと思ったのが「オブジェクトストレージをオブジェクトストレージとして使う」という、RADOS Gatewayの機能。
今回、RADOS GatewayとMastodonが持つS3保管させる機能を併用してみることにした。

RADOS Gatewayを作る
RADOS Gatewayで必要となるCeph関連コンポーネント一式とRADOS Gatewayの役割を構成する手順を実施する。
■SSHノーパスワード ssh-copy-id gemini.bluecore.net ■RADOSGWにするノードの必要コンポーネントインストール ceph-deploy install gemini.bluecore.net ■RADOSGWとして設定をする ceph-deploy --overwrite-conf rgw create gemini.bluecore.net
実はこれとは別にlibra.bluecore.netと言うサーバにも個別にRADOS Gatewayの役割を割り当て済みだったりする。GeminiとLibraの2つのサーバを用いて、冗長構成のRADOS Gatewayを構成する方針としている。
RADOS Gatewayユーザを作る
以下のようにコマンドを入力して、オブジェクトストレージとして取り扱えるユーザを追加する。
■RADOSGWで使用するユーザの作成 radosgw-admin user create --uid=localadms --display-name="localYouser" --email=words@bluecore.net
ユーザ作成後、ユーザ情報を出力してアクセスキーとシークレットキーを確認する。
■RADOSGWで使用するユーザの情報を確認する この中でaccess_key, secret_keyは重要なので必ず情報を保存すること。 radosgw-admin user info --uid=localadms ----- { "user_id": "localadms", "display_name": "localYouser", "email": "words@bluecore.net", "suspended": 0, "max_buckets": 1000, "auid": 0, "subusers": [], "keys": [ { "user": "localadms", "access_key": "****************", "secret_key": "***********************************" } ], "swift_keys": [], "caps": [], "op_mask": "read, write, delete", "default_placement": "", "placement_tags": [], "bucket_quota": { "enabled": false, "check_on_raw": false, "max_size": -1, "max_size_kb": 0, "max_objects": -1 }, "user_quota": { "enabled": false, "check_on_raw": false, "max_size": -1, "max_size_kb": 0, "max_objects": -1 }, "temp_url_keys": [], "type": "rgw", "mfa_ids": [] } -----
RADOS Gatewayのラッパーとなるリバースプロキシの構成
RADOS Gatewayそれ自体はデフォルト設定でポート 7480/tcp にてHTTP接続で待ち受ける。HTTPS接続にする事が望ましいんだけど、ぶっちゃけそのためにRADOS Gatewayいじるのは手間がかかると判断して、今回自前設備に既にある統合リバースプロキシにて新たにVirtualServerを立てる&SSL終端を構成することにした。

Nginx側の設定
/etc/nginx/conf.d/radosgw.conf として以下のコンフィグを作成した。
server { listen 80; server_name radosgw.bluecore.net; modsecurity_rules_file /etc/nginx/modsec/default.conf; root /var/www/html; location /.well-known/acme-challenge/ { allow all; } location /.well-known/pki-validation/ { allow all; } location / { allow all; return 301 https://$server_name$request_uri; } } server { listen 443 ssl http2; server_name radosgw.bluecore.net; ssl_session_cache shared:ssl_session_cache:10m; ssl_certificate /etc/letsencrypt/from_libra/_.bluecore.net.crt; ssl_certificate_key /etc/letsencrypt/from_libra/_.bluecore.net.key; ssl_protocols TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES128-SHA; ssl_prefer_server_ciphers on; client_max_body_size 80m; location / { satisfy any; allow 192.168.0.0/16; allow 172.25.0.0/16; allow 172.30.0.0/16; deny all; proxy_pass http://192.168.100.144:7480; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_http_version 1.1; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_cache cache1; proxy_cache_lock on; proxy_cache_use_stale updating; add_header X-Cache $upstream_cache_status; } location /mastodon { allow all; proxy_pass http://192.168.100.144:7480/mastodon; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_http_version 1.1; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_cache cache1; proxy_cache_lock on; proxy_cache_use_stale updating; add_header X-Cache $upstream_cache_status; } }
基本的な思想としては以下の通りです。
- HTTP接続要求が来たらHTTPS接続にリダイレクトさせる
- HTTPS接続要求が /mastodon に到達したら、要求を通す
- HTTPS接続要求が/mastodon以外に到達したら、要求を拒否する(ただし指定IPに限る)
で、Nginxを再起動させれば取り敢えず大丈夫そう。
systemctl restart nginx
名前解決設定
今回の構成だと、
- アクセス先=radosgw.bluecore.net⇒統合リバースプロキシ
となるように名前解決構成を組む必要がある。また、当該名前解決は内部だけでなく、外部からの名前解決も同様に行う必要がある。外部名前解決を忘れてしまうと、UI上で表示される画像・動画の全てがリンク切れを起こすので注意すること。
バケットの作成
S3互換と言うこともあり、当然AWS S3で定義される単位も同一である。入れ物としてバケットを作る必要があり、データコピーやアクセスをする際はこのバケットを選択して実行する。
実はこうした一連の操作はAWSでも行うことが可能であったりする。CentOS7環境だと以下のようにしてpipを使ってawscliのインストールを行い、設定することが出来る。
■移行サーバで必要となるツールのインストールを行う pip install awscli ■Ceph用のプロファイルを作成する aws configure --profile=ceph ----- AWS Access Key ID [None]: ******************* AWS Secret Access Key [None]: **************************************** Default region name [None]: Default output format [None]: json -----
これでバケットを作成可能になる。radosgw-adminコマンドからバケットのリストを出力させて、意図した名称のバケットが作られたかを確認する。
■Bucketを作成する aws --profile=ceph --endpoint=https://radosgw.bluecore.net s3 mb s3://mastodon ■Bucketを確認する radosgw-admin bucket list ----- [ "bucketceph", "backupdrive", "mastodon" ] -----
既存データコピー
AWSコマンドを使用して以下の通りCeph領域の配下にデータ同期を行う。
■データ移行を実施する aws --profile=ceph --endpoint=https://radosgw.bluecore.net s3 sync /ceph-data/mstdn-engine/public/system s3://mastodon
データコピー時のパフォーマンスを見てみた。
ceph iostatコマンドを使用することで、スループットやIO量の確認が出来る。見た感じ、思った以上にデータコピーのペースが速い。RESTAPIを介したからなのか、どうも細かいデータの大量処理はそこまで苦手ではなさそう。さすがはCeph。
■IOアクセスの推移を確認する ceph iostat +-----------+-------------+------------+-----------+------------+------------+ | Read | Write | Total | Read IOPS | Write IOPS | Total IOPS | +-----------+-------------+------------+-----------+------------+------------+ | 361 KiB/s | 397 KiB/s | 758 KiB/s | 112 | 204 | 317 | | 547 KiB/s | 584 KiB/s | 1131 KiB/s | 207 | 273 | 481 | | 547 KiB/s | 584 KiB/s | 1131 KiB/s | 207 | 273 | 481 | | 427 KiB/s | 577 KiB/s | 1004 KiB/s | 142 | 330 | 472 | | 427 KiB/s | 577 KiB/s | 1004 KiB/s | 142 | 330 | 472 | | 424 KiB/s | 532 KiB/s | 956 KiB/s | 142 | 302 | 445 | | 424 KiB/s | 532 KiB/s | 956 KiB/s | 142 | 302 | 445 | | 611 KiB/s | 1388 KiB/s | 1999 KiB/s | 211 | 462 | 673 | | 611 KiB/s | 1388 KiB/s | 1999 KiB/s | 211 | 462 | 673 | | 546 KiB/s | 1354 KiB/s | 1900 KiB/s | 189 | 424 | 613 | | 546 KiB/s | 1354 KiB/s | 1900 KiB/s | 189 | 424 | 613 | | 675 KiB/s | 1415 KiB/s | 2090 KiB/s | 272 | 486 | 759 | | 675 KiB/s | 1415 KiB/s | 2090 KiB/s | 272 | 486 | 759 | | 644 KiB/s | 1642 KiB/s | 2286 KiB/s | 193 | 517 | 710 | | 644 KiB/s | 1642 KiB/s | 2286 KiB/s | 193 | 517 | 710 | | 668 KiB/s | 1545 KiB/s | 2213 KiB/s | 190 | 424 | 614 | | 668 KiB/s | 1545 KiB/s | 2213 KiB/s | 190 | 424 | 614 | | 809 KiB/s | 1662 KiB/s | 2471 KiB/s | 275 | 529 | 804 | | 809 KiB/s | 1662 KiB/s | 2471 KiB/s | 275 | 529 | 804 |
Anonymousアクセスを許容する
S3互換領域に対しては、外部からアクセスする他インスタンスや利用者が直接アクセスしに来る。これは、アイコン等の画像や動画の応答処理へのリンクが全てMastodon本体ではなく、S3互換領域、つまりはRADOS Gatewayを示しているからである。
デフォルトだと作成したバケットにはAnonymousアクセスの許可は出ていないので、以下のように読み取り占用のアクセス許可を作って出す必要がある。まずは以下のようなJSONファイルを作成する。
mastodon-policy.json ----- { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": ["*"] }, "Action": "s3:GetObject", "Resource": [ "arn:aws:s3::*:mastodon", "arn:aws:s3::*:mastodon/*" ] } ] }
続いてs3cmdを使用してポリシーの適用を行う。今回、awscli以外に、s3操作に特化したs3cmdもインストールしてみた。Cephを相手にする場合の注意点を以下の通り示す。
cloudfront_host = cloudfront.amazonaws.com ★デフォルト設定でよさそう host_base = radosgw.bluecore.net ★RADOSGatewayのFQDNを適用 host_bucket = radosgw.bluecore.net ★RADOSGatewayのFQDNを適用 use_https = True ★HTTPSを使用する website_endpoint = ★空欄でよさそう
そして以下の通りポリシーをセットすることで、Anonymousアクセスが可能になる。
s3cmd setpolicy mastodon-policy.json s3://Mastodon
Mastodon側の設定をいじる
ConfigMapの設定変更
今回k3s上に構成している我がMastodonインスタンスには、.env.productionで必要な変数を格納したConfigmapとこのConfigmapを介してアクセスするSidekiq用Deploymentファイルが存在する。これらの修正を行って反映する。
まずはconfigMapに対して以下S3関連設定を「追加」する。
vi 03.create-configMap.yaml ----- S3_ENABLED: "true" S3_BUCKET: "mastodon" S3_REGION: "us-west-1" AWS_ACCESS_KEY_ID: "<access-key-strings>" AWS_SECRET_ACCESS_KEY: "<secret-key-strings>" S3_PROTOCOL: "https" S3_HOSTNAME: "radosgw.bluecore.net" S3_ENDPOINT: "https://radosgw.bluecore.net" S3_SIGNATURE_VERSION: "s3" -----
本来、S3の場合はリージョンやバケットの名前によってHostnameやEndpointが変わるようなのだけど、CephでRADOS Gatewayを構成した場合はそのような細かい構成までは行われないので、結局はRADOS Gatewayを担保するFQDNを一貫して割り当てる感じになる。
また、Signatureバージョンの指定を「s3」にする必要がある。これをやっておかないと、いざSidekiqが処理しようとする時に、Signature不一致でエラージョブがどんどんたまることになる。
Sidekiq Pod作成ファイルの修正
こちらは実はやらなくても良いことだったのだけど、処理キュー数を5->15に変更する際、DB_POOLの値を指定しなかったが故に、DB_Poolが枯渇してしまうと言う情けない現象を目の当たりにした。それをあわせて修正する形となった。
vi 04-01.create-deployment-siekiq.yaml ----- apiVersion: apps/v1 kind: Deployment metadata: name: mstdn-sidekiq spec: replicas: 1 selector: matchLabels: app: mstdn-sidekiq : 中略 : env: <-env設定を追加 - name: DB_POOL <-env設定を追加 value: "20" <-env設定を追加 volumeMounts: - mountPath: /mastodon/public/system name: mstdn-sidekiq-vol1 : 中略 : volumes: - name: mstdn-sidekiq-vol1 persistentVolumeClaim: claimName: mstdn-engine-pvc11
反映
以下コマンドによりPodを作り直す
kubectl apply -f 03.create-configMap.yaml kubectl apply -f 04-01.create-deployment-siekiq.yaml kubectl scale --replicas=0 deployment/mstdn-sidekiq kubectl scale --replicas=3 deployment/mstdn-sidekiq
できあがった後はSidekiqのダッシュボードから、再試行ジョブが増加してないことを確認するなど、ジョブの運行状態を確認して正常動作が分かれば作業は完了となる。