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

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

タグ:cloud

ウェブアプリケーションの開発ハンズオン(オンライン含めて)を行う場合、その開発環境の準備が面倒です。参加者の PC を使おうとすると言語ランタイムやエディタのインストールやバージョン管理を含めて事前に準備してもらう項目が多く、また特にオンライン環境だとネットワークの設定が問題になったりするので、色んな環境のケースを想定した準備が必要になります。

そんな「開発環境構築」を比較的容易にする RedHat CodeReady Workspaces を使う機会があったので、使い方やこれを使って構築する開発環境がどんなものかまとめてみました:
2023011700


【事前準備】
今回は RedHat OpenShift クラスタ環境を使って CodeReady Workspaces を準備します。というわけで OpenShift クラスタ環境が必要です。以下では IBM Cloud 上に作った OpenShift クラスタ環境を使って紹介しますが、他のクラウドやオンプレミス版などを使っていても構いません。


【OpenShift に RedHat CodeReady Workspaces を導入】
まずは OpenShift クラスタに RedHat CodeReady Workspaces を導入します。RedHat CodeReady Workspaces は OpenShift 上のオペレータとして提供されているのでこれを使って環境を作ります。最初に OpenShift のウェブコンソールを開いて、Administrator パースペクティブで左メニューから Operator - OperatorHub を選択します。そこでプロジェクト(例えば "default")を1つ選び、検索フィールドに "codeready" などと入力して "Red Hat CodeReady Workspaces" を見つけてクリックします:
2023011701


Red Hat CodeReady Workspaces の説明画面が表示されたら「インストール」ボタンをクリックします:
2023011702


次の画面を下までスクロールしてもう一度「インストール」ボタンをクリックします:
2023011703


ここでしばらく待つと RedHat CodeReady Workspaces オペレータがインストールされます:
2023011704


インストールが完了すると以下のような画面になるので、「Operator の表示」ボタンをクリックします:
2023011705


インストールされた RedHat CodeReady Workspaces オペレータが表示されます。実際に CodeReady Workspaces インスタンスを動かすために「提供される API」欄の "CodeReady Workspaces Instance Specification" の下の「インスタンスの作成」をクリックします:
2023011706


もう一度下までスクロールして「作成」ボタンをクリックするとインスタンスの作成が始まります:
2023011707


インスタンスが作成されると以下のような画面になるので、"codeready-workspaces" と書かれた箇所をクリックします:
2023011708


下のような画面になります。この画面ではまだインスタンスは準備中ですが、用意ができると "CodeReady Workspaces URL" の下にリンク URL 文字列が表示されます:
2023011709


このような表示になるとインスタンスの作成も完了しています。早速 CodeReady Workspaces URL 下のリンクをクリックします:
2023011710


最初の1回だけアクセス権の設定を行う必要があります。"user-full" にチェックが入っていることを確認して "Allow selected permissions" ボタンをクリックします:
2023011711


するとアカウント情報の画面が表示されます:
2023011712


この画面内でユーザー名、メールアドレス、ファースト/ラストネームを入力して、最後に Submit ボタンをクリックします。これでアカウント情報の登録も行われます:
2023011713


CodeReady Workspaces の起動が開始します。ここまで行うことができれば RedHat CodeReady Workspaces の環境構築手順は無事に成功しました:
2023011714



【RedHat CodeReady Workspaces を使ってアプリケーション開発を行う】

RedHat CodeReady Workspaces の起動が完了すると最初のワークスペースを作るようナビゲートされます:
2023011715


Git リポジトリを指定してソースコード一式をインポートすることもできますが、今回はテンプレートからワークスペースを作ってみます。(なんでもいいのですが)画面下の "NodeJS Express" と書かれた Node.js のシンプルなソースコードをベースに選択して、ここからワークスペースを作ることにしてみましょう:
2023011716


すると指定されたテンプレートを元にしたソースコード一式が作成されるので、完了するまで少し待ちます:
2023011717


ワークスペースの生成が完了するとこのような画面が表示されます。ウェブ版の VSCode が起動し、(今回の例であれば)シンプルな Node.js + Express のウェブアプリケーションの雛形となるソースコードが読み込まれています。指定したファイルを開いたり、その内容を変更することもできます(最初は README.md がプレビューモードで開かれています):
2023011718


依存関係や起動コマンドを確認するため、package.json を開いてみました。ここから最初に起動するべきファイルは app/app.js であることがわかります:
2023011719


実際に app/app.js も開いて内容を確認してみました。いわゆる「ハローワールド」のウェブ版のアプリケーションのようです:
2023011720


このアプリを起動するには VSCode 内でターミナルを起動します。メニューの Terminal - Open Terminal in specific container を選択し、"vscode-nodeajs" を選択します:
2023011721


すると画面右下にターミナルが現れ、CLI コマンドを実行できるようになります:
2023011722


実際にアプリケーションを起動してみましょう。まずはターミナルで "npm install" と入力してライブラリをインストールします:
2023011723


そして "node app/app.js" と入力してウェブアプリケーションを起動します。すると画面右下に「起動したウェブアプリケーションをブラウザで表示するか?」と聞かれるので、「新しいタブで起動(Open In New Tab)」を選択します:
2023011724


新しいブラウザタブが開いて、そこで起動したアプリケーションが実行されます。期待通り、"Hello World!" が表示できました。CodeReady Workspaces で作ったオンライン開発環境を使ってアプリケーションを開発/実行/動作確認までできることが確認できました:
2023011725


CodeReady Workspaces のエディタ画面で編集したアプリケーションをウェブブラウザで実行(表示)することができました:
2023011700


【まとめ】
OpenShift コンテナクラスタ環境を使うことで、開発環境も実行環境もコンテナ上で簡単に構築・実現することができました。個人の PC にはランタイムや CLI などを導入する必要がなく、ネットワークも(HTTP/HTTPS さえインターネットに通っていればいいので)環境による試行錯誤はほぼ不要だと思います。簡易的なアプリケーション開発環境の構築程度であれば非常に用意に作れると感じました。


先日のブログで IBM Cloud の VPC(Virtual Private Cloud : 仮想プライベートクラウド)上に OpenShift クラスタ環境を構築する手順を紹介しました:
IBM Cloud の VPC(Virtual Private Cloud) でプライベート OpenShift クラスタ環境を構築する


↑で紹介した内容は完全にインターネットと断絶したプライベートクラウドではなく、「プライベートクラウド側からインターネット側へのアクセスは許す」というものでした。デプロイ時に GitHub や DockerHub などを参照する場合はこのような設定になるので(それも許さない場合はこれらに相当する機能も VPC 内に用意する必要があるため)そこまで珍しい構成ではないと思っています。

今回紹介するのはもう1段階緩めのプライベートクラウドと言えるもので、「デプロイしたアプリはパブリックインターネットから利用できるようにする」というものです。OpenShift クラスタのウェブコンソールや oc CLI コマンドの利用は VPC 必須としたたま、コンテナとしてデプロイしたアプリはインターネットからでも利用可能にする、という設計です。OpenShift の管理者機能のみを VPC 内に入れ(VPN 必須として)、クラスタにデプロイしたアプリケーションについては利用制約を設けないというもので、これも現実的には珍しくないプライベートクラウド環境であると思っています。


【構築手順】
この環境を作る上で、まずは前回紹介した VPC での OpenShift 環境が必要です。こちらの内容を参照して仮想プライベートクラウド内に OpenShift クラスタを構築しておいてください:
IBM Cloud の VPC(Virtual Private Cloud) でプライベート OpenShift クラスタ環境を構築する


ここまでの環境準備ができ、かつ構築した VPN 接続ができている前提で以下を説明します。改めて IBM Cloud ダッシュボードにログインし、作成した OpenShift クラスタを表示します:
2023011501


この画面の "OpenShift Web コンソール" ボタンをクリックしてコンソール画面を表示します(VPN 接続していないとエラーになります):
2023011502


OpenShift ウェブコンソール画面が表示されたら右上の ID 部分をクリックし「ログインコマンドのコピー」を選択します:
2023011503


"Display Token" をクリックすると oc CLI でのログインコマンドが表示されます。"oc login" で始まるコマンドを確認します:
2023011504


ここまでできたら CLI で IBM Cloud (ibmcloud)および OpenShift (oc)環境にログインします:
$ ibmcloud login

$ oc login --token=sha256xxxxx(↑で確認したコマンド)

ログイン後、まずはドメイン名を調べます。以下のコマンドを入力します:
$ ibmcloud oc nlb-dns ls -c (OpenShift クラスタ名)

2023011501


実行結果の "Subdomain" 欄(上図では "kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-tok.containers.appdomain.cloud)を確認します。これが IBM Cloud から割り振られたドメイン名です。途中 i000 となっている部分がプライベートドメインであることを意味しています(i00x がプライベート、000x がパブリック)。パブリック用にこの x を1つカウントアップして(i000 -> 0001 として)以下のような IngressController 作成用 yaml ファイル(ingresscontroller-0001.yaml)を作成します:
(ingresscontroller-i001.yaml)

apiVersion: operator.openshift.io/v1
kind: IngressController
metadata:
  name: public
  namespace: openshift-ingress-operator
spec:
  replicas: 2
  domain: kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-tok.containers.appdomain.cloud
  endpointPublishingStrategy:
    loadBalancer:
      scope: External
    type: LoadBalancerService

そしてこれを実行します:
$ oc create -f ingresscontroller-0001.yaml

成功すると router-public という Pod が作成されているはずです:
$ oc get pods -n openshift-ingress
NAME READY STATUS RESTARTS AGE router-default-58cb7bcc6-6nmtv 1/1 Running 0 6h28m router-default-58cb7bcc6-mxlkd 1/1 Running 0 6h28m router-public-55cf9586d-5qwkv 1/1 Running 0 77s router-public-55cf9586d-bqnv6 1/1 Running 0 77s

この時、裏側でパブリックロードバランサーが作成されています。ロードバランサーのホスト名を確認します。結果の EXTERNAL-IP(下の場合であれば "b881dcc5-jp-tok-lb.appdomain.cloud)が確認できます:
$ oc get svc/router-public -n openshift-ingress

NAME            TYPE           CLUSTER-IP      EXTERNAL-IP                          PORT(S)                      AGE
router-public   LoadBalancer   172.21.233.58   b881dcc5-jp-tok.lb.appdomain.cloud   80:31920/TCP,443:32264/TCP   3m17s

パブリック用ドメイン名とロードバランサーのホスト名を紐づけて DNS 登録します:
$ ibmcloud oc nlb-dns create vpc-gen2 -c kkimura-mycluster-jp-tok2 --lb-host b881dcc5-jp-tok.lb.appdomain.cloud --type public --secret-namespace openshift-ingress

$ ibmcloud oc nlb-dns ls -c kkimura-mycluster-jp-tok2
OK
Subdomain
               Target(s)                            SSL Cert Status   SSL Cert Secret Name                                              Secret Namespace    Status
kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-tok.containers.appdomain.cloud   b881dcc5-jp-tok.lb.appdomain.cloud   creating          kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-0001   openshift-ingress   OK
kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-i000.jp-tok.containers.appdomain.cloud   57a0f594-jp-tok.lb.appdomain.cloud   created           kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-i000   openshift-ingress   OK

サービスをパブリックルーターで公開します。--hostname には 0001 のドメイン名に任意のサブドメインを指定します:
$ oc expose svc/hostname --name hostname-public --hostname hostname.kkimura-mycluster-jp-tok2-6fe57c7eaf38abe6232341d97eae54c0-0001.jp-tok.containers.appdomain.cloud

これでパブリッククラウドへの公開ができたはずです。VPN 接続を切ってから curl やウェブブラウザ等で --hostname に指定したホストに HTTP アクセスし、正しくアクセスできることを確認します:

(VPN 接続してプライベートアクセスした場合)
2023011501

(VPN 接続を切ってからパブリックアクセスした場合)
2023011502


プライベートネットワークにデプロイしてアプリケーションがパブリックインターネット経由でもアクセスできるようになりました。今回紹介した内容はパブリックインターネットから HTTP でアクセスする例でしたが、HTTPS でアクセスする場合の方法は別の機会に紹介するつもりです。


(参照)

あけましておめでとうございます。これが 2023 年最初のブログエントリとなります。本年もよろしくお願いいたします。



これまで IBM Cloud で OpenShift クラスタ環境を何度も作って(&そして削除して)来ました。が、それらは全てパブリッククラウド環境としての OpenShift クラスタでした。 このたびプライベートクラウド環境としての OpenShift クラスタを作る機会がありました。その考慮点や手順がパブリッククラウドの時と比べてかなり複雑だったので、備忘録の意味も含めて最初から最後まで手順をまとめてみました。


【プライベートクラウドの設計】
一言で「プライベートクラウド」といっても、その制約というか仕様には色々なパターンが考えられます。例えば「プライベートクラウド」なので、ウェブの管理コンソールや管理 CLI の操作、クラスタにデプロイしたアプリケーションにインターネットからアクセスできない(インターネット側からプライベートクラウドへのアクセスは不可とする)、というのは共通仕様だと思っています。一方で、デプロイするアプリケーションのイメージは Docker ハブから使いたい(つまりプライベートクラウド側からインターネットへのアクセスは許可したい)というケースはあるかもしれませんし、そこも制約として許可したくないケースもあると思います(後者の場合でイメージからデプロイするにはイメージリポジトリもプライベートクラウド内に構築する必要も出てきます)。

以下で手順を紹介するのは「前者」のパターンとします。つまり、
・CLI やウェブコンソールを含めて、インターネットからの直接アクセスは許可しない
・専用の VPN 環境を構築し、この VPN 接続が有効な場合のみアクセスを許可する
・プライベートクラウド側からインターネットへのアクセスは許可する(Docker ハブなどからのアプリケーションデプロイを可能とする)


という条件でプライベートクラウド環境を構築します。なお以下の作業を行う上で必要なユーザー権限は全て取得済みであるものとします。


【VPC とサブネットの作成】
まず、今回のようなパブリックインターフェースを持たないプライベートインターフェースのみの OpenShift クラスタを IBM Cloud に作る場合、クラシックインフラではなく VPC(Virtual Private Cloud)インフラに作る必要があります。そのためまずは OpenShift クラスタを作成するための VPC(Virtual Private Cloud:仮想プライベートクラウド)環境を作成します。IBM Cloud のダッシュボードから「リソースの作成」をクリックします:
2023010401


カタログ画面の検索バーに "Virtual Private" と入力すると選択候補の中に "Virtual Private Cloud" というのが出てくると思うので、これを選択します:
2023010402


VPC 環境を作成するロケーションを選択します。以下の例では「アジア太平洋地域」の「東京」を選択しています。ここは自分の環境にあったもの(なるべく利用者に近い場所)を選択してください。そして VPC の名称(下の例では "kkimura-vpc")を入力します:
2023010403


下にスクロールするとデフォルト接頭部とサブネットに関する情報が表示されます。デフォルトでは選択したロケーション(この場合は東京)の3つのゾーンに1つずつサブネットが用意されます。このままでもよければこのまま進めることもできますが、今回は規模の小さいプライベートクラウド環境を作る想定なので、デフォルト設定を無効にした上でサブネットを1つだけ用意することにします:
2023010404


というわけで、「各ゾーンのデフォルト接頭部の作成」のチェックを外してください。最後に「仮想プライベート・クラウドの作成」ボタンをクリックします:
2023010405


仮想プライベート・クラウドの一覧画面に遷移し、先ほど指定した名前の VPC が追加されたことを確認し、これを選択します:
2023010406


VPC の中身を確認するとアドレス接頭部が空になっています。ここに1つだけ追加することにします:
2023010407


同じ画面の「アドレス接頭部」タブを選択して「作成」ボタンをクリックします:
2023010408


画面右から「アドレス接頭部の作成」というウィンドウが表示されます。ここに作成するアドレス空間の範囲(下の例では 10.10.0.0/18)と、そのアドレス空間を作るロケーション(下の例では東京2)を指定して「作成」ボタンをクリックします:
2023010409


指定した値(10.10.0.0/18)のアドレス空間が定義できました:
2023010410


アドレス空間が定義できたので、次にサブネットを作成します。画面左ペインから「サブネット」を選択し、「作成」ボタンをクリックします:
2023010411


ロケーション(下の例ではアジア太平洋の東京2)とサブネットの名称(下の例では "sn-tok2")を指定します。まだ入力項目があるので、そのまま下にスクロールします:
2023010412


このサブネットを使う VPC を指定し、最後にアドレス接頭部(10.10.0.0/18)内のどの部分をサブネットとして利用するかを指定します(下の例では "10.10.0.0/24" を指定しています)。ここまで指定できたら最後に「サブネットの作成」ボタンをクリックします:
2023010413


VPC 用のサブネットを新しく1つ定義できました。この後 OpenShift 環境を構築する場合に、このサブネット上に構築することになります:
2023010414


【パブリックゲートウェイの作成】
今回のプライベートクラウドでは「プライベート環境側からインターネット側へのアクセスは許可する」前提で環境を構築します。これを実現するには「パブリックゲートウェイ」と呼ばれるゲートウェイをサブネットに紐づけることで実現します。 以下ではその手順を紹介します。なお「プライベート環境側からインターネット側へのアクセスも禁止する」というポリシーで構築する場合は、このパブリックゲートウェイ作成手順は読み飛ばしてください。

VPC インフラストラクチャー画面の左ペインで「パブリック・ゲートウェイ」を選択し、「作成」ボタンをクリックします:
2023010415


画面右からパブリック・ゲートウェイの設定画面が現れます。まずはパブリック・ゲートウェイを作成するロケーション(下の例ではアジア太平洋の東京2)を指定します:
2023010416


画面を下にスクロールして設定の続きを行います。作成するパブリック・ゲートウェイの名前("kkimura-pgw")、対象となる VPC("kkimura-vpc")を指定し、最後に「作成」ボタンをクリックします:
2023010417


しばらく待つとパブリック・ゲートウェイが作成されます。ただこの時点では作成されただけで、まだ対象となるプライベート・サブネットとの接続ができていない状態です:
2023010418


というわけで、プライベート・サブネットとの接続を行います。対象パブリック・ゲートウェイの右(必要に応じてスクロールしてください)のメニューをクリックして「接続」を選択します:
2023010419


接続するサブネットを選択します。ここでは先ほど作成した "sn-tok2" サブネットを指定しています。最後に「作成」ボタンをクリックします:
2023010420


パブリック・ゲートウェイとサブネットとの接続ができました。これで対象サブネット内に作られるサーバーリソースからインターネット側へアクセスすることができるようになりました:
2023010421


【Secrets Manager で証明書を管理】
この後 OpenShift クラスタをプライベート環境内に作るのですが、プライベート環境なのでそのままでは(ウェブコンソールにも oc CLI からも)アクセスできません。というわけで、プライベート環境にアクセスするための VPN をあらかじめ用意しておくことにします。

この VPN 接続をセキュアにするためには証明書が必要で、その証明書を Secrets Manager を使って管理します。したがってまず最初に Secrets Manager を(まだ使ったことがなければ)作成します。

IBM Cloud のダッシュボードから「リソースの作成」をクリックし、カタログ内の検索画面で "Secrets" と入力すると "Secrets Manager" が見つかるはずです。これを選択します:
2023010401


Secrets Manager の構成画面ではまずロケーション(下の例では「東京」)を選択します。また Secrets Manager の料金プランを選択します。Secrets Manager を初めて使う場合は無料の「トライアル」プランを 30 日間だけ使うこともできます:
2023010402


最後に、作成する Secrets Manager の名称(下の例では "kkimura-secrets-manager-tok2")を指定し、「以下のご使用条件を読み、同意します」にチェックを入れて「作成」します:
2023010403


Secrets Manager が作成できたら VPN サービスから Secrets Manager を使うことができるよう権限を設定する必要があります。IBM Cloud の画面右上のメニューから 管理→アクセス(IAM) を選択します:
2023010401


左ペインで「許可」を選択して許可の管理画面を表示してから「作成」をクリックします:
2023010402


サービス許可の設定をします。ここでは以下のように指定します:
 ソースアカウント:当該アカウント
 ソースサービス:VPC Infrastructure Services
 アクセス権限の範囲の指定:選択された属性に基づくリソース
 リソースタイプにチェックして "Client VPN for VPC" を選択
 (下に続く)

2023010403


 (上から続く)
 ターゲット・サービス: Secrets Manager
 アクセス権限の範囲の指定:全てのリソース
 サービス・アクセス:シークレット・リーダーにチェック

最後に「作成」ボタンをクリックします:
2023010404



指定した内容の許可レコードが作成されたことを確認します:
2023010405


ここまで準備できたら実際に証明書を作成します。ターミナル等で以下を実行します:
Easy-RSA 3 リポジトリをローカルフォルダに複製
$ git clone https://github.com/OpenVPN/easy-rsa.git

$ cd easy-rsa/easyrsa3
新しい PKI と CA を作成
$ ./easyrsa init-pki

$ ./easyrsa build-ca nopass
VPN サーバー証明書(vpn-server.vpn.ibm.com)を生成
$ ./easyrsa build-server-full vpn-server.vpn.ibm.com nopass

(プロンプトが止まったら "yes" と入力)
VPN クライアント証明書(client1.vpn.ibm.com)を生成
$ ./easyrsa build-client-full client1.vpn.ibm.com nopass

(プロンプトが止まったら "yes" と入力)
証明書ファイルの取り出し
./pki/ 以下をまとめて取り出しておく

作成した Secrets Manager に、作成した証明書を登録します。IBM Cloud で作成した Secrets Manager インスタンスを選択し、画面左の「シークレット」を選択し、画面右の「作成」をクリックします:
2023010401


次の画面では「TLS 証明書」を選択します:
2023010402


次の画面では「証明書のインポート」を選択します。また、この時点でシークレットの名称(下の例では "kkimura-vpn-certs" )を指定します:
2023010403


画面を下方向に、ファイルを3つアップロードするところまでスクロールします:
2023010404


3か所に、先ほど作成した証明書からそれぞれ以下のファイルを選択して指定します:
 証明書: "./pki/issued/vpn-server.vpn.ibm.com.crt"
 秘密鍵:"./pki/private/vpn-server.vpn.ibm.com.key"
 中間証明書:"./pki/ca.crt"
最後に「作成」ボタンをクリックします:
2023010405


正しく処理が行われて、新しいシークレットが作成されたことを確認します:
2023010406


これで VPN 接続をセキュアに行うための証明書を作成して Secrets Manager で管理するところまでの作業が完了しました。この後はこれらの情報を使って実際に VPN 環境を構築します。


【VPN 環境の作成】
改めて IBM Cloud の VPC のプライベート環境にアクセスするための VPN 環境を作ります。IBM Cloud のカタログから "client vpn for vpc" を検索して選択します:
2023010401


VPN の設定項目を指定しながら作成していきます。まず VPN タイプには「クライアントとサイト間のサーバー」を選択し、ロケーションは VPN サーバーをどのロケーションに設置するかを指定(下の例では「東京」)します:
2023010402


設定を続けます。次に VPN サーバー名を適当に(下の例では "kkimura-client-vpn")入力し、接続対象となる VPC(下の例では上で作成した "kkimura-vpc")を入力します。その下のクライアント IPv4 アドレスプールには VPN 接続したマシンに割り振られる IP アドレスの範囲(下の例では 10.244.0.0/16)を指定します。ここは上で作成したサブネットと被らないアドレス帯を指定してください:
2023010403


設定を続けます。VPN サーバーモードは「スタンドアロン・モード」を選択し、そのサブネットは上で作成したサブネット(下の例では "sn-tok2")を指定します:
2023010404


設定を続けます。次は認証に関わる設定を行います。サーバー認証の証明書ソースは "Secrets Manager" を選択し、「インスタンスで検索」をクリックしてからサーバー・シークレット・マネージャーとサーバー SSL 証明書として先ほど作成したもの(下の例では "kkimura-secrets-manager-tok2" と "kkimura-vpn-certs")を指定します:
2023010405


設定を続けます。次はクライアント認証の設定を行います。「クライアント証明書」にチェックを入れ、証明書ソースは "Secrets Manager"、「インスタンスで検索」を選んでから、上同様にクライアント・シークレット・マネージャーとクライアント SSL 証明書をそれぞれ先ほど作成したもの(下の例では "kkimura-secrets-manager-tok2" と "kkimura-vpn-certs")を指定します:
2023010406


設定を続けます。次は一番下までスクロールして追加の構成部分を設定します。トランスポートプロトコルは "UDP" 、VPN ポートは 443 を指定します。またトンネルモードは「フル・トンネル」か「分割トンネル」かを選びます。クライアントがこのプライベートネットワークに VPN 接続中もインターネットの利用をさせる場合は「分割トンネル」を、VPN 接続中はインターネットアクセスを無効にさせる場合は「フルトンネル」を、それぞれ目的に応じて選択します(下の例では「分割トンネル」)。最後に「VPN サーバーの作成」ボタンをクリックして作成します:
2023010407


作成して状況が「安定」になるまで少し時間がかかりますが、VPN の「クライアントとサイト間のサーバー」タブに VPN が1つ追加されたことが確認できます:
2023010401


直前の設定で UDP/443 ポートを使う旨を指定しましたが、この設定内容がセキュリティグループでも許可されている必要があります。そのための設定を追加します。

VPC インフラストラクチャー画面の左ペインで「セキュリティ・グループ」を選択します。そこから今設定している VPC(下の例であれば "kkimura-vpc")のセキュリティグループになっているものを探してクリックします:
2023010401


セキュリティグループの「ルール」タブを選択して、「作成」ボタンをクリックします:
2023010402


新しいインバウンドルールを追加します。今回の例であればプロトコルは "UDP"、ポートは「ポート範囲」を選択した上で、最小値と最大値の両方に "443" を入力します。またソースタイプは「すべて」を選択して、最後に「作成」ボタンをクリックします:
2023010403


これで新しいインバウンドルールが追加され、セキュリティグループでも UDP/443 を許可することができました:
2023010404


VPN の設定項目はまだ必要なのですが、ここまでの作業ができたら一旦終わりとして、次はプライベートな OpenShift クラスタを作ります。



【プライベート OpenShift クラスタ環境の作成】
パブリックな OpenShift 環境の場合はいきなり OpenShift クラスタを作ってそのまま使い始めることができますが、プライベート環境の場合は(そのセキュリティポリシーにもよりますが)かなり準備が必要でした。が、ここまでできていればあと少しで、OpenShift クラスタを作ることもできます。

IBM Cloud のダッシュボードから「リソースの作成」を選び、カタログの検索バーに "OpenShift" と入力すると "Red Hat OpenShift on IBM Cloud" が見つかるのでこれを選択します:
2023010401


作成する OpenShift クラスタの設定項目を指定していきます。まずセットアップの種類は「手動セットアップ」で、インフラストラクチャーは "VPC" を選びます。なお、この後マスターサービス・エンドポイントを指定する項目があるのですが、そこで「プライベートのみ」を選択できるのはここで "VPC" を選んだ場合のみです。つまりプライベートクラウドとしての OpenShift 環境を構築するにはここで VPC を選択する必要があり、そのためこの上で準備したような設定を行う必要があったのでした:
2023010402


続けて利用する VPC("kkimura-vpc")とクラウド・オブジェクト・ストレージを指定します。クラウド・オブジェクト・ストレージを使っていない場合は新規に1つ作ってからここで指定します。また OpenShift のバージョンは特にこだわりがなければ最新版を、OCP ライセンスは「このワーカー・プールの追加ライセンスを購入する」を選択します:
2023010403


ワーカー・ゾーンはワーカーノードをどのゾーンのどのサブネットに配置するかを指定します。サブネットを作成したゾーンと、そのサブネットを指定します。それ以外のゾーンのチェックを外します。またワーカープールのノード数は「2」を指定します(ここで1以下を指定すると作成前のチェックでエラーになりますが、この後の作業でワーカー・ノードは1つに変更できます)。ワーカープールのフレーバー(1台あたりのスペックは必要に応じて適宜変更してください):
2023010404


マスター・サービス・エンドポイントを指定します。ここで「プライベート・エンドポイントのみ」を指定します(OpenShift を VPC で作成する場合はプライベートのみが選択できます。Classic 環境だとプライベートのみという選択肢はありません)。また適当な名称でクラスター名を指定します:
2023010405


最後の統合項目ではアクティビティトラッキングやロギング、モニタリングを有効にすることも可能ですが、不要の場合はチェックを外しても問題ありません。最後に「作成」ボタンをクリックします:
2023010406


これでプライベート版 OpenShift クラスタ環境のセットアップが開始されます。セットアップが完了するまでしばらく(30分ほど?)待ちます:
2023010407


ステータスが「正常」になり、Ingress まで含めた全ての機能が正常に稼働している状態になることを確認します:
2023010401


この状態で「OpenShift Web コンソール」ボタンをクリックしても「OpenShift Web コンソール URL に到達できませんでした」というエラーになることを確認します。この環境はプライベートネットワーク上に構築されているので、これまでのようにインターネット経由でのアクセスができないからです(つまり現時点ではアクセスできないことが正しい状態で、クラスタがプライベートネットワーク上に構築されていることを意味します):
2023010402


では次にこのプライベート OpenShift クラスタにアクセスすることができるようになる VPN 環境を構築します:


【VPN サーバー環境の作成】
最後にもう一度 VPN の設定を行います。VPN の経路情報を登録します。VPC インフラストラクチャー画面の左ペインで "VPN" を選択し、「クライアントとサイト間のサーバー」タブに表示される VPN を選択します:
2023010401


この VPN の「VPN サーバー経路」タブを選択して「作成」ボタンをクリックします:
2023010402


まずサブネット 10.10.0.0/24 へのアクセスはそのまま通す必要があるので、CIDR に "10.10.0.0/24"、アクションは「配信」を選択して「作成」します(名前は適当に):
2023010403


同様に、他のクラウドサービスへもアクセスできる必要があるため、同じ作業を繰り返して、CIDR に "166.8.0.0/14" 、アクションは「変換」でルールを1つ追加します:
2023010404


下図のように VPN サーバー経路が2つ登録できていれば VPN の経路に関する設定は終わりです:
2023010401



【VPN クライアント環境の作成】
VPN サーバーの準備ができたので、次に VPN クライアントを用意します。まずは VPN クライアント用のプロファイルをダウンロードします。VPN インフラストラクチャー画面の左ペインで VPN を選び、「クライアントとサイト間のサーバー」タブから先ほど作成した VPN 接続を選択します:
2023010401



そして「クライアント」タブ内の「クライアント・プロファイルのダウンロード」を選択します。すると .ovpn という拡張子がついた Open VPN 向けプロファイルがダウンロードできます:
2023010402


自分が付けた VPN 名(この例では "kkimura-client-vpn")に ".ovpn" という拡張子が付いたファイル名のプロファイルがダウンロードされているはずです。このファイルをテキストエディタで開きます:
2023010403


ファイルの最後に近いあたりにある "#cert " で始まる行と、"#key " で始まる行両方の "#" を削除します。加えて "cert", "key" に続く文字列を以下のように変更して保存します:
 cert client1.vpn.ibm.com.crt
 key client1.vpn.ibm.com.key
2023010404


そして証明書ファイルを作った際の ./pki/issued/client1.vpn.ibm.com.crt および ./pki/private/client1.vpn.ibm.com.key の2つのファイルを OVPN ファイルを同じフォルダにコピーします:
2023010405


これで VPN 接続用プロファイルが準備できました。後は VPN クライアントを用意して、このプロファイルを適用するだけです。

VPN クライアントは(2023/01/04 時点では)OpenVPN クライアントの V2 および V3 が推奨されています。今回はこちらから OpenVPN V3 をダウンロード&インストールしました。自分のシステム環境プラットフォームにあったものを選んでダウンロードしてください:
https://openvpn.net/vpn-client/#tab-windows


OpenVPN クライアントのインストールが環境したら起動します。初回起動時にプロファイルのインポート画面になるので、FILE タブを選んで"BROWSE" ボタンをクリックし、上で用意した OVPN ファイル(kkimura-client-vpn.ovpn)を指定します:
2023010401


OVPN ファイルが正しく作成され、また正しく編集できていれば鍵ファイルごと読み込まれてプロファイルのインポートが完了します。ここで "CONNECT" ボタンを押すと VPN 接続が開始します:
2023010402


設定にミスがなければ VPN 接続は一瞬で完了するはずです。"CONNECTED" と表示されていれば接続できていることになります:
2023010403


試しにこの VPN 接続ができている間を使ってプライベート OpenShift クラスタが正しく作られていることを確認してみます。IBM Cloud のダッシュボード画面に戻り、作成した OpenShift クラスタを選択して「OpenShift Web コンソール」ボタンをクリックしてみます(先ほどはエラーになったオペレーションです):
2023010401


今回は OpenShift のウェブコンソール画面が表示されるはずです。VPN 接続が有効になっているので、この URL にアクセスすることができました。なお IBM Cloud の OpenShift では URL のリージョン部分(例えば "jp-tok")の直前が "0000" であればパブリック、"i000" であればプライベートな URL になっているとのことで、今回は "****-i000-jp-tok.***" というパターンになっているので、ここでもプライベートな URL にアクセスできていることが確認できます:
2023010404


ついでに今回は「プライベートクラウド側からインターネット側へのアクセスは許可」するポリシーで環境を構築しました。上述のパブリックゲートウェイが有効になっているので、プライベートクラウド側から Docker ハブ等にもアクセスできるはずなので、それを確認してみます。まずはパースペクティブを切り替えるため、画面左の "Administrator" と書かれた部分をクリックして "Developer" に変更します:
2023010406


Developer パースペクティブの状態で「+追加」をクリックし、プロジェクト(例えば "default")を選択してから「コンテナイメージ」を選択します(新しいアプリケーションをコンテナイメージから作る、という指示を意味しています):
2023010407


今回動作検証用に使うアプリケーションのイメージはこれを使うことにします。私が作って Docker ハブで公開しているもので、HTTP でアクセスすると /etc/hostname の内容を読み取って text/plain で返す、というシンプルなアプリケーションです。イメージ名は dotnsf/hostname です:
2023010401


このイメージを指定します。OpenShift の画面には docker.io/dotnsf/hostname と入力します(Docker ハブ上の dotnsf/hostname という指定です)。入力後に「検証済み」と表示されるまで少し待ちます:
2023010402


これ以外のオプションは特に変更の必要はありませんが、名称などはお好きなように指定してください。ただ「生成するリソースタイプの選択」は「デプロイメント」を指定するようにしてください。最後に「作成」ボタンをクリックします:
2023010403


少し時間がかかりますが、以下のような画面が表示されればデプロイ完了です。アイコン右上のリンクボタンをクリックして動作確認してます:
2023010404


別のウィンドウタブが開いて、プライベート OpenShift 上にデプロイされた dotnsf/hostname アプリにアクセスできることを確認します。またこの URL パターンが "***.i000-jp-tok***" となっていて、プライベート OpenShift のパターンになっていることも合わせて確認します:
2023010405


無事にプライベート OpenShift 環境を構築することができました。VPN 接続を切る場合、Windows であればタスクバーなどから OpenVPN クライアントを選択して disconnect することもできます:
2023010405




 

とある要件を実現するツールを作りました。同じことに悩む人がいた場合を想定して、ツールをソースごと公開することにしました。

某ウェブプラットフォームのサービス終了が決まり(わかる人はこれだけで何の話か推測されそうだけど・・)、現在稼働中のウェブアプリケーションを引っ越しすることになりました。引っ越しそのものはさほど難しくないのですが、問題は「サービスの URL が変わってしまう」ことでした。

図に示すとこのような感じです。これまで運用していたサービスの運用環境を A から B に引っ越しした結果、これまでの URL とは異なる URL で引き続き運用することになりました:
2022070601


これまで使っていたユーザーに対しても「サービスの URL が変わった」ことを知らせてあげたいのですが、具体的にどうするべきでしょうか?A で運用中の画面に「URL が変更になった」と注意書きを含めて、改めて B にアクセスしてもらうこともできます。が、もう少し気の利くやり方として「A にアクセスしたら自動的に B に転送させて、B で運用中の画面で URL が変更になった旨を記載しておく(そのままブックマークできるようにする)」という方法もあります。


この後者の方法を実現するためには A にアクセスした利用者に対して "301" という HTTP ステータスコードと、続けて変更先の URL を Location ヘッダに含めて返すことで実現できます。この HTTP ステータスコード 301 は "Moved Permanentaly" を意味していて「URL が(一時的ではなく)恒久的に変更になった」ことを示しています。続けて Location ヘッダに新しい URL を含めておくことでウェブブラウザ側で新しい URL に自動的に遷移してくれます。つまり「A にアクセスしたら自動的に B に移動させる」ことが実現できます(そして B 側で「URL が変わったので現在のページをブックマークして」といったメッセージを記しておく、といった対応になります)。ウェブページの引っ越しを行う場合の一般的な手段でもあります:
2022070602



問題となるのは、この「301 という HTTP ステータスコードを返す」機能です。A 側のサービスでそのような転送機能や転送の設定が提供されていればそれを使えばいいのですが、必ずしも提供されていないことも考えられます。そのようなケースに対応するため、今回「アプリケーションの機能として 301 HTTP ステータスコードと、引っ越し先 URL を返すアプリケーション」を作ったので、公開することにしました。これまで A で動いてたアプリケーションの代わりにこのアプリケーション(下図の app)をデプロイすることで、A へのアクセスがあった場合に、新しい B への URL に無条件で転送させることができるようになります:
2022070603


このような挙動を実現するための Node.js アプリケーションのソースコードを以下で公開しました:
https://github.com/dotnsf/301movedpermanently

動作確認する場合は、Node.js が導入済みの環境にソースコードをダウンロードするか git clone して、環境変数 URL に転送先の URL を指定して実行します。なおアプリケーションはデフォルトで 8080 番ポートで待ち受けますが、このポート番号を変えたい場合は環境変数 PORT に指定して実行してください:
$ git clone https://github.com/dotnsf/301movedpermanently

$ cd 301movedpermanently

$ npm install

$ URL=https://www.yahoo.co.jp/ node app (全てのリクエストを Yahoo! トップページに転送する場合)

起動後にアプリケーションにアクセスすると、全てのリクエストが環境変数 URL で指定した値(上の例だと https://www.yahoo.co.jp/)に転送されます。なおコード内ではメソッド/パス/パラメータに関係なく、全てのリクエストを GET リクエストに変換して転送しています。GET/HEAD メソッド以外のリクエストについては HTTP ステータス 308 を返して対応するケースもありますが、今回のような「サービスごと引っ越し」のケースでは新 URL のトップページに転送することが多いと思うので、今回は説明を控えます(下の青字部分が実行されます):
app.all( '*', function( req, res ){
  if( post_redirect ){
    var method = req.method;
    if( method == 'GET' || method == 'HEAD' ){
      //. https://developer.mozilla.org/ja/docs/Web/HTTP/Status/301
      res.status( 301 );
    }else{
      //. https://developer.mozilla.org/ja/docs/Web/HTTP/Status/308
      res.status( 308 );
    }
  }else{
    res.status( 301 );
  }

  res.set( 'Location', url );
  res.end();
});

古いサイト A が動いている間だけ有効な強制転送方法ですが、他に方法がない場合はこんな感じで古いサイトを訪ねた人を強制的に B へ転送する方法が有効だと考えています。

なお、docker イメージとしても公開しているので、移行元で docker が使える環境であればこちらを使っていただくのが手っ取り早いと思っています:
dotnsf/301movedpermanently


前回dokku を使ったプライベート PaaS 環境の構築、およびシンプルなアプリケーションのデプロイ手順を紹介しました。 今回はより実践的なアプリケーションとして PostgreSQL データベースを併用するアプリケーションのデプロイ手順を紹介します(といっても、実は heroku を CLI で操作する時の手順とあまり変わりません・・)。

なお今回紹介する内容は、前回のセットアップ時に "withcorona.world" という独自ドメインを設定している想定で紹介しています。異なるドメインで設定されている場合は自分で設定したドメインに適宜読み替えてください。


【dokku 内で PostgreSQL データベースを動かす】
まず dokku 環境内にデータベースサーバーを用意します。今回は PostgreSQL を使うケースを想定して以下で紹介します。まずは dokku サーバーにログインしておきます。

dokku ではいくつかのサービスが「プラグイン」という形で連携できるよう用意されています。PostgreSQL もその1つです。というわけで、まずは dokku にログインして PostgreSQL プラグインをインストールします:
# dokku plugin:install https://github.com/dokku/dokku-postgres.git

インストールが完了すると PostgreSQL データベースをインスタンス化することができるようになります。例えば "mydb" という名前を付けて1インスタンス作るには以下のように入力します:
# dokku postgres:create mydb

作成後に作ったデータベースインスタンスの情報を確認する場合は以下のように入力します:
# dokku postgres:info mydb

=====> mydb postgres service information
       Config dir:          /var/lib/dokku/services/postgres/mydb/data
       Config options:
       Data dir:            /var/lib/dokku/services/postgres/mydb/data
       Dsn:                 postgres://postgres:XXXXXXXX@dokku-postgres-mydb:5432/mydb
       Exposed ports:       -
       Id:                  a017a6896694987cb0e729b4ec1042f831eecd0d8f726d52eeea435ecd9fcf4e
       Internal ip:         172.17.0.3
       Links:               -
       Service root:        /var/lib/dokku/services/postgres/mydb
       Status:              running
       Version:             postgres:14.2

この確認結果の Dsn 値として紹介されている "postgres://" で始まる文字列がいわゆる接続文字列になっていて、(後述する)環境変数の値になります。 このインスタンスのシェルに入って PostgreSQL の CLI を使ってテーブルを1つ定義しておきたいので、以下のように実行してください(データベースに接続する時は接続文字列の "dokku-postgres-mydb" 部分を "localhost" に変えて実行してください):
# dokku postgres:enter mydb (シェルにログイン)

/# psql "postgres://postgres:XXXXXXXX@localhost:5432/mydb" (psql でデータベースに接続)

mydb=# create table if not exists items ( id varchar(50) not null primary key, name varchar(50) default '', price int default 0, created bigint default 0, updated bigint default 0 ); (SQL で items テーブル作成)

mydb=# \q (データベースから切断)

/# exit (シェルからログアウト)

これで dokku 環境内に mydb という名前の PostgreSQL データベースを1つ作り、items という名前のテーブルを1つ定義する所まで用意できました。続いて、この mydb データベースと items テーブルを使ったウェブアプリケーションを dokku 内で動かします。


【dokku 内で PostgreSQL データベースに接続するアプリケーションを動かす】
dokku 内で PostgreSQL データベースを使うアプリケーションを動かします。今回は以下のサンプルを使います(上記で作成した items テーブルを使うアプリケーションです):
https://github.com/dotnsf/cnapp_postgresql


このアプリケーションの PostgreSQL と接続する部分は以下のように記述されています:
  :
  :
var database_url = 'DATABASE_URL' in process.env ? process.env.DATABASE_URL : settings.database_url; 
var pg = null;
if( database_url ){
  console.log( 'database_url = ' + database_url );
  pg = new PG.Pool({
    connectionString: database_url,
    idleTimeoutMillis: ( 3 * 86400 * 1000 )
  });
  :
  :


具体的な挙動としては、アプリケーション実行時の "DATABASE_URL" という環境変数値を参照し、値が設定されていたらその内容を接続文字列とみなして PostgreSQL サーバーに接続する、という実装内容になっています(説明は省略しますが、接続後に items テーブルを読み書きする内容になっています)。

なので、このアプリケーションが dokku 内で実行される時に、先ほど作成した mydb データベースへの接続文字列が環境変数 DATABASE_URL として定義されていればこのアプリケーションは正しくデータベースに接続して動く、ということになります。

この辺りは heroku ユーザーであればなんとなく「同じだ・・」とわかると思います。で、その環境変数を設定するためには dokku 内のアプリケーションとデータベースがリンクさえされていれば実現できるようになっています(この辺りはクラウドネイティブアプリケーションを開発する際の 12 factors と呼ばれるベストプラクティスに沿った仕様となっています)。

というわけで、まずは dokku にアプリケーションを追加し、データベースとのリンクを設定します。今回は "cnapp" という名前のアプリケーションを作ることにして、この "cnapp" アプリケーションと "mydb" データベースをリンクしておきます(これで cnapp アプリの実行時にデータベース mydb に接続するための接続文字列が環境変数 DATABASE_URL にセットされて起動します):
# dokku apps:create cnapp

# dokku postgres:link mydb cnapp

そして前回同様にこのサンプルアプリを Git clone して、リモート接続先に dokku を追加して、main ブランチを push します:
# git clone https://github.com/dotnsf/cnapp_postgresql

# cd cnapp_postgresql

# git remote add dokku dokku@withcorona.world:cnapp

# git push dokku main

ここまでのコマンドが正しく実行されていると http://cnapp.withcorona.world/ でアクセスできるようになります※:

2022060101


※稀にこの URL では想定していないページ(Nginx のデフォルトページなど)が表示されることがあります。その場合は http://cnapp.withcorona:8080/ のようにポート番号をつけてアクセスするとうまくいきます。その後に以下のコマンドを実行するとポート番号指定なしでも正しく表示できるようになります:
# dokku proxy:ports-add cnapp http:80:8080


これだけでも一応動きますが、ついでに(?) https 接続できるよう、Let's Encrypt プラグインの設定も行っておきます:
# dokku letsencrypt:enable cnapp

最後にウェブブラウザで https://cnapp.withcorona.world/ にアクセスして動作確認します:
2022060102


最初は何も登録されていませんが、名前(name)と価格(price)を入力して追加(Create)すると、そのデータが( PostgreSQL の)mydb データベースの items テーブルに登録され、一覧として表示されるようになります:
2022060103


以上、dokku でデータベース連携アプリケーションを作って動かすための設定でした。PostgreSQL 以外にも dokku には公式機能として MySQL や Redis 、ElasticSearch といったプラグインが用意されているので、クラウドで認証した結果をセッション共有するようなアプリケーションでも動かすことができると思います。




ある程度 heroku を使ったことがある人であれば、dokku は文字通りに heroku ライクなプライベート環境に感じることができると思います(ただ dokku だとほとんどの作業が CLI からのコマンドになる点が、ウェブ GUI で色々用意されている heroku とは異なる点です)。また Cloud Foundry と比較しても、Cloud Foundry では
$ cf push (app)

のようにしてアプリをデプロイしていましたが、dokku はほぼ同様にして、
$ git remote add dokku (app) (を一度実行してから)
$ git push dokku main

といった形でアプリのデプロイができるので、Cloud Foundry の代わりとしても使いやすい環境のように感じています。今回紹介した withcorona.world というドメインは実験用の捨てドメインなのでおそらくこの紹介記事でしか使うことはないと思っていますが、他の取得ドメイン(とちょっと規模の大きめな IaaS 環境)を使って自分のプライベート Cloud Foundry 環境を作って運用してみるつもりでいます。



このページのトップヘ