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

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

タグ:ibm

このブログエントリは IBM Cloud アドベントカレンダー 2023 に参加しています。12/20 ぶんの記事です(このブログエントリ自体は 12/14 に公開します):
2023112901



IBM Cloud から提供されている機械学習型チャットボット生成の Watson Assistant が、ブランド名称変更により Watsonx Assistant となりました:

https://www.ibm.com/jp-ja/products/watsonx-assistant
2023121401


"Watson Assistant" で検索した結果からブラウズした場合は新ブランドページに推移します:
2023121402



私も「ブランド名の変更があっただけで、中身は変わってないよね?」と思っていました。結論として機能的にはそれほど大きく変わったわけではないのですが、新しくインスタンスを作成した直後の画面が変わっていて、従来のダイアログ機能(インテントやエンティティを定義して会話の流れを作るツールとその機能)を利用する方法が分かりにくくなっていると感じました。これまでの Watson Assistant では普通に使えていたダイアログ機能を新しい UI で使えるようになるまでの手順を以下に紹介します。


まず最初の(名前設定などの)初期設定を一通り実行します。その後に画面左のメニューから下の方にある "Assistant settings" を選択します:
2023121403


画面を下の方にスクロールすると、"Activate dialog" と書かれたボタンがあるのでクリックして、アクティベートします:
2023121404


アクティベート後に再度左メニューを開くと、先ほどまでは表示されていなかった "Dialog" という項目が表示されているはずです。これをクリック:
2023121405


これで従来のダイアログ設計画面が表示されます。ここからインテントやエンティティといった要素を定義できます:
2023121406


一瞬「あれ?ダイアログ無くなった??」とビビりました。 (^^;  よかったよかった。


ちょっとした UI 系トラブルに巻き込まれた結果とある機会に CLI 操作だけで IBM Cloud 上に Openshift クラスタ(いわゆる "ROKS")を作る必要が生じて、実際に試してみました。以下、その時に実行したコマンド群を順に紹介します。


【前提条件】
やりたかったことは単純に「VPC(Virtual Private Cloud)環境内に OpenShift クラスタを1つ作る」ことでした。既に VPC 自体はサブネット含めて作成済みで、バックアップストレージの Cloud Object Storage インスタンスも作成済みです。 後はこの VPC 内に OpenShift クラスタをスペックやワーカーノード数を指定して作るだけ、という状況です。


【CLI コマンド】
以下 CLI コマンドを記載します。ここまでに "ibmcloud login" は実行済みであるとします。

詳しくは下記参考ページを参照いただきたいのですが、VPC 内に OpenShift クラスタを作るための CLI コマンドは以下のようになります:
$ ibmcloud oc cluster create vpc-gen2 --name (クラスタ名) --zone (作成先ゾーン名) --vpc-id (作成先 VPC ID) --subnet-id (作成先サブネット ID)  --flavor (ワーカーノードのフレーバー) --version (OpenShift バージョン) --cos-instance (Cloud Object Storage の CRN) --workers (1ゾーンあたりのワーカーノード数)

で、このコマンドを実行するためには(上記コマンド内にも括弧がたくさんあるように)指定する必要がある多くのパラメータ情報を事前に集めておく必要があります。というわけでまずはパラメータ情報を収集するための CLI コマンドから説明します。

まず "--name" パラメータで指定する「クラスタ名」は自分で自由に指定することができるので説明は不要だと思います。次に "--zone" パラメータで指定する「作成先ゾーン名」ですが、これは目的のゾーンが例えば「大阪3」であったとして、この「大阪3」を指定するための文字列です。これを調べるには次のコマンドでゾーン一覧を取得して、そこから目的のゾーンの ID を取り出します(青字が入力コマンドです):
$ ibmcloud oc zone ls --provider vpc-gen2
OK
ID           Name         Metro             Flavors
au-syd-1     au-syd-1     Sydney            -
au-syd-2     au-syd-2     Sydney            -
au-syd-3     au-syd-3     Sydney            -
br-sao-1     br-sao-1     Sao Paulo         -
  :
eu-gb-3      eu-gb-3      London            -
jp-osa-1     jp-osa-1     Osaka             -
jp-osa-2     jp-osa-2     Osaka             -
jp-osa-3     jp-osa-3     Osaka             -
jp-tok-1     jp-tok-1     Tokyo             -
jp-tok-2     jp-tok-2     Tokyo             -
jp-tok-3     jp-tok-3     Tokyo             -
us-east-1    us-east-1    Washington D.C.   -
  :

この結果から「大阪3」を使う場合に指定するゾーン名が "jp-osa-3" であることが分かります。

次に作成先の「VPC ID」です。VPC が決まっていても、その ID を取り出して指定する必要があります。これは以下のコマンドを実行することで取り出せます:
$ ibmcloud oc vpcs --provider vpc-gen2
OK
Name              ID                                          Provider
xxxxxxx-vpc-osa   ****-********-****-****-****-************   vpc-gen2
  :

目的の VPC 名と照らし合わせることで ID を確認することができます("****-********-****-****-****-************" という値であったと仮定します)。

作成先の「サブネットID」も調べる必要があります。普段は名称で扱っていて、ID を意識することがあまりないのですが CLI 操作時には必要な情報です。これは以下のコマンドで "xxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" であることが確認できます:

$ ibmcloud oc subnets --provider vpc-gen2 --vpc-id (VPC ID) --zone (ゾーン名)
OK
Name                ID                                          Public Gateway Name                        Public Gateway ID                           IPv4 CIDR Block   Available IPv4 Addresses
sn-xxxxxxxxxxx-03   xxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx   pgw-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   10.xxx.xxx.0/24   244

ワーカーノードのフレーバー(スペック)も実行時に指定する必要のある情報です。これは以下のコマンドでフレーバーの一覧を取得し、目的のフレーバーの ID を取り出します。今回は "bx2.16x64" というスペックのフレーバーを使うことにします:
$ ibmcloud oc flavors --zone (ゾーン名) --provider vpc-gen2
OK
For more information about these flavors, see 'https://ibm.biz/flavors'
Name           Cores   Memory   Network Speed   OS             Server Type   Storage
  Secondary Storage   Flavor Class   Provider
bx2.16x64      16      64GB     24Gbps          UBUNTU_20_64   virtual       100GB     0B, *               bx2            vpc-gen2
bx2.2x8†       2       8GB      4Gbps           UBUNTU_20_64   virtual       100GB     0B                  bx2            vpc-gen2
bx2.32x128     32      128GB    25Gbps          UBUNTU_20_64   virtual       100GB     0B, *               bx2            vpc-gen2
b
  :
  :

OpenShift のバージョンも指定する必要がある項目ですが、これは普通に "4.11_openshift" などと指定できます。またゾーンあたりのワーカーノード数も普通に "2" などと数字で指定可能です。

最後に Cloud Object Storage の CRN を取得します。これは取得が面倒なのですが、作成済みリソースの一覧を取得し、そこから目的の Cloud Object Storage サービスを探して、その ID を見つける、という方法で取得します:
$ ibmcloud resource service-instances --long
OK
   :
   :
ID:                 crn:v1:bluemix:public:cloud-object-storage:global:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx::

GUID:               xxxxxxxxxxxxxx


Name                Cloud Object Storage for me


Location            global
   :
   :

これで OpenShift クラスタを作成するために必要な最低限の情報が揃いました:
情報
ゾーン名jp-osa-3
VPC ID****-********-****-****-****-************
サブネット IDxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ワーカーノードのフレーバーbx2.16x64
OpenShift バージョン4.11_openshift
1ゾーンあたりのワーカーノード数2
Cloud Object Storage の CRNcrn:v1:bluemix:public:cloud-object-storage:global:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx::



これらの情報を使って以下のコマンドを実行します:
$ ibmcloud oc cluster create vpc-gen2 --name (クラスタ名) --zone jp-osa-3 --vpc-id ****-********-****-****-****-************ --subnet-id xxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  --flavor bx2.16x64 --version 4.11_openshift --cos-instance crn:v1:bluemix:public:cloud-object-storage:global:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:: --workers 2
Creating cluster...
OK
Cluster created with ID ***************************

成功すると上述のようにクラスタ ID("***************************")が表示され、「デプロイ中」のステータスとなります。


なお、マルチゾーンで作成する場合は上記の作業を行ってシングルゾーンでクラスタを作成した上で、追加するゾーンのゾーン名とサブネット ID を取得してから以下のコマンドを実行してワーカープールにワーカーノードを追加します:
$ ibmcloud oc zone add vpc-gen2 --zone (追加するゾーン名) -c (クラスタ名) --worker-pool (追加先のワーカープール名) --subnet-id (サブネットID)
OK

これで IBM Cloud のダッシュボード画面にアクセスできなくてもしなくても、CLI だけで Openshift クラスタを作ることができそうです。


【参考】
https://cloud.ibm.com/docs/openshift?topic=openshift-cluster-create-vpc-gen2&interface=cli


 

先日このブログでも紹介した IBM watsonx.ai のベータ版 REST API を使って、Node-RED のカスタムノードを作り、公開してみました。以下で使い方を紹介しますが、実際に利用するには前回 REST API を利用する紹介をした時のブログでも作成した Watson Studuio と Watson Machine Learning を使ったプロジェクトを登録しておく必要があります(そのプロジェクトの ID が必要です)。またこれも同様に前回紹介しましたが、実行するユーザーの IBM Cloud IAM API キーを生成/取得しておく必要があるのでこちらも準備の上で試してみてください。

なお以下で紹介する内容では Node-RED v2.2.3 を使って動作確認しています。また私が提供しているものは公式のものではなく、あくまで個人的に開発したものを個人的に公開しているだけであること、現時点で提供されている REST API 自体がベータ版のため、今後近い将来の API 変更などにより動かなくなる可能性があることをご了承ください(とはいえ、私も使うので、少なくとも公式ノードが出るまではなるべくタイムリーにメンテナンスするつもりです)。


【パレットに watsonx.ai ノードを追加】
Node-RED のパレット画面を開き、右上のメニューから「パレットの管理」を選択してパレットのカスタマイズ画面に移動します。ここで「ノードを追加」タブを選び、"node-red-contrib-dotnsf-watsonxai" を検索してください(2023年7月20日時点では "watsonx" と検索すると1つだけ(!)見つかるのがそれです)。検索できたら「ノードを追加」ボタンをクリックします:
2023072001


以下のような確認ダイアログが表示されるので「追加」をクリックします:
2023072002


正しくインストールされると「ノードをパレットへ追加しました」というメッセージが表示されます。パレット管理画面を閉じます:
2023072003


元のパレット画面に戻ると、"watsonx.ai" というノードが追加されているはずです:
2023072004


【watsonx.ai ノードを使う】
では追加された watsonx.ai ノードを使ってみます。watsonx.ai ノードの前後に適当なノードを追加します。以下ではシンプルで分かりやすい代表として、inject ノードと debug ノードで挟んでみました:
2023072005



watsonx.ai ノードにデータを渡すノード(上図では inject ノード)からは、msg.payload にいわゆる「プロンプト」のテキストを指定します。watsonx.ai ノードは自分に送られてきたデータの msg.payload にプロンプト指定がされている前提でそのテキスト内容に書かれた指示の返答テキストを生成します。

以下の例では msg.payload の内容を(前回のブログで紹介した内容に合わせて)以下のようにしてみました:
入力:\nAbout Watson Discovery\\nIBM Watson® Discovery is an intelligent document processing engine that helps you to gain insights from complex business documents.\n翻訳\n

2023072006


入力内容が英語で書かれていて、最後に「翻訳(してね)」と指示している内容です。watsonx.ai ノードの手前のノード(inject ノード)の設定内容は以上です。

次に watsonx.ai ノード自体にも設定が必要です。watsonx.ai ノードをダブルクリックしてプロパティ設定画面を開き、API Key と Project ID をそれぞれ入力します:
2023072007


このうち API Key は IBM Cloud の IAM API キーの値で、Project ID は Watson Studio と Watson Machine Learning で作成したプロジェクトの ID です。これらも前回のブログで紹介したものと同じ内容の問い合わせを行うサンプルなので、この作業について詳しくはこちらも参照してください。


全ての設定が完了したら作成したフローを「デプロイ」します:
2023072008


デプロイ後に inject ノード左のボタンをクリックしてメッセージを送信すると、watsonx.ai ノードが API キー、プロジェクト ID 、そしてプロンプトテキスト内容を元に問い合わせを行い、その結果が画面右のデバッグ画面内に表示されます:
2023072009


私が 2023 年7月20日に試した際の結果は "Watson Discoveryはビジネスドキュメントに関する意見を得るための知能型ドキュメント処理エンジンです。" と表示されました。上述の英語文章を翻訳してほしい、というプロンプトだったので、質問意図を正しく理解して実行できているようでした。


(いずれにせよベータ版 API を使うことになりますが)REST API を直接実行することでカスタムアプリケーションを作ることもできますが、今回紹介した Node-RED のカスタムノードを使うことで、より手軽に watsonx.ai のテキスト生成機能を体験できるようになったのではないかと思っています。


IBM の新しい AI プラットフォームである IBM watsonx.ai (「ワトソンエックス・エーアイ」)が公開され、IBM Cloud のライトプラン(無料)でもある程度使うことができそうなことがわかったので、まだあまりドキュメントが整備されていなさそうな REST API について調べてみました。そして調べた結果わかった情報を使って、実際に REST API を使って動くサンプルアプリを作り、公開してみました。
watsonx.ai



【事前準備】
このブログエントリに書かれた内容を全て試すためには、IBM Cloud のアカウントが必要です。まだ所有していない場合はこちらから登録してください。なお 2023 年7月時点では新規登録の際にクレジットカード情報の入力が必須です(このブログで紹介する内容は無料範囲でも可能ですが、カードの登録は必須です)。

また今回はプロンプトを使った質問を入力して、その内容に対する回答を取得する、という内容の REST API を実際に動かすのですが、そのためには Watson StudioWatson Machine Learning という2つのサービスを利用する必要があります(いずれも従量課金型のサービスですが、ライトプランを使うことである程度まで無料で使うことができます)。 IBM Cloud のアカウントでログイン後にこれら2つのサービスインスタンスを作っておいてください。


【環境構築】
では実際にプロンプトで指示を行うための環境を構築します。IBM Cloud にログインし、リソースリスト(左メニューの上から2番目)から「AI/機械学習」カテゴリを参照します。他の用途で既に IBM Cloud を使っている場合はここにいくつかのサービスインスタンスが並んでいるかもしれませんが、少なくとも上述の事前準備が完了していれば "Watson Machine Learning" と "Watson Studio" の2つのインスタンスは表示されているはずです。これら2つが表示されていることを確認した上で(実際はどっちでもいいんですが) "Watson Machine Learning" の方をクリックします:
2023071700


クリックした "Watson Machine Learning" インスタンスが表示されます。ここで「IBM Cloud Pak for Data で起動」と書かれた青いボタンをクリックします:
2023071701


初回はこのような画面が表示されます。少し待ちます:
2023071701


少し待つとこのような画面が表示されます。「ML モデルの作成と管理」というダイアログが表示されていますが、実はこの時点では watsonx.ai ではなく IBM Cloud Pak for Data という別プラットフォームの画面になっています。プラットフォームを切り替えるため、この画面では「キャンセル」をクリックします:
2023071702


プラットフォームの切り替えは画面右上のメニューから行うことができます。画面右上から「IBM watsonx.ai」を選択し直します。これでプラットフォームが watsonx.ai 用のものに切り替わります:
2023071703


はじめて watsonx.ai を使う時にはこのような画面が表示されます。利用規約にチェックを入れ、また初回はまだプロジェクトがないので(先に進むためにはプロジェクトが最低1つ必要なので)「サンドボックスプロジェクトの作成」ボタンをクリックします(既に利用済みで、自分のプロジェクトが存在している場合はそのプロジェクトを選択しても構いません):
2023071704


プロジェクトが選択されていると、このような画面(プロジェクトの初期画面)になります。プロンプト指示を試す場合は、一番左の「ファウンデーション・モデルを・・」と書かれたプロンプト・ラボを選択します:
2023071705


プロンプト・ラボを始めて実行するとこのようなダイアログが表示されます。内容を確認して全てチェックします。説明を確認する場合は「ツアーを開始」をクリックしてもよいのですが、飛ばす場合は「ツアーのスキップ」をクリックします。これで環境構築は一通り完了です:
2023071706



【プロンプト実行】
ここまで正しく実行すると下のような画面になり、プロンプト指示を実際に試すことができるようになります。なお特に今回は REST API を操作することを目的としているので、プロンプトを試す前に画面右の「コードの表示」メニューをクリックしておきます:
2023071704


すると画面右側に curl の実行コマンドが表示されます。この後プロンプトで入力をすることになるのですが、その入力処理を curl で実行した場合の実行内容(接続 URL やヘッダ、データの中身)を確認することができるので、この内容を参考に REST API のプログラミングができるようになります:
2023071705


参照ページの例を参考にプロンプトで指示を出してみます。すると実行結果が返ってくるだけでなく、その結果を得るために実行された curl コマンドの REST API パスが /ml/v1-beta/generation/text?version=2023-05-29 であったことや、日本語で指示している場合も特に日本語であることを明示するパラメータが送られていないことを含め、どのような REST API が実行されていたかがわかります:
2023071706


【アクセストークンの取得】
後はこれと同じことを自分のプログラミングの中で実装すればよい、、のですが、この REST API を実行する上で欠くことのできない2つのパラメータがあります:
2023071707


1つは "project_id" というパラメータです。これは名前の通り「プロジェクトのID」で、プロンプト実行前に指定したプロジェクトを一意に示す ID です。この値はプロンプト実行時にブラウザが参照している URL を見ると、URL パラメータの1つとして指定されていることがわかります(つまりブラウザの URL から取得することができます):
2023071708


問題はもう1つの "YOUR_ACCESS_TOKEN" (つまりアクセストークン値)です。これはこの文字列をそのまま使っても正しく実行できません。しかもこの値はウェブブラウザを参照するなどの方法では取得できず、IAM API キーと呼ばれる値とプログラミングによって動的に取得する必要があるものです。この取得方法については本ブログの趣旨とは異なるので詳しくは解説しませんが、詳しくはこちらのドキュメントを参照してください(後述のサンプルでもこの方法でアクセストークンを取得しています)。


【サンプル】
ここまでに記載した情報を使って、実際に動く Node.js のサンプルアプリケーションを作って公開しました:
https://github.com/dotnsf/watsonx


サンプルといっても実体は "POST /api/generate_text" というエンドポイントを1つだけ実装した Swagger ドキュメントベースの API アプリケーションです。起動時に IAM API キーやプロジェクト ID を環境変数に指定することもできますし、API 実行時にパラメータで指定することもできます。

中身を簡単に説明すると、実装はほぼこの app.js ファイル1つだけで、アクセストークンの取得は getAccessToken() 関数で、テキスト生成(プロンプトの実行)は generateText() 関数で実装しています。興味ある方はこれらの関数内の実装部分を参考にしてください(といっても私も上の方法で知った curl コマンドとそのパラメータ指定をそのまま Node.js 内で使ってるだけなんですけど)。

サンプルアプリケーションを実行するには Node.js インストール済みの環境でソースコードを "git clone" して、"npm install" して、.env ファイルに API キーとプロジェクト ID を保存後に "npm start" するとアプリケーションが 8080 番ポートで起動するので、"http://localhost:8080/_doc" にアクセスすると Swagger ドキュメントが開きます:
2023071701


唯一の API である "POST /generate_text" 部分をクリックして開いて "Try it" ボタンをクリックするとパラメータ設定ができる画面になります。API Key や Project ID 、Model ID は環境変数で指定してあればここでは空のままで構いません。必須入力項目といえるのは Input 値くらいで、ここにプロンプトの内容を記載します。最後に "Execute" ボタンで実行します:
2023071702


正しく実行されると、API の実行結果が下部に表示されます。ちなみにこの例では Input が「入力:\nAbout Watson Discovery\\nIBM Watson® Discovery is an intelligent document processing engine that helps you to gain insights from complex business documents.\n翻訳\n」で、その結果が「Watson Discoveryはビジネスドキュメントに関する意見を得るための知能型ドキュメント処理エンジンです。」でした。どこにも「日本語サポート」とも書かれていないし、「日本語で翻訳」とも指定していないのにここまでできるのはそこそこ日本語でもプロンプトに書かれた意図を理解する力があると思っています。なお、このアプリケーションから実行する場合、 REST API 実行時のパラメータで max_new_tokens の値を(デフォルトの 20 から)100 に変更しています。日本語の場合、20 程度だとまともなある程度長い文章を返せなくなってしまうようで、このようにしています:
2023071703


ちなみにこのサービスを無料のライトプランで使う場合、1か月で使えるトークン数は 25000 だそうです。自分がこれまでにどのくらいのトークンを消費しているかは、IBM Cloud のプロジェクト選択画面で「管理」タブから「リソース使用率」を選択した先の画面で確認することができます。ご利用は計画的に:
2023071704


なお REST API のパス(/ml/v1-beta/generation/text)をみても分かると思いますが、現在の API は v1 のベータ版であり、近い将来に仕様含めて変更する可能性が高いと思っています。その辺りもご注意の上で参照してください。


【参照】
IBM watsonx.ai がやってきた
IBM watsonx.ai を試してみた ( コピペ OK )
Documentation ( IBM watsonx.ai 用)



以前のブログエントリで OpenShift 関連の以下2つの紹介をしていました:
IBM Cloud の VPC(Virtual Private Cloud) でプライベート OpenShift クラスタ環境を構築する
IBM Cloud の仮想プライベートクラウド OpenShift 上にデプロイしたコンテナアプリに HTTP パブリックアクセスを許可する


前者は IBM Cloud の VPC(Virtual Private Cloud) 環境を使って、プライベートな OpenShift クラスタ環境を作る手順(と VPN を使ったプライベート OpenShift へのアクセス手順)を紹介しました。 また後者ではこのプライベート環境を使ってデプロイしたプライベートなアプリケーションに対して、VPN なしでパブリックインターネットアクセスを(HTTPS ではなく、HTTP で)行うための設定手順を紹介しました。

今回のブログエントリではこの最終形ともいえる「デプロイしたアプリケーションに対する HTTPS によるアクセス」を実現するための手順を紹介します。上で紹介した2つのエントリの、後者に近い内容ではありますが、純粋な続きというわけではありません。前者の環境が構築済みであるという前提のもとで、oc CLI を使ったアプリケーションのデプロイと、プライベートアクセスを可能にするための手順を紹介していきます。


【3通りの方法】
今回のブログエントリの結論めいたことを最初に紹介します。この「プライベートクラウドにデプロイしたアプリケーションにパブリックにアクセスする」には目的や制約に応じた3通りの方法があります(うち(1)は前回説明済み):
(1)HTTP でパブリックアクセスする(前回説明
(2)HTTPS でパブリックアクセスする(自己証明書を使う場合)
(3)HTTPS でパブリックアクセスする(有効な証明書を使う場合)

要は「HTTP でいいのか? HTTPS が必要か?」「HTTPS の場合、自己証明書でいいか?NGか?」という2つの考慮点があり、その要件によって選択肢を選ぶ必要がある、ということです。「この中でどれか?と言われたら(3)でしょ?」と思うかもしれませんが、それぞれに制約事項があったりして(例えば(1)、(2)は IBM Cloud 内の設定のみで実現できますが、(3)の場合はカスタムドメインが別途必要になります)、検証目的なのか本運用なのか、といった背景によっても選択肢は変える必要が生じるものだと思います。

以下では、最終的には(3)を実現する前提として、(1)から順に実現手順を紹介します。なお(1)と同じことを前回紹介しているのですが、(3)まで行うことを考えると(1)も少し手順を変える必要があるので、改めて(1)から実現手順を、以下で順に紹介します。


【0. 事前準備】
まず事前準備としてプライベートクラウドでの OpenShift クラスタ環境と、同環境にアクセスするための VPN が必要です。これらの構築手順はシリーズ初回で紹介しているので、こちらの手順を参照してプライベートクラウドの OpenShift クラスタと、同クラスタにアクセスするための VPN 環境を作っておいてください。

ここまでが完了している前提で以下の説明を続けます。


【1. プライベート OpenShift クラスタにアプリケーションをデプロイし、パブリックアクセスのパスを定義する】

2023020205


以前にも紹介した内容ですが、今回は HTTPS でのアクセスまでを考慮した手順を紹介します。そのため、HTTP でアクセスするこの部分から以前とは異なる、oc CLI コマンドと yaml ファイルを使ってアプリケーションをデプロイする手順を紹介します。

まず前提として、以下のようなプライベート OpenShift クラスタ環境が IBM Cloud 内に作られているものと仮定します:

OpenShift クラスタ名: kkimura-mycluster-jp-osa-2-priv
2023020101


Ingress が定義したサブドメイン名: kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud
2023020102


アプリケーションをデプロイする OpenShift プロジェクト(ネームスペース): default

OpenShift にデプロイするアプリ:DockerHub 上の ibmcom/guestbook:v1
2023020103


※ちなみにこの ibmcom/guestbook をデプロイしてウェブブラウザでアクセスするとこのような↓画面が表示されます。この画面が表示されればデプロイとアクセスが成功している、と判断してください:
2023020101



以下では上述の名称や設定を前提として説明します。適宜自分の環境での名称や設定に読み替えてください。

では実際に oc CLI を使って、アプリケーションをプライベート OpenShift クラスタにデプロイします。まずはプライベート環境へアクセスする VPN を有効にした上で「OpenShift Web コンソール」を開きます(VPN が有効でないと開けません):
2023020104


OpenShift Web コンソールが開きます。今回は oc CLI と呼ばれるコマンドラインツールを使ってアプリケーションのデプロイや設定変更を行うので、まだこのツールをインストールしていない場合は導入が必要です。OpenShift Web コンソール画面上部のクエスチョンマークをクリックし、コンテキストメニューから「コマンドラインツール」を選択して、自分の PC 環境にあった oc CLI ツールをダウンロード&インストールしておいてください:
2023020108


同様にして ibmcloud CLI コマンドも(未導入の場合は)インストールしておきます:
https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli&locale=ja


oc CLI ツールと ibmcloud CLI ツールがインストールできている状態で、改めて OpenShift Web コンソール画面右上の名前部分をクリックしてコンテキストメニューを開き、「ログインコマンドのコピー」を選択します:
2023020105


新しいウィンドウが開くと、"Display Token" とだけ表示されているので、この部分をクリックしてトークン情報を参照します:
2023020106


するとトークン情報が書かれたページが表示されます。この中の "Log in with this token" で書かれた部分を(この後ペーストするので)コピーします:
2023020107


ターミナルを開き、さきほどコピーした内容をペーストして Enter キーを押すと、ターミナル画面で IBM Cloud の OpenShift 環境にログインできます:
2023020109


同様にして ibmcloud CLI ツールからも IBM Cloud へのログインを行っておきます:
$ ibmcloud login


ではここからプライベート OpenShift クラスタ内にアプリケーションをインストールします。改めて以下のような内容の yaml ファイル(guestbook_deployment.yaml)を用意します:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guestbook
  labels:
    app: guestbook
spec:
  replicas: 1
  selector:
    matchLabels:
      app: guestbook
  template:
    metadata:
      labels:
        app: guestbook
    spec:
      containers:
      - name: guestbook
        image: ibmcom/guestbook:v1
        imagePullPolicy: Always
        env:
          - name: NODE_ENV
            value: production
---
apiVersion: v1
kind: Service
metadata:
  name: guestbook-svc
  labels:
    app: guestbook
spec:
  type: ClusterIP
  ports:
  - port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: guestbook
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None


この yaml ファイルは Deployment, Service, Route を1つずつ含んでいて、これ一つでアプリケーションをデプロイして、クラスタ外に公開して、ホスト名でアクセスできるようなルーティングを設定しています。数か所ある太字部分を補足します。

まず Deployment 内の image 名で ibmcom/guestbook:v1 と記載している部分、これがデプロイするアプリケーションのイメージです。別のイメージを使う場合はここを変えてください。

また数か所ある 3000 という数字、これは ibmcom/guestbook アプリが内部的に待ち受けるポート番号です。使うアプリイメージによって変わるので、ここもそのアプリ向けの正しい値を指定してください。

最後に Route 内の host 名、ここは (アプリ固有の名).(Ingress サブドメイン) を指定します。今回の例ではアプリ固有の名として guestbook とし、Ingress サブドメインは上記で調べた値(kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud)を使っています。

では用意した yaml ファイルを使って実際にプライベート OpenShift にデプロイします:
$ oc apply -f guestbook_deployment.yaml

成功したら上述の host 名に HTTPS でアクセスし、期待通りに動作することを確認します(この時点でプライベートクラウドへはデプロイできました):
2023020101


次にこのアプリケーションをパブリッククラウドから(最初は HTTP で)アクセスできるように設定します。大まかな手順としては以下のことを行います:
・OpenShift 内にパブリック・ルーター(の Pod)とパブリック・ロードバランサーを作成し、
・パブリック・ロードバランサー → パブリック・ルーター を経由して、アプリケーションだけにアクセスできるようなパスを通し、
・パブリック・ロードバランサーの外部ホスト名でアプリケーションを公開できるよう DNS を変更する


では順に実行していきます。ロードバランサーを OpenShift のパブリックネットワーク側に作りたいのですが、その際の(パブリックネットワーク用の)サブドメインを決める必要があります。まずは以下の ibmcloud コマンドを実行します:
$ ibmcloud oc nlb-dns ls --cluster kkimura-mycluster-jp-osa-2-priv
(最後のパラメータは OpenShift クラスタ名)

(以下、実行結果例)
OK
Subdomain                                                                                          Target(s)                            SSL Cert Status   SSL Cert Secret Name                                             Secret Namespace    Status
kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud   59aa36c3-jp-osa.lb.appdomain.cloud   created           kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000   openshift-ingress   OK

この実行結果例では1行しか表示されていませんが、複数行表示されることもあります。で、この結果の Subdomain を見ると "i000" と表示されている部分があります。この "i" で始まって数字が3桁続くサブドメインはプライベートネットワークであることを示しています。

新たにパブリックなサブドメインを追加したいのですが、その名称は、
・↑の方法でプライベートネットワークのサブドメインのうち、"i" 以降の数字が最も大きいもの(今回であれば "000")を探して、
・その数字に1を追加し、"i" をゼロ(0)にしたもの(今回であれば "0001")
にします。つまり今回の例であれば、
kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud

をパブリック用のサブドメインとします。

パブリック用のサブドメインが決まったら、以下のような yaml ファイル(ingresscontroller.yaml)を作って実行し、パブリックネットワークにパブリックルーターとパブリックロードバランサーを作ります(太字部分はパブリック用サブドメイン):
apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
  name: public
  namespace: openshift-ingress-operator
spec:
  replicas: 2
  domain: kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud
  endpointPublishingStrategy:
    loadBalancer:
      scope: External
    type: LoadBalancerService
$ oc apply -f ingresscontroller.yaml

そして以下を実行し、結果の "EXTERNAL-IP" を参照します:
$ oc get svc router-public -n openshift-ingress

NAME            TYPE           CLUSTER-IP       EXTERNAL-IP                          PORT(S)                      AGE
router-public   LoadBalancer   172.21.191.252   5ca5ada9-jp-osa.lb.appdomain.cloud   80:31127/TCP,443:30992/TCP   32h

この例では EXTERNAL-IP は "5ca5ada9-jp-osa.lb.appdomain.cloud" となっています。これがパブリックロードバランサーのホスト名となります。

このサービスのパブリック VPC ホスト名を DNS に登録します。以下のコマンドを実行します:
$ ibmcloud oc nlb-dns create vpc-gen2 --cluster (OpenShiftクラスタ名) --lb-host (ロードバランサーホスト名) --type public

※今回の例だと、
 OpenShift クラスタ名: kkimura-mycluster-jp-osa-2-priv
 ロードバランサーホスト名: 5ca5ada9-jp-osa.lb.appdomain.cloud

最後にアプリのサービスをパブリックルーターで expose します:
$ oc expose svc/guestbook-svc --name guestbook-public --hostname guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud

これでサービス(guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud)がパブリックネットワーク上で HTTP で公開されました。いったん VPN を切ってから、パブリックホスト名(http://guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud)に HTTP でアクセスできることを確認してみましょう:
2023020201


【2. プライベート OpenShift クラスタにアプリケーションをデプロイし、自己証明書の HTTPS アクセスができるよう定義する】

2023020206


1. で説明した作業の続きとして、パブリックロードバランサー名(今回であれば "5ca5ada9-jp-osa.lb.appdomain.cloud")で自己証明書を使った HTTPS アクセスを可能にします。

そのためにはアプリケーションのデプロイ時に使った yaml ファイル(guestbook_deployment.yaml)を少し改良します。具体的には以下の青字部分を(自分の環境のホスト名やパブリックロードバランサー名を使って)追加します:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guestbook
  labels:
    app: guestbook
spec:
  replicas: 1
  selector:
    matchLabels:
      app: guestbook
  template:
    metadata:
      labels:
        app: guestbook
    spec:
      containers:
      - name: guestbook
        image: ibmcom/guestbook:v1
        imagePullPolicy: Always
        env:
          - name: NODE_ENV
            value: production
---
apiVersion: v1
kind: Service
metadata:
  name: guestbook-svc
  labels:
    app: guestbook
spec:
  type: ClusterIP
  ports:
  - port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: guestbook
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: 5ca5ada9-jp-osa.lb.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None

これによりパブリックロードバランサー(5ca5ada9-jp-osa.lb.appdomain.cloud)への HTTPS アクセスが可能になります。ただ証明書が自己証明書のため、アクセス制約がかかります。この yaml ファイルでデプロイしなおします(VPN を切ったままの場合は再接続してから):
$ oc apply -f guestbook_deployment.yaml


そしてブラウザでアクセスしてみます。具体的には以下のようになります。VPN を切断後にブラウザで https://5ca5ada9-jp-osa.lb.appdomain.cloud にアクセスすると、以下のような画面になります。「詳細設定」をクリックします:
2023020202


証明書が自己証明書なので安全ではない、というメッセージが表示されます。安全ではないことを理解した上でアクセスしてみます:
2023020203


すると、(保護はされていないけれど)一応 HTTPS でアクセスできることが確認できます:
2023020204


このパブリックロードバランサーのドメインは OpenShift 環境から自動発行されたもので、内部的に使うぶんにはこれで問題ないのですが、ユーザーが直接使う場合はちょっと気になる部分ではあります:
2023020205


後述の手順で正しい証明書を使った HTTPS アクセス方法を紹介しますが、こちらにも別の制約事項があるので、両方理解した上でどちらを使うべきか検討してください。


【3. プライベート OpenShift クラスタにアプリケーションをデプロイし、カスタムドメインの証明書を使って HTTPS アクセスができるよう定義する】

2023020207


最後のケースは HTTPS としては完成形のような位置づけですが、これまでのケースにはない制約事項が1つあります。それは「カスタムドメインを使う」必要がある点です。2. では IBM Cloud 発行のドメインを使って HTTPS ができなかったのを、自前のドメインを使うことで回避する方法になります。

例えば "pi314.jp" というカスタムドメインを使うことにして、2. までに説明してきたアプリケーションを "guestbook.pi314.jp" というホスト名で HTTPS アクセス可能にする、という想定で以下の説明を行います。

まずカスタムドメインの DNS を設定します。契約したドメイン会社や DNS 移管先によって具体的な設定手順は異なりますが、guestbook という CNAME に対して、パブリックロードバランサー名を設定します(下図は CloudFlare.com を使った場合のスクリーンショット):
2023020206


更にデプロイ用の guestbook_deployment.yaml をもう一度改良して、赤字部分を更に追加します:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guestbook
  labels:
    app: guestbook
spec:
  replicas: 1
  selector:
    matchLabels:
      app: guestbook
  template:
    metadata:
      labels:
        app: guestbook
    spec:
      containers:
      - name: guestbook
        image: ibmcom/guestbook:v1
        imagePullPolicy: Always
        env:
          - name: NODE_ENV
            value: production
---
apiVersion: v1
kind: Service
metadata:
  name: guestbook-svc
  labels:
    app: guestbook
spec:
  type: ClusterIP
  ports:
  - port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: guestbook
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-osa.containers.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.kkimura-mycluster-jp-osa-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-osa.containers.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: 5ca5ada9-jp-osa.lb.appdomain.cloud
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: guestbook-route
  labels:
    app: guestbook
spec:
  host: guestbook.pi314.jp
  to:
    kind: Service
    name: guestbook-svc
    weight: 100
  port:
    targetPort: 3000
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
  wildcardPolicy: None

この yaml ファイルを(VPN が切れている場合は再接続してから)再度適用します:
$ oc apply -f guestbook_deployment.yaml

そして VPN を切断し、ブラウザでカスタムドメイン側に登録した名前(guestbook.pi314.jp)に HTTPS アクセスしてみましょう:
2023020207


警告などが表示されることなく、ちゃんと HTTPS でパブリックアクセスできることが確認できるはずです。


以上、コンテナ環境を運用する上で
・コンテナ管理はプライベートネットワークで運用し、
・デプロイしたアプリはパブリックネットワークから利用する

という要件はあってもおかしくなさそうですが、今回紹介したような方法で実現できそうです。自己証明書を使うページはスマホから利用するのが難しいのですが、カスタムドメインという高めのハードルを越えることができればその問題もなくなります。利用環境や目的に応じて検討してください。


このページのトップヘ