検索

クリアフィルター
お知らせ
Mihoko Iijima · 2023年4月4日

テクノロジーボーナス詳細:InterSystems IRIS Cloud SQL and IntegratedML コンテスト 2023

開発者の皆さん、こんにちは。 技術文書ライティングコンテストの受賞者が発表されたばかりですが、次のコンテスト:InterSystems IRIS Cloud SQL and IntegratedML コンテスト 2023 のテクノロジーボーナス詳細が決定しましたのでお知らせします📣 IntegratedML の利用 オンラインデモ コミュニティに記事を投稿する コミュニティに2つ目の記事を投稿する YouTubeにビデオを公開する はじめてチャレンジされた方 InterSystems Idea 内 Community Opportunityの実装 獲得ポイントについて詳細は、以下ご参照ください。 IntegratedML の利用 - 5 points IRIS Cloud SQL の IntegratedML SQL エクステンションを使用した場合、5ポイント獲得できます。 オンラインデモの公開 - 2 points オンラインデモとしてアプリケーションをクラウドにプロビジョニングすると、2ポイント獲得できます。開発環境テンプレートやその他のデプロイメントオプションを使用することができます。例サンプルアプリケーションの使用方法についてはビデオをご参照ください。 コミュニティに記事を投稿する - 2 points コミュニティに応募したアプリケーションの概要を説明する記事を投稿すると2ポイントを獲得できます。異なる言語への翻訳も含まれます。 コミュニティに2つ目の記事を投稿する - 1 point 2つ目の記事を投稿する、または投稿したアプリケーション概要の翻訳記事を投稿することで、さらボーナスポイントを獲得できます。(3記事以降はポイントが加算されません。) YouTubeにビデオを公開する - 3 points 開発した作品の動画を作成し、YouTube に掲載した場合、3ポイント獲得できます。 例)過去投稿されたビデオ 初めてチャレンジされた方 - 3 points InterSystems Open Exhangeコンテストに初めて参加された場合、3ポイント獲得できます! InterSystems Idea の Community Oppotunity の実装 - 3 points InterSystems Ideaポータルに投稿されているCommunity Opportunityのアイデアを開発ツールとして実装された場合、3ポイント獲得できます! ※ ボーナスポイントについては、変更される可能性もあります。予めご了承ください。 ぜひ、コンテストにチャレンジしてみてください!
記事
Mihoko Iijima · 2020年6月28日

【はじめての InterSystems IRIS】セルフラーニングビデオ:アクセス編:Python の NativeAPI に挑戦

Python から InterSystems IRIS へ接続する方法の1つである「Native API」(※)の使用方法ご説明します。 ※ Python からのアクセスは、Native API の他に、PyODBC を利用した接続方法もあります。PyODBC の利用については別の記事でご説明します。 もくじ 最初~1:47 前回のビデオの学習(セルフラーニングビデオの索引記事もご参照ください) 1:47~3:18 今回の説明内容解説 3:18~5:14  Python Native APIを利用するための準備 5:14~10:48 IRISに接続する~^employee(1)の作成と参照 10:48~12:49 グローバル変数のサブスクリプトのループ方法(Iteratorの使い方) 12:49~14:19 グローバル変数の削除とコネクションのクローズ 14:19~20:21 国税庁が公開している都道府県別酒類消費量をグローバル変数に登録する (東京のアルコール消費量の登録) 20:21~21:43 大阪のアルコール消費量の追加登録 21:43~24:22 ^Alcoholから東京だけのデータ取得 24:22~27:22 ^Alcohol全件取得 27:22~28:27 特定のサブスクリプトの情報を削除する 28:27~30:53 おまけ(Pythonの便利なパッケージをインポートし、CSVデータのグラフ化) 30:53~最後まで まとめ ※ YouTubeでご覧いただくと、「もくじ」の秒数クリックでビデオをジャンプできます。 サンプルコード(Git) HelloWorldNativeAPI.ipynb 国税庁が公開している都道府県別酒類消費量をグローバル変数に登録する流れ(Sake.ipynb)
お知らせ
Mihoko Iijima · 2020年8月6日

第5回 InterSystems IRIS プログラミングコンテスト(IRIS for Health FHIR コンテスト)

開発者のみなさん、こんにちは! InterSystems IRIS Data Platform を使用してオープンソースソリューションを作成するコンテストへようこそ! 今回のコンテスト用テンプレートはこちら!(8/10 更新) ➡️ IRIS-FHIR-Template ⬅️(InterSystems IRIS for Health のプレビューリリース版:2020.3 が利用されている開発テンプレートです) テンプレートの日本語 Readme をご用意しています。 応募期間は 2020年8月10日~23日 です! 優勝特典 1、審査員から多く票を集めたアプリケーションには、以下の賞金が贈られます。 🥇 1位 - $2,000 🥈 2位 - $1,000 🥉 3位 - $500 2、Developer Community で多く票を集めたソリューションには、以下の賞金が贈られます。 🥇 1位 - $1,000 🥈 2位 - $500 複数の参加者が同数の票を獲得した場合、全参加者が勝者となり賞金は勝者間で分配されます。 参加資格 どなたでもご参加いただけます!(InterSystems 開発者コミュニティのアカウントを作成するだけでご応募いただけます) コンテストのスケジュール 8月10日~23日 応募期間8月24日~30日 投票8月31日 優秀者発表 コンテストのテーマ 💡 Fast Healthcare Interoperability Resources – FHIR 💡 第5回目のテーマは「InterSystems IRIS for Health を使用して FHIR ソリューションを開発する」です。 InterSystems IRIS for Health を使用し、FHIR に関連するライブラリ、パッケージ、ツール、FHIRソリューションを開発いただき、ご応募ください。 ご応募いただいた中から、最も優秀な作品に賞が贈られます。 アプリケーションは IRIS for Health Community Edition で動作するように作成してください。 アプリケーションはオープンソースであり、GitHub で公開されている必要があります。 特別な技術を活用して開発されたアプリケーションは、テクノロジボーナスの対象となります。ボーナスの詳細については、このページで後日お知らせします。 コンテスト用テンプレートについて(8/8更新) IRIS-FHIR-Template (8/10更新:テンプレートの日本語 Readme) ご参考情報 コンテスト応募方法(このページ末尾のビデオをご参照ください) ドキュメント FHIR Support in InterSystems Products(英語) オンラインコース(英語) Learn FHIR for Software Developers Building SMART on FHIR Apps with InterSystems FHIR Sandbox Exploring FHIR Resource APIs Using InterSystems IRIS for Health to Reduce Readmissions Connecting Devices to InterSystems IRIS for Health Monitoring Oxygen Saturation in Infants FHIR Integration QuickStart ビデオ(英語) 6 Rapid FHIR Questions SMART on FHIR: The Basics Developing with FHIR - REST APIs FHIR in InterSystems IRIS for Health FHIR API Management Searching for FHIR Resources in IRIS for Health その他:開発者コミュニティ(US版)のYouTubeもご参照ください ビデオ(日本語) InterSystems IRIS for Health による FHIR API の構築(1) InterSystems IRIS for Health による FHIR API の構築(2) FHIRについてのQA 開発者コミュニティのFHIRタグ(US版/日本版) community.fhir.org 審査及び投票ルール(英語) インターシステムズ社のプロダクトマネージャ、Developer Communityのモデレータ、グローバルマスターアドボケイト(VIPレベル)等、Developer Community 内での投票も行われます。 コンテストの審査および投票ルールについて詳細はこちらをご覧ください。 ご応募方法について 以下の応募方法ビデオをご参照ください。 以下、コンテストに応募する迄の手順をご説明します。 コンテスト応募までの流れは以下の通りです(※ビデオでは、3番以降の内容をご紹介しています)。 1、IRISプログラミングコンテスト用テンプレートを使用して、開発環境を準備します。 2、コンテスト用アプリケーションを作成します。 3、コンテストの準備が完了したら、ソースコードをローカルのGitリポジトリへコミットします。 初回コミット時に、Gitの初期設定がないためコミットが失敗することがあります。その場合は、以下のコマンドでGitユーザ名とEmailを設定します。 git config --global user.name "ここにユーザ名"git config --global user.email "ここにメールアドレス” 4、ローカルのGitリポジトリのコミットが完了したら、リモートのGitリポジトリを作成します。 リポジトリ作成後、リモートリポジトリのURLをコピーします。 5、リモートのGitリポジトリへPushします。 git push ここにリモートのリポジトリのURL 6、OpenExchangeにログインし、アプリケーションを追加します。 ※事前にDeveloper communityでユーザアカウントを作成する必要があります。ログイン後、Profile→Applications から Application をクリックし、4 でコピーしたリモートのGitリポジトリのURLを設定します。アプリケーションを登録すると、画面右上に「Send Approval」のボタンが表示されるので、クリックします。再度作成したアプリケーションを開くと、「Apply for Contest」ボタンが表示されるので、クリックすると応募が完了します。 開発テンプレートの使い方ビデオを公開しました! FHIR コンテストのテンプレートの使用方法をご紹介しています。 テンプレートをご利用いただくためには、以下 (3) にある3つのソフトウェアのインストールが必要です。 ビデオ内で記述しているコマンドなどは、(4)以降 をご参照ください。 (1) IRISオンラインプログラミングコンテスト FHIRコンテストの詳細 この記事 (2) テンプレートの説明 iris-fhir-template テンプレート iris-fhir-template の Git (3) 事前準備 Dockerのインストール(コンテナを管理するアプリ) Gitのインストール(ソースを管理するアプリ) VSCodeのインストール(IDE) (4) コマンド実行例 開発テンプレートのダウンロード cd 任意のディレクトリgit clone https://github.com/intersystems-community/iris-fhir-template.git コンテナ作成のためのビルド docker-compose build コンテナの開始 docker-compose up -d コンテナの停止 docker-compose stop コンテナ一覧を表示 docker ps コンテナ一覧に表示されるコンテナ名を使用してコンテナにログインします。 docker exec -it <コンテナ名> bash (5) 目次(YouTubeでご覧いただくと指定秒数にジャンプできます) 最初から~ コンテスト告知ページから開発テンプレート公開ページへの移動方法 00:49~ iris-fhir-template テンプレートのダウンロード(git clone) 01:11~ テンプレートに含まれるコードの紹介(VSCode利用) 01:51~ コンテナの中身について 03:05~ コンテナ開始     FHIRリポジトリにインポートしているサンプルデータについて 03:35~ RESTクライアントからの確認方法(Postman利用例について) 04:25~ サンプルアプリ(HTML)で実行しているGET要求の説明 06:20~ PostmanからサンプルURLを実行 (Patientに対するGET要求、AppointmentのPOST要求など) 10:10~ サンプルアプリ(HTML)の実行 12:25~ Patientデータを増やす方法 参考URL: https://github.com/intersystems-community/iris-fhir-template/blob/master/README-JP.md ぜひ、IRISプログラミングコンテストへご応募ください!
記事
Mihoko Iijima · 2020年7月7日

InterSystems IRISソリューションを CircleCI を使用して GCP Kubernetes Cluster GKE へデプロイする

私たちのほとんどは、多かれ少なかれDockerに慣れ親しんでいます。 Dockerを使用している人たちは、ほとんどのアプリケーションを簡単にデプロイし遊んで、何かを壊してしまってもDockerコンテナを再起動するだけでアプリケーションを復元できる点を気に入っています。 InterSystems も Docker を気に入っています。 InterSystems OpenExchange プロジェクトには、InterSystems IRISのイメージを簡単にダウンロードして実行できるDockerコンテナで実行するサンプルが多数掲載されています。また、Visual Studio IRISプラグインなど、その他の便利なコンポーネントもあります 。 特定のユースケース用の追加コードを使ってDockerでIRISを実行するのは簡単ですが、ソリューションを他のユーザーと共有する場合は、コマンドを実行し、コードを更新するたびに繰り返し実行するための何らかの方法が必要になります。 この記事では、継続的インテグレーション/継続的デリバリー (CI / CD)を使ってそのプロセスを簡素化する方法を説明します。 セットアップ まず、IRISをベースにしたシンプルなREST APIアプリケーションから紹介します。アプリケーションの詳細は、ビデオ「InterSystems IRIS、ObjectScript および DockerでREST API を作成する」でご覧いただけます。では、CI/CDを使用して似たようなアプリケーションを他の人と共有できるのかを見てみましょう。 最初は、コードを個人用GitHubリポジトリにCloneします。GitHubにアカウントを持っていない場合は、サインアップしてアカウントを1つ作成してください。便利なようにSSH経由でアクセスできるようにしてできるようにしておくとPullまたPushするたびにパスワードを入力する必要がなくなります。次に、GitHubのプロジェクトページintersystems-community/objectscript-rest-docker-templateGitHubに移動して、[Use this template] ボタンをクリックし、テンプレートに基づいて独自バージョンのリポジトリを作成します。「my-objectscript-rest-docker-template」のような名前を付けます。 次に、プロジェクトをローカルマシンにPullします。 $ git clone git@github.com:<your_account>/my-objectscript-rest-docker-template.git 次に、「hello, world!」を表示するRESTエンドポイントを追加します。 エンドポイントは src/cls/Sample/PersonREST.cls クラスで定義されています。 エンドポイントは次のようになります(最初の<Route>に定義されています)。 <Route Url="/helloworld" Method="GET" Call="HelloWorld"/><Route Url="/all" Method="GET" Call="GetAllPersons"/>... HelloWorldメソッドを呼び出します。 ClassMethod HelloWorld() As %Status{ Write "Hello, world!" Quit $$$OK} ここで、リモートリポジトリにPushするときこれがどのように機能するかを考慮する必要があります。 考慮点は次のとおりです。 Dockerイメージをビルドする。 Dockerイメージを保存する。 このイメージに基づいてコンテナを実行する。 すでにGitHubに統合されているCircleCIサービスを使用して、Dockerイメージをビルドします。 また、Dockerイメージを保存し、保存したイメージを基にしたコンテナをKubernetesで実行できるGoogle Cloudを使用します。 これについて少し掘り下げましょう。 Google Cloudの前提条件 サービスの無料枠を提供するGoogle Cloudのアカウントに登録したとしましょう。「Development」という名前のプロジェクトを作成し、「Create cluster」ボタンをクリックしてKubernetesクラスタを作成します。 デモでは、左側にある「Clusters」を選択します。Kubernetesの新しいバージョンを選択し、n1-standard-1のマシンタイプを選択します。 この目的では、1台のマシンで十分です。 「Create」ボタンをクリックして、クラスターへの接続を設定します。kubectlおよびgcloudユーティリティを使用します。 $ gcloud init[2] Create a new configurationConfiguration name. “development”[2] Log in with a new accountPick cloud project to useconfigure a default Compute Region and Zone? (Y/n)? yHere europe-west-1b was chosen $ gcloud container clusters get-credentials dev-cluster --zone europe-west1-b --project <project_id> [Connect]をクリックすると、最後のコマンドを取得できます。 kubectlからステータスを確認します。 $ kubectl config current-contextgke_possible-symbol-254507_europe-west1-b_dev-cluster $ kubectl get nodesNAME STATUS ROLES AGE VERSIONgke-dev-cluster-pool-2-8096d93c-fw5w Ready <none> 17m v1.14.7-gke.10 次に、ルートプロジェクトディレクトリの下に k8s/という名前のディレクトリを作成し、Kubernetesの将来のアプリケーションを記述する次の3つのファイルを保持します:ワークスペース、デプロイメント、サービスを記述するNamespaceです。 $ cat namespace.yamlapiVersion: v1kind: Namespacemetadata: name: iris $ cat deployment.yamlapiVersion: apps/v1beta1kind: Deploymentmetadata: name: iris-rest namespace: irisspec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: iris template: metadata: labels: app: iris spec: containers: - image: eu.gcr.io/iris-rest:v1 name: iris-rest ports: - containerPort: 52773 name: web $ cat service.yamlapiVersion: v1kind: Servicemetadata: name: iris-rest namespace: irisspec: selector: app: iris ports: - protocol: TCP port: 52773 targetPort: 52773 type: LoadBalancer これらの定義をk8s/ディレクトリからGoogle Kubernetes Engine(GKE)に送信します。 $ kubectl apply -f namespace.yaml$ kubectl apply -f deployment.yaml -f service.yaml まだDockerレジストリにeu.gcr.io/iris-rest:v1イメージを送信していないので正常に機能していません。そのため、エラーが表示されます。 $ kubectl -n iris get poNAME READY STATUS RESTARTS AGEiris-rest-64cdb48f78-5g9hb 0/1 ErrImagePull 0 50s $ kubectl -n iris get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEiris-rest LoadBalancer 10.0.13.219 <pending> 52773:31425/TCP 20s Kubernetesは、 LoadBalancerサービスを検出すると、Google Cloud環境でバランサーを作成しようとします。 成功した場合、サービスは外部IP = <pending>ではなく実際のIPアドレスを取得します。 Kubernetesから少し離れる前に、CircleCIにサービスアカウントを作成して、DockerイメージをレジストリにPushし、Kubernetesデプロイを再起動する機能を持たせてみましょう。プロジェクトにサービスアカウントの編集者権限を付与します。 サービスアカウントの作成と保存に関する情報はここにあります。   少し後でCircleCIでプロジェクトを作成して設定するとき、次の3つの環境変数を追加する必要があります。 これらの変数の名前はご覧のとおりです。 GCLOUD_SERVICE_KEYの値は、サービスアカウントの作成後に「CREATE KEY」ボタンを押してキーを選択した際にGoogleが送信するJSON構造体です。 CircleCI それでは、CircleCI に着目し、GitHubアカウントを使用して登録します([サインアップ]をクリックし、次に[GitHubでサインアップ]をクリックします)。 登録後、GitHubリポジトリのプロジェクトを含むダッシュボードが[Add Projects]タブに表示されます。 「my-objectscript-rest-docker-template」または、objectscript-rest-docker-templateリポジトリから作成された、任意の名前を付けたリポジトリの[Set Up Project]ボタンをクリックします。 注: すべてのCircleCIスクリーンショットは2019年10月の時点で作成されたものです。 新しいバージョンでは変更が加えられている場合があります。 開いたページに、プロジェクトをCircleCIで動作させる方法を説明しています。最初のステップは.circleciという名前のフォルダーを作成し、それにconfig.ymlという名前のファイルを追加することです。 この設定ファイルの構造は、公式ドキュメントで詳しく説明されています。 ファイルに含まれる基本的な手順は次のとおりです。 リポジトリをPullする Dockerイメージをビルドする Amazon Cloudで認証する Google Dockerレジストリにイメージをアップロードする このイメージに基づいてGKEでコンテナを実行する 運が良ければ、すでに作成された構成( orbsと呼ばれる)をいくつか見つけて使うことができます。 公認のorbとサードパーティのorbがあります。 公認のGCP-GKE orbには制限がいくつもあるので、私たちのニーズを満たすサードパーティのorb(duksis)を見てみましょう。これを使用すると、設定ファイルは次のようになります(クラスタ名などの名前を実装に適した名前に置き換えてください)。 $ cat .circleci/config.ymlversion: 2.1orbs: gcp-gke: duksis/gcp-gke@0.1.9workflows: main: jobs: - gcp-gke/publish-and-rollout-image: google-project-id: GOOGLE_PROJECT_ID gcloud-service-key: GCLOUD_SERVICE_KEY registry-url: eu.gcr.io image: iris-rest tag: ${CIRCLE_SHA1} cluster: dev-cluster namespace: iris deployment: iris-rest container: iris-rest publish-and-rollout-imageタスクの初期設定は、プロジェクトページで確認できます 。 この orb の最後の3つの通知ステップは、実際には必要ありませんが、理想的には一度作成した独自のorbを何度も使用できることができますが、ここではそれについて説明しません。 CircleCIの[Organization settings]タブで、サードパーティ製orbの使用を明確に許可する必要があることに注意してください。 最後に、すべての変更をGitHubとCircleCIに送信します。 $ git add .circleci/ k8s/ src/cls/Sample/PersonREST.cls$ git commit -m "Deploy project to GKE using CircleCI"$ git push CircleCIダッシュボードを確認してみましょう。 Googleサービスアカウントキーを追加するのを忘れた場合は、次のようになります。 ですから、Google Cloudの前提条件セクションの末尾で説明されているように、これらの環境変数を追加することを忘れないでください。 忘れた場合は、その情報を更新してから[Rerun workflow]をクリックしてください。 ビルドが成功すると、緑色のバーが表示されます。 CircleCI Web UIから別にKubernetesポッドの状態を確認することもできます。 $ kubectl -n iris get po -wNAME READY STATUS RESTARTS AGEiris-rest-64chdb48f78-q5sbw 0/1 ImagePullBackOff 0 15m…iris-rest-5c9c86c768-vt7c9 1/1 Running 0 23s その最後の行( 1/1ランニング)は良い兆候です。 テストしてみましょう。 あなたのIPアドレスは私のIPアドレスと異なることを忘れないでください。 また、HTTPを介したパスワードについては、この記事の範囲外であるため、独自に調べる必要があります。 $ kubectl -n iris get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEiris-rest LoadBalancer 10.0.4.242 23.251.143.124 52773:30948/TCP 18m $ curl -XGET -u _system:SYS 23.251.143.124:52773/person/helloworldHello, world! $ curl -XPOST -H "Content-Type: application/json" -u _system:SYS 23.251.143.124:52773/person/ -d '{"Name":"John Dou"}' $ curl -XGET -u _system:SYS 23.251.143.124:52773/person/all[{"Name":"John Dou"},] アプリケーションは動作しているようです。 プロジェクトページで説明されているテストを続行できます 。 要するに、GitHub、CircleCI、およびGoogle Cloud Kubernetes Engineの組み合わせは、完全に無料ではないとしても、IRISアプリケーションのテストとデプロイメントには非常に有望と思われます。また、Kubernetesクラスターを実行すると、仮想の(そして実際の)お金が徐々に消費される可能性があることを忘れないでください。 弊社はあなたに請求される可能性のある料金については責任を負わないものとします。
記事
Toshihiko Minamoto · 2023年7月13日

InterSystems Package Manager と git-source-control で IRIS インターオペラビリティのソース管理を有効にする

開発者の皆さん、こんにちは! ご存知のように、InterSystems IRIS インターオペラビリティソリューションには、プロダクション、ビジネスルール、ビジネスプロセス、データ変換、レコードマッパーなどの様々なエレメントが含まれています。 また、UI ツールを使用してこれらの要素を作成し、変更することもあります。  もちろん、UI ツールで行った変更をソース管理する便利で堅牢な方法も必要です。 長い間、これは手動(クラス、エレメント、グローバルなどのエクスポート)か面倒な設定作業手順によって行われてきました。そのため、ソース管理 UI の自動化で節約された時間は、設定のセットアップとメンテナンスの時間で相殺されていました。 現在では、この問題はなくなりました。 パッケージファースト開発と @Timothy.Leavitt の [git-source-control](https://openexchange.intersystems.com/package/Git-for-Shared-Development-Environments) という IPM パッケージの使用という 2 つのアプローチによる結果です。 詳細は以下のとおりです! _免責事項: これは、インターオペラビリティのプロダクションのエレメントがリポジトリ内のファイルである場合の、クライアント側の開発アプローチに関連しています。_ よって、ソリューションは非常に単純であるため、この記事は長くありません。 Docker で開発しており、IRIS を使って開発環境の Docker イメージをビルドしたら、ソリューションを IPM モジュールとして読み込んでいるとします。 これは、「パッケージファースト」開発と呼ばれ、関連する動画や記事が存在する手法です。 基本的には、開発環境の Docker イメージを IRIS でビルドすると、クライアントのサーバーにデプロイされるときに、ソリューションがパッケージとして読み込まれるという考え方になります。 ソリューションのパッケージファースト開発環境を作るには、module.xml をリポジトリに追加し、そのすべての要素を記述して、Docker イメージのビルドフェーズで「zpm load "repository/folder"」コマンドを呼び出します。 この考え方を、サンプルテンプレートの [IRIS Interoperability template](https://openexchange.intersystems.com/package/iris-interoperability-template) とそれに使用する [module.xml](https://github.com/intersystems-community/iris-interoperability-template/blob/ee85f96376aea5c725970d8b4d55e317ab86d00a/module.xml) を使って示しましょう。 パッケージは、Docker ビルド中に以下のように読み込まれています。 zpm "load /home/irisowner/irisdev/ -v":1:1 [ソース](https://github.com/intersystems-community/iris-interoperability-template/blob/ee85f96376aea5c725970d8b4d55e317ab86d00a/iris.script)  パッケージソース管理を読み込む前に配置された以下の 2 行を見てください。 これにより、ソース管理はパッケージ内のすべての相互運用性要素に対して自動的に動作し始め、適切なフォルダに適切な形式でエクスポートされます。 zpm "install git-source-control" do ##class(%Studio.SourceControl.Interface).SourceControlClassSet("SourceControl.Git.Extension") [ソース](https://github.com/intersystems-community/iris-interoperability-template/blob/ee85f96376aea5c725970d8b4d55e317ab86d00a/iris.script) 仕組み 最近、[git-source-control](https://openexchange.intersystems.com/package/Git-for-Shared-Development-Environments) アプリは、開発モードで読み込まれるソース管理用の IPM パッケージをサポートするようになりました。 エクスポートするフォルダを読み取り、module.xml からソースの構造をインポートします。 詳細は、@Timothy.Leavitt が説明できます。 ターミナルで、環境がビルドされた後の IPM モジュールのリストを確認すると、読み込まれたモジュールが実際に開発モードになっているのが分かります。 USER>zpm ============================================================================= || Welcome to the Package Manager Shell (ZPM). || || Enter q/quit to exit the shell. Enter ?/help to view available commands || ============================================================================= zpm:USER>list git-source-control 2.1.0 interoperability-sample 0.1.2 (DeveloperMode) sslclient 1.0.1 zpm:USER> では試してみましょう。  このリポジトリをクローンし、VSCode で開いてイメージをビルドしました。 以下では、相互運用性 UI とソース管理をテストしています。 UI に変更を加えるとすぐに、ソースと差分に表示されます。 ![](/sites/default/files/inline/images/images/source-control.gif) できました! それだけです!  まとめると、プロジェクトで相互運用性 UI 要素のソース管理を行うには、以下を行います。 1. Docker イメージをビルド中に、iris.script に 2 行を追加します。 zpm "install git-source-control" do ##class(%Studio.SourceControl.Interface).SourceControlClassSet("SourceControl.Git.Extension") その後、以下のようにして、ソリューションをモジュールとして読み込みます。 zpm "load /home/irisowner/irisdev/ -v":1:1 2. または、[Interoperability template](https://openexchange.intersystems.com/package/iris-interoperability-template) からリポジトリを作成して新規に始めることもできます。 お読みいただきありがとうございました! コメントやフィードバックをお待ちしています!
記事
Toshihiko Minamoto · 2023年9月15日

DeepSee Web: AngularJS による InterSystems の分析可視化。 パート 1

DeepSee BI ソリューションのユーザーインターフェース(UI)を配布するにはいくつかのオプションがあります。 最も一般的には以下の手法があります。 ネイティブの DeepSee ダッシュボードを使用し、Zen で Web UI を取得して、Web アプリに配布する。 DeepSee REST API を使用して、独自の UI ウィジェットとダッシュボードを取得・構築する。 最初の手法はコーディングを行わずに比較的素早く BI ダッシュボードを構築できるためお勧めですが、事前設定のウィジェットライブラリに限られます。これを拡張することはできますが、大きな開発の手間がかかります。 2 つ目の手法には、任意の総合 js フレームワーク(D3,Highcharts など)を使用して DeepSee データを可視化する手段がありますが、ウィジェットとダッシュボードを独自にコーディングする必要があります。 今日は、上の 2 つを組み合わせて Angular ベースの DeepSee ダッシュボード用 Web UI を提供するもう 1 つの手法をご紹介します。DeepSee Web ライブラリです。 詳しい説明 DeepSee Web(DSW)は、DeepSee ダッシュボードを表示する Angular.js Web アプリで、Highcharts.js、OpenStreetMap、および一部の自作 js ウィジェットを使用する特定のネームスペースで使用できます。 仕組み DSW は、MDX2JSON ライブラリを使用するネームスペースで使用できるダッシュボードとウィジェットのメタデータをリクエストします。 ダッシュボードを可視化するために、DSW はウィジェットとそのタイプのリストを読み取り、ほぼすべての DeepSee ウィジェットに実装されている js-widget アナログを使用します。 ダッシュボードにリストされる js ウィジェットをインスタンス化し、DeepSee REST API と MDX2JSON を使用してウィジェットのデータソースに従ってデータをリクエストし、JSON 形式でデータを取得して可視化を実行します。   そのメリットは? DSW は以下の理由により優れています。 標準のエディターを使用して DeepSee ダッシュボードを作成し、コーディングを一切行わずに Angular で UI を使用できます。 任意の js ライブラリまたは自作ウィジェットで簡単にウィジェットのライブラリを拡張できます。 DSW カバーにスマートタイルを追加できます。 地図にデータを表示できます。   モバイルデバイスは? DSW は、モバイルブラウザ(Safari、Chrome)で非常によく動作し、スタンドアロンの DeepSight iOS アプリもあります。以下はそのスクリーンショットです。   いくらですか? 無料です。 最新リリースのダウンロード、課題の送信、フォークの作成、修正や新機能を含むプルリクエストを自由に行えます。   プロダクションでの使用に適していますか? はい。 DSW は 2014 年にリリースされ、今日まで 60 回リリースされてきました。 DSW は数十社の DeepSee ソリューションで正常に使用されています。 たとえば、開発者コミュニティでも DSW を使用しています。 ただし、DSW ライセンス(MIT ライセンス)に従い、ご自身のリスクで使用していただきます。   サポートされていますか? コミュニティによってサポートされています。 ぜひ課題を開き、フォークを作成して、修正と新機能が含まれるプルリクエストを送信してください。 主要貢献者は [@Anton.Gnibeda​]、[@Nikita.Savchenko]、[@Eduard.Lebedyuk] です。    インストール方法は? 簡単です。 まず、MDX2JSON をインストールします。 リリースから最新のインストーラーをダウンロードして、USER ネームスペースで installer.xml をインポート/コンパイルし、以下を実行します。 USER> D ##class(MDX2JSON.Installer).setup() GitHub から最新のリリースがダウンロードされ、MDX2JSON データベースとネームスペースの作成と %All へのマッピングが行われ、/MDX2JSON Web アプリが作成されます。 すべてがうまくインストールされたことを確認するには、localhost:57772/MDX2JSON/Test を開きます。  すべてが正常である場合は、以下のように返されます。 { "DefaultApp":"\/dsw", "Mappings": { "Mapped":["%SYS","MDX2JSON","SAMPLES","USER" ], "Unmapped":["DOCBOOK" ] }, "Parent":"18872dc518d8009bdc105425f541953d2c9d461e", "ParentTS":"2017-05-26 16:20:17.307", "Status":"OK", "User":"", "Version":2.2 }​   次に DSW をインストールします。 最新の release xml をダウンロードします。 それを USER ネームスペースにインポート/コンパイルします。 以下を実行してください。 USER> Do ##class(DSW.Installer).setup() /dsw アプリが作成され、すべての js ファイルが /csp/dsw フォルダにインストールされます。 localhost:57772/dsw/index.html#!/?ns=SAMPLES を開き、動作することを確認してください。   既知の問題: 一部のシステムでは、CSP ゲートウェイで CSP 以外のファイルに UTF8 サポートを設定する必要があります。 ターミナルで以下を実行してオンにしてください。 USER> set ^%SYS("CSP","DefaultFileCharset")="utf-8"   いつインストールすべきですか? 今すぐインストールしましょう! ) 続きにご期待ください!  
記事
Toshihiko Minamoto · 2021年1月6日

GitHub Actions を使って EKS に InterSystems IRIS Solution をデプロイする

データ分析のためにInterSystemsで何ができるかを確認したいとしましょう。 理論を学んだので今度は実践したいと考えた時、InterSystems には、役に立つ実例をいくつか取り入れた Samples BI というプロジェクトが用意されています。 まずは、README ファイルを開き、Docker に関する内容はすべてスキップして、手順に従ってインストールしてゆきます。 仮想インスタンスを起動し、そこに IRIS をインストールしたら、手順に従って Samples BI をインストールします。そして綺麗なチャートやテーブルを見せて上司に感心してもらいましょう。 ここまでは順調ですね。  必然的に、ここで変更を加えることになります。 仮想マシンを自分でメンテすることにはデメリットがいくつかあり、クラウドプロバイダーに保管する方が無難であることが判明します。 Amazon は安定してそうなので、AWS アカウントを作成します (開始は無料)。 ルートユーザーの ID を使って日々の業務を行うのは危険であることを読んだあなたは、管理者権限を持つ普通の IAM ユーザーを作成します。 少し先に進んでから、独自の VPC ネットワーク、サブネット、EC2 の仮想インスタンスを作成し、さらには自分用の IRIS ウェブポート (52773) と SSH ポート (22) を開くために、セキュリティグループを追加します。 IRIS と Samples BI のインストールをもう一度実行します。 今回は、Bash シェルスクリプトを使います。Python でも OK。 また、ここでも上司に好印象を与えておきます。 しかし、DevOps がトレンドとなり、Infrastructure as Code (コードによるインフラの管理)について学び、導入したくなります。 そこで、Terraform を選択します。知名度が高く、世界中で多用されている上に、多くのクラウドプロバイダーにとっても調整が簡単なため適切な選択肢です。 インフラストラクチャを HCL 言語で定義し、IRIS と Samples BI のインストール手順を Ansible に変換します。 そしてTerraform を動かすIAM ユーザーをもう 1 つ作成し、それを実行します。そして 職場でボーナスをゲットします。 次第に、[マイクロサービス](https://martinfowler.com/articles/microservices.html)の時代に Docker を使わないのはもったいない、InterSystems がその[ドキュメンテーション](https://docs.intersystems.com/irislatestj/csp/docbook/DocBook.UI.Page.cls?KEY=ADOCK_iris)を提供してくれることを考えるとなおさらだ、という結論に至ります。 Samples BI のインストールガイドに戻り、複雑に思えなくなったDocker について書かれた行を読み通します。 $ docker pull intersystemsdc/iris-community:2019.4.0.383.0-zpm$ docker run --name irisce -d --publish 52773:52773 intersystemsdc/iris-community:2019.4.0.383.0-zpm$ docker exec -it irisce iris session irisUSER>zpmzpm: USER>install samples-bi   ブラウザーを http://localhost:52773/csp/user/_DeepSee.UserPortal.Home.zen?$NAMESPACE=USER, に変更したら、もう一度上司の所に戻り、良い仕事のご褒美として1 日休みをもらいます。 あなたはその時、“docker run” は単なる第一歩であり、少なくとも docker-compose を使う必要があるでは?と考え始めます。 問題ありません: $ cat docker-compose.ymlversion: "3.7"services:  irisce:    container_name: irisce    image: intersystemsdc/iris-community:2019.4.0.383.0-zpm    ports:    - 52773:52773$ docker rm -f irisce # We don’t need the previous container$ docker-compose up -d   こうして、Ansible を使ってDocker や docker-compose をインストールし、マシン上にイメージが無ければダウンロードするコンテナを単に実行します。 それから、Samples BI をインストールします。 カーネルの様々な機能へのインターフェイスとして活躍する、便利でシンプルな Docker を気に入らないはずがありません。 あなたは Docker を他の場所でも使い、頻繁に複数のコンテナを起動するようにもなりました。 また、コンテナはしばしばお互いに通信し合う必要があることを知ったあなたは、複数のコンテナを管理する方法について読み始めます。  そして、Kubernetes (クーべネティス) について知ります。  docker-compose から Kubernetes に素早く切り替える方法の 1 つに [kompose](https://kompose.io/) を使うという方法があります。 私個人的には、Kubernetes のマニフェストファイルをマニュアルからコピーし、自分で編集する方が好きなのですが、kompose でも小さなタスクを完了させるだけなら問題ありません。 $ kompose convert -f docker-compose.ymlINFO Kubernetes file "irisce-service.yaml" createdINFO Kubernetes file "irisce-deployment.yaml" created   これで、Kubernetes のクラスターに送信できるデプロイファイルとサービスファイルができました。 ここであなたは、minikube をインストールすれば、シングルノードの Kubernetes クラスターを実行できること、そしてこの段階ではまさにそれが必要なことを知ります。 minikube のサンドボックスを 1 日か 2 日使ってみた後、ライブの Kubernetes を実際に AWS クラウドのどこかでデプロイする準備が整いました。   セットアップ それでは、一緒にやりましょう。 この時点で、前提にしておきたい点がいくつかあります。 第一に、あなたが AWS のアカウントを持っていること、[その ID を知っている](https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html)こと、そしてルートユーザーの認証情報を使用していないということです。 [管理者権限](https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started_create-admin-group.html)とプログラムによるアクセス権だけを持つ IAM ユーザー (「my-user」とでも呼びましょう) を作成し、その認証情報を保管します。 また、「terraform」と名付けた IAM ユーザーも作成し、同じアクセス権を与えます。 ![](/sites/default/files/inline/images/images/iam_user.png) このユーザーの代わりに、Terraform はあなたの AWS アカウントにアクセスし、必要なリソースを作成、削除します。 これはデモなので、これらのユーザーに広範な権限を与えています。 両方の IAM ユーザーの認証情報はローカルに保存します。 $ cat ~/.aws/credentials[terraform]aws_access_key_id = ABCDEFGHIJKLMNOPQRSTaws_secret_access_key = ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123[my-user]aws_access_key_id = TSRQPONMLKJIHGFEDCBAaws_secret_access_key = TSRQPONMLKJIHGFEDCBA01234567890123   注意: 上の認証情報はコピー & ペーストしないでください。 例として作成したものなので、既に削除しています。 ~/.aws/credentials ファイルを編集し、独自のレコードを導入してください。 第二に、この記事では AWS アカウントのダミー ID に「01234567890」を、AWS の地域には「eu-west-1」を使用します。 別の地域を使っても構いません。 第三に、あなたが AWS は有料であること、使用するリソースは支払う必要があることを認識しているという前提で進めて行きます。 次に、あなたはコマンドラインを使って AWS と通信するために、 AWS CLI ユーティリティをインストールしています。 aws2 を使うこともできますが、kube の config ファイルに aws2 の使用範囲を具体的に設定する必要があります。詳しくは、こちらをご覧ください。 また、コマンドラインを使って AWS Kubernetes と通信する目的で、 kubectl ユーティリティもインストールしています。 さらに、Kubernetes のマニフェストファイルを変換するには、docker-compose.yml を使用する必要があり、そのためには kompose ユーティリティをインストールしなくてはいけません。 最後に、空の GitHub レポジトリを作成し、ホストにクローンしました。 ルートディレクトリは . として参照します。 このリポジトリでは、「.github/workflows/」、「k8s/」、「terraform/」という 3 つのディレクトリを作成し、中身を埋めていきます。 すべての関連するコードは、コピー & ペーストを簡単にできるよう、github-eks-samples-bi というリポジトリ内に複製されています。 それでは続けましょう。   AWS EKS のプロビジョニング EKS については、Amazon EKS を使ったシンプルな IRIS ベースのウェブアプリケーションをデプロイすると題した記事で既に紹介しています。 そのときに、クラスターを半自動的に作成しました。 つまり、クラスタをファイル内で定義し、 eksctl ユーティリティを手動でローカルマシンから起動した結果、クラスタが私たちの定義に従って作成されています。  eksctl は EKS クラスタを作成する目的で開発され、概念実証による導入には優れていますが、日々の使用には Terraform など、より広範な機能を提供するツールを採用する方が無難と言えます。 AWS EKS Introduction と題した非常に便利なリソースがあり、EKS クラスタの作成に必要な Terraform の設定が説明されています。 1、2 時間かけて読む価値は十分にあります。 Terraform はローカルで試すことができます。 そのためには、バイナリ (この記事の執筆時には Linux の最新バージョン 0.12.20 を使用しました) と Terraform を AWS にアクセスさせるに十分な権限を持つ IAM ユーザー「terraform」が必要です。 ディレクトリ「/terraform/」を作成し、 次の Terraform のコードを保管します。 $ mkdir /terraform$ cd /terraform   .tf ファイルは、複数作成できます (起動時にマージされます)。 AWS EKS Introduction に記載されているコードの例をコピー & ペーストし、以下のようなコードを実行します。 $ export AWS_PROFILE=terraform$ export AWS_REGION=eu-west-1$ terraform init$ terraform plan -out eks.plan   何らかのエラーが発生する可能性があります。 その場合は、デバッグモードを試してください。終わったら忘れずにオフにしてください。 $ export TF_LOG=debug$ terraform plan -out eks.plan$ unset TF_LOG   この体験はお役に立つと思います。また、EKS クラスタも起動できるでしょう (「terraform apply」を使ってください)。 AWS コンソールでお試しください。 ![](/sites/default/files/inline/images/images/eks.png)   飽きてしまったらクリーンアップしましょう。 $ terraform destroy   そして、次のレベルに移動し、Terraform EKS モジュールの使用を開始します。これは、同じ EKS Introduction がベースになっているためでもあります。 その使い方は、「examples/」ディレクトリで確認してください。 そこには、他の例も用意してあります。 例はある程度簡素化してあります。 こちらが、VPC の作成モジュールと EKS の作成モジュールが呼び出されるメインファイルです。 $ cat /terraform/main.tfterraform {  required_version = ">= 0.12.0"  backend "s3" {    bucket         = "eks-github-actions-terraform"    key            = "terraform-dev.tfstate"    region         = "eu-west-1"    dynamodb_table = "eks-github-actions-terraform-lock"  }} provider "kubernetes" {  host                   = data.aws_eks_cluster.cluster.endpoint  cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)  token                  = data.aws_eks_cluster_auth.cluster.token  load_config_file       = false  version                = "1.10.0"} locals {  vpc_name             = "dev-vpc"  vpc_cidr             = "10.42.0.0/16"  private_subnets      = ["10.42.1.0/24", "10.42.2.0/24"]  public_subnets       = ["10.42.11.0/24", "10.42.12.0/24"]  cluster_name         = "dev-cluster"  cluster_version      = "1.14"  worker_group_name    = "worker-group-1"  instance_type        = "t2.medium"  asg_desired_capacity = 1} data "aws_eks_cluster" "cluster" {  name = module.eks.cluster_id} data "aws_eks_cluster_auth" "cluster" {  name = module.eks.cluster_id} data "aws_availability_zones" "available" {} module "vpc" {  source               = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc?ref=master"   name                 = local.vpc_name  cidr                 = local.vpc_cidr  azs                  = data.aws_availability_zones.available.names  private_subnets      = local.private_subnets  public_subnets       = local.public_subnets  enable_nat_gateway   = true  single_nat_gateway   = true  enable_dns_hostnames = true   tags = {    "kubernetes.io/cluster/${local.cluster_name}" = "shared"  }   public_subnet_tags = {    "kubernetes.io/cluster/${local.cluster_name}" = "shared"    "kubernetes.io/role/elb" = "1"  }   private_subnet_tags = {    "kubernetes.io/cluster/${local.cluster_name}" = "shared"    "kubernetes.io/role/internal-elb" = "1"  }} module "eks" {  source = "git::https://github.com/terraform-aws-modules/terraform-aws-eks?ref=master"  cluster_name     = local.cluster_name  cluster_version  = local.cluster_version  vpc_id           = module.vpc.vpc_id  subnets          = module.vpc.private_subnets  write_kubeconfig = false   worker_groups = [    {      name                 = local.worker_group_name      instance_type        = local.instance_type      asg_desired_capacity = local.asg_desired_capacity    }  ]   map_accounts = var.map_accounts  map_roles    = var.map_roles  map_users    = var.map_users}   それでは、main.tf の「_terraform_」ブロックをもう少し細かく見てみましょう。 terraform {  required_version = ">= 0.12.0"  backend "s3" {    bucket         = "eks-github-actions-terraform"    key            = "terraform-dev.tfstate"    region         = "eu-west-1"    dynamodb_table = "eks-github-actions-terraform-lock"  }}   ここでは、Terraform 0.12 以上のバージョン (以前のバージョンに比べると多くの変更が加えられています) の構文に従うこと、また Terraform はその状態をローカルにではなく、むしろリモートの S3 バケットに保管することを指定しています。  Terraform のコードを色々な人が色々な場所から更新できると便利ですが、ユーザーの状態をロックできなければいけないということになるので、dynamodb テーブルを使ってロックを追加しました。 ロックの詳細は、State Locking のページをご覧ください。 バケットには AWS 全体で一意の名前が使われている必要があるので「eks-github-actions-terraform」という名前は使えません。 独自の名前を考えて、既に使用されていないことを確認してください (すると NoSuchBucket エラーが発生します): $ aws s3 ls s3://my-bucketAn error occurred (AllAccessDisabled) when calling the ListObjectsV2 operation: All access to this object has been disabled$ aws s3 ls s3://my-bucket-with-name-that-impossible-to-rememberAn error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist   名前を考えたら、バケットを作成し (ここでは IAM ユーザー「terraform」を使います。 管理者権限を持っているのでバケットを作成できます)、そのバージョン管理を有効にします (こうすることで、構成エラーが出ても落ち着いて対応できます)。 $ aws s3 mb s3://eks-github-actions-terraform --region eu-west-1make_bucket: eks-github-actions-terraform$ aws s3api put-bucket-versioning --bucket eks-github-actions-terraform --versioning-configuration Status=Enabled$ aws s3api get-bucket-versioning --bucket eks-github-actions-terraform{  "Status": "Enabled"}   DynamoDB では、一意性は必要ありませんが、最初にテーブルを作成する必要があります。 $ aws dynamodb create-table                                                                                     \  --region eu-west-1                                                                                                           \  --table-name eks-github-actions-terraform-lock                                              \  --attribute-definitions AttributeName=LockID,AttributeType=S                \  --key-schema AttributeName=LockID,KeyType=HASH                                   \  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5   ![](/sites/default/files/inline/images/images/dynamodb.png)   Terraform に障害が発生した場合は、ロックを AWS コンソールから手動で解除する必要があるかもしれません。 その場合は慎重に対応してください。 main.tf 内にある eks/vpc モジュールのブロックについてですが、GitHub で使用可能なモジュールを参照する方法はいたってシンプルです。 git::https://github.com/terraform-aws-modules/terraform-aws-vpc?ref=master   それでは、他の 2 つの Terraform ファイル (variables.tf と outputs.tf) を見てみましょう。 1 つ目のファイルには Terraform の変数が置かれます。 $ cat /terraform/variables.tfvariable "region" {  default = "eu-west-1"} variable "map_accounts" {  description = "aws-auth configmap に追加する他の AWS アカウント番号。 フォーマットの例は、examples/basic/variables.tf を参照してください。"  type        = list(string)  default     = []} variable "map_roles" {  description = "aws-auth configmap に追加する他の IAM ロール。"  type = list(object({    rolearn  = string    username = string    groups   = list(string)  }))  default = []} variable "map_users" {  description = "aws-auth configmap に追加する他の IAM ユーザー。"  type = list(object({    userarn  = string    username = string    groups   = list(string)  }))  default = [    {      userarn  = "arn:aws:iam::01234567890:user/my-user"      username = "my-user"      groups   = ["system:masters"]    }  ]}   ここで一番大事なのは map_users 変数に IAM ユーザー “my-user” を追加するということですが、01234567890 の代わりに自分のアカウント ID を使ってください。 これで何が起こるのか? ローカルの kubectl クライアントを通じて EKS と通信する場合は、EKS から Kubernetes API サーバーにリクエストが送られます。そして、各リクエストは認証プロセスと承認プロセスを通り、Kubernetes はリクエストの送信者とそのユーザーの権限を理解できるようになります。 そして、Kubernetes の EKS バージョンは、AWS の IAM にユーザー認証のサポートを求めます。 リクエストを送信したユーザーが AWS IAM (ここではユーザーの ARN をポイントしました) に載っている場合、リクエストは承認ステージに移動し、EKS が私たちの設定に従って直接処理します。 ここでは、IAM ユーザー “my-user” は信頼できる人である (group “system: masters”)、ことを示しています。 最後に、Terraform がジョブを完了した後に出力する内容を outputs.tf が説明します。 $ cat /terraform/outputs.tfoutput "cluster_endpoint" {  description = "EKS コントロールプレーンのエンドポイント"  value       = module.eks.cluster_endpoint} output "cluster_security_group_id" {  description = "クラスターのコントロールプレーンに関連付けられたセキュリティグループの ID。"  value       = module.eks.cluster_security_group_id} output "config_map_aws_auth" {  description = "この EKS クラスターに対して承認する Kubernetes 構成。"  value       = module.eks.config_map_aws_auth}   これで Terraform の部分に関する説明は終了です。 これらのファイルを起動する方法はもう少し後で説明します。   Kubernetes のマニフェスト ここまでは、アプリケーションをどこで起動するのかについて説明しましたが、 今度は、何を実行するのか、を見て行きます。  docker-compose.yml (サービス名を変更し、kompose が使用するラベルをいくつか追加しました) が、 /k8s/ ディレクトリにあることを覚えているでしょうか。 $ cat /k8s/docker-compose.ymlversion: "3.7"services:  samples-bi:    container_name: samples-bi    image: intersystemsdc/iris-community:2019.4.0.383.0-zpm    ports:    - 52773:52773    labels:      kompose.service.type: loadbalancer      kompose.image-pull-policy: IfNotPresent   kompose を実行して、下に強調表示されている箇所を追加します。 アノテーションは削除します (分かりやすくするためです)。 $ kompose convert -f docker-compose.yml --replicas=1$ cat /k8s/samples-bi-deployment.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata:  labels:    io.kompose.service: samples-bi  name: samples-bispec:  replicas: 1  strategy:    type: Recreate  template:    metadata:      labels:        io.kompose.service: samples-bi    spec:      containers:      - image: intersystemsdc/iris-community:2019.4.0.383.0-zpm        imagePullPolicy: IfNotPresent        name: samples-bi        ports:        - containerPort: 52773        resources: {}        lifecycle:          postStart:            exec:              command:              - /bin/bash              - -c              - |                echo -e "write\nhalt" > test                until iris session iris < test; do sleep 1; done                echo -e "zpm\ninstall samples-bi\nquit\nhalt" > samples_bi_install                iris session iris < samples_bi_install                rm test samples_bi_install        restartPolicy: Always   Recreate を使ったアップデート戦略を使用しますが、これはポッドが一旦削除されてから再度作成されることを意味します。 今回はデモなので、これでも大丈夫です。また、使用するリソースも少なくて済みます。 さらに、ポッドが起動すると同時にトリガーする postStart フックも追加しました。 IRIS が起動するのを待ち、デフォルトの zpm-repository から samples-bi をインストールします。 ここで、Kubernetes サービスを追加します (ここでもアノテーションは使いません)。 $ cat /k8s/samples-bi-service.yamlapiVersion: v1kind: Servicemetadata:  labels:    io.kompose.service: samples-bi  name: samples-bispec:  ports:  - name: "52773"    port: 52773    targetPort: 52773  selector:    io.kompose.service: samples-bi  type: LoadBalancer   そうです、今回は「デフォルト」のネームスペースでデプロイします。デモですからね。 _何_を_どこ_で実行するかが分かったところで、 最後は、_どのように_実行するか、を見ます。   GitHub Actions のワークフロー すべてを一から作るのではなく、[GitHub Actions を使って GKE に InterSystems IRIS Solution をデプロイする](https://jp.community.intersystems.com/node/483556) に記載されているワークフローに似たものを作成します。 今回は、コンテナを構築する必要はありません。 GKE 特有の箇所は EKS 特有の箇所に置き換えられます。 太文字の箇所は、コミットメッセージを受けて条件付きステップで使用することに関する記述です。 $ cat /.github/workflows/workflow.yamlname: EKS クラスタをプロビジョンし、そこに Samples BI をデプロイするon:  push:    branches:    - master # 環境変数。# ${{ secrets }} は GitHub -> Settings -> Secrets より取得されます# ${{ github.sha }} はコミットハッシュですenv:  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}  AWS_REGION: ${{ secrets.AWS_REGION }}  CLUSTER_NAME: dev-cluster  DEPLOYMENT_NAME: samples-bi jobs:  eks-provisioner:    # Inspired by:    ## https://www.terraform.io/docs/github-actions/getting-started.html    ## https://github.com/hashicorp/terraform-github-actions    name: EKS クラスタをプロビジョンする    runs-on: ubuntu-18.04    steps:    - name: Checkout      uses: actions/checkout@v2     - name: Get commit message      run: |        echo ::set-env name=commit_msg::$(git log --format=%B -n 1 ${{ github.event.after }})     - name: Show commit message      run: echo $commit_msg     - name: Terraform init      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'init'        tf_actions_working_dir: 'terraform'     - name: Terraform validate      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'validate'        tf_actions_working_dir: 'terraform'     - name: Terraform plan      if: "!contains(env.commit_msg, '[destroy eks]')"      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'plan'        tf_actions_working_dir: 'terraform'     - name: Terraform plan for destroy      if: "contains(env.commit_msg, '[destroy eks]')"      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'plan'        args: '-destroy -out=./destroy-plan'        tf_actions_working_dir: 'terraform'     - name: Terraform apply      if: "!contains(env.commit_msg, '[destroy eks]')"      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'apply'        tf_actions_working_dir: 'terraform'     - name: Terraform apply for destroy      if: "contains(env.commit_msg, '[destroy eks]')"      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.20        tf_actions_subcommand: 'apply'        args: './destroy-plan'        tf_actions_working_dir: 'terraform'   kubernetes-deploy:    name: Deploy Kubernetes manifests to EKS    needs:    - eks-provisioner    runs-on: ubuntu-18.04    steps:    - name: Checkout      uses: actions/checkout@v2     - name: Get commit message      run: |        echo ::set-env name=commit_msg::$(git log --format=%B -n 1 ${{ github.event.after }})     - name: Show commit message      run: echo $commit_msg     - name: Configure AWS Credentials      if: "!contains(env.commit_msg, '[destroy eks]')"      uses: aws-actions/configure-aws-credentials@v1      with:        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}        aws-region: ${{ secrets.AWS_REGION }}     - name: Apply Kubernetes manifests      if: "!contains(env.commit_msg, '[destroy eks]')"      working-directory: ./k8s/      run: |        aws eks update-kubeconfig --name ${CLUSTER_NAME}        kubectl apply -f samples-bi-service.yaml        kubectl apply -f samples-bi-deployment.yaml        kubectl rollout status deployment/${DEPLOYMENT_NAME}   もちろん、“terraform” ユーザーの認証情報を設定 (~/.aws/credentials ファイルから取得) し、Github がそのシークレットを使用することを許可する必要があります。 ![](/sites/default/files/inline/images/images/secrets.png)   ワークフローの強調表示されている箇所に注目してください。 これらは、“[destroy eks]” というフレーズを持つコミットメッセージをプッシュし、EKS クラスターを破壊することを可能にします。 私たちはそのようなコミットメッセージを持つ “kubernetes apply” は実行しません。 パイプラインを実行します。でも最初に .gitignore ファイルを作成します。 $ cat /.gitignore.DS_Storeterraform/.terraform/terraform/*.planterraform/*.json$ cd $ git add .github/ k8s/ terraform/ .gitignore$ git commit -m "GitHub on EKS"$ git push   GitHub のリポジトリページにある「Actions」タブでデプロイプロセスをモニタリングします。 正常に完了するのを待ってください。 ワークフローを最初に実行するときは、“Terraform apply” のステップにおよそ 15 分かかります (クラスターの作成にかかる時間とほぼ同じ時間です)。 次に起動するとき (クラスターを削除していなければ) は、ワークフローの実行速度が大幅にアップします。 こちらをチェックしてください。 $ cd $ git commit -m "Trigger" --allow-empty$ git push   もちろん、作成したワークフローを確認しておくとよいでしょう。 今回は、IAM “my-user” の認証情報をご自分のノートパソコンでお使いください。 $ export AWS_PROFILE=my-user$ export AWS_REGION=eu-west-1$ aws sts get-caller-identity$ aws eks update-kubeconfig --region=eu-west-1 --name=dev-cluster --alias=dev-cluster$ kubectl config current-contextdev-cluster $ kubectl get nodesNAME                                                                               STATUS   ROLES      AGE          VERSIONip-10-42-1-125.eu-west-1.compute.internal   Ready          6m20s     v1.14.8-eks-b8860f $ kubectl get poNAME                                                       READY        STATUS      RESTARTS   AGEsamples-bi-756dddffdb-zd9nw    1/1               Running    0                      6m16s $ kubectl get svcNAME                   TYPE                        CLUSTER-IP        EXTERNAL-IP                                                                                                                                                         PORT(S)                    AGEkubernetes        ClusterIP               172.20.0.1                                                                                                                                                                                443/TCP                    11msamples-bi         LoadBalancer     172.20.33.235    a2c6f6733557511eab3c302618b2fae2-622862917.eu-west-1.elb.amazonaws.com    52773:31047/TCP  6m33s   _[http://a2c6f6733557511eab3c302618b2fae2-622862917.eu-west-1.elb.amazonaws.com:52773/csp/user/_DeepSee.UserPortal.Home.zen?$NAMESPACE=USER](http://a2c6f6733557511eab3c302618b2fae2-622862917.eu-west-1.elb.amazonaws.com:52773/csp/user/_DeepSee.UserPortal.Home.zen?%24NAMESPACE=USER) _(リンクを自分の External-IP と入れ替えてください) に移動し、“_system”、“SYS” と順に入力して、デフォルトのパスワードを変更します。 たくさんの BI ダッシュボードが表示されるはずです。 ![](/sites/default/files/inline/images/images/deepsee_ui.png)   細かく確認するには、それぞれの矢印をクリックします。 ![](/sites/default/files/inline/images/images/deepsee_2.png)   samples-bi ポッドを再起動したら、すべての変更内容が消去されるので覚えておきましょう。 これはデモなので、意図的にそうなるようにしています。 永続化する必要がある方のために、例を 1 つ github-gke-zpm-registry/k8s/statefulset.tpl リポジトリに作成しておきました。 作業が終わったら、作成したものをすべて削除してください。 $ git commit -m "Mr Proper [destroy eks]" --allow-empty$ git push   まとめ この記事では、eksctl ユーティリティの代わりに Terraform を使って EKS クラスターを作成しました。 これで、AWS インフラストラクチャのすべてを「体系化」することに一歩近づけたと思います。 Github Actions と Terraform で git push 使い簡単にデモアプリケーションをデプロイする方法をデモンストレーションしました。 また、ツールボックスに kompose とポッドの postStart フックも追加しました。 今回は、TLS の有効化については触れていません。 それは、また近いうちに取り上げたいと思います。
記事
Shintaro Kaminaka · 2020年12月10日

GitHub Actions を使って GKE に InterSystems IRIS Solution をデプロイする

[以前](https://jp.community.intersystems.com/node/482356)紹介した記事 (お読みいただいたでしょうか) では、GitHub とパーフェクトに統合する CircleCI のデプロイシステムについてカバーしました。 なのにどうしてさらに掘り下げる必要があるのか? それは、GitHub には GitHub Actions という独自の CI/CD プラットフォームがあり、詳しく見ておく価値があるからです。 GitHub Actions を使えば、外部のサービスを使用する必要はありません。 この記事では、GitHub Actions を使って InterSystems Package Manager のサーバー部分である ZPM-registry を Google Kubernetes Engine (GKE) にデプロイする、ということを試したいと思います。 どのシステムもそうですが、ビルド / デプロイのシステムも結局は「これを実行して、そこに移動したら、あれを実行する」といった感じの流れになります。 GitHub Actions では、こうようなアクションはそれぞれが 1 つ以上のステップで構成されるジョブとして扱われ、集合的にはワークフローとして知られています。 GitHub は、ワークフローの説明を探して .github/workflows ディレクトリの YAML ファイル (拡張子が .yml または .yaml のファイル) を検索します。 詳細は、GitHub Actions の主要コンセプトをご覧ください。 これから実行するアクションはすべて ZPM-registry リポジトリのフォーク内で実行されます。 この記事では、このフォークを "zpm-registry" と呼び、そのルートディレクトリーを "<root_repo_dir>" と呼びます。 ZPM アプリケーションそのものに関する詳細は、Introducing InterSystems ObjectScript Package Manager および The Anatomy of ZPM Module: Packaging Your InterSystems Solution をご覧ください。 すべてのコードサンプルは、簡単にコピー & ペーストできるよう、こちらのリポジトリに保管されています。 必須事項は、CircleCI ビルドで GKE の作成を自動化すると題した記事に記載されている内容と同じです。 過去の記事を読まれていること、Google アカウントを既にお持ちであること、そして前回の記事で「Development」と名付けたプロジェクトを作成されているという前提で進めていきます。 この記事では、その ID を <PROJECT_ID>として表示しています。 下の例を実行する場合は、ご自身のプロジェクトの ID に変更してください。 Google には[無料のティア](https://cloud.google.com/free/)がありますが、Google クラウドプラットフォームは有料ですのでご注意ください。 [経費の管理](https://cloud.google.com/billing/docs/)にはお気を付けください。   ワークフローの基本 それでは、始めましょう。  以下はシンプルですが使いものにならないワークフローの例です。 $ cd <root_repo_dir>$ mkdir -p .github/workflows$ cat <root_repo_dir>/.github/workflows/workflow.yaml          name: Traditional Hello Worldon: [push]jobs:  courtesy:    name: Greeting    runs-on: ubuntu-latest    steps:    - name: Hello world      run: echo "Hello, world!   リポジトリにプッシュするときは、「Greeting」と名付けたジョブを実行する必要があります。このジョブは唯一のステップとしてウェルカムフレーズを出力します。 このジョブは、GitHub でホストされる仮想マシン「Runner」で実行されます。同マシンには Ubuntu の最新バージョンがインストールされています。 このファイルをリポジトリにプッシュした後に「Code GitHub」タブを開けば、すべて順調に実行されたことを確認できるはずです。 ![](/sites/default/files/inline/images/images/simple_workflow_result.png)   ジョブが失敗したら、緑のチェックマークの代わりに赤の「X」が表示されます。 詳細を見るには、緑のチェックマークと「Details」を順にクリックします。 もしくは、直接「Actions」タブに移動することもできます。 ![](/sites/default/files/inline/images/images/actions_tab.png)   ワークフローの構文に関する完全な詳細は、Workflow syntax for GitHub Actions と題したヘルプドキュメントをご覧ください。 リポジトリにイメージビルド用の Dockerfile が含まれている場合は、「Hello world」のステップをもっと役に立つものに置き換えることができます。こちらは starter-workflows から選んだ 1 例です。 steps:- uses: actions/checkout@v2- name: Build the Docker image  run: docker build . --file Dockerfile --tag my-image:$(date +%s)   新しいステップ "uses: action/checkout@v2" が追加されていることに注目してください。 "checkout" という名前からも、リポジトリをクローンするステップであるのは分かりますが、詳細はどこを見ればいいのか?  CircleCI については、便利なステップがたくさんありますが、書き直す必要はありません。 その代わりに、Marketplace という共有リソースから取り入れることができます。 そこで実行したいアクションを探します。「By actions」とマーキングされているアクション (カーソルを合わせると、"Creator verified by Github" と表示されるアクション) をおすすめします。     ワークフローの "uses" 句には、独自のモジュールを記述する代わりに、既成のモジュールを使用する意図が示されています。 アクションの実装そのものは、どの言語でも記述できますが、JavaScript が推奨されます。 JavaScript (または TypeScript) で書かれたアクションは、直接 Runner のマシンで実行されます。 他のかたちで実装する場合は、ユーザーが指定する Docker コンテナが希望する内部環境で実行されますが、もちろんその速度は少し遅くなります。 アクションの詳細は、そのままのタイトルが付けられた記事 About actions をお読みください。  checkout アクションは TypeScript で書かれています。 また、この記事の例で使う Terraform アクションは、Docker Alpine で起動される標準的な Bash シェルスクリプトです。 クローンしたリポジトリに Dockerfile がありますので、修得した知識を応用してみましょう。 ZPM レジストリのイメージをビルドし、Google Container Registry にプッシュします。 並行して、このイメージを実行する Kubernetes クラスターを作成します。これには Kubernetes のマニフェストを使用します。  私たちの計画を GitHub が理解できる言語で記述すると以下のようになります (見やすくするために行をたくさん省いて俯瞰したものですので、このコンフィグは使わないでください)。 name: ワークフローの説明# Trigger condition. In this case, only on push to ‘master’ branchon:  push:    branches:    - master # ここではすべての後続のジョブとそれらの各ステップで # 使用可能な環境変数について説明しています# これらの変数は GitHub Secrets ページで初期化できます# それらを参照する目的で “${{ secrets }}” を追加していますenv:  PROJECT_ID: ${{ secrets.PROJECT_ID }} # ジョブリストの定義 ジョブ / ステップの名前はランダムなもので構いませんが# 有意義なほうがベターですjobs:  gcloud-setup-and-build-and-publish-to-GCR:    name: gcloud ユーティリティをセットアップし、ZPM イメージをビルドし、Container Registry に公開する    runs-on: ubuntu-18.04    steps:    - name: チェックアウト    - name: gcloud cli をセットアップする    - name: gcloud を認証情報ヘルパーとして使用するよう Docker を設定する    - name: ZPM イメージをビルドする    - name: ZPM イメージを Google Container Registry に公開する   gke-provisioner:    name: Provision GKE cluster    runs-on: ubuntu-18.04    steps:    - name: チェックアウト    - name: Terraform 初期化    - name: Terraform 検証    - name: Terraform プラン    - name: Terraform 適用   kubernetes-deploy:    name: Kubernetes マニフェストを GKE クラスタにデプロイする    needs:    - gcloud-setup-and-build-and-publish-to-GCR    - gke-provisioner    runs-on: ubuntu-18.04    steps:    - name: チェックアウト    - name: プレースホルダーをステートフルセットのテンプレートに記載の値に置換する    - name: gcloud cli をセットアップする    - name: Kubernetes のマニフェストを適用する   これが実用的な config のスケルトン (骨格だけのコード) で、その筋肉、つまり各ステップで実行されるアクションは含まれていません。 アクションはシンプルなコンソールコマンドで実行できます ("run"、複数のコマンドがあれば "run |")。          - name: gcloud を認証情報ヘルパーとして使用するよう Docker を設定する  run: |    gcloud auth configure-docker   アクションは "uses" を使ってモジュールとして起動することもできます。 - name: チェックアウト  uses: actions/checkout@v2   デフォルトでは、すべてのジョブが並列して実行され、各ステップが順番に処理されます。 しかし、"needs" を使えば、そのジョブを残りのジョブが完了するまで待機するジョブとして指定できます。 needs:- gcloud-setup-and-build-and-publish-to-GCR- gke-provisioner   ちなみに、このように待機するジョブが GitHub の Web インターフェイスに表示されるのは、待機される側のジョブが実行されるときだけです。 "gke-provisioner" ジョブには過去の記事の中で検証した Terraform が記述されています。 GCP 環境で操作する場合の事前設定は、便宜上、別の markdown file で繰り返されます。 以下のリンクもご利用いただくと便利です。 Terraform Apply Subcommand documentation Terraform GitHub Actions repository Terraform GitHub Actions documentation "kubernetes-deploy" ジョブには、"Apply Kubernetes manifests" と呼ばれるステップがあります。 今回は、Deploying InterSystems IRIS Solution into GCP Kubernetes Cluster GKE Using CircleCI という記事に記載されている通りにマニフェストを使用しますが、少しだけ変更を加えます。 過去の記事で使った IRIS アプリケーションは、ステートレスでした。 つまり、ポッドを再起動したら、すべてのデータがデフォルトの場所に戻っていたのです。 これは良いことで、必要になることも多々あります。しかし、ZPM レジストリでは、ポッドを何回再起動する必要があっても、読み込まれたパッケージを何とかして保存する必要があります。 デプロイすれば出来るのですが、もちろんそれには制限があります。  ステートフルなアプリケーションには、StatefulSet のリソースを選択する方が無難です。 メリットとデメリットは、Deployments vs. StatefulSets の GKE ドキュメンテーションに関するトピックおよび Kubernetes Persistent Volumes with Deployment and StatefulSet と題したブログ記事に記載しています。 StatefulSet のリソースは、リポジトリの中にあります。 注目したいのは、以下の部分です。 volumeClaimTemplates:- metadata:    name: zpm-registry-volume    namespace: iris  spec:    accessModes:    - ReadWriteOnce    resources:      requests:        storage: 10Gi   このコードは、単一の Kubernetes ワーカーノードによってマウント可能な 10GB の読み取り / 書き込みディスクを作成します。 このディスク (およびその中のデータ) はアプリケーションの再起動後も残ります。 StatefulSet 全体を削除しても残ります。そのためには正しい Reclaim Policy を設定する必要がありますが、この記事ではカバーしていません。 ワークフローを起動させる前に、GitHub Secrets に変数をあといくつか追加しておきましょう。     以下のテーブルはこれらの設定の意味を説明するものです (サービスアカウントキー も含まれています): | 名前 | 意味 | 例 | | ----------------------------- | --------------------------------------------------------------------------------- | ----------------------- | |  GCR_LOCATION |  グローバル GCR ロケーション |  eu.gcr.io | |  GKE_CLUSTER |  GKE クラスタ名 |  dev-cluster | |  GKE_ZONE |  イメージを格納するゾーン |  europe-west1-b | |  IMAGE_NAME |  イメージのレジストリ名 |  zpm-registry | |  PROJECT_ID |  GCP プロジェクト ID |  possible-symbol-254507 | |  SERVICE\_ACCOUNT\_KEY |  GitHub が GCP に接続する際に使用する JSON key。 重要: Base64 でエンコードされている必要があります (下の注意点をお読みください) |  ewogICJ0eXB... | |  TF\_SERVICE\_ACCOUNT_KEY |  Terraform が GCP に接続する際に使用する JSON key (下の注意点をお読みください) |  {…} |   SERVICE_ACCOUNT_KEY において、JSON-key に、例えば、key.json のような名前が付いている場合は、下のコマンドを実行します。 $ base64 key.json | tr -d '\n'   TF_SERVICE_ACCOUNT_KEY について、その権限は CircleCI ビルドで GKE の作成を自動化すると題した記事にて説明してあります。 SERVICE_ACCOUNT_KEY のちょっとした注意点: 私がやってしまったように、base64 フォーマットに変換するのを忘れてしまうと、以下のような画面が表示されます。   ワークフローの主要部分を確認し、必要な変数を追加したところで、ワークフローの完全版を検証する準備が整いました (<root_repo_dir>/.github/workflow/workflow.yaml)。 name: ZPM レジストリのイメージを構築し、GCR にデプロイする。 GKE を実行。 ZPM レジストリを GKE で実行する。on:  push:    branches:    - master # 環境変数。# ${{ secrets }} は GitHub -> Settings -> Secrets より取得されます# ${{ github.sha }} はコミットハッシュですenv:  PROJECT_ID: ${{ secrets.PROJECT_ID }}  SERVICE_ACCOUNT_KEY: ${{ secrets.SERVICE_ACCOUNT_KEY }}  GOOGLE_CREDENTIALS: ${{ secrets.TF_SERVICE_ACCOUNT_KEY }}  GITHUB_SHA: ${{ github.sha }}  GCR_LOCATION: ${{ secrets.GCR_LOCATION }}  IMAGE_NAME: ${{ secrets.IMAGE_NAME }}  GKE_CLUSTER: ${{ secrets.GKE_CLUSTER }}  GKE_ZONE: ${{ secrets.GKE_ZONE }}  K8S_NAMESPACE: iris  STATEFULSET_NAME: zpm-registry jobs:  gcloud-setup-and-build-and-publish-to-GCR:    name: gcloud ユーティリティをセットアップ、ZPM イメージを構築、Container Registry に公開する    runs-on: ubuntu-18.04    steps:    - name: チェックアウト      uses: actions/checkout@v2     - name: gcloud cli をセットアップする      uses: GoogleCloudPlatform/github-actions/setup-gcloud@master      with:        version: '275.0.0'        service_account_key: ${{ secrets.SERVICE_ACCOUNT_KEY }}     - name: gcloud を認証情報ヘルパーとして使用するよう Docker を設定する      run: |        gcloud auth configure-docker     - name: ZPM イメージを構築する      run: |        docker build -t ${GCR_LOCATION}/${PROJECT_ID}/${IMAGE_NAME}:${GITHUB_SHA} .     - name: ZPM イメージを Google Container Registry に公開する      run: |        docker push ${GCR_LOCATION}/${PROJECT_ID}/${IMAGE_NAME}:${GITHUB_SHA}   gke-provisioner:  # Inspired by:  ## https://www.terraform.io/docs/github-actions/getting-started.html  ## https://github.com/hashicorp/terraform-github-actions    name: GKE クラスタをプロビジョンする    runs-on: ubuntu-18.04    steps:    - name: チェックアウト      uses: actions/checkout@v2     - name: Terraform 初期化      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.17        tf_actions_subcommand: 'init'        tf_actions_working_dir: 'terraform'     - name: Terraform 検証      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.17        tf_actions_subcommand: 'validate'        tf_actions_working_dir: 'terraform'     - name: Terraform プラン      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.17        tf_actions_subcommand: 'plan'        tf_actions_working_dir: 'terraform'     - name: Terraform 適用      uses: hashicorp/terraform-github-actions@master      with:        tf_actions_version: 0.12.17        tf_actions_subcommand: 'apply'        tf_actions_working_dir: 'terraform'   kubernetes-deploy:    name: Kubernetes マニフェストを GKE クラスタにデプロイする    needs:    - gcloud-setup-and-build-and-publish-to-GCR    - gke-provisioner    runs-on: ubuntu-18.04    steps:    - name: チェックアウト      uses: actions/checkout@v2     - name: プレースホルダーをステートフルセットのテンプレートに記載の値に置換する      working-directory: ./k8s/      run: |        cat statefulset.tpl |\        sed "s|DOCKER_REPO_NAME|${GCR_LOCATION}/${PROJECT_ID}/${IMAGE_NAME}|" |\        sed "s|DOCKER_IMAGE_TAG|${GITHUB_SHA}|" > statefulset.yaml        cat statefulset.yaml     - name: gcloud cli をセットアップする      uses: GoogleCloudPlatform/github-actions/setup-gcloud@master      with:        version: '275.0.0'        service_account_key: ${{ secrets.SERVICE_ACCOUNT_KEY }}     - name: Kubernetes マニフェストを適用する      working-directory: ./k8s/      run: |        gcloud container clusters get-credentials ${GKE_CLUSTER} --zone ${GKE_ZONE} --project ${PROJECT_ID}        kubectl apply -f namespace.yaml        kubectl apply -f service.yaml        kubectl apply -f statefulset.yaml        kubectl -n ${K8S_NAMESPACE} rollout status statefulset/${STATEFULSET_NAME}   リポジトリにプッシュする前には、terraform-code を github-gke-zpm-registry リポジトリの Terraform ディレクトリーから取得し、プレースホルダーを main.tf のコメントに記載されている通りに置換し、terraform/ ディレクトリーに配置する必要があります。 Terraform は、リモートのバケットを使用しますが、このバケットは最初から CircleCI ビルドで GKE の作成を自動化すると題した記事で言及されている通りに作成されている必要があることを覚えておきましょう。 また、Kubernetes-code は github-gke-zpm-registry リポジトリの K8S ディレクトリーから取得され、k8s/ ディレクトリーの中に配置されている必要があります。 これらのコードのソースは、スペースを節約するためにこの記事では省いています。  そして、以下のようにすればデプロイをトリガーできます。 $ cd <root_repo_dir>/$ git add .github/workflow/workflow.yaml k8s/ terraform/$ git commit -m “Add GitHub Actions deploy”$ git push   フォークされている ZPM リポジトリに変更内容をプッシュしたら、説明したステップの実装を確認できます。      ここまで、ジョブの数は 2 つしかありませんが、 3 つ目の "kubernetes-deploy" は、その 2 つのジョブに依存しており、それらが完了した後に表示されます。Docket イメージのビルドと公開には少し時間がかかります。   また、結果は GCR コンソールで確認できます。     "Provision GKE cluster" ジョブは、最初の実行時に GKE クラスターを作成するので、最初だけ完了までの時間が少し長くなります。 数分間、待機中の画面が表示されます。     やっと完了したときには、思わず嬉しくなります。     Kubernetes のリソースも喜んでいます。 $ gcloud container clusters get-credentials <CLUSTER_NAME> --zone <GKE_ZONE> --project <PROJECT_ID> $ kubectl get nodesNAME                                                                                                   STATUS ROLES   AGE        VERSIONgke-dev-cluster-dev-cluster-node-pool-98cef283-dfq2 Ready    <none> 8m51s   v1.13.11-gke.23 $ kubectl -n iris get poNAME                     READY   STATUS     RESTARTS   AGEzpm-registry-0   1/1         Running    0                      8m25s   他の内容の確認は、Running ステータスになるまで待機することをおすすめします。 $ kubectl -n iris get stsNAME                 READY   AGEzpm-registry   1/1          8m25s   $ kubectl -n iris get svcNAME                 TYPE                      CLUSTER-IP       EXTERNAL-IP    PORT(S)                       AGEzpm-registry   LoadBalancer    10.23.248.234   104.199.6.32     52773:32725/TCP   8m29s   ディスクまでが喜んでいます。 $ kubectl get pv -oyaml | grep pdName  pdName: gke-dev-cluster-5fe434-pvc-5db4f5ed-4055-11ea-a6ab-42010af00286     そして、一番喜んでいるのは ZPM レジストリです ("kubectl -n iris get svc" の External-IP 出力を使用しました): $ curl -u _system:SYS 104.199.6.32:52773/registry/_ping{"message":"ping"}   ログイン / パスワードを HTTP で処理しているのは残念ですね。今後の記事の中で何とかしたいと思います。 ちなみにですが、エンドポイントについてはソースコードを見ていただければ、詳細が書かれていますので、XData UrlMap セクションをご覧ください。 このリポジトリは、それ自体にパッケージをプッシュすればテストできます。 GitHub のダイレクトリンクだけをプッシュする便利な機能があります。 InterSystems ObjectScript の数式ライブラリで試してみましょう。 これをローカルマシンから実行します。 $ curl -XGET -u _system:SYS 104.199.6.32:52773/registry/packages/-/all[]$ curl -i -XPOST -u _system:SYS -H "Content-Type: application/json" -d '{"repository":"https://github.com/psteiwer/ObjectScript-Math"}' 'http://104.199.6.32:52773/registry/package'HTTP/1.1 200 OK$ curl -XGET -u _system:SYS 104.199.6.32:52773/registry/packages/-/all[{"name":"objectscript-math","versions":["0.0.4"]}]   ポッドを再起動して、データがきちんと配置されていることを確認します。 $ kubectl -n iris scale --replicas=0 sts zpm-registry$ kubectl -n iris scale --replicas=1 sts zpm-registry$ kubectl -n iris get po -w   実行中のポッドを待ちます。 そして、うまく処理されると以下が表示されます。 $ curl -XGET -u _system:SYS 104.199.6.32:52773/registry/packages/-/all[{"name":"objectscript-math","versions":["0.0.4"]}]   それでは、この数式パッケージをリポジトリからローカルの IRIS インスタンスにインストールしましょう。 ZPM クライアントが既にインストールされているものを選びます。 $ docker exec -it $(docker run -d intersystemsdc/iris-community:2019.4.0.383.0-zpm) bash$ iris session irisUSER>write ##class(Math.Math).Factorial(5)<CLASS DOES NOT EXIST> *Math.MathUSER>zpmzpm: USER>listzpm: USER>repo -listregistry    Source:     https://pm.community.intersystems.com    Enabled?    Yes    Available?    Yes    Use for Snapshots?    Yes    Use for Prereleases?    Yeszpm: USER>repo -n registry -r -url http://104.199.6.32:52773/registry/ -user _system -pass SYSzpm: USER>repo -list                                                                          registry    Source:     http://104.199.6.32:52773/registry/    Enabled?    Yes    Available?    Yes    Use for Snapshots?    Yes    Use for Prereleases?    Yes    Username:     _system    Password:     <set>zpm: USER>repo -list-modules -n registryobjectscript-math 0.0.4zpm: USER>install objectscript-math[objectscript-math]    Reload START...[objectscript-math]    Activate SUCCESS zpm: USER>quitUSER>write ##class(Math.Math).Factorial(5)                                               120   おめでとうございます!いらなくなった GKE クラスタは、忘れずに削除しておきましょう。    まとめ InterSystems のコミュニティには GitHub Actions のリファレンスがそれほど多くありません。 見つかったのは、エキスパート @mdaimor の メンション 1 つのみです。 ですが、GitHub Actions はコードを GitHub に保管するディベロッパーにとって非常に重宝すると思われます。 ネイティブアクションは JavaScript でしかサポートされていませんが、これはディベロッパーの多くがコードを使ってステップを説明することに慣れており、そうすることが望ましいということかもしれません。 いずれにしても、JavaScript に詳しくない方は Docker アクションを使えばいいと思います。 GitHub Actions の UI に関して、使っているうちに不便に感じたことがいくつかあり、知っておくべきだと思うことを紹介しておきます。 ジョブのステップが完了するまで、その状況を確認できない。 "Terraform apply" のステップのように、クリックすることができない。 失敗したワークフローは再実行できる一方で、成功したワークフローを再実行する方法が見つからなかった。 2 つ目のポイントの次善策として、次のコマンドを使います。 $ git commit --allow-empty -m "trigger GitHub actions"    これに関する詳細は、StackOverflow に投稿されている How do I re-run Github Actions? (Github Actions はどうすれば再実行できるか?) という質問をご覧ください。
お知らせ
Maki Hashizawa · 2023年1月18日

第3回 InterSystems 医療 x IT セミナー ソリューション開発編 II 開催のお知らせ

インタ―システムズでは、医療ITソリューション・サービスを提供される方々向けに、医療DXの推進やデータ活用を支援するシステムの要件、求められる姿について考察するオンラインセミナーをシリーズで開催しております。ソリューション開発編の第2段として、今回は「相互運用性/FHIR®の実装」をテーマに、下記の通り、開催する運びとなりました。 是非、ご参加いただきたくご案内致します。 【開催概要】 日時:2023年2月9日(木)13:00 - 14:30 (予定) 参加:無料(事前登録制) 対象:医療情報ステムベンダー、医療機器メーカー、医療向けサービスプロバイダーの事業企画・開発の皆様 主催:インタ―システムズジャパン株式会社 お申込み・詳細はこちらから 【プログラム】 13:00~13:05 開会挨拶 インターシステムズジャパン株式会社カントリーマネージャー 林 雅音 13:05~13:45「高度な医療DXを実現するための情報基盤とは」 宮崎大学医学部名誉教授 荒木 賢二 様 13:50 - 14:25「HL7® FHIR® × InterSystems IRIS for Health」 インターシステムズジャパン株式会社古薗 知子 13:25 - 14:30 閉会挨拶 ※ プログラムは、変更の場合がございます。最新の情報はWebサイトでご確認ください。 詳細・お申込みはこちらをご覧ください。 第3回 InterSystems 医療 x IT セミナー 詳細サイト 皆様のご参加をお待ちしています。
記事
Tomohiro Iwamoto · 2020年6月29日

InterSystems データプラットフォームとパフォーマンス - パート8 ハイパーコンバージドインフラストラクチャのキャパシティプランニングとパフォーマンス 

ここ数年の間、ハイパーコンバージドインフラストラクチャ(HCI)ソリューションが勢いを増しており、導入件数が急速に増加しています。 IT部門の意思決定者は、VMware上ですでに仮想化されているアプリケーションなどに対し、新規導入やハードウェアの更新を検討する際にHCIを考慮に入れています。 HCIを選択する理由は、単一ベンダーと取引できること、すべてのハードウェアおよびソフトウェアコンポーネント間の相互運用性が検証済みであること、IO面を中心とした高いパフォーマンス、単純にホストを追加するだけで拡張できること、導入や管理の手順が単純であることが挙げられます。 この記事はHCIソリューションの一般的な機能を取り上げ、HCIを初めて使用する読者に紹介するために執筆しました。 その後はデータベースアプリケーションの具体的な例を使用し、InterSystems データプラットフォーム上に構築されたアプリケーションを配置する際の、キャパシティプランニングとパフォーマンスに関する構成の選択肢と推奨事項を確認します。 HCIソリューションはパフォーマンスを向上させるためにフラッシュストレージを利用しているため、選択されたフラッシュストレージオプションの特性と使用例に関するセクションも含めています。 この記事のキャパシティ計画とパフォーマンスに関する推奨事項は、VMWare vSANに特化しています。 ただし、HCI市場で成長しているのはvSANだけではなく、同じく導入件数が増加しているNutanixをはじめとする他のHCIベンダーも存在します。 選択したHCIベンダーにかかわらず多くの機能は共通しているため、この記事の推奨事項は広く適用できます。 ただし、どの場合もアプリケーション固有の要件を考慮しながらHCIベンダーとこの記事の推奨事項について話し合うことが得策です。 InterSystems データプラットフォームとパフォーマンスに関する他の連載記事のリストはこちらにあります。 HCIとは? 厳密に言えばコンバージドソリューションは以前から長らく存在しますが、この記事ではWikipediaに「ハイパーコンバージェンスはパッケージ化された複数の個別システムから、市販されている商用x86ラックサーバーですべてが実行されるソフトウェア定義のインテリジェントな環境に進化している…」と記載されているような最新のHCIソリューションを取り上げています。 では、HCIは単独で存在するものなのでしょうか? 違います。 ベンダーに相談する際は、HCIには多くの置き換え可能な要素があることを覚えておく必要があります。コンバージドとハイパーコンバージドは具体的な青写真や標準ではなく、どちらかといえば一種のアーキテクチャなのです。 HCIハードウェアには商品性があるため、市場では複数のベンダーがソフトウェアレイヤーのほか、コンピューティング機能、ネットワーク機能、ストレージ機能、管理機能を組み合わせたその他の革新的な方法を使って差別化を図っています。 ここではあまり深追いしませんが、HCIの名が付いたあるソリューションでは、クラスター内の複数サーバー内にストレージを配置したり、サーバーのクラスターと独立したSANストレージ(複数の異なるベンダー製のものである可能性もあります)を使ったより普通の構成を作ることができます。これらはまた、相互運用性がテストおよび検証されており、単一のコントロールプレーンから管理できます。 キャパシティとパフォーマンスを計画する際は、ストレージがSANファブリック(ファイバーチャネルやイーサネットなど)を介して接続されたアレイにあり、ストレージプールがソフトウェアで定義され、サーバーノードの各クラスター内に配置され、複数のサーバー上でストレージが処理される場合とは異なるパフォーマンスと要件を備えたソリューションを検討する必要があります。 では、改めてHCIとは何でしょうか? この記事ではHCI、特にストレージが物理的にホストサーバー内にあるVMware vSANに焦点を当てています。 このようなソリューションでは、HCIソフトウェアレイヤーが処理を実行するクラスター内の複数のノードのそれぞれの内部ストレージを1つの共有ストレージシステムのように機能させています。 そのため、HCIソフトウェアにコストがかかったとしても、エンタープライズストレージアレイを使用するソリューションと比較して、HCIを使用すると大幅にコストを節約できる可能性があることがHCIを採用するもう一つの要因となっています。 この記事では、HCIがコンピューティング、メモリ、ストレージ、ネットワーク、および管理ソフトウェアを仮想化したx86サーバーのクラスターに統合するソリューションについてご紹介します。 一般的なHCIの特徴 上記のとおり、HCIソリューションの一例にはVMWare vSANとNutanixがあります。 どちらもHCIに対して類似した高レベルのアプローチを採用しており、良い典型例だと言えます。 VMware vSAN にはVMware vSphereが必要であり、複数のベンダーのハードウェアで利用できます。 利用可能なハードウェアの選択肢は多数ありますが、これらはVMwareのvSANハードウェア互換性リスト(HCL)に厳密に依存しています。 ソリューションは、パッケージ化および事前構成されたEMC VxRailなどで購入できます。または、HCLでコンポーネントを購入して独自に構築することもできます。 Nutanixは、最大で2U、4ノードのアプライアンスを構成済みブロック化したハードウェアを含むオールインワンソリューションとして購入および導入することもできます。 Nutanixソリューションは、他のベンダーのハードウェアで検証された独自のソフトウェアソリューションとしても利用できます。 実装にはいくつかのバリエーションがありますが、一般的にHCIにはパフォーマンスとキャパシティの計画に関して、あなたが知っておくべき一般的な特徴があります。 仮想マシン(VM)はVMware ESXiなどのハイパーバイザーで実行されますが、Hyper-VやNutanix Acropolis Hypervisor(AHV)などのハイパーバイザーでも実行されます。 NutanixはESXiを使用して導入することもできます。 ホストサーバーは多くの場合、コンピューティング、ストレージ、ネットワークのブロックに統合されます。 例えば、4つのノードを持つ2Uアプライアンスがあります。 管理と可用性のために、複数のホストサーバーがクラスターに統合されます。 ストレージは階層化されており、オールフラッシュ、またはフラッシュキャッシュ層とキャパシティ層として利用する回転式ディスクとのハイブリッドになっています。 ストレージは、容量、パフォーマンス、可用性を確保するためのデータ配置とポリシーを含むソフトウェア定義のプールとして表現されます。 容量とIOのパフォーマンスは、クラスターにホストを追加することでスケーリングされます。 データは複数のクラスターノード上のストレージに同期的に書き込まれるため、クラスターはデータを失うことなくホストやコンポーネントの障害に耐えることができます。 VMの可用性とロードバランシングは、vMotion、VMware HA、DRSなどのハイパーバイザーによって提供されます。 上記の通り、外部ストレージアレイ、ストレージ専用ノードのサポートなど、このリストに変更を加えた他のHCIソリューションもあります。このリストはベンダーのリストと同じく長いです。 HCIの採用が加速し、ベンダー間の競争がイノベーションとパフォーマンスの向上を推進しています。 HCIがクラウドを導入するための基本的要素になっていることも注目に値します。 InterSystemsの製品はHCIでサポートされていますか? オペレーティングシステムが仮想化されている場合を含め、さまざまなプロセッサのタイプとオペレーティングシステムに対してInterSystemsの製品を検証およびリリースするのは、InterSystemsのポリシーであり、決まりでもあります。 詳細については、InterSystemsサポートポリシーおよびリリース情報を参照してください。 例えばx86ホスト上のvSANの場合、Caché 2016.1をRed Hat 7.2オペレーティングシステム上で実行することができます。 注意:独自のアプリケーションを作成しない場合は、アプリケーションベンダーのサポートポリシーも確認する必要があります。 vSANキャパシティプランニング このセクションでは、Caché、Ensemble、HealthShareなどのInterSystemsデータプラットフォーム上のデータベースアプリケーションにVMware vSANを導入する場合の考慮事項と推奨事項について説明します。 ただし、これらの推奨事項はHCIベンダーと検討するための一般的な構成関連の質問リストとして使用することもできます。 VM vCPUとメモリ まず、複数の同じプロセッサを持つVMware ESXiにアプリケーションを導入するためにすでに使用中のものと同じキャパシティプランニングのルールをデータベースVMのvCPUとメモリに使用します。 Cachéの一般的なCPUとメモリのサイジングについて復習するには、この連載の他の記事のリスト「キャパシティ計画とパフォーマンスに関する連載の索引」を参照してください。 HCIシステムの特徴の1つに、非常に低いストレージIOレイテンシと高いIOPS機能があります。 この連載の第2回目の投稿にあった、CPU / メモリ / ストレージ / ネットワークを示すハードウェアの食品群の図を覚えていらっしゃるかもしれません。 その記事ではこれらのコンポーネントがすべて相互に関連しているため、1つのコンポーネントに対する変更が別のコンポーネントに影響する可能性があり、予期しない結果が生じることがあることを指摘していました。 例えば、私はストレージアレイの特にひどいIOボトルネックを解消するとCPU使用率が100%に跳ね上がり、ユーザーエクスペリエンスがさらに悪化した事例を見たことがあります。これは、システムが突然自由に作業量を増やせるようになったものの、ユーザー活動とスループットの増加に対応するためのCPUリソースがなかったために発生したものです。 新しいシステムを計画する際、サイジングモデルがパフォーマンスの低いハードウェアのパフォーマンスメトリックに基づいている場合はこのような影響を考慮する必要があります。 新しいプロセッサを搭載した新しいサーバーにアップグレードする場合でも、新しいプラットフォームでのIOレイテンシが低いために適切なサイズにする必要がある場合は、データベースVMの動作を注意深く監視する必要があります。 また、後述するように物理ホストのCPUリソースとメモリリソースをサイジングする際はソフトウェア定義のストレージIO処理も考慮する必要があります。 ストレージキャパシティプランニング ストレージキャパシティプランニングを理解し、データベースの推奨事項を理解するには、まずvSANと従来のESXiストレージの基本的な違いを理解する必要があります。 最初にこれらの違いを説明し、次にCachéデータベースに関するすべてのベストプラクティスの推奨事項を詳しく説明します。 vSANストレージモデル vSANおよびHCIでは、一般的にソフトウェア定義ストレージ(SDS)が重要な役割を果たしています。 データの保存方法と管理方法は、ESXiサーバーのクラスターと共有ストレージアレイを使用する場合とは大きく異なります。 HCIの利点の1つはLUNがないことです。その代わり、必要に応じてVMに割り当てられるストレージのプールがあり、VMDKごとに可用性、容量、およびパフォーマンスの機能を表すポリシーが適用されています。 例えば、パフォーマンスと可用性の要件に応じて、ディスクの数やタイプが異なるさまざまなサイズのディスクグループやディスクプールにまとめられた回転式ディスクのシェルフで構成された従来のストレージアレイを想像してください。 その後、ディスクグループは多数の論理ディスク(ストレージアレイボリュームまたはLUN)として表現され、データストアとしてESXiホストに提示され、VMFSボリュームとしてフォーマットされます。 VMはデータストア内のファイルとして表現されます。 可用性とパフォーマンスに関するデータベースのベストプラクティスでは、データベース(ランダムアクセス)、ジャーナル(シーケンシャル)、およびその他(バックアップや非本番システムなど)用に、少なくとも独立したディスクグループとLUNを使用することを推奨しています。 vSANの場合はそうではありません。vSANのストレージは、ストレージポリシーベースの管理(SPBM)を使用して割り当てられます。 ポリシーは、以下を含む機能を組み合わせて作成できます(ただし、これ以外の機能もあります)。 冗長なデータのコピー数を決める許容障害数(FTT)。 容量を節約するイレイジャーコーディング(RAID-5またはRAID-6)。 パフォーマンスを向上させるディスクストライプ。 シックまたはシンディスクプロビジョニング(vSANではデフォルトでシン)。 その他... VMDK(個々のVMディスク)は適切なポリシーを選択することにより、vSANストレージプールから作成されます。 したがって、属性を設定してアレイ上にディスクグループとLUNを作成するのではなく、SPBMを使用してストレージの機能をvSANのポリシーとして定義します。例えば、「データベース」は「ジャーナル」やその他の必要なものとは異なります。 VM用のディスクを作成する際は容量を設定し、適切なポリシーを選択します。 もう一つ重要な概念があります。VMはVMDKデータストア上のファイルのセットではなく、ストレージオブジェクトのセットとして保存されます。 例えば、データベースVMはVMDK、スワップ、スナップショットなどを含む複数のオブジェクトとコンポーネントで構成されます。vSAN SDSは選択したポリシーの要件を満たすため、すべてのオブジェクト配置メカニズムを管理します。 ストレージ階層とIOパフォーマンスのプランニング 高いパフォーマンスを確保するため、2つの階層のストレージがあります。 キャッシュ層 - 高耐久性フラッシュである必要があります。 キャパシティ層 - フラッシュ、またはハイブリッドの場合は回転式ディスクを使用します。 下の図に示すように、ストレージは複数の階層とディスクグループに分かれています。 vSAN 6.5では、各ディスクグループに単一のキャッシュデバイスと最大7台の回転式ディスクまたはフラッシュデバイスが含まれます。 最大5つのディスクグループを使用できるため、ホストごとに最大35台のデバイスを使用できます。 次の図は、4つのホストを持つオールフラッシュvSANクラスターを示しています。各ホストには2つのディスクグループがあり、それぞれに1台のNVMeキャッシュディスクと3台のSATAキャパシティディスクがあります。 図1. それぞれの層とディスクグループを示すvSANオールフラッシュストレージ それぞれの層の設定方法やキャッシュ層とキャパシティ層に使うフラッシュのタイプを検討する場合、IOパスを考慮する必要があります。レイテンシを最小にしてパフォーマンスを最大にするため、書き込みはキャッシュ層に移され、ソフトウェアがその書き込みをまとめてキャパシティ層に移します。 キャッシュの使用状況は導入モデルに依存します。例えばvSANハイブリッド構成の場合はキャッシュ層の30%が書き込みキャッシュですが、オールフラッシュの場合はキャッシュ層の100%が書き込みキャッシュで、読み込みは低レイテンシなフラッシュキャパシティ層から行われます。 オールフラッシュを使用すると、パフォーマンスが向上します。 大容量で耐久性のあるフラッシュドライブが利用できるようになった今、回転式ディスクが必要かどうかを検討する必要があります。 近年のビジネス事例では回転式ディスクの代わりにフラッシュが採用されており、はるかに低いIOPS単位のコスト、パフォーマンス(低レイテンシ)、高信頼性(可動部品が故障せず、必要なIOPSで故障するディスクが少ない)、低電力かつ低発熱なプロファイル、小さな設置面積などを特徴としています。 また、その他のHCI機能のメリットも得られます。例えばvSANでは、オールフラッシュ構成でのみ重複排除と圧縮が許可されます。 推奨: 最高のパフォーマンスとTCOの削減のため、オールフラッシュを検討してください。 最高のパフォーマンスを得るには、特にvSANの場合はディスクグループごとにキャッシュデバイスが1つしかないため、キャッシュ層のレイテンシを最低にする必要があります。 推奨: SASでも問題ありませんが、可能であればキャッシュ層にNVMe SSDを選択してください。 推奨: キャッシュ層で高耐久性フラッシュデバイスを選択し、高負荷なI/Oを処理するようにしてください。 キャパシティ層のSSDについては、SAS SSDとSATA SSDの性能の差はごくわずかです。 データベースアプリケーションについては、キャパシティ層でNVMe SSDのコストを負担する必要はありません。 ただし、いかなる場合も、電源障害保護などの機能を備えたエンタープライズクラスのSATA SSDを使用するようにしてください。 推奨: キャパシティ層には大容量のSATA SSDを選択してください。 推奨: 電源障害保護機能を備えたエンタープライズSSDを選択してください。 スケジュールによってはIOPSが高い3D Xpointなどの新しいテクノロジーを使用し、レイテンシを下げ、容量を増やし、耐久性を高めることができます。 この記事の最後に、フラッシュストレージの構成を記しています。 推奨: キャッシュ層とキャパシティ層には、3D Xpointなどの新しいテクノロジーを組み込むことを検討してください。 前述したように、ホストごとに最大5つのディスクグループを作成でき、ディスクグループのキャパシティ層は1台のフラッシュデバイスと最大7台のデバイスで構成されます。 1台のフラッシュデバイスかつ必要な容量の単一のディスクグループ、またはホストごとに複数のディスクグループを作成できます。 ホストごとに複数のディスクグループを持たせると、次のようなメリットがあります。 パフォーマンス:階層内に複数のフラッシュデバイスがあると、ホストごとのIOPSが増加します。 障害ドメイン:キャッシュディスクの障害はディスクグループ全体に影響しますが、vSANが自動的に再構築されるため、可用性は維持されます。 可用性、パフォーマンス、容量のバランスをとる必要がありますが、一般的にはホストごとに複数のディスクグループを用意することをお勧めします。 推奨: ストレージの要件を確認し、ホストごとに複数のディスクグループを用意することを検討してください。 どのようなパフォーマンスを期待できますか? アプリケーションのユーザーエクスペリエンスを向上させるには、ストレージのレイテンシを下げることが重要です。通常は、データベースの読み取りIOのレイテンシを10ミリ秒未満にすることが推奨されています。詳細については、この連載のパート6の表を参照してください。 既定のvSANストレージポリシーとCaché RANREADユーティリティを使用してCachéデータベースのワークロードをテストした結果、キャパシティ層でIntel S3610 SATA SSDを使用したオールフラッシュvSANのレイテンシは1ミリ秒未満で、3万IOPS超のランダムな読み取りIOが100%持続されることを確認しました。 Cachéデータベースが基本的に可能な限り多くのデータベースIOにメモリを使用するようにインスタンスをサイジングすることを考慮すれば、オールフラッシュのレイテンシとIOPS能力はほとんどのアプリケーションに十分な余裕を与えるものです。 メモリのアクセス時間は、NVMeフラッシュストレージよりも桁違いに短いことを覚えておいてください。 いつものことですが、何が最適なのかは状況によって違います。ストレージポリシー、ディスクグループの数、ディスクの数とタイプなどがパフォーマンスに影響するため、ご自身のシステムで検証してください! キャパシティとパフォーマンスのプランニング vSANストレージプールの物理容量(TB)は、キャパシティ層のディスクの合計サイズとして大まかに計算できます。 図1の構成例では、合計24個のINTEL S3610 1.6 TB SSDがあります。 クラスターの物理容量:24 x 1.6TB = 38.4 TB ただし、利用可能な容量は選択する構成によって大きく異なり、計算が煩雑になります。例えば、使用されるポリシー(データのコピー数を指定するFTTなど)のほか、重複排除や圧縮が有効になっているかどうかによって決まります。 ここでは選択されたポリシーを段階的に追い、その容量とパフォーマンスへの影響とデータベースのワークロードに関する推奨事項について説明します。 私が目にするあらゆるESXiの導入環境は、複数のVMで構成されています。例えば、統合医療情報システムであるTrakCareはInterSystemsの医療情報プラットフォーム上に構築されており、HealthShareの中核には「ティア1ビジネスクリティカルアプリケーション」の説明に完全に適合する少なくとも1台の大規模(モンスター)データベースサーバーVMがあります。 ただし、導入環境には本番ウェブサーバー、プリントサーバーなど、単一の目的を持つ他のVMも混在しています。 テスト用、トレーニング用、および本番用ではないその他のVMもです。 通常、すべてが単一のESXiクラスターに導入されます。 ここではデータベースVMの要件に焦点を当てていますが、SPBMはすべてのVMに対してVMDKごとに調整できることを覚えておいてください。 重複排除と圧縮 vSANの場合、重複排除と圧縮はクラスター全体でオン/オフします。 重複排除と圧縮は、オールフラッシュ構成を使用している場合にのみ有効にできます。 両方の機能を同時に有効にできます。 一見すると、重複排除と圧縮は良い考えのように思えます。キャパシティ層で(より高価な)フラッシュデバイスを使用している場合は特に容量を節約したいものです。 重複排除と圧縮を有効にすると容量を節約できますが、大規模な本番データベースや常にデータが上書きされているクラスターではこの機能を有効にしないことをお勧めします。 重複排除と圧縮によってホストの処理負荷が1桁の%CPU使用率の範囲で増加する可能性がありますが、それはデータベースに推奨されない主な理由ではありません。 要約すると、vSANは4Kブロックを使用する単一ディスクグループ内のキャパシティ層にデータが書き込まれるときにデータの重複排除を試みます。 したがって、図1の例では重複排除するデータオブジェクトは、同じディスクグループのキャパシティ層に存在していなければなりません。 一意のポインタやコンテンツなどを含む8Kのデータベースブロックで埋められた基本的に巨大なファイルであるCachéデータベースファイルの容量が大幅に減るとは思えません。 また、vSANは重複ブロックの圧縮のみを試み、圧縮率が50%以上に達した場合にのみブロックが圧縮されたと見なします。 重複排除されたブロックが2Kに圧縮されない場合は、圧縮されずに書き込まれます。 オペレーティングシステムや他のファイルに重複がある場合がありますが、重複排除と圧縮の本当のメリットはVDI用に導入されたクラスターにあります。 また、重複排除と圧縮が有効になっている場合、ディスクグループ内の1台のデバイスの(まれではありますが)障害がグループ全体に影響を及ぼすことにも注意すべきです。 ディスクグループ全体が「異常」とマークされると、クラスター全体に影響があります。グループが異常とマークされると、ディスクグループ上のすべてのデータがそのグループから他の場所に退避され、その後はデバイスを交換しなければならず、vSANはリバランスするためにオブジェクトを再同期します。 推奨: データベースの導入では、圧縮と重複排除を有効にしないでください。 補足:InterSystemsのデータベースミラーリングについて。 最高の可用性を必要とするミッションクリティカルなティア1のCachéデータベースアプリケーションインスタンスの場合、仮想化されている場合でもInterSystemsの同期データベースミラーリングをお勧めします。仮想化ソリューションにはHAが組み込まれています。例えばVMWare HAの場合、ミラーリングを使用すると次のようなメリットもあります。 最新データの独立したコピーが存在します。 秒単位でフェイルオーバーできます(VMを再起動してからオペレーティングシステムを起動し、Cachéをリカバリするよりも高速です)。 アプリケーション/Cachéに障害が発生した場合(VMwareでは検出されません)にフェイルオーバーできます。 同じクラスターでデータベースをミラーリングしている場合に重複排除を有効にすると、問題があることに気付きましたか? ミラーデータの重複排除を試すことは 一般的には賢明ではなく、処理のオーバーヘッドも発生します。 HCIでデータベースをミラーリングするかどうかを決定する際は、必要な合計ストレージ容量を考慮する必要があります。 vSANは可用性を確保するためにデータのコピーを複数作成します。このデータストレージもミラーリングによって複製されます。 ストレージの追加コストと、VMware HAによるわずかな稼働時間の増加を天秤に掛ける必要があります。 稼働時間を最大にするため、2つのクラスターを作成し、データベースミラーの各ノードを完全に独立した障害ドメインに配置できます。 ただし、このレベルの稼働時間を提供するには、サーバーとストレージの合計容量に注意してください。 暗号化 また、保管データの暗号化方法も考慮する必要があります。 IOスタックには、次のようないくつかの選択肢があります。 Cachéのデータベース暗号化を使用する(データベースの暗号化のみ)。 ストレージで暗号化する(SSDでのハードウェアディスク暗号化など)。 暗号化がパフォーマンスに与える影響はごくわずかですが、HCIで重複排除または圧縮を有効にすると容量に大きな影響を与える可能性があります。 重複排除や圧縮を選択した場合、暗号化されたデータは設計上ランダムであり、十分に圧縮されないため、Cachéのデータベース暗号化を使用することは望ましくありません。 保護対象の場所や回避したいリスク(ファイルの盗難とデバイスの盗難のどちらを危険視するかなど)を検討してください。 推奨: 最低限の暗号化を行うには、可能な限り最下層のIOスタックで暗号化してください。 ただし、回避したいリスクが多くなるほど、スタックの階層は高くなります。 許容障害数(Failures To Tolerate, FTT) FTT は、ストレージオブジェクトにクラスター内で少なくともn件のホスト、ネットワーク、またはディスクの障害が同時に発生してもオブジェクトの可用性を確保するようストレージに要件を設定します。 デフォルトは1(RAID-1)です。VMのストレージオブジェクト(VMDKなど)はESXiホスト間でミラーリングされます。 したがって、vSAN構成には少なくともn + 1個の複製(データのコピー)が含まれている必要があります。これは、クラスター内に2n + 1台のホストがあることも意味します。 例えば許容障害数が1のポリシーに従うには、たとえ1台のホストに障害が発生したとしても常に最低3台のホストを稼働させておく必要があります。 したがって、1台のホストがオフラインになったときのメンテナンスやその他の時間を考慮に入れるには、4台のホストが必要です。 推奨: vSANクラスターの場合、可用性を確保するには少なくとも4台のホストが必要です。 ただし、2台のホストと1台のリモート監視VMを想定したリモートオフィスブランチオフィス(ROBO)構成のような例外もあります。 イレイジャーコーディング vSANのデフォルトのストレージ方式はRAID-1-データレプリケーションまたはミラーリングです。 イレイジャーコーディングは、ストレージオブジェクト/コンポーネントがクラスター内のストレージノードに分散されるRAID-5またはRAID-6です。 イレイジャーコーディングの主なメリットは、データ保護レベルを維持したまま容量効率を上げられることです。 前のセクションのFTTの計算を例として使用します。VMが2つの障害を許容する場合、RAID-1を使用するに、ストレージオブジェクトのコピーが3つ必要です。つまり、VMDKはベースVMDKのサイズの300%を消費します。 RAID-6ではVMが2つの障害に耐えることができ、VMDKのサイズの150%しか消費しません。 ここでは、パフォーマンスと容量のどちらかを選択する必要があります。 容量を節約できるのは素晴らしいことですが、イレイジャーコーディングを有効にする前にデータベースのIOパターンを考慮する必要があります。 容量効率が向上する代わりに、I/O処理が増加することになります。コンポーネントの障害が発生している間はさらに負荷が高くなるため、最高のデータベースパフォーマンスを確保するにはRAID-1を使用してください。 推奨: 本番データベースではイレイジャーコーディングを有効にしないでください。 本番環境以外で有効にしてください。 イレージャーコーディングはクラスターの必要なホスト数にも影響します。 例えばRAID-5の場合はクラスター内に最低4台のノードが必要で、RAID-6の場合は最低6台のノードが必要です。 推奨: イレイジャーコーディングの構成を計画する前に、追加ホストのコストを検討してください。 ストライピング ストライピングはパフォーマンスを向上させるのに役立ちますが、役に立ちそうなのはハイブリッド構成だけだと思われます。 推奨: 本番データベースではストライピングを有効にしないでください。 オブジェクトスペースの予約(シンまたはシックプロビジョニング) この設定の名前は、オブジェクトを使用してVM(VMDKなど)のコンポーネントを格納するvSANに由来しています。 デフォルトでは、VSANデータストアにプロビジョニングされるすべてのVMのオブジェクトスペースの予約は0%(シンプロビジョニング)になっています。この設定は容量を節約し、vSANのデータをより自由に配置できるようにします。 ただし、本番データベースでは予約値に100%(シックプロビジョニング)を使用し、作成時に容量を割り当てるのが最適です。 vSANの場合は、各ブロックへ初めて書き込まれるときに0が書き込まれるLazy Zeroedになります。 本番データベースの予約値に100%を選択する理由としては、データベースが拡張される際の遅延が少なくなり、必要なときにストレージを使用できることが保証されることが挙げられます。 推奨: 本番データベースのディスクの予約値には100%を使用してください。 推奨: 本番以外のインスタンスの場合、ストレージはシンプロビジョニングのままにしてください。 それぞれの機能をいつ有効化すべきですか? 通常はシステムをしばらく使用した後(システム上にアクティブなVMとユーザーが存在する状態)に可用性と容量節約の機能を有効化できます。 ただし、パフォーマンスと容量に影響します。 元のデータに加えてデータの複製が必要になるため、データを同期中は追加の容量が必要になります。 経験上、大規模なデータベースを使用するクラスターでこの種の機能を有効にすると、処理時間が非常に長くなり、可用性が低下する可能性があります。 推奨: 本番稼働を開始する前に、そして大規模なデータベースを読み込む前には必ず、事前に時間をかけて重複排除や圧縮などのストレージの機能を理解して構成するようにしてください。 ディスクバランシングや障害発生時用の空き領域を残すなど、他にも考慮事項があります。 つまり、この記事の推奨事項とベンダー固有の選択肢を考慮して物理ディスクの要件を把握する必要があります。 推奨: 多くの機能と置き換え可能な要素があります。 まずは合計GB容量の要件を見積もり、この記事の推奨事項を(アプリケーションベンダーと一緒に)確認してからHCIベンダーに相談してください。 ストレージ処理のオーバーヘッド ホスト上でのストレージ処理のオーバーヘッドを考慮する必要があります。 かつてはエンタープライズストレージアレイのプロセッサが処理していたストレージの処理が、クラスター内の各ホストで実行されるようになっています。 各ホストのオーバーヘッドの大きさは、ワークロードと有効になっているストレージの機能によって決まります。 vSAN上のCachéで行ったテストの結果を見る限り、特に現在のサーバーで使用可能なコアの数を考慮すると、過度な処理要件はないと言えます。 VMwareはホストのCPU使用率が5〜10%になるよう計画することを推奨しています。 上記はサイジングを行う際にまず考慮すべき内容ですが、何が最適なのかは状況によって異なるため、確認が必要です。 推奨: CPU使用率が10%となる最悪のケースを想定し、実際のワークロードを監視してください。 ネットワーク ベンダーの要件を確認してください。最小10GbEのNICを採用し、ストレージトラフィックや管理(例: vMotion)などに複数のNICを使うことを想定してください。 私は自分の苦い経験から、クラスターの最適な運用にはエンタープライズクラスのネットワークスイッチが必要であることを皆さんに伝えることができます。結局、可用性を確保するためにどんな書き込みもネットワーク経由で同期的に送信されるからです。 推奨: ストレージトラフィック用に最低10GbEの帯域幅を持つネットワークスイッチを使用してください。 ベストプラクティスに従い、ホストごとに複数のNICを用意してください。 フラッシュストレージの概要 HCIにはフラッシュストレージが必須であるため、フラッシュストレージの現状と今後の展望を確認することをお勧めします。 HCIを使用するかどうかにかかわらず、現時点でフラッシュを搭載したストレージを使用してアプリケーションを導入していないのであれば、次に購入するストレージにはフラッシュが搭載されている可能性があります。 ストレージの現状と未来 一般的に導入されているストレージソリューションの機能を確認し、用語を明確にしましょう。 回転式ディスク 昔ながらの SASまたはSATAインターフェースを備えた7,200回転、10,000回転、または15,000回転の回転式物理ディスクです。 ディスクあたりのIOPSは低いです。 このディスクは大容量にできますが、GBあたりのIOPSは減少します。 パフォーマンスを確保するため、通常は複数のディスクにデータを分散して「十分な」IOPSと大容量を実現します。 SSDディスク - SATAおよびSAS 現在、フラッシュは一般的にNANDフラッシュを使用する、SASまたはSATAインターフェースを持つSSDとして導入されています。 また、SSDにはいくらかのDRAMが書き込み可能なバッファメモリとして搭載されています。 エンタープライズSSDには停電保護機能が搭載されており、停電時にはDRAMの内容がNANDに書き込まれます。 SSDディスク - NVMe SSDディスクと同様ですが、NVMeプロトコル(SASまたはSATAではない)とNANDフラッシュを使用します。 NVMeメディアはPCI Express(PCIe)バス経由で接続されるため、システムはホストバスアダプターやストレージファブリックのオーバーヘッドなしで直接通信でき、レイテンシが大幅に短縮されます。 ストレージアレイ エンタープライズアレイは保護機能と拡張機能を提供します。 現在、ストレージの構成はハイブリッドアレイまたはオールフラッシュが一般的です。 ハイブリッドアレイにはNANDフラッシュのキャッシュ層のほか、7,200回転、10,000回転、または15,000回転の回転式ディスクを使用する1つ以上のキャパシティ層が含まれています。 NVMeアレイも利用できるようになっています。 ブロックモードNVDIMM このデバイスは出荷が始まったばかりであり、非常に低いレイテンシが必要な場合に使用されます。 NVDIMMはDDRメモリソケットに装着され、レイテンシは約30ナノ秒です。 現在は8GBモジュールで出荷されているため、レガシーなデータベースアプリケーションには使用されない可能性が高いですが、新しいスケールアウトアプリケーションではこのパフォーマンスを利用できます。 3D XPoint これは未来の技術であり、2016年11月時点では利用できません。 MicronおよびIntelによって開発されました。 また、Optane(Intel)およびQuantX(Micron)としても知られています。 少なくとも2017年までは利用できませんが、NANDと比較して容量が大きく、IOPSが10倍以上、レイテンシが10倍以上低くなるため、非常に高い耐久性と一貫したパフォーマンスが約束されます。 最初はNVMeプロトコルが採用される予定です。 SSDデバイスの耐久性 キャッシュ層とキャパシティ層のドライブを選択する際は、SSDデバイスの耐久性を考慮することが重要です。 フラッシュストレージの寿命は有限です。 SSDフラッシュのセルは、決まった回数だけ削除および再書き込みできます(読み取りに関しては制限はありません)。 デバイスのファームウェアはSSDの寿命を最大化するため、ドライブ全体に書き込みを分散するように調整します。 また、エンタープライズSSDは通常見た目よりも実際にはフラッシュの容量が大きいため(オーバープロビジョニング)、800 GBのドライブには1 TBを超えるフラッシュが搭載されている場合があります。 ストレージベンダーと話し合うために注目すべき指標は、一定年数で保証されている1日あたりのドライブ全体の書き換え可能回数(Drive Writes Per Day, DWPD)です。 例えば、1 DWPD(5年対応)の800 GB SSDは、1日に800 GBを5年間書き込むことができます。 したがって、DWPD(および年数)が多いほど耐久性が高くなります。 測定基準の計算方法を変え、指定されたSSDデバイスをテラバイト書き込み量(Terabytes Written, TBW)で表すこともできます。同じ例のTBWは、1,460 TB(800GB * 365日 * 5年)です。 どちらの方法でも、予想されるIOに基づいてSSDの寿命を知ることができます。 要約 この記事では、HCI、特にVMWare vSANバージョン6.5を導入する際に考慮すべき最も重要な機能について説明しました。 説明していないvSANの機能もあります。ここで言及していない機能については、デフォルト値を使用すべきだと考えてください。 ただし、ご質問やご意見がありましたら、コメントセクションで回答いたします。 今後の投稿ではHCIに戻る予定です。HCIは確実に発展していくアーキテクチャであるため、HCIに導入するインターシステムズのお客様が増えることを期待しています。
お知らせ
Mihoko Iijima · 2025年3月10日

★受賞者発表!★InterSystems 技術文書ライティングコンテスト 2025(USコミュニティ)

開発者の皆さん、こんにちは! 25人のコミュニティメンバーが参加した InterSystems 技術文書ライティングコンテスト(USコミュニティ) ですが、なんと! 🌟 38 の素晴らしい記事 🌟の投稿がありました! コンテストは、革新性と専門知識をもった参加者による非常に質の高い記事が数多く投稿されたため、審査員によるベスト 3 の選出は簡単ではありませんでした。 それでは、受賞作品を発表します! ⭐️ Expert Awards – InterSystems の審査員よる選出: 🥇 1位: Creating FHIR responses with IRIS Interoperability production by @Laura.BlázquezGarcía 🥈 2位: Monitoring InterSystems IRIS with Prometheus and Grafana by @Stav 🥉 3位: SQLAchemy-iris with the latest version Python driver by @Dmitry.Maslennikov ⭐️ Community Award – コミュニティメンバーによる選出 🏆 Generation of OpenAPI Specifications by @Alessandra.Carena そして... ⭐️ コンテストに 8 記事の投稿をしてくださった方がいます! @Julio.Esquerdo 技術文書ライティングコンテスト #6 に参加してくださった投稿者の皆様をご紹介します! @Robert.Cemper1003 @Stav @Aleksandr.Kolesov @Alessandra.Carena @Dmitry.Maslennikov @André.DienesFriedrich @Ashok.Kumar @Julio.Esquerdo @Andre.LarsenBarbosa @Yuri.Marx @sween @Eric.Fortenberry @Jinyao @Laura.BlázquezGarcía @Corentin.Blondeau @Rob.Tweed @Timothy.Scott @Muhammad.Waseem @Robert.Barbiaux @rahulsinghal @Alice.Heiman @Roy.Leonov @Parani.K @Suze.vanAdrichem @Sanjib.Pandey9191 開発者コミュニティに素晴らしい記事を投稿してくださりありがとうございした! 賞品は現在製作中です。発送準備が整い次第、参加者の皆様にご連絡いたします。
記事
Shintaro Kaminaka · 2020年7月3日

InterSystems IRIS Open Authorization Framework(OAuth 2.0)の実装 - パート1

この記事と後続の2つの連載記事は、InterSystems製品ベースのアプリケーションでOAuth 2.0フレームワーク(簡略化のためにOAUTHとも呼ばれます)を使用する必要のある開発者またはシステム管理者向けのユーザーガイドを対象としています。 作成者:Daniel Kutac(InterSystemsシニアセールスエンジニア) 公開後の修正および変更の履歴 2016年8月3日 - 新しいバージョンのページを反映するため、Googleのクライアント設定のスクリーンショットを修正し、Google APIのスクリーンショットを更新しました。 2016年8月28日 - Cache 2016.2でのJSON対応への変更を反映するため、JSON関連コードを変更しました。 2017年5月3日 - Cache 2017.1でリリースされた新しいUIと機能を反映するため、テキストと画面を更新しました。 2018年2月19日 - 最新の開発内容を反映するために、CachéをInterSystems IRISに変更しました。 製品名は変更されていますが、この記事はすべてのInterSystems製品(InterSystems IRIS Data Platform、Ensemble、Caché)を対象としています。 パート1. クライアント 概要 これは、3部構成のInterSystemsによるOpen Authorization Frameworkの実装に関する連載記事の最初の記事です。 この最初のパートでは、このトピックについて簡単に紹介し、InterSystems IRISアプリケーションが認証サーバーのクライアントとして機能し、保護されたリソースを要求する簡単なシナリオを示します。 パート2ではより複雑なシナリオについて説明します。そこではInterSystems IRIS自体が認証サーバーとして機能するほか、OpenID Connectを介した認証サーバーとしても機能します。 このシリーズの最後のパートでは、OAUTHフレームワーククラスの個々の部分について説明します。それらはInterSystems IRISにより実装されているからです。 Open Authorization Framework[1]とは 皆さんの多くはすでにOpen Authorization Frameworkとその使用目的について聞いたことがあるかと思います。 そのため、この記事では初めて同フレームワークを耳にした方のために簡単な要約を掲載します。 現在はバージョン2.0であるOpen Authorization Framework(OAUTH)は、クライアント(データを要求するアプリケーション)とリソース所有者(要求されたデータを保持するアプリケーション)の間に間接的な信頼を確立することにより、主にWebベースのアプリケーションが安全な方法で情報を交換できるようにするプロトコルです。 この信頼自体は、クライアントとリソースサーバーの両方が認識して信頼する機関によって提供されます。 この機関は認証サーバーと呼ばれます。 次の事例を使用して簡単に説明します。 Jenny(OAUTH用語ではリソース所有者)がJennyCorp社のプロジェクトに取り組んでいるとします。 彼女はより大きな潜在的なビジネスのプロジェクト計画を作成し、JohnInc社のビジネスパートナーであるJohn(クライアントユーザー)にドキュメントのレビューを依頼します。 ただし、彼女はジョンに自社のVPNへのアクセスを許可することを快く思っていないので、ドキュメントをGoogleドライブ(リソースサーバー)または他の同様のクラウドストレージに置いています。 そうすることで、彼女は彼女とGoogle(認証サーバー)の間に信頼関係を確立していました。 彼女はJohnと共有するドキュメントを選びます(JohnはすでにGoogleドライブを使用しており、Jennyは彼のメールアドレスを知っています)。 Johnはドキュメントを閲覧したいときには自分のGoogleアカウントで認証し、モバイルデバイス(タブレットやノートパソコンなど)からドキュメントエディタ(クライアントサーバー)を起動し、Jennyのプロジェクトファイルを読み込みます。 とてもシンプルに聞こえますが、2人とGoogleの間には多くの通信が発生しています。 どの通信もOAuth 2.0仕様に準拠しているため、Johnのクライアント(リーダーアプリケーション)は最初にGoogleで認証する必要があります(OAUTHはこのステップに対応していません)。ジョンがGoogleが提供するフォームにJohnが同意して認証すると、Googleはアクセストークンを発行し、リーダーアプリケーションにドキュメントへのアクセスを許可します。 リーダーアプリケーションはアクセストークンを使用してGoogleドライブにリクエストを発行し、Jennyのファイルを取得します。 以下の図に、個々の当事者間の通信を示しています。 注意: どのOAUTH 2.0通信もHTTPリクエストを使用していますが、サーバーは必ずしもWebアプリケーションである必要はありません。 InterSystems IRISを使ってこの簡単なシナリオを説明しましょう。 簡単なGoogleドライブのデモ このデモでは、私たち自身のアカウントを使ってGoogleドライブに保存されているリソース(ファイルのリスト)をリクエストする小さなCSPベースのアプリケーションを作成します(ついでにカレンダーのリストも取得します)。 前提条件 アプリケーションのコーディングを始める前に、環境を準備する必要があります。 この環境には、SSLが有効になっているWebサーバーとGoogleのプロファイルが含まれます。 Webサーバーの構成 上記のように、認証サーバーとはSSLを使用して通信する必要があります。これは、OAuth 2.0がデフォルトでSSLを要求するためです。 データを安全に保つためには必要なことですよね? この記事ではSSLをサポートするようにWebサーバーを構成する方法は説明しませんので、お好みの各Webサーバーのユーザーマニュアルを参照してください。 皆さんの好奇心をそそるため、この詳細な例ではMicrosoft IISサーバーを使用します(後でいくつかのスクリーンショットを掲載するかもしれません)。 Googleの構成 Googleに登録するには、Google API Manager(https://console.developers.google.com/apis/library?project=globalsummit2016demo)を使用する必要があります デモのために、GlobalSummit2016Demoというアカウントを作成しました。 Drive APIが有効になっていることを確認してください。 次に、認証情報を定義します。 次の点に注意してください。 承認済みのJavaScript生成元 – 呼び出し元のページに対し、ローカルで作成されたスクリプトのみを許可します。 承認済みのリダイレクトURI – 理論上はクライアントアプリケーションを任意のサイトにリダイレクトできますが、InterSystems IRISのOAUTH実装を使用する場合は https://localhost/csp/sys/oauth2/OAuth2.Response.cls にリダイレクトする必要があります。 スクリーンショットのように複数の承認済みのリダイレクトURIを定義できますが、このデモでは2つのうち2番目のエントリのみが必要です。 最後に、InterSystems IRISをGoogle認証サーバーのクライアントとして構成する必要があります。 Cachéの構成 InterSystems IRIS OAUTH2クライアントの構成は2段階で行われます。 まず、サーバー構成を作成する必要があります。 SMPで、System Administration(システム管理) > Security(セキュリティ) > OAuth 2.0 > Client Configurations(クライアント構成)を開きます。 「サーバー構成の作成」ボタンをクリックし、フォームに入力して保存します。 フォームに入力したすべての情報は、Google Developers Consoleのサイトで確認できます。 InterSystems IRISはOpen IDの自動検出に対応しています。 ただし、ここでは自動検出を使用せず、すべての情報を手動で入力しました。 次に、新しく作成された発行者エンドポイントの横にある「Client Configurations」(クライアント構成)リンクをクリックし、「Create Client Configuration」(クライアント構成を作成する)ボタンをクリックします。 「Client Information」(クライアント情報)タブと「JWT Settings」(JWT設定)タブは空のままにし(デフォルト値を使用します)、クライアントの認証情報を入力してください。 注意:ここでは、Confidential Clientを作成しています。これはPublic Clientよりも安全であり、クライアントシークレットがクライアントサーバーアプリケーションを離れることはありません(ブラウザに送信されません)。 また、「Use SSL/TLS」(SSL/TLSを使用する)がチェックされ、ホスト名(ここではクライアントアプリケーションにローカルにリダイレクトしているため、localhostにします)が入力され、さらにはポートとプレフィックスが入力されていることを確認してください(これは同じマシンに複数のInterSystems IRISがある場合に役立ちます)。 入力した情報に基づいてクライアントリダイレクトURLが生成され、上の行に表示されます。 上のスクリーンショットでは、GOOGLEという名前のSSL構成を選択しました。 この名前自体は、多くのSSL構成のうちどれをこの特定の通信チャネルで使用するかを決定するためにのみ使用されます。 CachéはSSL/TLS構成を使用し、サーバー(この場合はGoogle OAuth 2.0 URI)との安全なトラフィックを確立するために必要なすべての情報を保存しています。 より詳細な説明については、ドキュメントを参照してください。 Googleの認証情報定義フォームから取得したクライアントIDとクライアントシークレットの値を入力します(手動構成を使用する場合)。 これですべての構成ステップが完了し、CSPアプリケーションのコーディングに進むことができます。 クライアントアプリケーション クライアントアプリケーションは、シンプルなWebベースのCSPアプリケーションです。 そのため、Webサーバーによって定義および実行されるサーバー側のソースコードと、Webブラウザによってユーザーに公開されるユーザーインターフェイスで構成されています。 クライアントサーバー クライアントサーバーは単純な2ページのアプリケーションです。 アプリケーション内では次の処理を実行します。 ·        Google認証サーバーのリダイレクトURLを組み立てます。 ·        Google Drive APIおよびGoogle Calendar APIへのリクエストを実行し、結果を表示します。 ページ1 これはアプリケーションの1ページであり、そのリソースについてGoogleを呼び出すことにしました。 以下はこのページを表す最小限の、しかし完全に動作するコードです。 Class Web.OAUTH2.Google1N Extends %CSP.Page { Parameter OAUTH2CLIENTREDIRECTURI = "https://localhost/csp/google/Web.OAUTH2.Google2N.cls"; Parameter OAUTH2APPNAME = "Google"; ClassMethod OnPage() As %Status { &html<<html> <head> </head> <body style="text-align: center;"> <!-- ページの内容をここに挿入します --> <h1>Google OAuth2 API</h1> <p>このページのデモでは、OAuth2認証を使用してGoogle API関数を呼び出す方法を示しています。 <p>ユーザーとユーザーのGoogleドライブのファイル、およびカレンダーエントリに関する情報を取得します。 > // Googleで認証するにはopenidのスコープを指定する必要があります set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" set properties("approval_prompt")="force" set properties("include_granted_scopes")="true" set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(..#OAUTH2APPNAME,scope, ..#OAUTH2CLIENTREDIRECTURI,.properties,.isAuthorized,.sc) w !,"<p><a href='"_url_"'><img border='0' alt='Googleサインイン' src='images/google-signin-button.png' ></a>" &html<</body> </html>> Quit $$$OK } ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ] { #dim %response as %CSP.Response set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) { set %response.ServerSideRedirect="Web.OAUTH2.Google2N.cls" } quit 1 } } 以下にこのコードの簡単な説明を記します。 1.     OnPreHTTPメソッド - まず、すでに有効なアクセストークンをGoogleの認証結果として取得しているかどうかを確認します。この認証は、例えば単にページを更新したときに発生する可能性があります。 トークンを取得できていない場合は、認証する必要があります。 トークンを取得できている場合は、結果表示ページにページをリダイレクトするだけです。 2.      OnPageメソッド - 有効なアクセストークンがない場合にのみここに到達します。その場合、認証してGoogleに対する権限を付与し、アクセストークンを付与してもらうために通信を開始しなければなりません。 3.      Google認証ダイアログの動作を変更するスコープ文字列とプロパティ配列を定義します(私たちのIDに基づいて認証する前に、Googleに対して認証する必要があります)。 4.      最後にGoogleのログインページのURLを受け取り、それをユーザーに提示してから同意ページを表示します。 追加の注意事項: ここでは実際のリダイレクトページを https://www.localhost/csp/google/Web.OAUTH2.Google2N.cls(OAUTH2CLIENTREDIRECTURIパラメータ内)で指定しています。 ただし、Google認証情報の定義ではInterSystems IRIS OAUTH Frameworkのシステムページを使用しています。 リダイレクトは、OAUTHハンドラークラスによって内部的に処理されます。 ページ2 このページにはGoogle認証の結果が表示されます。成功した場合はGoogleのAPIコールを呼び出してデータを取得します。 繰り返しになりますが、このコードは最小限でも完全に機能します。 受信データがどのような構造で表示されるかは、皆さんのご想像にお任せします。 Include %occInclude Class Web.OAUTH2.Google2N Extends %CSP.ページ { Parameter OAUTH2APPNAME = "Google"; Parameter OAUTH2ROOT = "https://www.googleapis.com"; ClassMethod OnPage() As %Status { &html<<html> <head> </head> <body>> // アクセストークンがあるかどうかを確認します set scope="openid https://www.googleapis.com/auth/userinfo.email "_ "https://www.googleapis.com/auth/userinfo.profile "_ "https://www.googleapis.com/auth/drive.metadata.readonly "_ "https://www.googleapis.com/auth/calendar.readonly" set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) if isAuthorized { // Googleにはイントロスペクションエンドポイントがありませんので、呼び出す必要はありません。イントロスペクションエンドポイントと表示結果については、RFC 7662を参照してください。 w "<h3><span style='color:red;'>GetUserInfo API</span>からのデータ</h3>" // userinfoには専用のAPIがありますが、Get() メソッドを適切なURLで呼び出すだけでも取得できます。 try { set tHttpRequest=##class(%Net.HttpRequest).%New() $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME)) $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).GetUserinfo(..#OAUTH2APPNAME,accessToken,,.jsonObject)) w jsonObject.%ToJSON() } catch (e) { w "<h3><span style='color: red;'>エラー: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>" } /****************************************** * * * 他のAPIから情報を取得する * * * ******************************************/ w "<hr>" do ..RetrieveAPIInfo("/drive/v3/files") do ..RetrieveAPIInfo("/calendar/v3/users/me/calendarList") } else { w "<h1>認証されていません!</h1>" } &html<</body> </html>> Quit $$$OK } ClassMethod RetrieveAPIInfo(api As %String) { w "<h3><span style='color:red;'>"_api_"</span>からのデータ</h3><p>" try { set tHttpRequest=##class(%Net.HttpRequest).%New() $$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME)) $$$THROWONERROR(sc,tHttpRequest.Get(..#OAUTH2ROOT_api)) set tHttpResponse=tHttpRequest.HttpResponse s tJSONString=tHttpResponse.Data.Read() if $e(tJSONString)'="{" { // JSONではない d tHttpResponse.OutputToDevice() } else { w tJSONString w "<hr/>" /* // 新しいJSON API &html<<table border=1 style='border-collapse: collapse'>> s tJSONObject={}.%FromJSON(tJSONString) set iterator=tJSONObject.%GetIterator() while iterator.%GetNext(.key,.value) { if $isobject(value) { set iterator1=value.%GetIterator() w "<tr><td>",key,"</td><td><table border=1 style='border-collapse: collapse'>" while iterator1.%GetNext(.key1,.value1) { if $isobject(value1) { set iterator2=value1.%GetIterator() w "<tr><td>",key1,"</td><td><table border=0 style='border-collapse: collapse'>" while iterator2.%GetNext(.key2,.value2) { write !, "<tr><td>",key2, "</td><td>",value2,"</td></tr>" } // このようにして埋め込みオブジェクト/配列をどんどん進めていきます w "</table></td></tr>" } else { write !, "<tr><td>",key1, "</td><td>",value1,"</td></tr>" } } w "</table></td></tr>" } else { write !, "<tr><td>",key, "</td><td>",value,"</td></tr>" } } &html<</table><hr/> > */ } } catch (e) { w "<h3><span style='color: red;'>エラー: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>" } } }     コードを簡単に見てみましょう。 1.      まず、有効なアクセストークンがあるかどうかを確認する必要があります(そのため、認証を受けました)。 2.      トークンがある場合はGoogleが提供し、発行されたアクセストークンがカバーするAPIにリクエストを発行できます。 3.       そのためには標準の %Net.HttpRequest クラスを使用しますが、呼び出されたAPIの仕様に従ってGETメソッドまたはPOSTメソッドにアクセストークンを追加します。 4.       ご覧のように、OAUTHフレームワークには GetUserInfo() メソッドが実装されていますが、RetrieveAPIInfo() ヘルパーメソッドの場合と同様に、Google APIの仕様を利用して直接ユーザー情報を取得できます。 5.       OAUTHの世界ではJSON形式でデータを交換するのが一般的であるため、ここでは受信データを読み取り、それを単にブラウザに出力してます。 受信データを解析して整形し、それをユーザーが理解できる形で表示できるようにするのはアプリケーション開発者の責任です。 しかし、それはこのデモの範囲を超えています。 (ただし、いくつかのコメントアウトされたコードで構文解析のやり方を示しています。) 以下は、未加工のJSONデータが表示された出力のスクリーンショットです。 パート2に進んでください。そこでは、認証サーバーおよびOpenID Connectプロバイダーとして機能するInterSystems IRISについて説明します。   [1] https://tools.ietf.org/html/rfc6749、https://tools.ietf.org/html/rfc6750
記事
Shintaro Kaminaka · 2020年8月26日

InterSystems IRIS Open Authorization Framework(OAuth 2.0)の実装 - パート3

作成者:Daniel Kutac(InterSystems セールスエンジニア)   パート 3. 付録 ## InterSystems IRIS OAUTH クラスの説明 この連載の[前のパート](https://jp.community.intersystems.com/node/480201)では、InterSystems IRIS を OAUTH クライアントおよび認可/認証サーバー(OpenID Connect を使用)として機能するように構成する方法について学びました。 この連載の最後のパートでは、InterSystems IRIS OAuth 2.0 フレームワークを実装するクラスについて説明します。 また、一部の API クラスのメソッドの使用例についても説明します。 OAuth 2.0 を実装する API クラスは、目的に応じて 3 種類のグループに分けることができます。 すべてのクラスは %SYS ネームスペースで実装されています。 これらの一部は(% package 経由で)公開されていますが、一部は非公開になっており、開発者が直接呼び出すことはできません。 ### 内部クラス これらのクラスは OAuth2 パッケージに属しています。 次の表に、対象となるクラスの一部を掲載しています(クラスの完全なリストについては、CachéあるいはIRIS インスタンスのオンラインクラスリファレンスを参照してください)。 以下の表に掲載されているものを除き、これらのクラスをアプリケーション開発者が直接使用することはできません。 クラス名 説明 OAuth2.AccessToken Persistent(永続クラス) OAuth2.AccessToken は、OAuth 2.0 アクセストークンとその関連情報を格納します。 これは、アクセストークンの OAUTH クライアントコピーです。OAuth2.AccessToken は、SessionId と ApplicationName の組み合わせによってインデックス化されます。 したがって、SessionId/ApplicationName ごとに 1 つのスコープのみをリクエストできます。 2 回目のリクエストが別のスコープで行われ、アクセストークンがまだ付与されている場合は、新しいリクエストのスコープが予期されるスコープになります。 OAuth2.Client Persistent(永続クラス) OAuth2.Application クラスは OAuth2 クライアントを記述し、RFC 6749 に基づいてアプリケーションを認可するために使用する認可サーバーを参照します。 クライアントシステムは、さまざまなアプリケーションで複数の認可サーバーと共に使用できます。 OAuth2.Response CSPページ これは、InterSystems IRIS OAuth 2.0 クライアントコードから使用される OAuth 2.0 認可サーバーからの応答用のランディングページです。 応答はここで処理され、最終的なターゲットにリダイレクトされます。 OAuth2.ServerDefinition Persistent(永続クラス) OAUTH クライアント(この InterSystems IRIS インスタンス)が使用する認可サーバーの情報が格納されています。 認可サーバーの定義ごとに複数のクライアント構成を定義できます。 OAuth2.Server.AccessToken Persistent(永続クラス) アクセストークンは OAUTH サーバーの OAuth2.Server.AccessToken によって管理されます。 このクラスには、アクセストークンと関連プロパティが格納されます。 このクラスは、認可サーバーのさまざまな要素間の通信手段でもあります。 OAuth2.Server.Auth CSP ページ 認可サーバーは、RFC 6749 で指定されている認可コードおよびインプリシットグラントタイプの認可制御フローをサポートします。 OAuth2.Server.Auth クラスは認可エンドポイントとして機能し、RFC 6749 に従ってフローを制御する %CSP.Page のサブクラスです。 OAuth2.Server.Client Persistent(永続クラス) OAuth2.Server.Configuration は、この認可サーバーに登録したクライアントを記述する永続クラスです。 OAuth2.Server.Configuration Persistent(永続クラス) 認可サーバーの構成が格納されます。 すべての構成クラスには、ユーザーが構成の詳細を入力するためのシステム管理ポータルページがそれぞれ存在します。 OAuth2.Client、OAuth2.ServerDefinition、OAuth2.Server.Client、OAuth2.Configuration の各オブジェクトは UI を使用せずに開き、変更し、作成または変更した構成を保存できます。 これらのクラスを使用し、構成をプログラムで操作できます。 ### サーバーカスタマイズ用クラス これらのクラスは %OAuth2 パッケージに属しています。 このパッケージには、一連の内部クラス(ユーティリティ)が含まれています。ここでは、開発者が使用できるクラスについてのみ説明します。 これらのクラスは、OAuth 2.0 Server Configuration(サーバー構成)ページで参照されます。 %OAuth2.Server.Authenticate CSPページ %OAuth2.Server.Authenticate はデフォルトの Authenticate クラスだけでなく、ユーザーが作成したすべての Authenticate クラスのサブクラスとして機能します。 Authenticate クラスは、ユーザーを認証するために OAuth2.Server.Auth の認可エンドポイントによって使用されます。 このクラスを使用すると、認証プロセスをカスタマイズできます。次のメソッドを OAuth2.Server のデフォルトをオーバーライドするために実装できます。 DirectLogin– ログインページを表示しない場合にのみ使用します。 DisplayLogin – 認可サーバーのログインフォームを実装します。 DisplayPermissions – リクエストされたスコープのリストを使用してフォームを実装します。CSS を変更することで、外観や操作性をさらにカスタマイズできます。 CSS スタイルは DrawStyle メソッドで定義されます。loginForm は DisplayLogin フォーム用です。permissionForm は DisplayPermissions フォーム用です。 %OAuth2.Server.Validate CSP ページ これは、サーバーに含まれているデフォルトの Validate User Class(ユーザー検証クラス)です。 デフォルトのクラスは、認証サーバーが配置されている Cache またはIRISインスタンスのユーザーデータベースを使用してユーザーを検証します。 サポートされるプロパティは、issuer(Issuer)、role、sub(Username)です。Validate User Class は Authorization Server Configuration(認可サーバーの構成)で指定されます。 ユーザー名とパスワードの組み合わせを検証し、ユーザーに関連付けられたプロパティ一式を返す ValidateUser メソッドを含める必要があります。 %OAuth2.Server.Generate Registered Object(登録オブジェクト) %OAuth2.Server.Generate は、サーバーに含まれているデフォルトのGenerate Token Class(トークン生成クラス)です。 デフォルトのクラスは、ランダムな文字列を opaque アクセストークンとして生成します。Generate Token Class は、Authorization Server Configuration で指定されます。 ValidateUser メソッドによって返されるプロパティの配列に基づいてアクセストークンを生成するために使用される GenerateAccessToken メソッドを含める必要があります。 %OAuth2.Server.JWT Registered Object(登録オブジェクト) %OAuth2.Server.JWT は、サーバーに含まれている JSON Web トークンを作成する Generate Token Class です。 Generate Token Class は、Authorization Server Configuration で指定されます。 ValidateUser メソッドによって返されるプロパティの配列に基づいてアクセストークンを生成するために使用される GenerateAccessToken メソッドを含める必要があります。 %OAuth2.Utils Registered Object(登録オブジェクト) このクラスは、さまざまなエンティティのログの記録を実装します。 カスタマイズの章のサンプルコードに、可能な使用法を示しています。 次の画像に、OAuth 2.0 認可サーバー構成の対応するセクションを示します。 ![](/sites/default/files/inline/images/1_7.png) OpenID Connect を JWT 形式の識別情報トークン(id_token)と共に使用する場合は、構成内のデフォルトの Generate Token Class である _%OAuth2.Server.Generate_ を _%OAuth2.Server.JWT_ に置換してください。それ以外の場合は、デフォルトの Generate クラスのままにしてください。 カスタマイズオプションについては、後で別の章で詳しく説明します。 ### 公開 API クラス 公開 API クラスは、アプリケーション開発者が Web アプリケーションのメッセージフローに正しい値を渡し、アクセストークンの検証やイントロスペクションなどを実行するために使用されます。 これらのクラスは %SYS.OAuth2 パッケージで実装されています。 次の表に、実装されているクラスの一部を掲載しています。 %SYS.OAuth2.AccessToken Registered Object(登録オブジェクト) %SYS.OAuth2.AccessToken クラスは、リソースサーバーへの認証にアクセストークンを使用できるようにするクライアント操作を定義します。基本となるトークンは、CACHESYS データベースの OAuth2.AccessToken に格納されます。 OAuth2.AccessToken は、SessionId と ApplicationName の組み合わせによってインデックス化されます。 したがって、SessionId/ApplicationName ごとに 1 つのスコープのみをリクエストできます。 2 回目のリクエストが別のスコープで行われ、アクセストークンがまだ付与されている場合は、新しいリクエストのスコープが予期されるスコープになります。 %SYS.OAuth2.Authorization Registered Object(登録オブジェクト) %SYS.OAuth2.Authorization クラスには、アクセストークンを取得してクライアントを認可するために使用される操作が含まれています。基本となるトークンは、CACHESYS データベースの OAuth2.AccessToken に格納されます。 OAuth2.AccessToken は、SessionId と ApplicationName の組み合わせによってインデックス化されます。 したがって、SessionId/ApplicationName ごとに 1 つのスコープのみをリクエストできます。 2 回目のリクエストが別のスコープで行われ、アクセストークンがまだ付与されている場合は、新しいリクエストのスコープが予期されるスコープになります。このクラスは CACHELIB にあるため、どこでも使用できることに注意してください。 ただし、トークンストレージは CACHESYS にあるため、ほとんどのコードでは直接使用できません。 %SYS.OAuth2.Validation Registered Object(登録オブジェクト) %SYS.OAuth2.Validation クラスは、アクセストークンの検証(または無効化)に使用されるメソッドを定義します。 基本となるトークンは、CACHESYS データベースの OAuth2.AccessToken に格納されます。 OAuth2.AccessToken は、SessionId と ApplicationName の組み合わせによってインデックス化されます。 したがって、SessionId/ApplicationName ごとに 1 つのスコープのみをリクエストできます。 2 回目のリクエストが別のスコープで行われ、アクセストークンがまだ付与されている場合は、新しいリクエストのスコープが予期されるスコープになります。 このグループのいくつかのメソッドとクラスを詳しく見てみましょう。 アクセストークンを使用するすべてのクライアントアプリケーションクラスは、その有効性をチェックする必要があります。 このチェックは、OnPage メソッド(または ZEN か ZENMojo ページの対応するメソッド)のどこかで実行されます。 こちらがそのコードスニペットです。 // OAuth2 サーバーからのアクセストークンがあるかどうかをチェックします。 set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,"scope1, scope2",.accessToken,.idtoken,.responseProperties,.error) // アクセストークンがあるかどうかをさらにチェックします。 // 以下は実行可能なすべてのテストであり、あらゆるケースで必要なわけではありません。 // 各テストで返される JSON オブジェクトが単に表示されています。 if isAuthorized { // 何らかの処理を実行します – リソースサーバーの API を呼び出して目的のデータを取得します。 } リソースサーバーの API を呼び出すたびに、アクセストークンを提供する必要があります。 この処理は、_%SYS.OAuth2.AccessToken_ メソッドの **AddAccessToken** メソッドによって実行されます。こちらがそのコードスニペットです。 set httpRequest=##class(%Net.HttpRequest).%New() // AddAccessToken は現在のアクセストークンをリクエストに追加します。 set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken( httpRequest,, ..#SSLCONFIG, ..#OAUTH2APPNAME) if $$$ISOK(sc) { set sc=httpRequest.Get(.. Service API url …) } この連載の前のパートで提供したサンプルコードでは、最初のアプリケーションページ(Cache1N)の OnPreHTTP メソッドでこのコードを確認することができました。 このコードは、アプリケーションの最初のページでアクセストークンチェックを実行するのに最適な場所です。 ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ] { set scope="openid profile scope1 scope2" #dim %response as %CSP.Response if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,, scope,.accessToken,.idtoken,.responseProperties,.error) { set %response.ServerSideRedirect="Web.OAUTH2.Cache2N.cls" } quit 1 } 上記のコードにある _SYS.OAuth2.AccessToken_ クラスの **IsAuthorized** メソッドは有効なアクセストークンが存在するかどうかをチェックし、存在しない場合に認可サーバーの認証フォームを指すログインボタン/リンクを使用してページの内容を表示し、存在する場合に実際にデータ取得処理を実行する 2 番目のページにリダイレクトします。 ただし、このコードは次のように変更できます。 ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ] { set scope="openid profile scope1 scope2" set sc=##class(%SYS.OAuth2.Authorization).GetAccessTokenAuthorizationCode( ..#OAUTH2APPNAME,scope,..#OAUTH2CLIENTREDIRECTURI,.properties) quit +sc } この場合は、結果が異なります。 _%SYS.OAuth2.Authorization_ クラスの **GetAccessTokenAuthorizationCode** メソッドを使用すると、アプリケーションの最初のページの内容を表示せずに、認可サーバーの認証フォームに直接移動します。 これは、Web アプリケーションがモバイルデバイスのネイティブアプリケーションから呼び出され、一部のユーザー情報がネイティブアプリケーション(ランチャー)によってすでに表示されており、認可サーバーを指すボタンを含む Web ページを表示する必要がない場合に便利です。 署名付き JWT トークンを使用する場合は、その内容を検証する必要があります。 この検証は次のメソッドで実行されます。 set valid=##class(%SYS.OAuth2.Validation).ValidateJWT(applicationName,accessToken,scope,,.jsonObject,.securityParameters,.sc) メソッドパラメーターの詳細については、Class Reference ドキュメントをご覧ください。 ## カスタマイズ OAUTH が認証 / 承認 UI のカスタマイズ用に提供しているオプションについてもう少し説明します。 勤務先のポリシーで、スコープの付与に関してより限定的な動作が要求されているとします。 たとえば、取引先銀行内のさまざまな金融システムに接続するホームバンキングアプリケーションを実行できるとしましょう。 銀行は、取得対象の実際の銀行口座に関する情報を含むスコープへのアクセスのみを許可します。 銀行は非常に多くの口座を運営しているため、すべての口座に静的なスコープを定義することは不可能です。 代わりに、認可処理中にその場でスコープを生成する処理をカスタム認可ページのコードに組み込むことができます。 デモを行うため、ここではサーバー構成にもう 1 つのスコープを追加する必要があります。次の画像を参照してください。 ![](/sites/default/files/inline/images/2_2.png) また、%OAuth2.Server.Authenticate.Bank という名前のカスタム Authenticate クラスへの参照も追加しました。 では、銀行の認証クラスはどのようになるのでしょうか? 次は想定されるクラスの例です。 このクラスは、ユーザーが提供するデータを使用して標準の認証フォームと認可フォームを拡張します。 **BeforeAuthenticate**、**DisplayPermissions**、**AfterAuthenticate** の各メソッド間を流れる情報は、_%OAuth2.Server.Properties_ クラスの _properties_ 変数によって渡されます。 Class %OAuth2.Server.Authenticate.Bank Extends %OAuth2.Server.Authenticate { /// account(口座)のスコープに CUSTOM BESTBANK のサポートを追加します。 ClassMethod BeforeAuthenticate(scope As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status { // 起動スコープが指定されていない場合は何もしません。 If 'scope.IsDefined("account") Quit $$$OK // 起動クエリパラメーターから起動コンテキストを取得します。 Set tContext=properties.RequestProperties.GetAt("accno") // コンテキストがない場合は何もしません。 If tContext="" Quit $$$OK try { // ここで BestBank コンテキストを照会する必要があります。 Set tBankAccountNumber=tContext // accno のスコープを追加します。 -> 動的にスコープを変更(account なし:<accno> スコープはサーバー構成に存在します) // この特定のスコープは、それが Cookie サポートを使用して account または account:accno によって // 以前に選択されていた場合に account 経由で同じ accno にアクセスできるようにするために使用されます。 Do scope.SetAt("Access data for account "_tBankAccountNumber,"account:"_tBankAccountNumber) // 処理が終わった account のスコープはもう必要ありません。 // これにより、account スコープが存在することで DisplayPermissions が強制的に呼び出されるのを防ぎます。 Do scope.RemoveAt("account") // AfterAuthenticate が応答プロパティに変換する accno プロパティを追加します。 Do properties.CustomProperties.SetAt(tBankAccountNumber,"account_number") } catch (e) { s ^dk("err",$i(^dk("err")))=e.DisplayString() } Quit $$$OK } /// account のスコープに CUSTOM BESTBANK のサポートを追加します。 /// account_number カスタムプロパティが BeforeAuthenticate(account)または /// DisplayPermissions(account:accno)によって追加された場合は、必要な応答プロパティを追加します。 ClassMethod AfterAuthenticate(scope As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status { // account_number(account)または accno(account:accno)プロパティが存在しない限り、ここで実行することは何もありません。 try { // カスタムログを記録する例 If $$$SysLogLevel>=3 { Do ##class(%OAuth2.Utils).LogServerScope("log ScopeArray-CUSTOM BESTBANK",%token) } If properties.CustomProperties.GetAt("account_number")'="" { // 応答に accno クエリパラメーターを追加します。 Do properties.ResponseProperties.SetAt(properties.CustomProperties.GetAt("account_number"),"accno") } } catch (e) { s ^dk("err",$i(^dk("err")))=e.DisplayString() } Quit $$$OK } /// BEST BANK の account のテキストを含むように変更された DisplayPermissions ClassMethod DisplayPermissions(authorizationCode As %String, scopeArray As %ArrayOfDataTypes, currentScopeArray As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status { Set uilocales = properties.RequestProperties.GetAt("ui_locales") Set tLang = ##class(%OAuth2.Utils).SelectLanguage(uilocales,"%OAuth2Login") // $$$TextHTML(Text,Domain,Language) Set ACCEPTHEADTITLE = $$$TextHTML("OAuth2 Permissions Page","%OAuth2Login",tLang) Set USER = $$$TextHTML("User:","%OAuth2Login",tLang) Set POLICY = $$$TextHTML("Policy","%OAuth2Login",tLang) Set TERM = $$$TextHTML("Terms of service","%OAuth2Login",tLang) Set ACCEPTCAPTION = $$$TextHTML("Accept","%OAuth2Login",tLang) Set CANCELCAPTION = $$$TextHTML("Cancel","%OAuth2Login",tLang) &html<<html>> Do ..DrawAcceptHead(ACCEPTHEADTITLE) Set divClass = "permissionForm" Set logo = properties.ServerProperties.GetAt("logo_uri") Set clientName = properties.ServerProperties.GetAt("client_name") Set clienturi = properties.ServerProperties.GetAt("client_uri") Set policyuri = properties.ServerProperties.GetAt("policy_uri") Set tosuri = properties.ServerProperties.GetAt("tos_uri") Set user = properties.GetClaimValue("preferred_username") If user="" { Set user = properties.GetClaimValue("sub") } &html<<body>> &html<<div id="topLabel"></div>> &html<<div class="#(divClass)#">> If user '= "" { &html< <div> <span id="left" class="userBox">#(USER)#<br>#(##class(%CSP.Page).EscapeHTML(user))#</span> > } If logo '= "" { Set espClientName = ##class(%CSP.Page).EscapeHTML(clientName) &html<<span class="logoClass"><img src="#(logo)#" alt="#(espClientName)#" title="#(espClientName)#" align="middle"></span>> } If policyuri '= "" ! (tosuri '= "") { &html<<span id="right" class="linkBox">> If policyuri '= "" { &html<<a href="#(policyuri)#" target="_blank">#(POLICY)#</a><br>> } If tosuri '= "" { &html<<a href="#(tosuri)#" target="_blank">#(TERM)#</a>> } &html<</span>> } &html<</div>> &html<<form>> Write ##class(%CSP.Page).InsertHiddenField("","AuthorizationCode",authorizationCode),! &html<<div>> If $isobject(scopeArray), scopeArray.Count() > 0 { Set tTitle = $$$TextHTML(" is requesting these permissions:","%OAuth2Login",tLang) &html<<div class="permissionTitleRequest">> If clienturi '= "" { &html<<a href="#(clienturi)#" target="_blank">#(##class(%CSP.Page).EscapeHTML(clientName))#</a>> } Else { &html<#(##class(%CSP.Page).EscapeHTML(clientName))#> } &html<#(##class(%CSP.Page).EscapeHTML(tTitle))#</div>> Set tCount = 0 Set scope = "" For { Set display = scopeArray.GetNext(.scope) If scope = "" Quit Set tCount = tCount + 1 If display = "" Set display = scope Write "<div class='permissionItemRequest'>"_tCount_". "_##class(%CSP.Page).EscapeHTML(display)_"</div>" } } If $isobject(currentScopeArray), currentScopeArray.Count() > 0 { Set tTitle = $$$TextHTML(" already has these permissions:","%OAuth2Login",tLang) &html<<div>> &html<<div class="permissionTitleExisting">> If clienturi '= "" { &html<<a href="#(clienturi)#" target="_blank">#(##class(%CSP.Page).EscapeHTML(clientName))#</a>> } Else { &html<#(##class(%CSP.Page).EscapeHTML(clientName))#> } &html<#(##class(%CSP.Page).EscapeHTML(tTitle))#</div>> Set tCount = 0 Set scope = "" For { Set display = currentScopeArray.GetNext(.scope) If scope = "" Quit Set tCount = tCount + 1 If display = "" Set display = scope Write "<div class='permissionItemExisting'>"_tCount_". "_##class(%CSP.Page).EscapeHTML(display)_"</div>" } &html<</div>> } /*********************************/ /* BEST BANK CUSTOMIZATION */ /*********************************/ try { If properties.CustomProperties.GetAt("account_number")'="" { // Display the account number obtained from account context. Write "<div class='permissionItemRequest'><b>Selected account is "_properties.CustomProperties.GetAt("account_number")_"</b></div>",! // or, alternatively, let user add some more information at this stage (e.g. linked account number) //Write "<div>Account Number: <input type='text' id='accno' name='p_accno' placeholder='accno' autocomplete='off' ></div>",! } } catch (e) { s ^dk("err",$i(^dk("err")))=e.DisplayString() } /* original implementation code continues here... */ &html< <div><input type="submit" id="btnAccept" name="Accept" value="#(ACCEPTCAPTION)#"/></div> <div><input type="submit" id="btnCancel" name="Cancel" value="#(CANCELCAPTION)#"/></div> > &html<</form> </div>> Do ..DrawFooter() &html<</body>> &html<<html>> Quit 1 } /// CUSTOM BESTBANK の場合、入力された患者を検証する必要があります。 /// ! このメソッドの javascript はユーザーに追加データを DisplayPermissions メソッド内 /// で入力させる場合にのみ必要です ! ClassMethod DrawAcceptHead(ACCEPTHEADTITLE) { &html<<head><title>#(ACCEPTHEADTITLE)#</title>> Do ..DrawStyle() &html< <script type="text/javascript"> function doAccept() { var accno = document.getElementById("accno").value; var errors = ""; if (accno !== null) { if (accno.length < 1) { errors = "Please enter account number name"; } } if (errors) { alert(errors); return false; } // submit the form return true; } </script> > &html<</head>> } } ご覧のとおり、%OAuth2.Server.Properties クラスにはいくつかの配列が含まれており、渡されています。 具体的には、以下の配列です。 ·        RequestProperties – 認可リクエストのパラメーターが含まれています。 ·        CustomProperties – 上記のコードの間でデータをやり取りするためのコンテナ。 ·        ResponseProperties – トークンリクエストに対する JSON 応答オブジェクトに追加されるプロパティのコンテナ。 ·        ServerProperties – 認可サーバーがカスタマイズコードに公開する共有プロパティが含まれます(logo_uri、client_uri など…) さらに、認可サーバーが返す必要のあるクレームを指定するのに使用されるいくつかの "claims" プロパティが含まれています。 この認証ページを正しく呼び出すため、最初のクライアントページのコードを次のように変更しました。 set scope="openid profile scope1 scope2 account" // このデータはアプリケーションに由来し(フォームデータなど)、リクエストのコンテキストを設定します。 // ここでは Authenticate クラスをサブクラス化することで、このデータをユーザーに表示することができます。 // それにより、該当ユーザーはアクセスを許可するかどうかを決めることができます。 set properties("accno")="75-452152122-5320" set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint( ..#OAUTH2APPNAME, scope, ..#OAUTH2CLIENTREDIRECTURI, .properties, .isAuthorized, .sc) if $$$ISERR(sc) { write "GetAuthorizationCodeEndpoint Error=" write ..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",! } ご覧のとおり、ここではアプリケーションのさまざまな部分から発生する可能性のあるコンテキスト値を使用して account のスコープとプロパティ配列ノード “accno” を追加しました。 この値はアクセストークンの内部でリソースサーバーに渡され、さらに処理されます。 上記のロジックは、電子的な患者記録を交換するための FHIR 標準で実際に使用されています。 デバッグ OAUTH フレームワークには、デバッグ機能が組み込まれています。 クライアントとサーバー間の通信はすべて暗号化されているため、これは非常に便利です。 デバッグ機能を使用すると、API クラスによって生成されたトラフィックデータをネットワーク経由で送信する前にキャプチャできます。 コードをデバッグするため、以下のコードに従って単純なルーチンまたはクラスを実装できます。 InterSystems IRIS インスタンスのすべての通信でこのコードを実装する必要があることに注意してください! その場合、OAUTH フローのプロセス内での役割を示す名前をファイル名に指定することをお勧めします。 (以下のサンプルコードは rr.mac ルーチンとして保存されていますが、どんな名前を付けるかはあなた次第です。) // d start^rr() start() public { new $namespace set $namespace="%sys" kill ^%ISCLOG set ^%ISCLOG=5 set ^%ISCLOG("Category","OAuth2")=5 set ^%ISCLOG("Category","OAuth2Server")=5 quit } // d stop^rr() stop() public { new $namespace set $namespace="%sys" set ^%ISCLOG=0 set ^%ISCLOG("Category","OAuth2")=0 set ^%ISCLOG("Category","OAuth2Server")=0 quit } // display^rr() display() public { new $namespace set $namespace="%sys" do ##class(%OAuth2.Utils).DisplayLog("c:\temp\oauth2_auth_server.log") quit } 次に、テストを開始する前にターミナルを開き、すべての InterSystems IRIS ノード(クライアント、認可サーバー、リソースサーバー)で d start^rr() を呼び出してください。 完了後、d stop^rr() と d display^rr() を実行してログファイルを読み込んでください。 最後に この連載記事では、InterSystems IRIS OAuth 2.0 の実装を使用する方法を学びました。 パート1では簡単なクライアントアプリケーションのデモを行い、パート2では複雑な例を説明しました。 最後に、OAuth 2.0 の実装で最も重要なクラスについて説明し、ユーザーアプリケーション内でそれらを呼び出す必要がある場合について説明しました。 時々私が投げかけるくだらない質問に我慢強く回答し、この連載をレビューしてくれた Marvin Tener に心から感謝の意を表します。  
記事
Shintaro Kaminaka · 2020年8月20日

InterSystems IRIS Open Authorization Framework(OAuth 2.0)の実装 - パート2

作成者:Daniel Kutac(InterSystems セールスエンジニア) 注意: _*使用されている URL に戸惑っている方のために*。*元の連載記事では、dk-gs2016 と呼ばれるマシンの画面を使用していました。 新しいスクリーンショットは別のマシンから取得されています。 **WIN-U9J96QBJSAG という URL は dk-gs2016*_ であると見なしても構いません。 ### パート2. 認可サーバー、OpenID Connect サーバー この短い連載の[前のパート](https://jp.community.intersystems.com/node/478821)では、OAUTH[[1](#note1)] クライアントとして機能する単純な使用事例について学びました。 今回は私たちの経験をまったく新しいレベルに引き上げましょう。 InterSystems IRIS がすべての OAUTH の役割を果たす、より複雑な環境を構築します。 クライアントの作成方法はすでに分かっていますので、認可サーバーだけでなく、OpenID Connect[[2](#note2)] プロバイダーにも注意を向けましょう。 前のパートと同様に、環境を準備する必要があります。 今回はより多くの変動要素があるため、より注意を要します。 具体例を見る前に、OpenID Connect について少し説明する必要があります。 前のパートの内容を覚えていらっしゃるかと思いますが、Google から認可してもらうため、まずは自身がGoogle で認証を受けることを求められていました。 認証は OAUTH フレームワークには含まれていません。 実際、OAUTH に依存しない多くの認証フレームワークがあります。 そのうちの1つに OpenID と呼ばれるものがあります。 当初は単独の構想で開始されましたが、最近では OAUTH フレームワークによって提供されるインフラストラクチャ、つまり通信とデータ構造が活用されています。 その結果、OpenID Connect が誕生しました。 事実、多くの人がこれを OAUTH の強化版と呼んでいます。 実際、OpenID Connect を使用すると、認可するだけでなく、OAUTH フレームワークのよく知られたインターフェースを使用して認証することもできます。 # 複雑な OpenID Connect のデモ ここでは、パート 1 のクライアントコードの多くを活用します。 そうすることで多くの手間が省けるため、環境のセットアップに集中できます。 ## 前提条件 今回は、SSL が有効になっている既存の Web サーバーに PKI インフラストラクチャを追加する必要があります。 OpenID Connect の要件である暗号化が必要です。 ユーザー認証が必要な場合は、第三者がエージェント(クライアント、認証サーバーなど)になりすますし、ネットワーク経由でユーザーの機密データを送信できないようにする必要があります。 そこで X.509 ベースの暗号化の出番です。 注意 : Cache 2017.1 以降では、X.509 証明書を使用して JWT / JWKS(JSON Web Key Set)を生成する必要はありません。 ここでは下位互換性と単純化を図るためにこのオプションを使用しています。 ### PKI 厳密に言えば、Caché PKI インフラストラクチャを使用する必要はまったくありません。ただし、openssl などのツールを直接使用してすべての証明書を生成するよりは楽です。 詳細は InterSystems IRIS の[ドキュメント](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_pki#GCAS_C157792)や他の場所でも確認できますので、ここでは証明書の生成に関する詳細は触れません。 証明書を生成した結果、3 つの公開鍵/秘密鍵のペアと関連する証明書が作成されます。 これらを次のように呼びます。 * 発行元認証局の root_ca(root_ca.cer) * 認可サーバーおよび OpenID サーバー用の auth(auth.cer および auth.key) * クライアントアプリケーションサーバー用の client(client.cer および client.key) ### X.509 資格情報 デモ中に交換された JSON Web Token(JWT)に署名して検証できるよう、個々のサーバーで X.509 資格情報を定義する必要があります。 ### 認可サーバーと認証サーバーの構成 ここでは X.509 資格情報の定義方法については詳しく説明せず、AUTHSERVER インスタンスの資格情報のスクリーンショットを示します。 ![](/sites/default/files/inline/images/1_4.png) 画像のように、AUTHSERVER にはその秘密鍵と証明書がありますが、CLIENT に関しては公開鍵を含む証明書しかありません。 ### **クライアントサーバーの構成** 同様に、CLIENT インスタンスで資格情報が定義されています。 ![](/sites/default/files/inline/images/2_0.png) ここで、CLIENT には秘密鍵と証明書がありますが、AUTHSERVER に関しては公開鍵を含む証明書しかありません。 ### **リソースサーバーの構成** このセットアップの例では、RESSERVER インスタンスで X509 資格情報を定義する必要はありません。 ## **OAUTH の構成** この連載のパート 1 で説明した構成と同様に、サーバーを OAUTH 用に構成する必要があります。 まずは、OAUTH 構成全体の中心的なコンポーネントである AUTHSERVER インスタンスから始めましょう。 ### **AUTHSERVER** System Management Portal で、**System Administration > Security > OAuth 2.0 > Server Configuration** を開きます。 メニューのリンクをクリックし、次のフォーム項目に入力します。 * Host name(ホスト名) * Port(ポート、省略可) * Prefix(プレフィックス、省略可) – これら 3 つのフィールドは *Issuer endpoint*(発行者エンドポイント)を構成します。 * 更新トークンを返す条件を指定します。 * Supported grant types(サポートされているグラント種別)をチェックします。このデモでは 4 つの種別すべてにチェックします。 ただし、認可コードのみが使用されます。 * 必要に応じて Audience required(オーディエンスを要求)をチェックします。これにより、*aud* プロパティが認可コードと暗黙的な許可(Implicit)のリクエストに追加されます。 * 必要に応じて Support user session(ユーザーセッションをサポート)をチェックします。これにより、認可サーバーが現在このブラウザを使用しているユーザーのログイン状態を維持するために *httpOnly* Cookie が使用されます。 2 回目以降のアクセストークンの要求では、ユーザー名とパスワードの入力は求められません。 * Endpoint intervals(エンドポイントの間隔)を指定します。 * このサーバーでサポートされるスコープ(Supportted scopes)を定義します。 * Customization Options(カスタマイズオプション)のデフォルト値を受け入れるか、カスタム値を入力します。**注意: **JWT が単なる opaque トークンではなくアクセストークンとして使用されるよう、Generate token class(生成トークンクラス)の値を **%OAuth2.Server.Generate** から **%OAuth2.Server.JWT** に変更してください。 * OAuth 2.0 の要求に従い、HTTP 上の SSL を確立するための登録済み SSL 構成の名前を入力します。 * JSON Web Token(JWT)の設定を入力します。 以下はサンプル構成のスクリーンショットです。 ![](/sites/default/files/inline/images/4_4_1.png) ![](/sites/default/files/inline/images/5_3_0.png) ![](/sites/default/files/inline/images/6_2_1.png) ![](/sites/default/files/inline/images/7_0_1.png) ![](/sites/default/files/inline/images/8_0.png) サーバー構成を定義したら、サーバークライアント構成を入力する必要があります。 サーバー構成フォームのページ内で、「Client Configurations」(クライアント構成)ボタンをクリックし、CLIENT インスタンスおよび RESSERVER インスタンスの「Create New Configuration」(新しい構成の作成)をクリックします。 (IRISを使用して構成している場合は、「クライアントディスクリプション」の構成ページから以下の設定を行います。) 以下の画像は CLIENT の構成を示しています。 ![](/sites/default/files/inline/images/9_0.png) ![](/sites/default/files/inline/images/10_0.png) ![](/sites/default/files/inline/images/11_0.png) JWT Token(JWT トークン)タブはデフォルト値である空のままにします。 ご覧のとおり、実際のアプリケーションの場合とは異なり、フィールドには無意味なデータが入力されています。 同様に、RESSERVER の構成を示します。 ![](/sites/default/files/inline/images/12_0.png) ![](/sites/default/files/inline/images/13_0.png) ご覧のとおり、リソースサーバーに必要なのは非常に基本的な情報だけです。具体的には、Client type(クライアント種別)をリソースサーバーに設定する必要があります。 CLIENT では、より詳細な情報とクライアント種別を入力する必要があります(クライアントはクライアントシークレットをサーバーで維持し、クライアントエージェントには送信しない Web アプリケーションとして動作するため、機密(confidential)を選択します)。 ### **CLIENT** SMP で、**System Administration > Security > OAuth 2.0 > Client Configurations** を開きます。 「Create Server Configuration」(サーバー構成の作成)ボタンをクリックし、フォームに入力して保存します。 ![](/sites/default/files/inline/images/14_0.png) Issuer Endpoint(発行者エンドポイント)が、AUTHSERVER インスタンスで前に定義した値に対応していることを必ず確認してください! また、認可サーバーのエンドポイントを Web サーバーの構成に従って変更する必要があります。 この場合は各入力フィールドに「authserver」を埋め込んだだけです。 次に、新しく作成された発行者エンドポイントの横にある「**Client Configurations**」(クライアント構成)リンクをクリックし、「**Create Client Configuration**」(クライアント構成の作成)ボタンをクリックします。 ![](/sites/default/files/inline/images/15_0.png) ![](/sites/default/files/inline/images/16.png) ![](/sites/default/files/inline/images/17.png) ![](/sites/default/files/inline/images/18.png) 以上です! ここまでの手順で CLIENT と AUTHSERVER の両方を構成しました。 多くの使用事例ではこれだけで十分です。リソースサーバーは単なる AUTHSERVER のネームスペースにすぎず、結果的にすでに保護されている場合があるからです。 しかし、外部の医師が内部の臨床システムからデータを取得しようとしている使用事例に対応する場合を考えてみましょう。 このような医師がデータを取得できるようにするため、この医師のアカウント情報を監査目的と法医学的な理由でリソースサーバー内に確実に保存したいと思います。 この場合は、続けて RESSERVER を定義する必要があります。 ### **RESSERVER** SMP で、**System Administration > Security > OAuth 2.0 > Client Configurations** を開きます。 「**Create Server Configuration**」(サーバー構成の作成)ボタンをクリックし、フォームに入力して保存します。 ![](/sites/default/files/inline/images/19.png) ここでは、Cache 2017.1 に実装された新機能である検出機能を使用しました。 ご覧のように、この構成は CLIENT インスタンスの対応する構成と同じデータを使用しています。 次に、新しく作成された発行者エンドポイントの横にある「**Client Configurations**」(クライアント構成)リンクをクリックし、「**Create Client Configuration**」(クライアント構成の作成)ボタンをクリックします。 ![](/sites/default/files/inline/images/20.png) ![](/sites/default/files/inline/images/21.png) X.509 資格情報から JWT を作成することはお勧めしませんが、ここでは互換性のために使用しました。 ![](/sites/default/files/inline/images/22.png) CLIENTとほとんど同じ手順でしたが、必要なものでした。 しかし、これで先に進んでコーディングできるようになりました! ## **クライアントアプリケーション** 話をできる限り簡単にするため、パート1で説明した Google の例から多くのコードをリサイクルします。 クライアントアプリケーションは /csp/myclient で実行されるわずか 2 つの CSP ページからなるアプリケーションです。セキュリティは強制せず、未認証ユーザーとして実行されます。 ### **ページ 1** Class Web.OAUTH2.Cache1N Extends %CSP.Page { Parameter OAUTH2CLIENTREDIRECTURI = "https://dk-gs2016/client/csp/myclient/Web.OAUTH2.Cache2N.cls"; Parameter OAUTH2APPNAME = "demo client"; ClassMethod OnPage() As %Status { &html // 適切なリダイレクトとスコープを持つ認証エンドポイントの URL を取得します。 // 返された URL は下のボタンで使用されます。 // DK: 'dankut' アカウントを使用して認証します! set scope="openid profile scope1 scope2" set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint( ..#OAUTH2APPNAME, scope, ..#OAUTH2CLIENTREDIRECTURI, .properties, .isAuthorized, .sc) if $$$ISERR(sc) { write "GetAuthorizationCodeEndpoint Error=" write ..EscapeHTML($system.Status.GetErrorText(sc))_"",! } &html< Authorize for ISC > Quit $$$OK } ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ] { #dim %response as %CSP.Response set scope="openid profile scope1 scope2" if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) { set %response.ServerSideRedirect="Web.OAUTH2.Cache2N.cls" } quit 1 } } ### **ページ 2** Class Web.OAUTH2.Cache2N Extends %CSP.Page { Parameter OAUTH2APPNAME = "demo client"; Parameter OAUTH2ROOT = "https://dk-gs2016/resserver"; Parameter SSLCONFIG = "SSL4CLIENT"; ClassMethod OnPage() As %Status {     &html          // OAuth2 サーバーからのアクセストークンがあるかどうかをチェックします。     set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,"scope1 scope2",.accessToken,.idtoken,.responseProperties,.error)          // アクセストークンがあるかどうかをさらにチェックします。     // 以下は実行可能なすべてのテストであり、あらゆるケースで必要なわけではありません。     // 各テストで返される JSON オブジェクトが単に表示されています。     if isAuthorized {         write "Authorized!",!                           // JWT の場合、検証してからアクセストークンから詳細を取得します。         set valid=##class(%SYS.OAuth2.Validation).ValidateJWT(..#OAUTH2APPNAME,accessToken,"scope1 scope2",,.jsonObject,.securityParameters,.sc)         if $$$ISOK(sc) {             if valid {                 write "Valid JWT"_"",!                 } else {                 write "Invalid JWT"_"",!                 }             write "Access token="             do jsonObject.%ToJSON()             write "",!         } else {             write "JWT Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!             }         write "",!         // イントロスペクションエンドポイントを呼び出して結果を表示します。RFC 7662 を参照してください。         set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection(..#OAUTH2APPNAME,accessToken,.jsonObject)         if $$$ISOK(sc) {             write "Introspection="             do jsonObject.%ToJSON()             write "",!         } else {             write "Introspection Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!             }         write "",!                  if idtoken'="" {             // ID トークンの検証と表示。OpenID Connect Core の仕様を参照してください。             set valid=##class(%SYS.OAuth2.Validation).ValidateIDToken(                 ..#OAUTH2APPNAME,                 idtoken,                 accessToken,,,                 .jsonObject,                 .securityParameters,                 .sc)             if $$$ISOK(sc) {                 if valid {                     write "Valid IDToken"_"",!                     } else {                     write "Invalid IDToken"_"",!                     }                 write "IDToken="                 do jsonObject.%ToJSON()                 write "",!             } else {                 write "IDToken Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!                 }         } else {             write "No IDToken returned"_"",!         }         write "",!              // アプリケーションロジックには不要ですが、委任認証に渡すことができるユーザーに関する情報を提供します。              // Userinfo エンドポイントを呼び出して結果を表示します。OpenID Connect Core の仕様を参照してください。         set sc=##class(%SYS.OAuth2.AccessToken).GetUserinfo(             ..#OAUTH2APPNAME,             accessToken,,             .jsonObject)         if $$$ISOK(sc) {             write "Userinfo="             do jsonObject.%ToJSON()             write "",!         } else {             write "Userinfo Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!             }         write "",!         /***************************************************         *                                                  *         *   リソースサーバーを呼び出し、結果を表示します。   *         *                                                  *         ***************************************************/                          // オプション 1 - リソースサーバー - 定義によれば認可サーバーからのデータを信頼します。         //     そのため、リソースサーバーに渡されたアクセストークンが有効である限り         //  要求元を問わずデータを提供します。                  // オプション 2 - または委任認証(OpenID Connect)を使用して          //  (委任認証を保護して)別の CSP アプリケーションを呼び出すこともできます。         //  - これはまさにこのデモで実施する内容です。                           write "リソースサーバーの呼び出し(委任認証)","",!         set httpRequest=##class(%Net.HttpRequest).%New()         // AddAccessToken は現在のアクセストークンをリクエストに追加します。         set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken(             httpRequest,,             ..#SSLCONFIG,             ..#OAUTH2APPNAME)         if $$$ISOK(sc) {             set sc=httpRequest.Get(..#OAUTH2ROOT_"/csp/portfolio/oauth2test.demoResource.cls")         }         if $$$ISOK(sc) {             set body=httpRequest.HttpResponse.Data             if $isobject(body) {                 do body.Rewind()                 set body=body.Read()             }             write body,"",!         }         if $$$ISERR(sc) {             write "Resource Server Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!             }         write "",!              write "Call resource server - no auth, just token validity check","",!         set httpRequest=##class(%Net.HttpRequest).%New()         // AddAccessTokenは現在のアクセストークンをリクエストに追加します。         set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken(             httpRequest,,             ..#SSLCONFIG,             ..#OAUTH2APPNAME)         if $$$ISOK(sc) {             set sc=httpRequest.Get(..#OAUTH2ROOT_"/csp/portfolio2/oauth2test.demoResource.cls")         }         if $$$ISOK(sc) {             set body=httpRequest.HttpResponse.Data             if $isobject(body) {                 do body.Rewind()                 set body=body.Read()             }             write body,"",!         }         if $$$ISERR(sc) {             write "Resource Server Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"",!             }         write "",!     } else {         write "Not Authorized!",!         write "Authorize me"     }         &html     Quit $$$OK } } 次のスクリーンショットで処理を説明します。 AUTHSERVER インスタンスの認可 / OpenID Connect 認証サーバーのログインページ ![](/sites/default/files/inline/images/11.png) AUTHSERVER のユーザー同意ページ ![](/sites/default/files/inline/images/12.png) その後、最終的に結果ページが表示されます。 ![](/sites/default/files/inline/images/13.png) ご覧のとおり、実際にコードを読んでもパート 1 で示したクライアントのコードとほとんど違いはありません。 ページ 2 には違いがあります。 これはデバッグ情報であり、JWT の有効性をチェックしています。 返ってきた JWT を検証した後、AUTHSERVER からのユーザー識別情報に関するデータに対してイントロスペクションを実行できました。 ここではこの情報をページに出力しただけですが、それ以上のこともできます。 上記で説明した外部の医師の使用事例と同様に、必要に応じて識別情報を使用し、それを認証目的でリソースサーバーに渡すことができます。 または、この情報をパラメーターとしてリソースサーバーへの API 呼び出しに渡すこともできます。 次の段落では、ユーザー識別情報の使用方法について詳しく説明します。 ## **リソースアプリケーション** リソースサーバーは認可 / 認証サーバーと同じサーバーにすることができ、多くの場合はそうなります。 しかし、このデモでは 2 つのサーバーを独立した InterSystems IRIS インスタンスにしました。 したがって、リソースサーバーでセキュリティコンテキストを使用する方法には 2 つあります。 ### **方法 1 – 認証なし** これは最も単純なケースです。 認可 / 認証サーバーはまったく同じ Caché インスタンスです。 この場合、単一の目的のために特別に作成された csp アプリケーションにアクセストークンを渡すだけで、OAUTH を使用するクライアントアプリケーションにデータを提供し、データを要求することを認可できます。 リソース csp アプリケーションの構成(ここでは /csp/portfolio2 と呼びました)は、以下のスクリーンショットのようになります。 ![](/sites/default/files/inline/images/14.png) 最小限のセキュリティをアプリケーション定義に組み込み、特定の CSP ページのみを実行できるようにします。 または、リソースサーバーは従来の Web ページの代わりに REST API を提供できます。 実際のシナリオでは、セキュリティコンテキストを微調整するのはユーザー次第です。 ソースコードの例: Class oauth2test.demoResource Extends %CSP.Page { ClassMethod OnPage() As %Status { set accessToken=##class(%SYS.OAuth2.AccessToken).GetAccessTokenFromRequest(.sc) if $$$ISOK(sc) { set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection("RESSERVER resource",accessToken,.jsonObject) if $$$ISOK(sc) { // 必要に応じて jsonObject のフィールドを検証します w "Hello from Caché server: /csp/portfolio2 application!" w "running code as $username = "_$username_" with following $roles = "_$roles_" at node "_$p($zu(86),"*",2)_"." } } else { w "NOT AUTHORIZED!" w "" w i $d(%objlasterror) d $system.OBJ.DisplayError() w "" } Quit $$$OK } } ### **方法 2 – 委任認証** これはもう 1 つの極端なケースです。ユーザーがリソースサーバーの内部ユーザーと同等のセキュリティコンテキストで作業しているかのように、リソースサーバーでユーザーの識別情報を可能な限り最大限に活用したいと考えています。 解決方法の 1 つは、委任認証を使用することです。 この方法を実行するには、さらにいくつかの手順を実行してリソースサーバーを構成する必要があります。 ·        委任認証を有効にする ·        ZAUTHENTICATE ルーチンを提供する ·        Web アプリケーションを構成する(この場合、/csp/portfolio で呼び出しました) ここではユーザー識別情報とそのスコープ(セキュリティプロファイル)を提供した AUTHSERVER を信頼しているため、ZAUTHENTICATE ルーチンの実装は非常に単純で簡単です。そのため、ここではいかなるユーザー名も受け入れ、それをスコープと共にリソースサーバーのユーザーデータベースに渡しています(OAUTH スコープと InterSystems IRIS のロール間で必要な変換を行ったうえで)。 それだけです。 残りの処理は InterSystems IRIS によってシームレスに行われます。 これは ZAUTHENTICATE ルーチンの例です。 #include %occErrors #include %occInclude ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC { set tRes=$SYSTEM.Status.OK() try { set Properties("FullName")="OAuth account "_Username //set Properties("Roles")=Credentials("scope") set Properties("Username")=Username //set Properties("Password")=Password // Credentials 配列を GetCredentials() メソッドから渡せないため、一時的に書き換えます。 set Properties("Password")="xxx" // OAuth2 アカウントのパスワードは気にしません。 set Properties("Roles")=Password } catch (ex) { set tRes=$SYSTEM.Status.Error($$$AccessDenied) } quit tRes } GetCredentials(ServiceName,Namespace,Username,Password,Credentials) Public { s ts=$zts set tRes=$SYSTEM.Status.Error($$$AccessDenied) try { If ServiceName="%Service_CSP" { set accessToken=##class(%SYS.OAuth2.AccessToken).GetAccessTokenFromRequest(.sc) if $$$ISOK(sc) { set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection("RESSERVER resource",accessToken,.jsonObject) if $$$ISOK(sc) { // ToDo: 標準アカウントと委任されたアカウント(OpenID)が競合する可能性があるため、注意してください! set Username=jsonObject.username set Credentials("scope")=$p(jsonObject.scope,"openid profile ",2) set Credentials("namespace")=Namespace // temporary hack //set Password="xxx" set Password=$tr(Credentials("scope")," ",",") set tRes=$SYSTEM.Status.OK() } else { set tRes=$SYSTEM.Status.Error($$$GetCredentialsFailed) } } } else { set tRes=$SYSTEM.Status.Error($$$AccessDenied) } } catch (ex) { set tRes=$SYSTEM.Status.Error($$$GetCredentialsFailed) } Quit tRes } CSP ページ自体は非常にシンプルになります。 Class oauth2test.demoResource Extends %CSP.Page { ClassMethod OnPage() As %Status { // アクセストークン認証は委任認証によって実行されます! // もう一度ここで行う必要はありません。 // これはリクエストからアクセストークンを取得し、イントロスペクションエンドポイントを // 使用してアクセストークンの有効性を確認するダミーのリソースサーバーです。 // 通常、応答はセキュリティに関連しませんが、リクエストパラメーターに基づく // 興味深いデータが含まれている可能性があります。 w "Hello from Caché server: /csp/portfolio application!" w "running code as $username = "_$username_" with following $roles = "_$roles_" at node "_$p($zu(86),"*",2)_"." Quit $$$OK } } そして最後に、/csp/portfolio の Web アプリケーション構成を示します。 ![](/sites/default/files/inline/images/15.png) あなたが本当に心配であったなら、最初のバリエーションで行ったように _Permitted クラス_を設定できたかもしれません。 または、REST API を使用していたかもしれません。 しかし、これらの設定に関してはこの記事の中では説明しません。 次回は、InterSystems IRIS OAUTH フレームワークによって導入される個々のクラスについて説明します。 それらの API、およびそれらを呼び出すタイミングと場所について説明します。   [1] この記事で OAUTH について言及する場合、常に RFC 6749()で規定されている OAuth 2.0 を指しています。 ここでは簡略化のために短縮形の OAUTH を使用しています。 [2] OpenID Connect は OpenID Foundation()によって管理されています。
記事
Hiroshi Sato · 2021年11月24日

InterSystems製品開始時に特定の処理を実行する方法

これは InterSystems FAQ サイトの記事です。 InterSystems製品開始時に、OSの実行ファイルやコマンド、InterSystems製品内に作成したプログラムを実行したい場合は SYSTEM^%ZSTART ルーチンに処理を記述します。 (%ZSTARTルーチンは%SYSネームスペースで作成します) SYSTEM^%ZSTART に記述する処理は、事前にあらゆる条件下でうまく動作することを確認してください。 ^%ZSTART ルーチンの記述ミスや、記述は正しくとも起動時にコマンドが応答を返さなかったり処理でエラーが起こった場合、InterSystems製品が起動できなくなることがあります。 詳しくは、以下ドキュメントをご参照ください。 %ZSTART、%ZSTOPルーチンの記述について【IRIS】%ZSTART、%ZSTOPルーチンの記述について