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

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

タグ:ibm

昨年、ブログを使ってこのような夏休み企画(休んでないけど)を実施しました:
「30 日後に死ぬ k8s」


IBM Cloud から提供されている k8s 環境は(ワーカーノード1つなどの条件の下で)30 日間無料で使えます。この環境を使って 30 日間で毎日1つずつ、計 30 種類のコンテナをデプロイして動かす、というものでした。後に追加で動作確認できたり、動作確認はできなかったがデプロイできてるっぽいものもあったのですが、その際も IBM Db2 は公式イメージを docker で動かすことはできたのですが、この k8s 環境ではうまくデプロイできずにいました。

が、最近になって改めて挑戦し、今度は同環境で IBM Db2 コンテナを動かすことができました。Db2 クライアントからの接続も確認できました。以前できなくて今回できた理由ははっきりとはわかっていませんが、自分の腕が上がったのか、イメージ側の更新と関係があるのか、あるいはその両方か・・・ ともあれ、遅ればせながら IBM Db2 コンテナイメージを IBM Cloud の 30 日間無料 k8s クラスタにデプロイして動かす方法を紹介します。


【30 日間無料の k8s クラスタを用意】
こちらの記事を参照して、IBM Cloud の 30 日間無料 k8s クラスタをセットアップしてください。また具体的な操作をする際に必要な ibmcloud CLI や kubectl CLI といったコマンドツール類のインストール手順も含まれています(リンク先最後の "$ kubectl get all" コマンドが正しく実行できる状態にしてください):
http://dotnsf.blog.jp/archives/1079287640.html


なおリンク先でも触れられていますが、この k8s クラスタ環境はシングルワーカーノードです。イメージをデプロイした後に "type = NodePort" を指定することでサービスを公開できます(Ingress などが提供されていないため、他の方法でサービス公開はできません)。


【IBM Db2 コンテナイメージ】
公式な IBM Db2 コミュニティ版のコンテナイメージはこちらから無料で提供されています:
https://hub.docker.com/r/ibmcom/db2


同ページ内の説明文によると、この無料コミュニティ版コンテナイメージを動かす前提条件として、「CPU: 4コア以下、メモリ: 16GB 以下」が挙げられています:
2022011801


一方で IBM Cloud の 30 日間無料 k8s クラスタのワーカーノードは「CPU: 2 コア、メモリ: 4GB」であることがわかっています:
2022011802


一応、条件を満たした環境で使う、ということになります。なおコンテナイメージのリンク先情報によると、2022/01/18 時点で特にバージョンを指定せずにこのコンテナを利用しようとすると、最新版である v11.5.7.0 が使われるようです(以下の内容はこのバージョンで動作確認しています):
2022011803



【IBM Db2 を k8s にデプロイするための yaml ファイル】
色々試行錯誤してできあがった IBM Cloud の 30 日間無料 k8s クラスタにデプロイできるよう調整した yaml ファイルはこちらです:
https://github.com/dotnsf/yamls_for_iks/blob/main/db2_deployment_nodeport.yaml

このファイルを指定して、
$ kubectl apply -f https://github.com/dotnsf/yamls_for_iks/blob/main/db2_deployment_nodeport.yaml

とすることでデプロイできます(その場合は db2inst1 ユーザーのパスワードは db2inst1 、サンプル(SAMPLE)データベースは作成、それとは別に db2inst1 ユーザーから使える mydb という空のデータベースも作成する、というオプションが初期設定されています)。

あるいは以下のように一度 wget コマンドでローカルにダウンロードしてから内容をカスタマイズして、その後に改めて kubectl コマンドでデプロイすることも可能です:
$ wget https://github.com/dotnsf/yamls_for_iks/blob/main/db2_deployment_nodeport.yaml

(ダウンロードした db2_deployment_nodeport.yaml ファイルをカスタマイズ)

$ kubectl apply db2_deployment_nodeport.yaml


例えば db2inst1 ユーザーのパスワードを変えたい場合は 38 行目を変更、サンプルデータベースを作る必要がなければ 39, 40 行目を削除、mydb データベースもこの時点で作る必要がなければ 41, 42 行目を削除、といった具合です。

なお後者のカスタマイズをする場合に指定可能なオプション内容については公式ページ内"Advanced Configuration Options" 欄を参考に編集してください。

"kubectl get all" コマンドでデプロイ後のクラスタの様子を確認してみます:
$ kubectl get all

NAME                            READY   STATUS    RESTARTS   AGE
pod/db2-54ff649944-w7d24        1/1     Running   0          2d14h
pod/hostname-7b4f76fd59-c8b2l   1/1     Running   0          8d

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)           AGE
service/db2          NodePort    172.21.54.211           50000:30500/TCP   2d14h
service/hostname     NodePort    172.21.105.49           8080:30080/TCP    8d
service/kubernetes   ClusterIP   172.21.0.1              443/TCP           8d

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/db2        1/1     1            1           2d14h
deployment.apps/hostname   1/1     1            1           8d

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/db2-54ff649944        1         1         1       2d14h
replicaset.apps/hostname-7b4f76fd59   1         1         1       8d

↑のように Db2 関連の pod, service, deployment, replicaset が1つずつ追加されていればコマンドは成功しています。 また service を見ると 50000 番ポート(Db2 の標準動作ポート)が 30500 番で公開されていることも確認できます。つまり Db2 は
 (ワーカーノードのIPアドレス※):30500
で公開されて動いていることがわかります(※ワーカーノードの IP アドレス確認方法はこちらを参照してください)。


【動作確認】
では実際に Db2 サーバーが正しく動作していることを動作確認してます。。。 と、実はここが大きな問題でした。Db2 サーバーは正しくデプロイされて動作しているのですが、Db2 サーバーに接続試験を行おうとすると、その環境準備がちょっと面倒だったりするのです。

Db2 サーバーに CLI でアクセスしようとすると Db2 クライアントライブラリのダウンロード&インストールが必要です。Node.js などのプログラムから接続するにもクライアントライブラリが必要になります。この Db2 クライアントライブラリをインストールするには、無料版の Db2 Community Edition をダウンロードしてインストールする必要があり、ここで急にハードルが上がってしまいます。

そこでこれらの手間を省くために別の動作確認方法を考えました。それは「Db2 のコンテナインスタンスをもう1つ作って、そこからリモートサーバーにアクセスする」という方法です。マネージドサービスと異なり、Db2 サーバーのコンテナイメージからはライブラリセットアップ済みの CLI も使えるのです。そのための Db2 コンテナをもう1つ追加で作ってクライアントとして使うことで(手間という意味では)比較的容易に動作確認用クライアント環境が用意できると思いました。

というわけで、先程の yaml ファイルを少し変更(サンプルDB 作らず、mydb DBも作らず、ポート番号は 30501 に変更、コンテナの名称も db2 から db2cli に変更)した yaml ファイルを使って、(機能的にはサーバー機能もインストール済みですが)新しい Db2 クライアントコンテナを作成します:
$ kubectl apply -f https://github.com/dotnsf/yamls_for_iks/blob/main/db2cli_deployment_nodeport.yaml

作成後にクラスタ内の様子を kubectl コマンドで確認すると、新しい db2cli インスタンスが起動しているはずです:
$ kubectl get all
NAME                            READY   STATUS    RESTARTS   AGE
pod/db2-54ff649944-w7d24        1/1     Running   0          2d14h
pod/db2cli-86d476659d-m7c6d     1/1     Running   0          9s
pod/hostname-7b4f76fd59-c8b2l   1/1     Running   0          8d

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)           AGE
service/db2          NodePort    172.21.54.211           50000:30500/TCP   2d14h
service/db2cli       NodePort    172.21.112.13           50000:30501/TCP   11s
service/hostname     NodePort    172.21.105.49           8080:30080/TCP    8d
service/kubernetes   ClusterIP   172.21.0.1              443/TCP           8d

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/db2        1/1     1            1           2d14h
deployment.apps/db2cli     1/1     1            1           11s
deployment.apps/hostname   1/1     1            1           8d

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/db2-54ff649944        1         1         1       2d14h
replicaset.apps/db2cli-86d476659d     1         1         1       11s
replicaset.apps/hostname-7b4f76fd59   1         1         1       8d

ここからは db2cli コンテナからの作業となります。まずは db2cli コンテナ上のシェルにログインする必要があります。まずは IBM Cloud の k8s サービスからダッシュボードにアクセスします:
2022011801


ダッシュボード画面左メニューから「ポッド」を選び、ポッド一覧から "db2cli" で始まる名前のポッドを探し、画面右のメニューから「実行」を選択します:
2022011802


これで該当 Pod のシェルをブラウザからリモート操作できるようになります:
2022011803


以下の手順で(最初に作った方の)Db2 サーバーに接続して SQL を実行する、という動作確認をします。

まずは Db2 を利用可能なユーザー(db2inst1)にスイッチします(プロンプトが # から $ へ切り替わります):
# su - db2inst1
$

次に Db2 コマンドをインタラクティブに実行できるシェルに切り替えます(プロンプトが $ から db2 => に切り替わります):
$ db2
db2 =>

この状態から Db2 サーバーに接続したり、ログインしたり、SQL を実行したりできます。まずは Db2 サーバーへの接続が必要です。Db2 ではリモートサーバーやデータベースを「カタログ」という単位で管理するので、まず最初にリモート Db21 サーバーを db2onk8s という名前でカタログに登録します。以下のコマンドを実行します(1.2.3.4 部分はワーカーノードの IP アドレスに置き換えてください):
db2 => catalog tcpip node db2onk8s remote 1.2.3.4 server 30500

次にこのサーバー上の SAMPLE データベースをカタログに登録します:
db2 => catalog database sample at node db2onk8s

これで Db2 クライアントから Db2 サーバー上の SAMPLE データベースに接続するためのカタログができました。

ではこのデータベースカタログを使って Db2 サーバー上の SAMPLE データベースに接続します。このコマンドを入力した後に db2inst1 ユーザーのパスワードを聞かれるので、Db2 サーバーのコンテナを作成した時に指定した db2inst1 ユーザーのパスワード(変更していない場合は db2inst1)を入力してください:
db2 => connect to sample user db2inst1
Enter current password for db2inst1:


正しいパスワードを入力すると目的のデータベースに接続します。この状態で Db2 クライアントから Db2 サーバーに接続しています:
db2 => connect to sample user db2inst1
Enter current password for db2inst1:

   Database Connection Information

 Database server        = DB2/LINUXX8664 11.5.7.0
 SQL authorization ID   = DB2INST1
 Local database alias   = SAMPLE

db2 => 

SQL を使って Db2 サーバーの SAMPLE データベースのテーブルからレコードを取り出したり、追加・変更・削除といった操作が可能です:
db2 => select * from employee;

EMPNO  FIRSTNME     MIDINIT LASTNAME        WORKDEPT PHONENO HIREDATE   JOB      EDLEVEL SEX BIRTHDATE  SALARY      BONUS       COMM       
------ ------------ ------- --------------- -------- ------- ---------- -------- ------- --- ---------- ----------- ----------- -----------
000010 CHRISTINE    I       HAAS            A00      3978    01/01/1995 PRES          18 F   08/24/1963   152750.00     1000.00     4220.00
000020 MICHAEL      L       THOMPSON        B01      3476    10/10/2003 MANAGER       18 M   02/02/1978    94250.00      800.00     3300.00
000030 SALLY        A       KWAN            C01      4738    04/05/2005 MANAGER       20 F   05/11/1971    98250.00      800.00     3060.00
000050 JOHN         B       GEYER           E01      6789    08/17/1979 MANAGER       16 M   09/15/1955    80175.00      800.00     3214.00
000060 IRVING       F       STERN           D11      6423    09/14/2003 MANAGER       16 M   07/07/1975    72250.00      500.00     2580.00
000070 EVA          D       PULASKI         D21      7831    09/30/2005 MANAGER       16 F   05/26/2003    96170.00      700.00     2893.00
000090 EILEEN       W       HENDERSON       E11      5498    08/15/2000 MANAGER       16 F   05/15/1971    89750.00      600.00     2380.00
000100 THEODORE     Q       SPENSER         E21      0972    06/19/2000 MANAGER       14 M   12/18/1980    86150.00      500.00     2092.00
000110 VINCENZO     G       LUCCHESSI       A00      3490    05/16/1988 SALESREP      19 M   11/05/1959    66500.00      900.00     3720.00
000120 SEAN                 O'CONNELL       A00      2167    12/05/1993 CLERK         14 M   10/18/1972    49250.00      600.00     2340.00
000130 DELORES      M       QUINTANA        C01      4578    07/28/2001 ANALYST       16 F   09/15/1955    73800.00      500.00     1904.00
000140 HEATHER      A       NICHOLLS        C01      1793    12/15/2006 ANALYST       18 F   01/19/1976    68420.00      600.00     2274.00
000150 BRUCE                ADAMSON         D11      4510    02/12/2002 DESIGNER      16 M   05/17/1977    55280.00      500.00     2022.00
000160 ELIZABETH    R       PIANKA          D11      3782    10/11/2006 DESIGNER      17 F   04/12/1980    62250.00      400.00     1780.00
000170 MASATOSHI    J       YOSHIMURA       D11      2890    09/15/1999 DESIGNER      16 M   01/05/1981    44680.00      500.00     1974.00
000180 MARILYN      S       SCOUTTEN        D11      1682    07/07/2003 DESIGNER      17 F   02/21/1979    51340.00      500.00     1707.00
000190 JAMES        H       WALKER          D11      2986    07/26/2004 DESIGNER      16 M   06/25/1982    50450.00      400.00     1636.00
000200 DAVID                BROWN           D11      4501    03/03/2002 DESIGNER      16 M   05/29/1971    57740.00      600.00     2217.00
000210 WILLIAM      T       JONES           D11      0942    04/11/1998 DESIGNER      17 M   02/23/2003    68270.00      400.00     1462.00
000220 JENNIFER     K       LUTZ            D11      0672    08/29/1998 DESIGNER      18 F   03/19/1978    49840.00      600.00     2387.00
000230 JAMES        J       JEFFERSON       D21      2094    11/21/1996 CLERK         14 M   05/30/1980    42180.00      400.00     1774.00
000240 SALVATORE    M       MARINO          D21      3780    12/05/2004 CLERK         17 M   03/31/2002    48760.00      600.00     2301.00
000250 DANIEL       S       SMITH           D21      0961    10/30/1999 CLERK         15 M   11/12/1969    49180.00      400.00     1534.00
000260 SYBIL        P       JOHNSON         D21      8953    09/11/2005 CLERK         16 F   10/05/1976    47250.00      300.00     1380.00
000270 MARIA        L       PEREZ           D21      9001    09/30/2006 CLERK         15 F   05/26/2003    37380.00      500.00     2190.00
000280 ETHEL        R       SCHNEIDER       E11      8997    03/24/1997 OPERATOR      17 F   03/28/1976    36250.00      500.00     2100.00
000290 JOHN         R       PARKER          E11      4502    05/30/2006 OPERATOR      12 M   07/09/1985    35340.00      300.00     1227.00
000300 PHILIP       X       SMITH           E11      2095    06/19/2002 OPERATOR      14 M   10/27/1976    37750.00      400.00     1420.00
000310 MAUDE        F       SETRIGHT        E11      3332    09/12/1994 OPERATOR      12 F   04/21/1961    35900.00      300.00     1272.00
000320 RAMLAL       V       MEHTA           E21      9990    07/07/1995 FIELDREP      16 M   08/11/1962    39950.00      400.00     1596.00
000330 WING                 LEE             E21      2103    02/23/2006 FIELDREP      14 M   07/18/1971    45370.00      500.00     2030.00
000340 JASON        R       GOUNOT          E21      5698    05/05/1977 FIELDREP      16 M   05/17/1956    43840.00      500.00     1907.00
200010 DIAN         J       HEMMINGER       A00      3978    01/01/1995 SALESREP      18 F   08/14/1973    46500.00     1000.00     4220.00
200120 GREG                 ORLANDO         A00      2167    05/05/2002 CLERK         14 M   10/18/1972    39250.00      600.00     2340.00
200140 KIM          N       NATZ            C01      1793    12/15/2006 ANALYST       18 F   01/19/1976    68420.00      600.00     2274.00
200170 KIYOSHI              YAMAMOTO        D11      2890    09/15/2005 DESIGNER      16 M   01/05/1981    64680.00      500.00     1974.00
200220 REBA         K       JOHN            D11      0672    08/29/2005 DESIGNER      18 F   03/19/1978    69840.00      600.00     2387.00
200240 ROBERT       M       MONTEVERDE      D21      3780    12/05/2004 CLERK         17 M   03/31/1984    37760.00      600.00     2301.00
200280 EILEEN       R       SCHWARTZ        E11      8997    03/24/1997 OPERATOR      17 F   03/28/1966    46250.00      500.00     2100.00
200310 MICHELLE     F       SPRINGER        E11      3332    09/12/1994 OPERATOR      12 F   04/21/1961    35900.00      300.00     1272.00
200330 HELENA               WONG            E21      2103    02/23/2006 FIELDREP      14 F   07/18/1971    35370.00      500.00     2030.00
200340 ROY          R       ALONZO          E21      5698    07/05/1997 FIELDREP      16 M   05/17/1956    31840.00      500.00     1907.00

  42 record(s) selected.


この時点で k8s ダッシュボード画面はこのようになっています。必要に応じて続けてコマンドを実行することもできます:
2022011800


操作を終了して接続を切るには quit コマンドを実行します:
db2 => quit

$ exit

# 


改めて k8s 上にデプロイした Db2 サーバーコンテナ(とクライアントコンテナ)が期待通りに動いていることが確認できました。


2022 年最初のブログエントリとなります。遅ればせながら本年もよろしくお願いいたします。

今年最初のエントリは React や Angular といったフロントエンドフレームワークにまつわるネタです。最近流行りのこれらのフロントエンドフレームワークを使うことで、比較的簡単に SPA(Single Page Application) を作ることができます。SPA 化のメリット/デメリットを理解した上で作る必要があるとは思いますが、SPA 化することによる大きなメリットの1つに「Amazon S3 などのオブジェクトストレージや Github ページといった、安価かつ滅多にサービスダウンしない環境でフロントエンドのウェブホスティングができる」ことがあると思っています。

この点を少し補足しておきます。「自分が作って管理しているサービスやアプリケーションを安定運用したい」というのは誰でも思うことだと思っています。ただ現実にはこれは簡単なことではありません。サービスやアプリケーションは利用者が直接参照するフロントエンド部分に加えて、データベースなどのバックエンド部分、そしてこれらを繋ぐ API サーバーなどから成り立っていて、これら全てを安定運用するのは簡単なことではありません。特にフロントエンドや API サーバーについては最近よく耳にするコンテナオーケストレーションなどによって比較的安価に安定運用することもできるようになりました。ただフロントエンド部がアプリケーションサーバーを持たないシンプルな静的ウェブコンテンツであれば、上述したような Amazon S3 のウェブコンテンツ機能を使ったり、Github ページ機能を使うことで、滅多にサービスダウンしないウェブページとして公開するという方法もあります。現実問題として、この方法だとフロントエンドページの公開は簡単ですが、API サーバーなどのバックエンドとの通信時に CORS を考慮する必要が生じたりして、これはこれで面倒な設定も必要になるのですが、一方で「ほぼ無料」レベルの安価で利用できるウェブサーバーにしてはかなり安定しているのも事実で、用途によっては(「面倒な設定」の面倒レベルがあまり高くならないケースであれば)運用方法の考慮に含めてもいいのではないかと思っています。補足は以上。


一方、最近のウェブアプリケーション開発において IDaaS の利用ケースもよくあります(個人的にも業務で多く使っている印象です)。ログインや認証、ユーザー管理といった ID 周りの機能がマネージドサービスとして提供されていて、自分で面倒な開発をしなくても API や SDK から利用可能になる、というものです。具体的なサービスとしては AWS CognitoAuth0 などが有名どころでしょうか。IBM Cloud からも AppID という IDaaS が提供されています。

そしてフロントエンドアプリケーションと IDaaS の組み合わせというのが実は相性が悪かったりします(苦笑)。多くの IDaaS では OAuth と呼ばれるプロトコルでの認証/認可が主流となっているのですが、アプリケーションサーバーを持たない静的なフロントエンドアプリケーションでは OAuth のコールバックの仕組みとの相性が悪く、実装が非常に難しいという事情があります。その結果として、各 IDaaS ベンダーから認証専用の JavaScript SDK が提供され、それらを使って認証機能を実装することになります。IBM Cloud の AppID も専用の JavaScript SDK が用意され、React などで作ったフロントエンドに組み込むことで AppID へのログインや認証を実現できます(この SDK では PKCE と呼ばれる方法を使ってログインを実現しています)。

で、ここまでのアプリ開発方法に関する内容についてはこちらのページでも紹介されているのですが、問題はこの先にありました。この方法で作った React のフロントエンドアプリを Github ページにホスティングして運用しようとすると・・・結論としては少し特殊なリダイレクト URI の設定が必要でした。これを知らないと Github ページでの運用時にトラブルが起こりそうだと思ったので、将来の自分への備忘録の意味も含めてブログに設定内容を記載しておくことにしました。

前段部分の長いブログですが、といった背景の中で「IBM AppID の JavaScript SDK を使って作った SPA アプリを Github ページで動かす」ためのアプリ開発手順と設定内容について、順に紹介します。


【React で SPA アプリを作成する】
この部分は上述ページでも紹介されている内容ではありますが、一般的な内容でもあるので、特にコメントに色をつけずに紹介します。以下のコマンドで react-appid というフォルダに React のサンプルアプリを作成します:
$ npx create-react-app react-appid

$ cd react-appid

ここまでは普通の React アプリと同じです。ここから下で AppID の認証に対応したり、Github ページで運用する場合特有の設定を加えていきます。


【IBM AppID サービスインスタンスの準備を行う】
ここは上述ページでも触れられてはいるのですが、あまり詳しくは紹介されていないので、ここで改めて手順を紹介します。

IBM Cloud にログインして AppID サービスを作成後にインスタンスを開き、「アプリケーション」タブから「アプリケーションの追加」ボタンで対象アプリケーションを追加します。その際にアプリケーションのタイプを「単一ページ・アプリケーション」(SPA) として追加するようにしてください:
2022011301


追加したアプリケーションを選択して、内容を確認します。type の値が "singlepageapp" となっていることを確認してください。確認できたらこの中の "clientId" 値と "discoveryEndpoint" 値を後で使うことになるのでメモしておきます:
2022011303


まだ AppID にユーザーを登録していない場合はここでユーザーも登録しておきましょう。メニューの「クラウド・ディレクトリー」から「ユーザー」を選択し、「ユーザーの追加」からログイン可能なユーザー(の ID とパスワード)を登録しておきます:
2022011302


またリダイレクト URL を登録しておきましょう。「認証の管理」から「認証設定」を選択して、リダイレクト URL に http://localhost:3000/ を追加しておきます:
2022011303



IBM AppID 側で準備する内容は以上です。取得した情報を使ってアプリ開発を続けます。


【React アプリに IBM AppID モジュールを追加して、AppID のログイン機能を追加する】
ここは上述ページでも詳しく記載されている内容です。まずは IBM AppID を利用するためのライブラリを追加します:
$ npm install ibmcloud-appid-js

そしてソースコードの src/App.js をテキストエディタで開き、以下の内容に書き換えます(詳しい内容は上述ページを参照してください)。この時に太字で示している clientId の値と discoveryEndpoint の値には先程 AppID の画面で確認した clientId 値と discoveryEndpoint 値をコピペして指定してください:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import AppID from 'ibmcloud-appid-js';
function App() {
  const appID = React.useMemo(() => {
    return new AppID()
  }, []);
  const [errorState, setErrorState] = React.useState(false);
  const [errorMessage, setErrorMessage] = React.useState('');
  (async () => {
    try {
      await appID.init({
        clientId: 'AppID の clientID の値',
        discoveryEndpoint: 'AppID の discoveryEndpoint の値'
      });
    } catch (e) {
      setErrorState(true);
      setErrorMessage(e.message);
    }
  })();
  const [welcomeDisplayState, setWelcomeDisplayState] = React.useState(false);
  const [loginButtonDisplayState, setLoginButtonDisplayState] = React.useState(true);
  const [userName, setUserName] = React.useState('');
  const loginAction = async () => {
    try {
      const tokens = await appID.signin();
      setErrorState(false);
      setLoginButtonDisplayState(false);
      setWelcomeDisplayState(true);
      setUserName(tokens.idTokenPayload.name);
    } catch (e) {
      setErrorState(true);
      setErrorMessage(e.message);
    }
  };
  return (
    <div  classname="App">
    <header  classname="App-header">
      <img  alt="logo" classname="App-logo" src="{logo}">
        {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
        {loginButtonDisplayState && <button  onclick="{loginAction}" id="login" style="{{fontSize:">Login</button>}
        {errorState && <div  style="{{color:">{errorMessage}</div>}
    </header>
    </div>
  );
}
export default App;

この状態で一度動作確認します:
$ npm start

ウェブブラウザが起動して http://localhost:3000/ が開き、以下のような画面が表示されます:
2022011301


"Login" と書かれた箇所をクリックするとログイン画面がポップアップ表示されます。ここで AppID に登録済みのユーザー ID とパスワードを指定してサインインします:
2022011304


正しくログインできると先程の画面に戻り、登録したユーザーの氏名が表示とともに "You are now authenticated." と表示され、ログインが成功したことが明示されます:
2022011305


【React アプリをビルドして、SPA アプリでもログインできることを確認する】
今作成した React アプリをビルドして index.html にまとめた SPA アプリにしたあとでも AppID でログインできることを確認します。nginx などの HTTP サーバーがあればそれを使ってもいいのですが、ここでは Node.js のシンプルなウェブサーバーを使って動作確認します。以下のような内容で app.js を作成し、react-appid フォルダ直下(README.md などと同じ階層)に保存します:
var express = require( 'express' ),
    app = express();

app.use( express.static( __dirname + '/build' ) );

var port = process.env.PORT || 3000;
app.listen( port );
console.log( "server starting on " + port + " ..." );

↑ build/ フォルダをコンテキストルートにして 3000 番ポートで HTTP リクエストを待ち受けるだけのシンプルなコードです。

app.js が準備できたら、まずは一度 React コードをビルドして SPA アプリ化します:
$ npm run build

ビルドが完了すると React アプリが SPA 化されて圧縮されて build/ フォルダにまとめられますので、これを app.js を使って起動します:
$ node app

改めてウェブブラウザで http://localhost:3000/ にアクセスして同じアプリが起動していることを確認し、Login から AppID アカウントでログインできることを確認します。アプリケーション・サーバーを持たない SPA アプリでも IBM AppID を使って認証できることが確認できました:
2022011305


【React アプリをビルドした SPA アプリを Github ページでも動くように調整する】
ここまでできれば後は build/ フォルダを Github ページで公開するだけで動くんじゃないか? と思うかもしれませんが、実は期待通りに動くようになるまではいくつかの落とし穴があります。1つずつ解いていきましょう。

(1)絶対パス→相対パスへの書き換え

React の SPA アプリをただビルドしただけだと、コンテキストルート直下で動く想定のアプリになってしまいます。どういうことかというと、例えば http://localhost:3000/ とか https://xxx.xxxxxxx.com/ といったサブディレクトリを持たない GET / というリクエストに対して動くアプリになります(要はビルドで作成される index.html 内で参照される外部 CSS や JavaScript は "/" ではじまる絶対パスになっています)。一方、Github ページで動かす際は https://dotnsf.github.io/react-appid/ のようなサブディレクトリ上で動くことになります。ここがコンフリクトになってしまい、絶対パスで指定された CSS や JavaScript のロードエラーが発生してしまうのでした。これを回避するために index.html 内の該当箇所を絶対パス指定から相対パス指定に変更する必要があるのでした。

具体的にはこの下の(3)までの作業後に改めて $ npm run build を実行してから、build/index.html の以下の <head> 部分で頭に . を付けて、絶対パス指定から相対パス指定に書き換えてください:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="./favicon.ico"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Web site created using create-react-app"/>
<link rel="apple-touch-icon" href="./logo192.png"/>
<link rel="manifest" href="./manifest.json"/>
<title>React App</title>
<script defer="defer" src="./static/js/main.85d3d620.js"></script>
<link href="./static/css/main.073c9b0a.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>



(2)回転する画像の URL を絶対指定

(1)と似た内容ですが、SPA アプリ内で表示される画像も絶対パスのままだと正しく表示されません。ただこちらは JavaScript 内で {logo} と指定されている画像なので単純に絶対パスを相対パスにすればよいという対処が使えないのでした。 まあアプリケーションとしては必須ではないので単純に画像ごと削除してしまってもいいのですが、残したい場合は Git リポジトリ上の画像 URL を直接指定する、などの方法で対処する必要があります。よくわからない人は以下の例でそのまま書き換えてください:
    :
  return (
    <div className='App'>
    <header className='App-header'>
      <img src="https://raw.githubusercontent.com/dotnsf/react-appid/main/src/logo.svg" className='App-logo' alt='logo' />
        {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
        {loginButtonDisplayState && <button style={{fontSize: '24px', backgroundColor: 'skyblue', 
          border: 'none', cursor: 'pointer'}} id='login' onClick={loginAction}>Login</button>}
        {errorState && <div style={{color: 'red'}}>{errorMessage}</div>}
    </header>
    </div>
  );
    :


(3)デフォルトの .gitignore を変更

$ npx create-react-app コマンドで作成した React プロジェクトにははじめから .gitignore ファイルが含まれています。が、この .gitignore では /build フォルダを除外するよう記述されています。今回は build/ フォルダを Github ページで運用することが目的なので、/build フォルダが除外されてしまっては意味がありません。.gitignore ファイルを編集して、/build フォルダを含めるよう(コメントアウトするなど)してください:
  :
  :
#/build
  :
  :

(1)でも紹介しましたが、ここまでの変更を行ったら再度 SPA アプリケーションをビルドし、その後に build/index.html ファイルに対して(1)の変更を行ってください。


(4)AppID のリダイレクト URL の設定が特殊

これまではアプリケーションを http://localhost:3000/ という開発時専用の URL で実行していたので、AppID のリダイレクト URL も http://localhost:3000 だけを登録して動作確認できました。では実際に Github ページでアプリケーションを動かす際にはどのようなリダイレクト URL を指定すればいいのでしょうか? 実はここがくせ者でした。

例えば私(Github ID は dotnsf )が react-appid という github リポジトリを作って、このリポジトリを Github ページとして公開して運用しようとすると、アプリケーションの URL は以下のようになります:
 https://dotnsf.github.io/react-appid

2022011306


ということは AppID に設定するリダイレクト URL にもこの値を指定するべき・・・だと思っていたのですが、なんとリポジトリ名部分が不要で、 https://dotnsf.github.io/ を指定するのが正しい設定内容のようでした:
2022011308


この URL がリダイレクト URL として設定されていれば AppID SDK が正しく動作して、認証も正しく実行できるようになりました(自分は実はここで結構つまずきました):
2022011307


(5)build フォルダを Github ページとして公開する方法
最後に作成したプロジェクトを Github に登録して、build フォルダを Github ページで公開する方法についてです。root フォルダや docs フォルダを Github ページで公開する場合は選択肢から選ぶだけなんですが、任意のフォルダを Github ページで公開するのは少しコツが必要です。

例えば react-appid というリポジトリを使う場合は、まず Github 上でこのリポジトリを公開設定で作成します。そして普通に main リポジトリに登録します:
$ git init

$ git add .

$ git commit -m 'first commit'

$ git branch -M main

$ git remote add origin https://github.com/dotnsf/react-appid.git

$ git push -u origin main

その後、build フォルダを Github ページで公開するには以下のコマンドを実行します:
$ git subtree push --prefix build origin gh-pages

これで該当フォルダが Github ページとして公開されます。実際に以下のページはこの設定を使って公開しています:

https://dotnsf.github.io/react-appid/

2022011306


ID: username1@test.com, パスワード: password1 でログインできるので、よければ試してみてください:
2022011307


React で作成した SPA アプリケーションの認証をどうするか、という問題を IBM AppID (と SDK)で解決する方法と、ビルドした SPA アプリを Github ページで運用する場合の特殊な設定やコマンドについて紹介しました。




本ブログエントリは IBM Cloud アドベントカレンダー 2021 に参加しています。12/3 ぶんのネタです。

2021 年になった IBM Cloud 関連の変更の中でもインパクトの多かったものの1つがアカウント作成に関することだったと感じています。Bluemix 時代からの特徴でもあった新規に作成したアカウントでの Cloud Foundry ランタイム無料枠がなくなったことに加え、新規アカウント登録時にクレジットカードの登録が必要となりました。経緯など詳しくはソリューションブログを参照ください:

新規アカウント登録時にクレジット・カード情報の入力が必須になりました | IBM ソリューション ブログ


ちょっとわかりにくいのが「新規アカウント登録時」と同時にクレジットカードを登録する必要がある、という表現です。「IBM Cloud を使うにはアカウント(以前の言い方だと「組織」)が必要」であることはその通りなのですが、2021/11/08 時点では「クレジットカードを登録しないと使えない」というわけではありません。「自分のアカウントを登録せずに(ログインアカウントだけ作って)使う」ことができるのでした。これは上記 URL でも紹介されている方法で、例えば IBM Cloud を使ったハンズオンセミナーなどを開催したい場合に、主催者が利用者の使用料金を肩代わりする形で支払い、利用者はアカウントだけ作って無料で(クレジットカード登録も不要で)利用する、といったケースを想定しています。

ただ上記 URL ではその具体的なアカウント作成手順などが紹介されているわけではないため、ちょっとわかりにくい部分もあると感じました。というわけで、本ブログエントリではこのような使い方をする場合の(利用者の)アカウント作成手順を紹介します。なおスクリーンショットを含めた情報は 2021/12/03 時点のものであることをご了承ください。


【クレジットカード登録なしで IBM Cloud アカウントを作成できる条件】
クレジットカード登録せずに IBM Cloud アカウントを作成するには条件があります。そもそも 2021/12/03 時点で普通にここからアカウントを作成しようとすると、途中でクレジットカードの登録が求められます。スタート地点からして違う方法が必要だったりします。

その条件がこちら:
既存のベーシックアカウント(またはエンタープライズアカウント。要はクレジットカードを登録済みか請求書登録など、支払いの準備が済んでいるアカウント)からの招待を受けたメールアドレスで作成すること

やみくもにアカウントを新規に作成しようとすると、普通の方法でアカウントを登録することになり、この場合はクレジットカード登録が必要です。その方法ではなく、既にベーシックアカウントを所有している人からの招待を受けてアカウントを作成する場合のみクレジットカード登録を回避できる、というものです。なお、この方法で招待された人がアカウントを作成した場合、その人の利用によって料金が発生した場合は招待した人に支払いが請求される点に注意してください。あくまで「クレジットカード登録が不要」なだけで、無料で使えるわけではありません(使った分は招待した人が支払うことになるので、そこまで理解した上で招待してください)。


【既存アカウントからユーザーを招待する方法】
上述の点を注意した上で、では具体的に既存 IBM Cloud アカウントからユーザーを招待する方法を紹介します。繰り返しになりますが、この方法で招待されたユーザーが IBM Cloud の有償サービスを利用した場合は招待した人に請求される点に注意してください(無料で参加できるハンズオンセミナーの主催などを想定しています)。

まず招待する人が IBM Cloud にログインします。招待する人はクレジットカード登録が済んでいて、有償アカウント契約済みであると想定します。

ログイン後、画面上部のメニューに ID と一緒に自分の名前(組織)が表示されていることを確認します。多くのケースでここは1つしか選択肢がないので問題ないと思いますが、もし自分の名前とは異なる組織が表示されている場合は、赤枠部分をクリックして表示される選択肢から自分の名称を選択します(この後招待するユーザーが有償サービスを使った場合、最初にここで選択したユーザー組織に請求されることになります):
2021110901


自分の組織が選択されていることを確認した上で、その左横にあるメニューから 管理→アクセス(IAM) を選択します:
2021110902


アクセス権付与を行う画面に移動します。ここで画面左のメニューから ユーザー を選択すると、選択した組織を利用することができるユーザーの一覧が表示されます(初めてこの機能を使う場合は自分だけが表示されているはずです)。この組織を使うことができるユーザーを招待する形で登録します。画面右の ユーザーの招待 ボタンをクリックします:
2021110903


以下のようなユーザーを招待する画面が表示されます。ここから招待するユーザーのメールアドレスと、それぞれのユーザーに与える権限を指定して招待していきます。以下、順に説明していきます:
2021110904


まずは招待するユーザーのメールアドレスを入力します。複数の場合はカンマか改行で区切ります(1回で指定できる最大ユーザー数は 100 です)。入力したメールアドレスの数が画面右側にユーザー数として表示されるので、数が間違っていないことをここで確認します。このままだと権限の指定がまだなので(利用権限のない状態で招待されてしまうので)、まだ招待ボタンを押さずに作業を続けます:
2021110905


招待するユーザーへ与える権限を設定するため ユーザーへの追加のアクセス権限の割り当て と書かれた箇所をクリックして展開します:
2021110906


以下のような画面が表示されるので、アクセス権を与えたい内容にそって指定します。大きく(1)Cloud Foundry ランタイム (2)クラシック・インフラストラクチャー(IaaS) (3)IAM サービス (4)アカウント管理 の4つのカテゴリに分かれていて、それぞれ個別に指定します。デフォルトはいずれも「なし」なので、権限を与える必要のあるものだけを指定して設定していきます(例えば「アカウント管理」の設定をしない場合、招待されたユーザーはアカウント情報を設定することも見ることもできない、ということになります):
2021110907


単に「ハンズオンセミナーなどで使ってもらう」だけであれば、(4)アカウント管理 については設定不要です。後は使ってもらう有償サービスの内容に沿って権限を与えていきます。以下は(アプリケーションサーバーとなる) Cloud Foundry ランタイムと(マネージド・データベースなどの) IAM サービスだけを使ってもらう想定(IaaS サービスを使わせない想定)の例を紹介します。

まず(1)Cloud Foundry 利用権限を設定します。ここでは「ハンズオンセミナーで Cloud Foundry ランタイムを作って動かす」ことを想定して、ランタイムを作る権限まで含めて指定することにします。

画面から "Cloud Foundry" と書かれた四角を選択します:
2021110908


最初に Cloud Foundry を使う組織を選択します(端的に言うと「招待されたユーザーが使った分を誰が払うか」を指定します)。今回は(この作業を行っている)自分の組織に請求させるため、選択肢の中から自分のメールアドレスを指定します。

次に(招待するユーザーの)組織内での役割を指定します。ここではリソースの表示はできるが変更権限のない「監査員」を選択しています(実際の作業目的に合わせて指定します)。

更に「どのリージョン/スペースで、どのような権限を与えるか」を指定します。全てのリージョン/全てのスペースに、全ての権限を与えることも可能ですが、目的が明確になっている場合は、その目的のための最小限の権限を与えるのが安全です。以下の例では「シドニー」リージョンの「dev」スペースのみに「開発者」としての権限を与えています。 権限の指定ができたら最後に右下の 追加 ボタンをクリックします:
2021110909


追加ボタンをクリックすると、指定した権限が「割り当て」に追加されます。招待前であれば追加した内容を削除したり、編集することができます:
2021110910


今回は IAM サービスも利用できるような権限を付与します。ここも実際に作業する上で必要な権限を与えていくのですが、今回は特定のサービスだけを使わせるのではなく、全てのサービスを作成できるような内容で招待する例を紹介します。指定が完了したら 招待 ボタンをクリックします:
2021110911


追加ボタンをクリックすると、指定した権限が「割り当て」に追加されます。この作業を招待されたユーザーの権限として必要なだけ繰り返しますが、今回はここまでの権限を与えた状態でユーザーを招待することにします。画面右下の 招待 ボタンをクリックします:
2021110912


ユーザーを招待する側の作業はこれで終わりです。


【招待を受けたユーザーの登録方法】
続いて上記作業によって招待を受けたユーザー側の手順を説明します。

まずメールアドレスを指定されて招待されたユーザーには以下のようなメールが IBM Cloud から届きます。このメール本文内にある Join now と書かれたリンク部分をクリックします:
2021110901


IBM Cloud のユーザー登録ページに遷移します。

この時、該当ユーザー(メールアドレス)が既に IBM Cloud のアカウントに登録済みであった場合はメールアドレス、氏名とも既に既存の登録情報が入力されている状態で開き、「アカウントに追加」というボタンだけが押せる状態でページが開きます。この場合は アカウントに追加 ボタンをクリックすることで招待された組織が使えるようになります。

該当ユーザーがまだ IBM Cloud に未登録であった場合はメールアドレスのみ入力済みの状態でページが開き、氏名部分は入力可能なテキストフィールドになっています。ここに氏名を入力し、最後に アカウントに追加 ボタンをクリックし、次の画面でパスワードを指定します:
2021110902


※たまーに、ここでこの状態のままになるんですよね。。その場合はログイン画面に移動しちゃってください:
2021110903


IBM Cloud に未登録だったユーザーがこの手順で登録した場合のみ、IBM Cloud の使用許諾画面に遷移するので、内容を確認して続行してください。これで IBM Cloud に登録完了です:
2021110904


この方法で招待されたユーザーが IBM Cloud にログインすると、画面上部の組織アカウント一覧には招待してくれた人の名前が表示され、他の選択肢を選ぶことはできません。そしてこの状態でしか使うことができないため、このユーザーが有償サービスを利用した場合は組織アカウントのオーナー(つまり招待した人)に請求される、という仕組みとなります:
2021110905


普通にアカウントを登録するとクレジットカード登録が必須となってしまったので、企業内で社員向けのハンズオンセミナーを実施しようとしても各ユーザーがクレジットカード登録をしないといけないのでは面倒ですが、この方法であれば(一括でまとめて払う人の有償アカウント登録のみ必須ですが)アカウント作成時のハードルは以前通りの簡単な手順で登録できます。


IBM Cloud 的には特別目新しいサービスではないのですが、リモートワーク時代に向いているサービスだと思ったので、改めてその視点で紹介させていただきます。

新型コロナウィルス流行の影響もあり以前よりもリモートワークが広まっています。中にはリモートワークの需要はあるんだけど、リモートから社内システムを利用するための環境構築ができなかったり、料金的な問題があったりして実現できずにいる人や組織もあると思っています。一般的には VPN(Virtual Private Network) と呼ばれる仕組みを導入して、社内システムを安全な形で外部に公開して利用する、という方法が多く用いられていると思っていますが、この「安全な形で外部に公開」するのは単純ではなく、リモートワークする人も専用のソフトウェアを導入&設定する必要があったり、想定する利用量の規模に合わせて専用のサーバーを1台以上追加する必要があり、その見積もりも簡単ではありません。本格的に利用しようとすると、この追加コストだけでもかなりの額になってしまい、結局二の足を踏まざるを得なくなっているケースもあると思います。

そんな要望に対して IBM Cloud からは SecureGateway と呼ばれるサービスが提供されています。通信用のサーバーは1台必要ですが、VPN とは異なる形で社内システムを外部公開することができ、リモートワークする人から見ても(専用ソフトウェアの導入&設定無しで)ほぼそのまま使うことができます。とりあえず実験的に社内の1つのシステムを公開するだけであれば(1ヶ月 500MB という通信量の制約はありますが)無料で試してみることができます。リモートワークの需要は今後も増えることが予想されるので、特に中小企業向けの小規模利用であれば安価に構築できる環境として紹介します:
2021101600


※SecureGateway は「非推奨」サービスという扱いになっていますが、この社会事情も含めて考慮され、現時点では少なくとも 2022 年6月まではサービス継続することがアナウンスされています。


【SecureGateway の仕組み】
まず、このブログエントリで説明する内容の中で想定しているユースケースを紹介します。企業内や自宅内などにネットワークが構築されていて、そのネットワークに接続している状態からであれば利用できるシステム(=これを「機密システム」と呼ぶことにします)があると想定します。機密システムには機密情報の含まれるデータベースや、それらを業務で扱うウェブアプリケーションなどがあります。 問題になるのはこのシステムが(もともとプライベートネットワークアドレスで構築された環境内に存在しているなどの理由で)社内ネットワーク向けに構築されていて、一般的なインターネットからは利用できない状態にあり、そのためそのままリモートワークへ移行できない、というケースです:
2021101501


このような環境でインターネットからのアクセスをさせようとすると、一般的には VPN(Virtual Private Network) という仕組みを構築します。専用の VPN サーバーを内部ネットワーク内に構築し、VPN サーバーへだけはファイアウォールやパケットフィルタリングを通す設定にしたうえで、インターネットから利用する各 PC に VPN クライアントをインストールして、目的の VPN サーバーに接続するための設定をします。これによって各 PC は VPN サーバーを経由してインターネットからでも「仮想的にプライベートネットワーク(Virtual Private Network)内にいるかのような状態」を作り出すことができ、社内の機密システムにアクセスすることができるようになります:
2021101502


ただこの方法には課題もいくつかあります。主なものとしては以下に挙げるものです:
・新たにサーバーを用意した上で、VPN サーバー機能を構築する必要がある
・VPN サーバーだけはインターネットからアクセスできる状態にする必要があるため、事前にファイアウォールやパケットフィルタリングで許可する必要がある
・インターネットから利用する全ユーザー(正確には全 PC)に VPN クライアントをインストールして、VPN サーバーに接続するための設定を行う、という手間がかかる
・VPN クライアントを導入する PC の台数や利用頻度にもよるが、VPN サーバーや社内ネットワーク、社内とインターネットを繋ぐネットワークは全ての VPN クライアントからの接続を処理することができるだけのスペックが必要になる。この見積も難しく、環境を構築した後に問題が発生すると改めて再設計が求められる可能性もある


簡単に言ってしまうと、仕組みの肝となる VPN サーバーを自分で用意する必要があるため、「充分な性能を用意しておく」という見積段階から用意する側の責任範囲となってしまうことにあります(とはいえ VPN の仕組み上、VPN サーバーは接続先である社内に用意する必要があります)。また各 PC 1台ごとに接続のための設定を行うのも面倒です。


一方、今回紹介する IBM Cloud の SecureGateway (以降 "SG" と表記します)は似た概念ですが、この「VPN の面倒な部分」を大幅に簡易化したものです。スペック見積もりから面倒なサーバー部分は IBM Cloud がサービスインスタンスとして用意するので、クライアント増加による負荷対策を考慮する必要はありません(値段は変わりますけどw)。社内に専用のマシン(SG クライアント)を用意する必要がある点は VPN と同じですが、こちらは docker イメージが提供されていて、社内に docker 環境があれば "docker run" だけで簡単に構築できます。

そして個人的に最大のアドバンテージだと思う2つの点があります。1つは利用量の制約(1か月に 500MB 以内の通信)がありますが、機密システムが1つだけであれば無料で環境構築できること、もう1つは、接続 PC 個別に(VPN クライアントに相当するような)専用の設定を行う必要がなく、IBM Cloud 経由でそのまま社内の機密システムにアクセスできるようになる、という2点です:
2021101503


以下ではこの環境を構築する例として「社内ネットワークに接続したラズベリーパイに社外から SSH で接続する」ための設定手順を紹介します。「ラズベリーパイに SSH 接続」する部分はあくまで例※であり、「社内のある機密システムを利用」すると読み替えていただくとより実像がイメージしやすいかもしれません。

※1つの例であることはその通りですが、一般的な Windows でも Linux でもなければ、HTTP サーバーでもない、という意味では比較的特殊なシステムと考えることができ、それでも接続できることを示した例といえます。
2021101504





【SecureGateway の準備】
実際にこの例で SecureGateway を使って機密システムにアクセスできるようにするまでの手順を紹介します。設定手順は大きく3段階ですが、いずれも専門の知識を必要とするものではなく、docker 環境さえあれば②も簡単に実現できます(docker を使わない場合は SG クライアントに相当するアプリケーションをダウンロード&インストールする必要があります):

①IBM Cloud 内に SG サービスインスタンスの作成
②社内ネットワークに SG クライアントの用意
③どの機密システムを利用するか、の設定

2021101505


では3つの段階を順に説明します。まずは①の「SG サービスインスタンスの作成」です:
2021101506




SecureGateway は IBM Cloud から提供されているサービスですが、無料の(アカウント作成時にクレジットカードを登録しない)ライトアカウントでは利用できません。SG 自体は無料でも利用できますが、ライトアカウントでは使えない、という点に注意してください。必要に応じてクレジットカードを登録するなどして、通常アカウントの権限で以下を作業する必要があります。

改めて通常アカウントで IBM Cloud にログインし、「リソースの作成」から "Secure Gateway" を検索して選択します:
2021101507


2021101508


利用プランが選択できますが、"Essential" プランを選択すると料金はかかりません(ただし接続先は1つのみ、一ヶ月間の通信データ量は 500MB 以下という制約があります)。最後に「作成」でサービスインスタンスを作ります。
2021101501


以下のような画面になれば SG のインスタンスが作成されたことになります。簡単でしたね:
2021101502


続いて②の SG クライアントを作成します。こちらは社内ネットワーク内に準備する必要があります:
2021101501


まずは①で作成した SG インスタンスの画面で「ゲートウェイの追加」をクリックします:
2021101502


ゲートウェイの名前を適当に入力して(下図では「自宅ネットワーク」)、「ゲートウェイの追加」ボタンをクリックします:
2021101503


以下のような画面になればゲートウェイが準備できました。ただ、この時点ではまだ①の SG インスタンス側に接続の準備ができただけで、社内ネットワークとは接続できていません(画面右上に赤いアイコンが表示されているのが未接続を意味しています)。社内ネットワーク側の準備をするために「クライアントの接続」をクリックします:
2021101504


以下のような画面が表示され、社内ネットワーク側で行う必要のある SG クライアントの準備手順が表示されます。SecureGateway では SG クライアントを以下の3種類から選ぶことができ、それぞれの手順が表示されています:
 - 専用アプリケーション(社内ネットワークに接続された PC にダウンロードしてインストール)
 - docker コンテナ(社内ネットワークに接続された docker エンジンで実行)
 - 専用ハードウェア(社内ネットワークに追加設置)
2021101505


本ブログエントリでは最も手軽と思われる docker コンテナを使った方法を紹介します。docker アイコンを選択すると、社内の docker エンジンで実行するためのコマンド(1行!)が表示されるので、この内容をコピーします:
2021101506


コピーした内容を社内の機密システムと同じネットワーク上で稼働する docker エンジン環境で実行します。

なお、先ほどの画面で表示されていた内容は
 $ docker run -it ibmcom/secure-gateway-client XXXXX

という内容でしたが、私が確認した環境では、
 $ docker run --net=host -it ibmcom/secure-gateway-client XXXXX

と、"--net=host" というオプションを付けないと正しく実行できませんでした。念のため記載しておきます:
2021101507


docker コンテナが正しく実行されると、画面に "Your Client ID is XXXXXX" と表示されます。この段階で SG クライアントも起動し、SG インスタンスとの接続も完了しています:
2021101508


念のため IBM Cloud 内の SecureGateway ダッシュボード画面を確認すると、クライアントが追加され、正常を示す緑色のアイコンが表示されているはずです:
2021101509


これで①と②が用意できました。続けて③の、具体的な機密システムまでの接続作業を行います。①、②ほど簡単ではありませんが、作業は個別のシステム内容に合わせて順に実行していくだけなのでそれほど難しいことはないと思っています:
2021101501


SG ダッシュボード画面で、まだクライアントを選択していない場合はこのタイミングで選択しておきます:
2021101502


SG クライアントが選択された状態になりました。ここで「宛先(0)」と表示されていることを確認します(登録されている機密システムが0個、という意味です)。ここに新しい機密システムを追加するため、宛先(0) のタブを選択後にプラスのアイコンをクリックします:
2021101503


「宛先の追加」ダイアログが表示され、この画面の質問に答えていく形で宛先を追加できます。まずは「ガイド付きセットアップ」が選択されていることを確認し、リソースの場所として(今回は社内ネットワーク内のラズベリーパイに接続することが目的なので)「オンプレミス」を選択し、「次へ」をクリックします:
2021101504


次に具体的な接続先の IP アドレスとポート番号を指定します。今回の例ではラズベリーパイへ SSH 接続することが目的なので、該当ラズベリーパイの IP アドレスと、SSH のポート番号を指定します。SSH ポート番号は通常 22 ですが、意図的に変更している場合は変更後のポート番号を指定します。

※ここでプライベート IP アドレスである 192.168.xx.xx というアドレスを指定している点に注目してください。プライベートネットワーク内の、プライベート IP アドレスを指定しているので、通常の方法ではインターネットからアクセスすることはできないシステムを指定しています。 また対象システムもウェブアプリケーションだったり、データベースだったり、ホストコンピュータだったり、いろんなパターンがあると思いますが、いずれの場合も IP アドレスおよびポート番号(対象によっては認証の仕組みも)を正しく指定できることが必要です。

その後「次へ」をクリックします:
2021101505


機密システムへ接続する際に用いるプロトコルを指定します。今回は SSH への接続なので「TCP」を選択します(機密システムがウェブアプリケーションであれば HTTP を選択するなど、実際の接続先に合わせて選んでください)。その後「次へ」をクリックします:
2021101506


次の画面で接続に利用する認証方式を選択します。今回の例では不要なので「None」を選択しましたが、ここも実際の接続先に合わせて選択します。「次へ」をクリック:
2021101507


設定後、この仕組みを利用可能な PC に IP アドレス指定をかけることが可能です。制約をかける場合はこの画面で対象 PC の IP アドレスを指定して追加します。特に不要であればそのままでも構いません。「次へ」をクリックします:
2021101508

※例えば機密のデータベースシステムを公開して、特定のパブリックなウェブアプリケーションから利用するような場合であれば、データベースそのものは誰でも使えるような形にはせず、対象のウェブアプリケーションサーバーからしか接続できないようにしたい、といった場合にそのウェブアプリケーションサーバーの IP アドレスを指定する、といった形で制約をかけます。


最後にこの宛先に名前を付けて保存します。下図の例では「ラズパイSSH」という名前を指定しています。そして「宛先の追加」をクリックします:
2021101501


1つ前の画面に戻り、指定した名前の宛先が追加されたことが確認できます。ただこの時点ではまだ宛先の右上に赤い印が表示されていて、接続許可が足りないことがわかります:
2021101502


というわけで、同機密システムへの接続許可を SG クライアントに指定します。docker 画面を再び表示して、以下のように(2回にわけて)入力します:
> A 192.168.0.101:22 1

> S

A(allow) コマンドに続けて 宛先のIPアドレス:宛先のポート番号 1 を指定します(最後の1はワーカーIDと呼ばれるもので、ここは1を指定します)。そして S(show) コマンドで設定内容を確認します:
2021101503


↑このように「宛先の追加」ダイアログで指定したものと同じ内容が docker コンテナの SG クライアントでも指定できていれば成功です。

この時点で先ほどまでダッシュボード画面に表示されていた赤いアイコンは消え、宛先である機密システムへの接続が確立できているはずです:
2021101504


ここまでが SecureGateway によるプライベートネットワークへの接続に必要な設定手順です。繰り返しますが、実際の接続先である機密システムや、この後接続時に使う PC への設定は一切不要です。また SG インスタンスや SG クライアントも非常に簡単に設定できている点がメリットだと考えています。


では実際にインターネットからプライベートネットワーク内の機密システム(=ラズベリーパイの SSH )に接続できるか動作確認してみましょう:
2021101505


まずはダッシュボード画面で接続時に必要な情報を確認します。宛先として作成したアイコンの右下の歯車をクリックします:
2021101506


以下のようなダイアログが表示されます。この「クラウド・ホスト:ポート」と表示されている部分にインターネットからアクセスすると、SG クライアントを通じて内部ネットワークから「リソース・ホスト:ポート」へ接続できるようになっています(この画面を確認したら右上の×をクリックして消します):
2021101507


改めてインターネットにアクセスできるPCから「クラウド・ホスト:ポート」で表示されている宛先に接続します。今回は接続先が SSH サーバーなので SSH クライアントを使って、ホスト名とポート番号を SG インスタンスのダッシュボードで表示されたものを指定して接続を試みます:
2021101508


今回は SSH への接続なので、この後で認証を求められます。内部ネットワークから利用する時と同じユーザー名とパスワード(場合によっては秘密鍵など)を入力します:
2021101501


正しい認証情報が入力できればログインが完了します。このアプリケーションの場合は接続先のホスト名がタイトルウィンドウに表示されますが、内部ネットワークの IP アドレスではなく、SG ダッシュボードで表示されたホスト名が表示されている点に注目してください。VPN を使わずに内部ネットワークとインターネットがトンネリングされ、専用のホスト名とポート番号だけで内部ネットワーク内の機密システムにアクセスすることができました:
2021101502


内部ネットワークからでないとアクセスできなかったシステムに無事にアクセスできました。SecureGateway は Essential プラン(無料)の場合は宛先を1つしか設定できませんが、別の有償プランにすることで1つの SG インスタンスで複数の宛先を指定することもできるようになるので、③の手続きを必要な宛先のぶんだけ繰り返すことで必要な社内ネットワークシステムへのアクセスが可能になります。

中小規模のリモートワーク環境を整える上で、手続きやコストで有力な選択肢になると思っているので、ぜひ試してみてください。

 

みんな大好き 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 を使うことである程度の規模まで無料でこの環境での運用ができるので、バケットを複数用意して使うケースも含めて、実運用前の構築段階ではとても便利に使うことができそうです。

このページのトップヘ