まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2021/09

みんな大好き WordPress 。自分もローカルコンテナやクラウド、ホスティングなど色々な形態で活用しています。最近は k8s などのパブリックなコンテナ環境下で使われることも珍しくなくなってきました。

実際に環境構築した経験のある人はわかると思いますが、この WordPress はシングルサーバーのシングルテナント構成(サーバーもインスタンスも1台で、1つの WordPress サイトを作る構成)であれば構築手順は簡単なのですが、負荷分散を考慮して複数サーバーインスタンス化した構成を考慮に入れるととたんにややこしくなります。理由もシンプルではないのですが、最大の問題はメディアストレージと呼ばれる画像などのファイルアップロード空間がファイルシステムを前提としている点にあります。簡単に説明すると、コンテンツ内で使う画像をアップロードすると、その画像はサーバーのファイルシステム上に保存されます。表示される際も当然サーバーのファイルシステム上の画像を参照することになります。サーバーが1台であればこれで問題ないのですが、サーバーが複数台(例えばAとBの2台)あった場合、サーバーAのファイルシステムに画像を保存して、サーバーAから画像を参照すると画像が見えますが、サーバーBのファイルシステムには画像は存在していないので、参照しても見つからない、という結果になってしまいます。負荷分散の仕組みが存在している以上、サーバーAを使うかサーバーBを使うかは毎回変わる可能性があるので、アクセスするたびに画像が見えたり見えなかったりすることになります。サーバーが3台、4台と増えていくと、画像が見える確率はどんどん下がっていきます。これでは使い物になりません。

そのため、WordPress では一般的にメディアストレージには全サーバーから参照可能な共有ストレージをマウントして指定します。こうすることで全サーバーが同じストレージにメディアファイルをアップロードすることになり、全てのサーバーから参照可能になる、というわけです。WordPress 環境構築時によく使う典型的な作業でもあり、この機能に関連した多くのプラグインも提供されています。

で、この WordPress の共有メディアストレージに IBM Cloud の Object Storage を指定する方法を調べたので、自分用メモの意味も込めてまとめました。IBM Cloud の Object Storage はいわゆる「S3互換」のオブジェクトストレージで、一定の条件下で無料利用(しかもクレジットカード不要!)できます。詳しくは後述の説明を参照いただきたいのですが、無料枠でも容量としては 25GB まで使えるので、WordPress の規模としてはそこそこ大きなものでもまかなえる共有ストレージだと思っています。


【WordPress 環境の準備】
まずは WordPress 環境を用意します。具体的には MySQL データベースサーバーと、WordPress が展開された HTTP サーバー。複数サーバーでなければ localhost で構築してもいいですし、ローカルの docker/docker Compose で用意してもいいし、有料/無料のクラウド/ホスティングサーバーを使っても構いません。管理者権限でログイン可能な WordPress 環境を1つ用意してください。この時点ではメディアストレージに特別な設定は不要で、デフォルトのファイルシステムをそのまま使う設定であると仮定します。


【IBM Cloud Object Storage(IBM COS) の準備】
次に上述環境のメディアストレージとなる IBM COS を用意します。IBM Cloud アカウントを所有していない場合はこちらから作成してください(作成後にクレジットカードを登録することでより多くのサービスを利用できるようになりますが、今回紹介している内容をそのまま実行するだけであれば登録しないアカウントでも可能です):
https://cloud.ibm.com/registration


IBM Cloud アカウントでログイン後、ダッシュボード画面から「リソースの作成」ボタンをクリックします:
2021092101


検索ボックスに "object storage" と入力し、見つかった "Object Storage" を選択します:
2021092102


次の画面で料金プランを選択します。料金体系が "Free" と表示されたライトプランが選択されていることを確認してください(ライトプランは無料ですが、Standard プランは有料です)。そして画面右下の「作成」ボタンをクリックします:
2021092103



なお、このライトプランの制約事項は以下のようなものです。ストレージ容量としては 25GB ですが、リクエストの回数やパブリックアウトバウンド容量などに一か月単位の上限が設定されています。また30日間非アクティブな状態が続くと削除されます(このプランのままこの制約を超えて使うことはできませんが、有料プランにアップグレードすることで制約を超えて使うことは可能です)。ご注意ください:
2021092104


Cloud Object Storage サービスが作成され、以下のような画面が表示されます:
2021092105


ではこの Object Storage にデータ(今回は画像)を格納するバケットを作成します。左メニューから「バケット」を選び、「バケットの作成」ボタンをクリックします:
2021092101


バケットの属性を選択したり、カスタマイズできたりするのですが、今回は定義済みのバケットを使うことにします。「すぐに始める」と書かれたカードの→印部分をクリック:
2021092102


ここでは3ステップでバケットを定義しますが、実質的に最初のここだけがカスタマイズ部分となります。これから作成するバケットの名称を適当に入力します。以下の例では WordPress のメディアストレージ向けということで、自分の名前も使って "kkimura-wordpress-media" と入力しています。ここは皆さんがわかりやすい名前を入力してください。そして「次へ」:
2021092103


次の画面ではそのまま「次へ」:
2021092104


最後の画面も特に変更する必要はありません。そのまま「バケット構成の表示」ボタンをクリックします:
2021092105


すると入力した名称でバケットが作成されます。この時に「ロケーション」欄に表示されている内容は後で使うのでメモしておきます(下図の例だと "jp-tok"):
2021092106


バケットが作成できたら、次はこのバケットを外部から(今回の例だと WordPress から)利用できるようにするための情報を取得します。画面左のメニューで「サービス資格情報」を選択し、「新規資格情報」ボタンをクリックします:
2021092107


資格情報の作成ダイアログが表示されたら「詳細オプション」を開き、役割が「ライター」になっていることを確認した上で、「HMAC 資格情報を含める」をオンにしてから「追加」ボタンをクリックします:
2021092108


いま追加した資格情報が一覧に表示されるので、名称部分をクリックして展開します:
2021092109


以下のように追加したサービス資格情報が表示されます:
2021092110


サービス資格情報は以下のようなフォーマットになっているので、"cos_hmac_keys" の中の "access_key_id" の値と、"secret_access_key" の値をメモしておいてください:
{
  "apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "cos_hmac_keys": {
    "access_key_id": "xxxxxxxxxxxxxxxxxxxxxxxx",
    "secret_access_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "endpoints": "https://control.cloud-object-storage.cloud.ibm.com/v2/endpoints"
    :
    :
}

※ サービス資格情報に cos_hmac_keys というキーそのものが存在していない場合は、資格情報を作成する際に「HMAC 資格情報を含める」オプションをオンにしていない可能性があります。上述の手順を再度確認してもう一度実行してみてください。


最後にエンドポイントを確認します。画面左メニューから「エンドポイント」を選択し、バケットを作成した時に確認したロケーション情報( Regional の jp-tok)に合わせて「回復力」と「ロケーション」を設定し、そこで表示されるパブリック URL の内容をメモしておきます:
2021092111


これで IBM COS 側の設定と準備も完了です。改めて、この時点で  IBM COS に関する以下の情報が入手できていることを確認してください。これらがすべてわかっていれば WordPress のメディアストレージとしてマウントできるようになります(手順は後述します):
取得・確認方法
バケット名バケット作成時に指定した名前
access_key_idサービス資格情報内
secret_access_keyサービス資格情報内
エンドポイント作成したバケットのロケーション情報に合わせて取得したパブリック URL


【Media Cloud プラグインのインストールと設定】
ここまでの手順が正しく実施できて、上述の4つの情報が取得できていれば WordPress に IBM COS のバケットをメディアストレージとしてマウントできる準備が整ったことになります。以下では実際にマウントする手順を紹介します。

まず WordPress の管理機能にアクセスできるアカウントでログインし、管理画面に移動します。まずObject Storage をメディアストレージとしてマウントするための Media Cloud プラグインをインストールします。画面左のメニューから「プラグイン」を選び、「新規追加」ボタンをクリックします:
2021092201


キーワード検索ボックスに "Media Cloud" と入力します:
2021092202


候補がいくつか見つかりますが、その中に以下のような "Media Cloud for Amazon S3, ..." というプラグインが見つかります。ここの「今すぐインストール」をクリックしてインストールします:
2021092203


インストールに成功すると「有効化」というボタンに変わります。このボタンもクリックしてプラグインを有効にします:
2021092204


更新通知機能をオプトインするかどうかを聞かれます。オプトインする場合は「許可して続ける」、したくない場合は「スキップ」のいずれかを選んで先へ進みます:
2021092205


Media Cloud のセットアップウィザードが起動します。まずは Next をクリック:
2021092206


最初に Object Storage のプロバイダーを選択します。IBM COS の場合は "S3 Compatible" を選択します:
2021092207


S3 互換ストレージの設定が開始されます。"Next" をクリック:
2021092201


次の画面で先程準備した IBM COS の情報を入力します。入力内容は以下のようにします:
項目名入力する値
ACCESS KEYaccess_key_id の値
SECRETsecret_access_key の値
BUCKET作成したバケット名
REGION(Automatic のまま)
CUSTOM ENDPOINTエンドポイント URL

最後に "Next" をクリック:
2021092202


入力した値が正しく利用できるかどうかを確認するいくつかのテストを行います。"START TESTS" をクリック:
2021092203


実際にバケットに対してファイルをアップロードして、アップロードできていることを確認して、削除して・・・といったテストが行われます。すべて成功すると下図のように5つのグリーンでチェックマークが表示され、正しく利用できる状態であることが確認されました。"Next" をクリック:
2021092204


これで Media Cloud を利用できる状態になりました。"FINISH & EXIT WIZARD" をクリックして設定ウィザードを終了します:
2021092205


WordPress の管理画面に戻り、"Media Cloud" メニュー内の "Enable Cloud Storage" がオンに設定されていることを確認します:
2021092206


ちなみに、ここで設定した値は MySQL データベースの wp_options テーブル内に格納されているようです。なので、この状態で(データベースへの接続情報が含まれた状態で)サーバーの内容をコンテナイメージ化すれば、コンテナ環境でも動く状態が作れる、と思います:
2021092201



最後にメディアストレージが Object Storage に切り替わっていることを WordPress 内からも確認しておきましょう。管理画面の「メディア」を選択し、メディアライブラリの「新規追加」で適当な画像ファイルを選択して追加します:
2021092207


試しにこんな感じで1つ画像をアップロードしてみました(この画像は WordPress のコンテンツとして利用することができず状態になっています):
2021092208


改めて IBM Cloud のダッシュボードに戻り、IBM Cloud Object Storage のバケット内を表示してみると、画像ファイルが追加されているはずです(2つ追加されているのは、1つのアップロードに対して元画像とサムネイル用の2つの画像が追加されているからのようです):
2021092209


画像を1つ選択すると右にウィンドウが表示され、ここからダウンロードすることができます:
2021092201


実際にダウンロードして表示し、WordPress 画面からアップロードした画像が Object Storage に格納されていることを確認します:
2021092202


WordPress のメディアストレージとして IBM Cloud の Object Storage を利用できることが確認できました。IBM Cloud の Cloud Object Storage を使うことである程度の規模まで無料でこの環境での運用ができるので、バケットを複数用意して使うケースも含めて、実運用前の構築段階ではとても便利に使うことができそうです。

日本での正式な発売直後に品切れになり、しばらく入手できなかったラズベリーパイ 400 (以下「ラズパイ 400」)の販売が再開されたようです:
品切れだったラズパイ内蔵キーボード「Raspberry Pi 400」日本語版、販売を再開 9790円



幸いなことに自分はこれ(日本語キーボード版)を入手することができていました。そして罰当たりなことに、このラズパイ 400 を「起動したまま SSH で使う」という使い方をしていました。SSH 接続ならなにもラズパイ 400 じゃなくて普通のラズパイでも・・・という声が聞こえてきそうです。ごめんなさい、その通りだと思いつつ使ってました。

言い訳をするつもりはないのですが、ラズパイ 400 はキーボード本体内にラズパイ4(4GBモデル)が内蔵されているもので、ここに電源とモニター(と現実的にはマウス)をつないで使うことになります。マウスは Bluetooth でもよいので物理的な接続は必須ではなく、(内蔵バッテリーがないため)電源をつながないといけないのは仕方ないとして、モニターもつながないと実質的に使えないというのが普段使いにはちょっとしたハンディキャップになっていました。本体とキーボードのセットと考えるとかなり小型化されていて持ち運びに便利な一方で、モニターも HDMI ケーブルごと持ち歩かないといけないのだとするとラズパイ 400 の機動性はかなり落ちてしまいます。モニターのある部屋からモニターのある部屋へ移動して使うにはいいんだけど、それだと利用シーンはかなり限られてしまうような気もしてしまいます。。

で、今回のブログのテーマとなります。そんなラズパイ 400 ですが、普段から持ち歩いているスマートフォン(スマホ)をモニター代わりに使うことができるのであれば、実質的にモニターを持ち歩いているようなものなので、上述の課題を解決できるのではないかと考えました。これは実現性を無視した都合のよい話ではなく、HDMI input をサポートした Android 11 以降であれば理論上はできるはず、、ということで試してみたのでした。ちなみに iOS だとまだこの機能はサポートされていない模様です。


【システム構成】
今回試したシステム構成はこのような形です:
2021091901


通常、ラズパイとディスプレイとを HDMI ケーブルで接続するのですが、そのディスプレイの代わりにスマートフォンを使うことになります。その際に HDMI ケーブルをスマートフォンの端子(USB Type-C)にアダプタで直接変換すればよいわけではなく、間にキャプチャーカードと呼ばれる機器を挟む必要があります。今回は以下の「BANANAJOY ビデオキャプチャカード」を使いました:



また、このキャプチャー結果の信号を USB Type-C 端子を持つスマホに送るための変換ケーブルとしては以下の「MacLab. USB Type-C OTG 変換 ケーブル」を使いました:



加えて、スマホ側にも単に信号を送っただけではディスプレイとして表示されないので、「スマホを外部ディスプレイ化するアプリ」が必要です。今回はこの「USB Camera」アプリをダウンロードして使いました。なおこのアプリは無料ですが、有料版(USB Camera Pro)だと広告が表示されないだけでなく、画面サイズを変更できるなどの追加機能が使えるようです:
USB Camera - Connect EasyCap or USB WebCam


ちなみに今回の実験で使ったスマホはこの Unihertz Titan Pocket です。この機種である必要はありませんが、Android 11 が搭載されている必要があると思っています:
QWERTYキー付き小型スマホ、Unihertz「Titan Pocket」が到着!

※あと今回のレビューでは使っていませんが、この機種だと標準アプリとして「USBカメラ」というものが含まれていて、これで外部ディスプレイ化できることも確認できました。


で、上述のシステム構成のようにラズパイ 400 、HDMI ケーブル、キャプチャーカード、OTG 変換ケーブル、そしてスマホを接続し、スマホにインストールした USB Camera アプリを起動した状態でラズパイ 400 に電源を入れると・・・
IMG_3924


↑ラズパイ 400 が起動し、その画面がスマホ内に表示されました!期待通り!!

↓ブラウザとターミナルを開いて使っている時の画面の様子がこちらです:
IMG_3925


あっけなく実現できてしまいました。今回スマホとして使った Unihertz Titan Pocket は決して広い画面を持っているわけではないので、実用性という観点ではあまりよくないかも(苦笑)しれません。まあ、でも普通の画面(?)の Android 11 を所有していて、有料版の USB カメラアプリで画面をポートレートモード(横)固定で使えばもう少し見やすくなりそうだと思ってます。

※ここでスマホに表示される画面はあくまで「外部ディスプレイ化されたスマホに単に表示されている画面」です。タッチ操作に対応はしていません。タッチしても何の反応もありません。


さて、ということはラズパイ 400 を持ち歩いて使う上で、かなり進展があったと思っています。何しろ「普段から外部ディスプレイ(=スマホ)を持ち歩いていた」ことになるので、接続用のケーブル類だけ持ち歩いていればどこでもラズパイを使える、ということになります!!

その接続用に持ち歩くケーブル類ですが、具体的には・・・
・電源ケーブル(USB-C ケーブルと、コンセントにつなぐ電源アウトレット)
・Micro HDMI -> HDMI 変換アダプタ
・HDMI ケーブル
・HDMI キャプチャーカード
・USB Type-C OTG 変換ケーブル
・外付けマウス(有線か、または Bluetooth)


・・・んー、意外と多いな(汗) (^^; それと結局バッテリーがないからコンセントかモバイルバッテリーに繋いで使う必要があるんですよね。これが次のネックになりそうな気がしてきました。。

※なお、HDMI キャプチャーカードが利用する電源は出力側(スマホ側)から使われる模様です。つまりこの仕組を使っている間、スマホ側のバッテリーは想定以上に消費されることになる点に注意してください。


ともあれ、Android 11 が使えるユーザーにとってはフルキーボード付きの Debian Linux 環境を持ち歩いて使える手段の目途が立つ実験結果でした。


QR コード生成サービスを作ってソースコードごと公開してみました:
https://dotnsf.github.io/qrgenerator/

2021091203



同じようなコンセプトのサービスは世に多くあることを知っていますが、自分が作ったものは以下のような特徴を持ってます。便利に感じた方はぜひ使ってみてください。
  • (静的コンテンツだけで構成される)Github ページで公開している。バックエンドサービスが使えない形で公開しているので、実は裏で URL を集めている、といった心配は無用
  • 単に情報を QR コード化するだけのサービスではなく、考えつく限りの以下のようなカスタマイズを可能にしている

カスタマイズできる内容は以下の通りです:
・内容(テキスト、URLなど ここはカスタマイズできて当然)
誤り訂正レベル(デフォルトは M(約15%))
・生成する画像のサイズ(デフォルトは 512x512)
・前景色(デフォルト値は黒)
・背景色(デフォルト値は白)
・画面中央にロゴのように含める画像(デフォルトなし 誤り訂正レベルに応じて自動拡大縮小)


たとえばこんな感じにマンホールマップの URL と画像を設定して「生成」をクリックすると、
2021091201


指定された通りの QR コードがこんな感じで作成されて、
2021091202


「画像ダウンロード」をクリックすると、生成された QR コードを画像としてダウンロードできます:
1631428093716

細かい仕様としては、生成された QR コード部分をクリックすると情報テキストがクリップボードにコピーされます。上の例だと "https://manholemap.juge.me/" がクリップボードにコピーされるので、ブラウザのアドレス欄にペーストするだけでマンホールマップに遷移できるようになる、というわけです。


カスタマイズできる内容のうち、誤り訂正レベルについては自分も知らなかったので解説を加えておきます。

QR コードは紙やビニールといった「クシャクシャになったりして、そもそも印刷内容が平らに見えない可能性がある媒体に印刷して使う」可能性があることを考慮した仕様になっています。それが誤り訂正レベルです。QR コード自体を冗長化する形で作成して、画面の一部が読み取り不可な状態になっていても含まれている情報を認識できるようにするためのものです。

これが誤り訂正レベルで、QR コードでは4段階(H(約30%), Q(約25%), M(約15%), L(約7%))用意されています。H だと全体の30%が読み取り不可になっていても大丈夫、という意味ですが、QR コード自体が他よりも細かなものになります。ちなみに以下はどちらも "https://manholemap.juge.me/" の QR コードですが、左は誤り訂正レベル H 、右は Q です。白黒の細かさの違いがわかると思います:
Q


そのため最終的な印字サイズが小さいと読み取り自体が難しくなったりします。どの誤り訂正レベルを選ぶかは(真ん中にロゴ画像を含めるかどうかにも関わりますが)試行錯誤しながら読み取れそうなものを作りながら選ぶ必要があると思っています。

ちなみにソースコードはこちらです。裏で URL を保存してたりしない証拠としてこちらも公開しておきます:
https://github.com/dotnsf/qrgenerator


よかったら使ってください。


IBM Cloud から提供されている IDaaS サービスである AppID を使っています。AWS の Cognito や Auth0 のようなユーザーログインの(オンラインサインアップやオンラインパスワード変更、多要素認証なども含めた)機能全般をまとめて提供するサービスです。IBM Cloud のライトアカウントを使って無料枠内で利用することも可能です。単なるログイン機能程度であれば自分で実装してもいいのですが、オンラインサインアップや多要素認証まで考慮すると実装は面倒だし、(実在しないアドレスを使うこともできますが)メールアドレス含めて管理するのは個人情報管理の観点からも、特に試験的/実験的な段階では可能であれば避けたいという運用側の事情もあるため、こういったマネージドサービスを使うメリットは大きいと考えています。事実、自分は公私でよく使っています。
2021082902


で、この AppID を使ってサービス開発をしている中で「ユーザーを無効化したい」という意見をいただきました。実験的な意味合いのあるサービスを作って、ある特定の期間だけはそのサービスを使ってもらい、その期間が完了した後は使わせたくない。でもユーザーを削除してしまうと、何かの際に再度ログインしてもらうこともできなくなったり、再作成しても ID のデータ紐付けが切れてしまってたまったデータとの関連が切れてしまうのでそれは都合が悪い。そのため「いったん無効化してログインできないようにしたい」という、運用上ごもっともな意見でした。

「ユーザーの無効化ね。まあ機能的に用意されてるんだろうな」と楽観視していたのですが、AppID のダッシュボードを調べてみると、どうも「ダッシュボード画面からはユーザーの削除はできるが無効化はできなそう」でした。おっと、これはまずいぞ。。。
2021082903


このユーザー無効化を実現するためのワークアラウンドとして考えたのが「パスワードの強制変更」でした。要は管理者の権限で該当ユーザーのパスワードを強制的に変更することで事実上ログインできなくしてしまう、という方法です。アカウントは残るのでデータやその紐付けも消えることはなく、再度初期パスワードに変更すれば、万が一の時にもう一度ログインしてもらうこともできるようになる、と考えました。

で、じゃあダッシュボードからどのようにパスワードを強制変更するのかというと・・・実はパスワードの強制変更もダッシュボードからできるわけではありません。2021年8月現在、AppID ダッシュボードからはユーザーを削除することはできますが、ユーザーを無効化することも、パスワードを強制変更することもできません。

ただ、AppID SDK にはパスワードを強制変更する API がありました(文字通りの「無効化」は SDK からでもできなさそう・・):
https://www.npmjs.com/package/ibmcloud-appid

2021082901
(↑このページ内の "Set User New Password" 欄参照)


つまり AppID SDK のこの setUserNewPassword 関数を使って自分でプログラムを作れば、ユーザーのパスワードを強制的に変更すること自体は実現できそうでした。それによって実質的な無効化も可能にできるのでは・・と考え、挑戦してみました。以下はその成果の共有です。


【サンプルソースコードの紹介】
AppID のユーザーパスワードを変更するサンプルの Node.js アプリケーションを作って、ソースコードを公開しました:
https://github.com/dotnsf/appid_changepassword

このリポジトリを git clone するかダウンロード&展開してください。

使い方は後述しますが、まずは依存ライブラリをまとめてインストールしておいてください(この手順はソースコードを用意した後に1回だけ行ってください):
$ cd appid_changepassword

$ npm install

このフォルダには4つの JavaScript コードファイルが含まれていますが、それぞれ以下のような役割です(create_users_to_appid.js とか、何気に便利なツールだと自負してます):
ファイル役割
app.jsApp ID の動作確認用ウェブアプリケーション。AppID にログイン&ログアウトするだけ
settings.js動作設定用ファイル
create_users_to_appid.jsCSV ファイルから AppID のユーザーをまとめて作成する CLI アプリ
change_password.jsユーザーIDと新パスワードを指定して、パスワードを強制変更する CLI アプリ


パスワードの変更処理を行うのは change_password.js ですが、app.js や create_users_to_appid.js も含めて、この3つのファイルを動かすためにはあらかじめ settings.js に AppID の接続情報を記載しておく必要があります。以下、一通りの使い方を紹介します。


【サンプルソースコードを動かすための事前設定項目】
このアプリを動かすには、何はともあれ App ID のインスタンスが必要です。IBM Cloud にログインしてダッシュボードから App ID を選択してリソースインスタンスを作成してください。その際に「ライト」プランを選択すると 1000 ユーザー&(一ヶ月)1000 ログインまで無料で利用することが可能です(ログインアカウントがライトアカウントの場合は、ライトプランしか選択できません):
2021082904


作成した AppID インスタンスの「サービス資格情報」から(必要であれば1つ作成した上で)サービス資格情報を選択して、資格情報の内容を確認します:
2021082905


資格情報の中は以下のような JSON フォーマットのテキストになっています:
{
  "apikey": "xxxxx",
  "clientId": "xxxxxxxxxxxxxxxxxxx",
  "secret": "xxxxxxxxxxxxxxxxxxx",
  "tenantId": "xxxxx",
  "profilesUrl": "https://jp-tok.appid.cloud.ibm.com",
    :
    :
}

この中の以下5つの情報が必要です:
・apikey
・secret
・clientId
・tenantId
・region(profilesUrl の "https://" と ".appid.cloud.ibm.com" の間の文字列(上例だと "jp-tok"))

これら5つの値を見つけたら、settings.js 内の該当する変数値として入力し、保存します:
//. IBM App ID
exports.region = '(region の値)';
exports.tenantId = '(tenantId の値)';
exports.apiKey = '(apikey の値)';
exports.secret = '(secret の値)';
exports.clientId = '(clientId の値)';

exports.redirectUri = 'http://localhost:8080/appid/callback';
exports.oauthServerUrl = 'https://' + exports.region + '.appid.cloud.ibm.com/oauth/v4/' + exports.tenantId;

準備の最後に App ID のコールバック URL を登録します。今回のアプリケーションはローカルホストで動かす想定をしていて、その場合はローカルホストで動かす際の URL を事前に AppID に登録しておく必要があります。

IBM Cloud ダッシュボードで作成した App ID を開き、「認証の管理」、「認証設定」を選択して、「Web リダイレクト URL の追加」から "http://localhost:8080/appid/callback" を追加します(ローカルホストで動かすのではなく、公開アドレスで動かす場合はその URL を使って "http(s)://ホスト名/appid/callback" を追加してください):
2021082906


これでアプリケーションを動かす準備ができました。


【サンプルソースコードを使ってユーザーを登録】
このブログエントリの目的は「AppID のユーザーパスワードを変更する方法の紹介」です。そのため実際にログインできるユーザーを使って動作確認する必要がありますが、まずはそのユーザーを登録する必要があり、そのためのツールを紹介します。 既に AppID にユーザーが登録されていて、そのユーザーのパスワードを変更してもいい場合、ここは無視しても構いません。

まずは登録するユーザーを以下のようなフォーマットの CSV ファイルで用意します(newusers.csv 参照):
ユーザー表示名,ユーザーID,ユーザーパスワード
ユーザー表示名,ユーザーID,ユーザーパスワード
    :
    :


1列目のユーザー表示名はログインしたユーザーをアプリケーション内で表示する際に表示される文字列です。AppID の場合、ここはなぜか「8文字以上」という制約があるので、8文字以上の表示名を指定します。

2列目のユーザーIDは「メールアドレス形式のログインID」です。App ID の設定によってはオンラインでパスワードリセットを行ったりすることも可能で、その場合のメール送信先にもなるものです(オンラインパスワードリセットを無効にして運用する場合は実在しないメールアドレスでも構いません)。

3列目のユーザーパスワードは、2列目の ID でログインするユーザーのログイン時に指定するパスワードです。 この3列を一組とした行を必要なユーザーぶんだけ用意します。 なおサンプルで newusers.csv というファイルが含まれていて、その内容は以下のような2行になっています(以下、このファイルを使って紹介しますが、自分でこのファイルに相当する内容のファイルを用意した場合は、そのファイル名に置き換えて読み進めてください):
User0001,user0001@mydomain.com,password1
User0002,user0002@mydomain.com,password2

ではこのファイルを使って AppID にユーザーを登録します。コマンドの最後に CSV ファイル名を指定して、以下のように実行します:
$ node create_users_to_appid newusers.csv

画面に色々出力された後にプロンプトに戻ります。成功していると CSV ファイル内で指定されたユーザーが AppID に登録されているはずです。ダッシュボードから「クラウド・ディレクトリー」、「ユーザー」を選択し、CSV ファイルで指定したユーザーが追加されていることを確認します:
2021082907


検証用のユーザーが AppID に登録できました。まずはこのユーザー ID(と CSV ファイルで指定したパスワード)でログインできるか一度確認しておきます。


【サンプルソースコードを使って登録したユーザーでログイン確認】
では今登録したユーザーが登録したパスワードでログインできるかどうかを確認するため、サンプルのウェブアプリケーションを起動します:
$ node app

App ID のメッセージが数行表示された後に "server starting on 8080 ..." と表示されれば起動完了です。ウェブブラウザを開いて http://localhost:8080/ にアクセスします。すると(ログイン前なので) AppID のログイン画面に転送され、以下のような画面が表示されます:
2021082901


この Email 欄と Password 欄にそれぞれユーザーのメールアドレスとパスワードを入力して "Sign in" ボタンをクリックします。先程 CSV ファイルで作成したユーザーのメールアドレスとパスワード(例えば user0001@mydomain.com と password1)を入力してみます:
2021082902


正しく入力されている状態で "Sign in" ボタンをクリックするとログイン処理が成功し、画面が遷移して以下のような画面に切り替わります。ユーザーの ID、表示名、メールアドレスが表示されています。アドレスはログイン前に指定したものと同じ http://localhost:8080/ ですが、ログインが完了しているのでログイン画面には転送されず、この画面が表示されています。"Logout" ボタンでログアウトできます:
2021082903


ログアウトするとログイン画面に戻ります。試しにもう1人のユーザー情報(ID: user0002@mydomain.com, パスワード: password2)でもログインしてみます:
2021082904


User0002 でもログインできました。CSV ファイルから作成したユーザーで正しくログインできる所まで確認できました:
2021082905


ここまで確認できたらいったんサンプルウェブアプリケーションを終了しておきます。"node app" を実行した画面で Ctrl+C を押してアプリケーションを強制終了させておきます。


【サンプルソースコードを使ってユーザーのパスワードを変更する】
さていよいよ本ブログエントリの本題となる「ユーザーパスワードの強制変更」の箇所です。change_password.js ファイルを使って以下のように実行します:
$ node change_password user0001@mydomain.com Password1

実行時に指定するパラメータは2つあります。1つ目がパスワードを変更したいユーザーのログインID(メールアドレス)、2つ目が変更後のパスワードです(つまり上の例だと user0001@mydomain.com ユーザーのパスワードを Password1 に変更します)。旧パスワードを指定せずに強制的に変更する、という点に注意してください。実行すると色々画面に表示されますが、これも処理が完了するとプロンプトに戻ります。

パスワード変更が正しく行われたかどうかを再度サンプルウェブアプリケーションで確認します:
$ node app

ウェブブラウザで https://localhost:8080/ にアクセスしてログイン画面に移動したら、最初は変更前のパスワード(password1)を指定して user0001@mydomain.com でログインを試みてください。先程はログインできましたが、今はパスワードが変更されているのでログインできないはずです(メールアドレスとパスワードが一致しない、という旨のエラーメッセージが表示されます):
2021082906


改めて変更後のパスワードを指定してログインすると、今度はログインが成功してログイン後の画面が表示されます:
2021082907


これで AppID SDK を使ってログインパスワードを強制的に変更することができることが確認できました。

ちなみにソースコード上でのこのパスワード変更処理箇所はこのようになっています:
var SelfServiceManager = require( 'ibmcloud-appid' ).SelfServiceManager;

  :
  :

selfServiceManager.setUserNewPassword( uuid, password, "en", null, null ).then( function( user ){
  resolve( user );
}).catch( function( err ){
  reject( err );
});

  :
  :


ibmcloud-appid ライブラリを呼び出して SelfServiceManager をインスタンス化し、(省略した部分で)アクセストークンを取得してメールアドレスで指定したユーザーの ID(上記コード上では uuid)を特定します。特定できたら selfServiceManager.setUserNewPassword( uuid, password, "en", null, null ); を実行してパスワードを変更しています(password が新しいパスワード)。3番目のパラメータは言語情報らしいですが現在使われていないらしく、既定値の "en" を指定しています。このあたり、詳しくは SDK の説明もご覧ください。


ともあれ、これで当初の目的である「App ID ユーザーの無効化」に近いオペレーションが実現できそうでした。めでたしめでたし。



Bonsai Elasticsearch はその名前の通り、高速多機能な検索エンジンである Elasticsearch による検索機能をダッシュボード機能も含めてマネージドサービスとして提供するプロバイダーです。日本語の情報が少ないせいか、技術記事含めて情報自体を見ることがあまりありませんでした。偉そうに言ってますが、自分も以下の件で興味を持つまでは存じ上げていませんでした:
2021083002


Bonsai Elasticsearch は Salesforce から提供されている PaaS 環境である herokuサードパーティサービスの1つでもあります。heroku 自体も無料で使える利用枠が提供されていて、また Bonsai Elasticsearch にも無料枠があるため、heroku を経由してサービスを申し込むと「検索エンジンの使えるアプリケーションを無料で開発・運用できる環境」を手に入れることができます。世に多くのクラウド環境はありますが、Elasticsearch が無料で使えるアプリケーション・サーバー環境、という時点でかなり珍しいといえます:
2021083003


一方で、現実的に日本人を対象とするアプリケーションを考えると、検索機能は日本語検索ができなければあまり実用的とはいえません。英語のように単語と単語の間に明確なスペースが入って区切られるわけではない日本語は、テキスト内の単語と単語の区切りを見つける時点でかなりハードルが高く、そこから更に検索インデックスを作る必要があるためです。ここまでの機能がサポートされていないと検索エンジンとしては使いにくいのでした。 例えば自分で自分の(手元の)PC やサーバーに Elasticsearch をインストールした場合であれば、自分で更に日本語形態素解析機能を追加でインストールすればいいのですが、クラウドのマネージドサービスとして提供されている場合、そのような権限をもらえないことも多いため、サービスとして提供されている機能の中に多言語対応(日本語対応)が含まれていないといけない、という高めのハードルが設定されているのでした。

そんな中で見つけたこの Bonsai Elasticsearch 。ネットの日本語情報が少ないということは、日本語が使えないということかな・・・ と勝手に想像していました。 が、Bonsai のドキュメントを調べてみると Bonsai Elasticsearch に始めから導入されているプラグイン一覧の中に日本語形態素解析である "Kuromoji Analyzer" の文字を見つけました。あれ?これはもしかして期待できるやつ?:
https://docs.bonsai.io/article/135-plugins-on-bonsai

2021083001


というわけで、実際に Bonsai Elasticsearch で日本語検索ができるかどうかを調べてみました。結論としてはどうやら使えそう(!!!)だと思ってます。以下はその際の記録です。


【調査に使ったシナリオ】
最初に迷ったのは「何を調べれば日本語検索ができるといえそうか」でした。日本語データを入れて、日本語で検索して、日本語のそれっぽいデータが返ってきたらいいのか?? というと、そうともいえないし・・・

で、今回は Elastic 社の日本語エンジニアリングブログから提供されていた『Elasticsearchで日本語のサジェストの機能を実装する』というエントリで紹介されていた方法が Bonsai Elasticsearch でできるかどうかを調べました。詳細はリンク先を確認していただきたいのですが、大まかな内容としては kuromoji を使う前提で日本語でのサジェスト機能を実装するためのインデックスおよびマッピングを作成し、日本語のデータを入れた上で検索して結果を見る、というものです。ここで紹介されているのは入力ミスまでを考慮した曖昧な検索を行うという内容で、この結果が期待通りになればそれはもう大丈夫でしょ、という判断です。

ちなみに同ページで紹介されている内容自体が日本語サジェストを実現するための考え方なども紹介されていて非常に有用でした。その上で、ここで紹介されていることと同じ内容が Bonsai Elasticsearch に対して行っても同じ結果になるか(Bonsai Elasticsearch で日本語形態素解析が使えれば同じ結果になるはず)、を試してみました:
https://www.elastic.co/jp/blog/implementing-japanese-autocomplete-suggestions-in-elasticsearch


【Bonsai Elasticsearch の準備】
まずは Bonsai Elasticsearch のインスタンスを準備します。自分の場合は heroku 経由でインスタンスを作成したので、その場合の手順を紹介します。

まず heroku で無料アカウントを作成し、クレジットカードを登録しておきます(無料版の Bonsai Elasticsearch を使いますが、そのためには heroku アカウントにクレジットカードの登録が必要です)。改めてブラウザで heroku にログインし、(必要であれば)アプリケーションを1つ作成した上でそのアプリケーションにアドオンとして紐付ける形で Bonsai Elasticsearch を追加します。アプリケーションを選択して、"Overview" タブから "Configure Add-ons" を選択します:
2021083004


アドオンを選択する画面で検索ボックスに "Bonsai" と入力すると "Bonsai Elasticsearch" が見つかるのでこれを選択します:
2021083005


こんな感じの確認画面が表示されたら、無料プランの "Sandbox -  Free" が選択されていることを確認して(これ以外は有料です) "Submit Order Form" ボタンをクリックします:
2021083006


正しく処理されると、アプリケーションのアドオン一覧に "Bonsai Elasticsearch" が追加されます:
2021083007


追加された Bonsai Elasticsearch にアクセスするための接続 URL を確認します。同アプリケーションの "Settings" タブから "Reveal Config Vars" ボタンをクリックします:
2021083008


すると、このアプリケーションの動作時に設定される環境変数の一覧が表示されます。さきほど、Bonsai Elasticsearch を追加した際のオペレーションで "BONSAI_URL" という環境変数が設定されているはずです:
2021083009


ここでコピーした BONSAI_URL の値は、このようなフォーマットのテキストになっているはずです:
https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443

この値をこの後のインデックス作成やクエリー実行時に使うことになります。何度も使うことになるのでコピペできるよう、どこかに退避しておきましょう。

ここまで完了すれば Bonsai Elasticsearch の準備は環境です。


【Bonsai Elasticsearch に日本語インデックスと日本語データを作成して検索】
ここまでの準備ができれば Elastic 社の日本語エンジニアリングブログで紹介されていた、この内容を実際に試すことができるようになります:
https://www.elastic.co/jp/blog/implementing-japanese-autocomplete-suggestions-in-elasticsearch

ただ具体的なコマンド入力を考慮すると、このままだと少しわかりにくいため、この内容がもう少し試しやすくなるようなファイルやコマンド集(なんなら実行結果も含まれてますw)を作って公開しました。単に挙動や結果だけを確認したい人はこちらをダウンロードして使ってください:
https://github.com/dotnsf/bonsai_elasticsearch


以下に curl を使った具体的な手順とともに紹介します。日本語エンジニアリングブログによると、最初に kuromoji を使った日本語インデックスを作成する必要があります。上述でダウンロードしたファイルの中に含まれている my_suggest.json が日本語インデックスとマッピングを構成するファイルなので、以下の curl コマンドを実行してこれを Bonsai Elasticsearch に PUT します:
$ curl -XPUT https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest -d @my_suggest.json -H 'Content-Type: application/json'

https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443 部分は先述の BONSAI_URL の値に置き換えて指定してください。以下同様)

次に検索対象となる日本語データをまとめてバルクアップロードします。データファイルは japanese.json です(厳密には JSON フォーマットではなく、複数の JSON が繋がっているフォーマットです。そのため後述のコマンドで特殊な Content-Type を指定する必要があります)。このファイルを以下の curl コマンドで Bonsai Elasticsearch に POST します:
$ curl -XPOST https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_bulk --data-binary @japanese.json -H 'Content-Type: application/x-ndjson'

(Content-Type の指定値が 'application/json' ではなく、'application/x-ndjson' となっている点に注意してください)

これで Bonsai Elasticsearch に日本語インデックスと日本語データが格納されました。これらが正しく日本語で検索できるかどうか(このブログの本来の目的でいえば、5種類の曖昧な日本語検索をしても同じ結果になるかどうか)を確認してみます。5種類の検索クエリーを5つのファイル(query1.json, query2.json, ..., query5.json)で用意したので、5回に分けてそれぞれを実行し、その結果を result1.json, result2.json, ..., result5.json に格納します:
$ curl -XGET https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_search -d @query1.json -H 'Content-Type: application/json' > result1.json

$ curl -XGET https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_search -d @query2.json -H 'Content-Type: application/json' > result2.json

$ curl -XGET https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_search -d @query3.json -H 'Content-Type: application/json' > result3.json

$ curl -XGET https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_search -d @query4.json -H 'Content-Type: application/json' > result4.json

$ curl -XGET https://username:password@xxxxxxxx.us-east-1.bonsaisearch.net:443/my_suggest/_search -d @query5.json -H 'Content-Type: application/json' > result5.json

ちなみにこれらの検索の内容ですが、「日本」という文字を多く含む日本語データがある程度格納されている状況下で、query1.json では「日本」を、query2.json では「にほn」を、query3.json では「にhん」を、query4.json では「にっほん」を、そして query5.json では「日本ん」を検索します。このように入力ミスまで考慮した曖昧な検索をしても同じ検索結果になるようなインデックスとマッピングを作る、という内容でした。実際に result1.json から result5.json まで見ると、検索結果はどれも一致しているはずです。


・・・というわけで、無料の Bonsai Elasticsearch (と導入済みの kuromoji)を使って日本語インデックスで日本語データを期待通りに検索できることがわかりました。こりゃ、いいモン見つけたかも!



このページのトップヘ