#Kubernetes

0 フォロワー · 19 投稿

Kubernetes(一般的にk8sとして様式化されています)は、アプリケーションのデプロイ、スケーリング、および管理を自動化するためのオープンソースのコンテナーオーケストレーションシステムです。 ホストのクラスター全体でアプリケーションコンテナーの展開、スケーリング、および操作を自動化するためのプラットフォームを提供することを目的としています。 Dockerを含むさまざまなコンテナーツールで動作します。 詳細はこちら

記事 Toshihiko Minamoto · 11月 5, 2025 8m read

新しい InterSystems IRIS® Cloud SQL と InterSystems IRIS® Cloud IntegratedML® クラウド製品のユーザーであり、デプロイメントのメトリクスにアクセスして独自の可観測性プラットフォームに送信しようと考えている方のために、メトリクスを Google Cloud Platform Monitoring(旧称 StackDriver)に送信して手っ取り早く行う方法をご紹介します。

クラウドポータルには、概要メトリクス表示用のトップレベルのメトリクスが含まれており、ユーザーに公開されているメトリクスエンドポイントを使用しますが、ある程度探索しなければ、そこにあることには気づきません。

🚩 このアプローチは、「今後名前が付けられる予定の機能」を利用している可能性があるため、それを踏まえると将来性があるものではなく、確実に InterSystemsでサポートされているアプローチではありません。


では、より包括的なセットをエクスポートしたい場合はどうでしょうか?この技術的な記事/例では、メトリクスを取得して可観測性に転送する方法を紹介します。Open Telemetry Collector を使用して、任意のメトリクスターゲットを取得し、任意の可観測性プラットフォーム送信できるように、ニーズに合わせて変更することができます。

上記の結果に導く仕組みは多数の方法で得られますが、ここでは Kubernetes pod を使用して、1 つのコンテナーで Python スクリプトを実行し、もう 1 つのコンテナーで Otel を実行して、メトリクスのプルとプッシュを行います... 自分のやり方を選択することはできますが、この例と記事では、k8s を主人公に Python を使って行います。

手順:

  • 前提条件
  • Python
  • コンテナー
  • Kubernetes
  • Google Cloud Monitoring

前提要件:

  • IRIS®  Cloud SQL の有効なサブスクリプション
  • 実行中の 1 つのデプロイメント(オプションで Integrated ML を使用)
  • 環境に提供するシークレット

環境変数

 
 シークレットの取得
この内容は少し複雑で本題から少し外れているためティーザーに入れましたが、これがシークレットの生成に必要なる値です。
ENV IRIS_CLOUDSQL_USER 'user'
ENV IRIS_CLOUDSQL_PASS 'pass'

☝ これは https://portal.live.isccloud.io の認証情報です。

ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'
ENV IRIS_CLOUDSQL_CLIENTID 'clientid'
ENV IRIS_CLOUDSQL_API 'api'

☝ これはブラウザの開発ツールから取得する必要があります。

  • `aud` = clientid
  • `userpoolid`= iss
  • `api` = request utl

ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'

☝これはクラウドサービスポータルから取得できます

 

Python:

以下に、クラウドポータルからメトリクスを取得し、それを Otel Collectorが取得するメトリクスとしてローカルにエクスポートする Python ハッキングを示します。

 
iris_cloudsql_exporter.py
import time
import os
import requests
import json

from warrant import Cognito from prometheus_client.core import GaugeMetricFamily, REGISTRY, CounterMetricFamily from prometheus_client import start_http_server from prometheus_client.parser import text_string_to_metric_families

classIRISCloudSQLExporter(object):definit(self): self.access_token = self.get_access_token() self.portal_api = os.environ['IRIS_CLOUDSQL_API'] self.portal_deploymentid = os.environ['IRIS_CLOUDSQL_DEPLOYMENTID']

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">collect</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-comment"># Requests fodder</span>
    url = self.portal_api
    deploymentid = self.portal_deploymentid
    print(url)
    print(deploymentid)

    headers = {
        <span class="hljs-string">'Authorization'</span>: self.access_token, <span class="hljs-comment"># needs to be refresh_token, eventually</span>
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
    }

    metrics_response = requests.request(<span class="hljs-string">"GET"</span>, url + <span class="hljs-string">'/metrics/'</span> + deploymentid, headers=headers)
    metrics = metrics_response.content.decode(<span class="hljs-string">"utf-8"</span>)

    <span class="hljs-keyword">for</span> iris_metrics <span class="hljs-keyword">in</span> text_string_to_metric_families(metrics):
        <span class="hljs-keyword">for</span> sample <span class="hljs-keyword">in</span> iris_metrics.samples:

            labels_string = <span class="hljs-string">"{1}"</span>.format(*sample).replace(<span class="hljs-string">'\''</span>,<span class="hljs-string">"\""</span>)
            labels_dict = json.loads(labels_string)
            labels = []

            <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> labels_dict:
                labels.extend(labels_dict)
            <span class="hljs-keyword">if</span> len(labels) &gt; <span class="hljs-number">0</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric(list(labels_dict.values()), <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">else</span>:
                g = GaugeMetricFamily(<span class="hljs-string">"{0}"</span>.format(*sample), <span class="hljs-string">'Help text'</span>, labels=labels)
                g.add_metric([<span class="hljs-string">""</span>], <span class="hljs-string">"{2}"</span>.format(*sample))
            <span class="hljs-keyword">yield</span> g

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_access_token</span><span class="hljs-params">(self)</span>:</span>
    <span class="hljs-keyword">try</span>:
        user_pool_id = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USERPOOLID'</span>] <span class="hljs-comment"># isc iss </span>
        username = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_USER'</span>]
        password = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_PASS'</span>]
        clientid = os.environ[<span class="hljs-string">'IRIS_CLOUDSQL_CLIENTID'</span>] <span class="hljs-comment"># isc aud </span>
        print(user_pool_id)
        print(username)
        print(password)
        print(clientid)
        
        <span class="hljs-keyword">try</span>:
            u = Cognito(
                user_pool_id=user_pool_id,
                client_id=clientid,
                user_pool_region=<span class="hljs-string">"us-east-2"</span>, <span class="hljs-comment"># needed by warrant, should be derived from poolid doh</span>
                username=username
            )
            u.authenticate(password=password)
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> p:
            print(p)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(e)

    <span class="hljs-keyword">return</span> u.id_token

ifname == 'main':

start_http_server(<span class="hljs-number">8000</span>)
REGISTRY.register(IRISCloudSQLExporter())
<span class="hljs-keyword">while</span> <span class="hljs-keyword">True</span>:
    REGISTRY.collect()
    print(<span class="hljs-string">"Polling IRIS CloudSQL API for metrics data...."</span>)
    <span class="hljs-comment">#looped e loop</span>
    time.sleep(<span class="hljs-number">120</span>)</code></pre>

 

Docker:

 
Dockerfile
FROM python:3.8ADD src /src
RUN pip install prometheus_client
RUN pip install requests
WORKDIR /src
ENV PYTHONPATH '/src/'ENV PYTHONUNBUFFERED=1ENV IRIS_CLOUDSQL_USERPOOLID 'userpoolid'ENV IRIS_CLOUDSQL_CLIENTID 'clientid'ENV IRIS_CLOUDSQL_USER 'user'ENV IRIS_CLOUDSQL_PASS 'pass'ENV IRIS_CLOUDSQL_API 'api'ENV IRIS_CLOUDSQL_DEPLOYMENTID 'deploymentid'RUN pip install -r requirements.txt
CMD ["python" , "/src/iris_cloudsql_exporter.py"]
docker build -t iris-cloudsql-exporter .
docker image tag iris-cloudsql-exporter sween/iris-cloudsql-exporter:latest
docker push sween/iris-cloudsql-exporter:latest


デプロイメント:

k8s、ネームスペースを作成します:

kubectl create ns iris

k8s、シークレットを追加します:

kubectl create secret generic iris-cloudsql -n iris \
    --from-literal=user=$IRIS_CLOUDSQL_USER \
    --from-literal=pass=$IRIS_CLOUDSQL_PASS \
    --from-literal=clientid=$IRIS_CLOUDSQL_CLIENTID \
    --from-literal=api=$IRIS_CLOUDSQL_API \
    --from-literal=deploymentid=$IRIS_CLOUDSQL_DEPLOYMENTID \
    --from-literal=userpoolid=$IRIS_CLOUDSQL_USERPOOLID

otel、構成を作成します:

apiVersion: v1
data:
  config.yaml: |
    receivers:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'IRIS CloudSQL'
              # Override the global default and scrape targets from this job every 5 seconds.
            scrape_interval: 30s
            scrape_timeout: 30s
            static_configs:
                    - targets: ['192.168.1.96:5000']
            metrics_path: /
exporters:
  googlemanagedprometheus:
    project: "pidtoo-fhir"
service:
  pipelines:
    metrics:
      receivers: [prometheus]
      exporters: [googlemanagedprometheus]

kind: ConfigMap metadata: name: otel-config namespace: iris

k8s、otel 構成を configmap としてロードします:

kubectl -n iris create configmap otel-config --from-file config.yaml

k8s、ロードバランサー(確実にオプション)、MetalLB をデプロイします。これはクラスタ外部からグスクレイピングして検査するために行っています。

cat <<EOF | kubectl apply -f -n iris -
apiVersion: v1
kind: Service
metadata:
  name: iris-cloudsql-exporter-service
spec:
  selector:
    app: iris-cloudsql-exporter
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 8000
EOF

gcp、Google Cloud へのキーが必要です。サービスアカウントにスコープを設定する必要があります

  • roles/monitoring.metricWriter
kubectl -n iris create secret generic gmp-test-sa --from-file=key.json=key.json

k8s; deployment/pod そのもの。2 つのコンテナー:

 
deployment.yaml
kubectl -n iris apply -f deployment.yaml

実行

特に問題がなければ、ネームスペースを詳しく調べて、状況を確認してみましょう。

✔ GCP と Otel 用の 2 つの configmap

 

✔ 1 つのロードバランサー

 

✔ 1 つの pod、2 つのコンテナーが正しくスクレイピングされます

  

Google Cloud Monitoring

可観測性を調べてメトリクスが正しく受信されていることを確認し、可観測性を高めましょう!

 

0
0 20
記事 Andre Larsen Barbosa · 6月 9, 2025 3m read

image

相手に隙を与えないノックアウトパンチのように、オープンソースプラットフォームであるKubernetesは、その可用性(つまり、サポート、サービス、ツールの容易な入手性)により、無限の可能性を秘めています。Kubernetesはコンテナ内のジョブとサービスを管理できるプラットフォームであり、これらのプロセスの構成と自動化を大幅に簡素化します。

しかし、タイトルイメージにふさわしい、このツールに「正しい」名前、InterSystems Kubernetes Operatorを与えましょう。

原理は至ってシンプルです。サービスを選択し、ゲームのルールを定義するだけで(ここでもKnockoutを参照)、すべてが可能な限り透明性と効率性を高めて提供されます。これは、インストール、修復、そして事前定義された要件を満たさない場合の最終的な復旧にも適用されます。

では、IKOと他のオペレーターの違いは何でしょうか?Kubernetes API(ここではK8sと略します)の拡張機能であるIrisClusterカスタムコンポーネントは、ロックされたIRISクラスター、分散Cachéクラスター、さらには匿名インスタンスとしてデプロイするオプションを備えています。これらすべてを、最も多様なKubernetesプラットフォーム上で実行できます。最後に、InterSystemsのクラスタ管理機能も搭載されており、以前は手動でしか実行できなかったノードの追加によるタスクの自動化が可能になります。

これは非常に魅力的で、スポーツやゲームに例えられていますが、なぜ必要なのでしょうか?答えは比較的簡単です。InterSystems IRISをK8sに統合するためにIrisClusterは必要ありません。しかし、K8sはスタンドアロンアプリケーションであるため、これらのIRISインスタンスを構成するための定義とスクリプトを作成する必要があります。このように、IKOはこのプロセスを自動化し、メンテナンスを容易にします。コンテナを使用することは、必要な一連のアクティビティをパッケージ化する優れた方法です。

ところで、この機会を活用してみてはいかがでしょうか?コンテナとは何かご存知ですか?ヒント:単なるボードゲームではありません。

image

答えは、あるパッケージの「輸送」に大きく関係しています。アプリケーションとサービスをパッケージ化して分離し、他の部分とは独立して実行できるようにするためです。これにより、必要に応じて、ある環境から別の環境への「輸送」が容易になります。

InterSystemsの膨大なドキュメントを活用し、IKOのインストールとその後の設定および調整手順へのリンクを以下に示します。

https://docs.intersystems.com/components/csp/docbook/DocBook.UI.Page.cls...

K8sというニックネームに誰も興味を持たないように。Kubernetesという名前はギリシャ語に由来し、水先案内人、つまり指揮官を意味します。そして、先頭の「K」と末尾の「S」の間の文字数は8です。つまり、「K8s」です。

0
0 35
InterSystems公式 Seisuke Nakahashi · 8月 14, 2023

InterSystems Kubernetes Operator (IKO) 3.6 がリリースされました。 IKO 3.6 は、多くのバグ修正に加えて、大幅に新機能を追加しました。注目の機能は以下のようになっています。

IKO のダウンロード、インストール、開始手順については インストールガイド にしたがってください。 IKO 3.6 ドキュメント には、IKO ならびに IKO を InterSystems IRIS や InterSystems IRIS for Health とどのように組み合わせるかの手順が記載されています。 IKO は WRC からダウンロードいただけます (Kubernetes で検索してください。) コンテナは InterSystems コンテナレジストリ から入手いただけます。

0
0 145
InterSystems公式 Toshihiko Minamoto · 4月 18, 2023

InterSystems Kubernetes Operator (IKO) 3.5 が一般公開されました。  IKO 3.5では、多数のバグフィックスとともに、重要な新機能が追加されています。  

主な内容は以下の通りです

  • Web Gateway、ECP、Mirroring、Super Server、IAM間でのTLSのセットアップを簡素化
  • コンピュートノードやデータノードとともにサイドカーコンテナを実行する機能 - コンピュートノードとともにWebゲートウェイをスケーリングするのに最適な機能です。
  • IRIS 2023.1以降、IKO 3.5を使用する場合、CPF configmapとIRIS 秘密鍵はIRISインスタンスによって自動的に処理されます。
  • initContainerがUID/GIDとイメージの両方で設定できるようになりました。
  • IKOがtopologySpreadConstraintsをサポートし、ポッドのスケジューリングをより簡単に制御できるようになりました。
  • より幅広いIRISインスタンスに対応する互換性バージョン
  • コンピュートノードの自動スケール化 (体験版)
  • IKO は ARM でも実行可能
0
0 119
お知らせ Mihoko Iijima · 5月 22, 2022

開発者のみなさん、こんにちは!

2022年3月9日開催「InterSystems Japan Virtual Summit 2022」のセッション「オープンソースだけで IRIS on Kubernetes を動かそう」のアーカイブを YouTube に公開いたしました。

(プレイリストはこちら


Docker は使いこなせるようになってきたけど、Kubernetes はなかなか手を出すチャンスがないな、という方々を対象に手持ちの Windows/PC 上に K8s 環境を構築して色々試すことを最終目標としたセッションです。

開発者コミュニティの記事「microk8sでKubernetesをお手軽に試す」との紙面連動です。

ぜひご参照ください。

<iframe width="521" height="293" src="https://www.youtube.com/embed/zMhPuBFl6l4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

【目次】

01:43 Kubernetes について
 参考動画:KubernetesでのIRISの運用とそれを支える仕組み

05:48 Microk8S 利用の動機付け

09:50 デモ
 記事:microk8sでKubernetesをお手軽に試す の内容に沿ってデモを動かしています。

14:00 デモ - IRIS起動の確認

15:35 デモ - MicroK8S 環境をクラスタ構成にする

17:32 デモ - Longhorn をデプロイする

19:28 デモ - Longhorn を Persistent Volume としてIRISが使用するように構成する

20:58 デモ - ノード障害発生時の動作を確認

23:15 まとめ
 ↓参考となるオンラインラーニング(英語)↓
 https://learning.intersystems.com/course/view.php?name=KubernetesExercise

0
0 156
記事 Toshihiko Minamoto · 4月 14, 2022 12m read

IRIS ベースのアプリケーションを GCP Kubernetes で実行する方法については、すでに「InterSystems IRIS ソリューションを CircleCI を使用して GCP Kubernetes Cluster GKE へデプロイする」で検討しました。 また、IRIS ベースのアプリケーションを AWS Kubernetes で実行する方法については、「Amazon EKS を使用したシンプルな IRIS ベースの Web アプリケーションのデプロイ」で確認しました。 そこで今回は、アプリケーションを Azure Kubernetes Service(AKS)にデプロイする方法を説明することにします。

Azure

この記事では、Azure の無料サブスクリプションを使用します。 価格の詳細については、Azure の価格表ページをご覧ください。 

登録が完了すると、Microsoft Azure ポータルが表示されます。

便利なポータルではありますが、この記事では使用しません。 代わりに、Azure コマンドラインインターフェースをインストールしましょう。 執筆時点での最新バージョンは 2.30.0 です。

$ az version{  "azure-cli": "2.30.0",  "azure-cli-core": "2.30.0",  "azure-cli-telemetry": "1.0.6",  "extensions": {}}

それでは、Azure にログインしましょう。

$ az login

CircleCI パイプライン

強力な CircleCI の CI/CD を使用して、AKS のセットアップと IRIS アプリケーションのインストールを行います。 つまり、GitHub ベースのプロジェクトを使って、コードとしてのインフラストラクチャと共にいくつかのパイプラインファイルを追加し、GitHub に変更をプッシュし直して、その結果を使いやすい CircleCI UI で確認します。

GitHub アカウントを使えば、簡単に CircleCI との統合を作成できます。 詳細については、「Seamless integration with GitHub」の記事をご覧ください。

では、「Amazon EKS を使用したシンプルな IRIS ベースの Web アプリケーションのデプロイ」で使用したプロジェクトの更新バージョンを使用しましょう。つまり、secured-rest-api です。 それを開き、Use this Template をクリックして、新しいリポジトリ内にバージョンを作成します。 この記事では、そこに含まれるコードサンプルを参照します。

ローカルにリポジトリを Clone し、以下に示すファイルを含む .circleci/ ディレクトリを作成します。

$ tree .circleci/.circleci/├── config.yml└── continue.yml

ダイナミックコンフィグパスのフィルタリングを使用して、変更のあるファイルに応じて全体または一部を実行するようにパイプラインを設定します。 この例では、Terraform コードに変更がある場合にのみ Terraform ジョブを実行します。 最初の config.yml ファイルは単純です。 2 つ目の .circleci/continue.yml を呼び出し、Terraform コードが最新である場合に特定のブール値パラメーターを渡します。
 

$ cat config.ymlversion: 2.1# Enable CircleCI's dynamic configuration featuresetup: true# Enable path-based pipelineorbs:  path-filtering: circleci/path-filtering@0.1.0workflows:  Generate dynamic configuration:    jobs:      - path-filtering/filter:          name: Check updated files          config-path: .circleci/continue.yml          base-revision: master          mapping: |            terraform/.* terraform-job true

2 つ目の continue.yml ファイルを説明する前に、この secured-rest-app プロジェクトを CircleCI に追加して、.circleci/config.yml の変更を GitHub にプッシュしましょう。

$ git add .circleci/config.yml$ git commit -m "Add circleci config.yml"$ git push

そして、CircleCI Projects ページを開いて、プロジェクトを選択し、Set Up Project をクリックします。

 

  提示される推奨事項に従って、Setup Workflow を有効にします(詳細は、「CircleCI のダイナミック コンフィグの使用を開始する」をご覧ください。

 

これで、2 つ目の continue.yml ファイルに進む準備が整いました。 このファイルの構造は次のようになっています。

  • Version: CircleCI パイプラインのバージョンです。
  • Parameters: Terraform が実行中であるかどうかを決定する変数です。
  • Orbs: 他の人が作成した再利用可能な構成の一部です。
  • Executors: 一部のジョブに使用する Asure コマンドラインを含む docker イメージです。
  • Jobs: 実際のデプロイステップです。
  • Workflows: Terraform の有無に関係なくパイプラインを実行するためのロジックです。
  • Jobs セクションには、次のジョブが含まれます。

  • Build and push Docker image to ACR: このジョブは、az コマンドラインツールがインストールされた docker イメージ内で実行します。 Azure にログインし、イメージをビルドして Azure Container Registry(ACR)にプッシュします。
  • Terraform: このジョブは、Terraform orb を使用してインフラストラクチャを作成します。 詳細は、以下の Terraform に関するセクションをご覧ください。
  • Setup packages: このジョブは、IRIS アプリケーションといくつかのサービスアプリケーションをインストールします。 詳細は、以下の「パッケージのセットアップ」セクションをご覧ください。
  • Terraform

    インフラストラクチャの作成には、infrastructure as code アプローチを使用して、Terraform の力を利用します。 Terraform は Azure プラグインを使って AKS と対話します。 ラッパーの役割を果たし、リソース作成を単純化する AKS Terraform モジュール を使用すると便利です。

    Terraform を使用して AKS リソースを使用する例は、「Creating a Kubernetes Cluster with AKS and Terraform」にあります。 ここでは、Terraform がデモと単純化の目的でですべてのリソースを管理するように、Owner ロールを割り当てます。 アプリケーションとしての Terraform は Service Principal を使用して Azure に接続します。 厳密には、「Create an Azure service principal with the Azure CLI」に説明されているとおりに、Owner ロールを Service Principal に割り当てます。 

    ローカルマシンでコマンドをいくつか実行してみましょう。 Azure サブスクリプション ID を環境変数に保存します。

    $ export AZ_SUBSCRIPTION_ID=$(az account show --query id --output tsv)$ az ad sp create-for-rbac -n "Terraform" --role="Owner" --scopes="/subscriptions/${AZ_SUBSCRIPTION_ID}"{  "appId": "&lt;appId>",  "displayName": "&lt;displayName>",  "name": "&lt;name>",  "password": "&lt;password>",  "tenant": "&lt;tenant>"}

    後で、Service Principals をリスト表示して Terraform という表示名を探すと、appIdtenantId を見つけ出すことができます。

    $ az ad sp list --display-name "Terraform" | jq '.[] | "AppId: \(.appId), TenantId: \(.appOwnerTenantId)"'

    ただし、この方法ではパスワードが表示されません。 パスワードを忘れた場合には、資格情報をリセットするしかありません。

    パイプラインでは、AKS の作成には、一般に公開されている Azure Terraform モジュールと Terraform バージョン 1.0.11 を使用します。

    取得した、Terraform が Azure への接続に使用する資格情報を使用して、CircleCI project 設定に環境変数を設定します。 また、DOMAIN_NAME 環境変数も設定します。 このチュートリアルは demo-iris.myardyas.club ドメイン名を使用していますが、実際にはユーザーが登録したドメイン名を使用します。 パイプラインでこの変数を使用して、IRIS アプリケーションへの外部アクセスを有効にします。 CircleCI 変数と az create-for-rbac コマンドのマッピングは次のとおりです。

    ARM_CLIENT_ID: appIdARM_CLIENT_SECRET: passwordARM_TENANT_ID: tenantARM_SUBSCRIPTION_ID: Value of environment variable AZ_SUBSCRIPTION_IDDOMAIN_NAME: your domain name

    Terraform Remote の状態を有効にするには、Azure Storage の Terraform の状態を使用します。 これを実現するために、ローカルマシンで次のコマンドを実行してみましょう。 

    $ export RESOURCE_GROUP_NAME=tfstate$ export STORAGE_ACCOUNT_NAME=tfstate14112021 # Must be between 3 and 24 characters in length and use numbers and lower-case letters only$ export CONTAINER_NAME=tfstate
      # Create resource group$ az group create --name ${RESOURCE_GROUP_NAME} --location eastus
      # Create storage account$ az storage account create --resource-group ${RESOURCE_GROUP_NAME} --name ${STORAGE_ACCOUNT_NAME} --sku Standard_LRS --encryption-services blob  
    # Enable versioning. Read more at https://docs.microsoft.com/ja-jp/azure/storage/blobs/versioning-overview$ az storage account blob-service-properties update --account-name ${STORAGE_ACCOUNT_NAME} --enable-versioning true  
    # Create blob container$ az storage container create --name ${CONTAINER_NAME} --account-name ${STORAGE_ACCOUNT_NAME}

    Terraform ディレクトリに配置した Terraform コードです。 これは、次の 3 つのファイルに分割されています。

  • provider.tf: Azure プラグインのバージョンと、Terraform 状態を保存するリモートストレージへのパスを設定します。
  • variables.tf: Terraform モジュールの入力データです。
  • main.tf: 実際のリソースの作成です。 
  • Azure リソースグループパブリック IPAzure コンテナレジストリなどを作成します。 ネットワーキングAzure Kubernetes サービスについては、一般に公開されている Terraform モジュールを活用します。

    パッケージのセットアップ

    新たに作成された AKS クラスタにインストールするものは、helm ディレクトリにあります。 説明的な Helmfile アプローチを使用することで、アプリケーションとその設定を helmfile.yaml ファイルに定義することができます。

    セットアップは、単一の helmfile sync コマンドで実行します。 このコマンドによって、IRIS アプリケーションと 2 つの追加アプリケーション、cert-manager、および ingress-nginx がインストールされ、外部からアプリケーションを呼び出せるようになります。 詳細については、GitHub の「releases」セクションをご覧ください。

    IRIS アプリケーションは、「CircleCI ビルドで GKE の作成を自動化する」の説明と同じ Helm チャートを使用してインストールします。 単純化するために、deployment を使用します。 つまり、ポッドの再起動時に、データは保持されません。 永続させる場合は、Statefulset またはより優れた Kubernetes IRIS Operator(IKO)を使用することをお勧めします。 IKO デプロイメントの例は、iris-k8s-monitoring リポジトリにあります。

    パイプラインの実行

    .circleci/、terraform/、および helm/ ディレクトリを追加したら、それらを GitHub にプッシュします。

    $ git add .$ git commit -m "Setup everything"$ git push

    すべて問題がなければ、以下のような CircleCI UI 画面が表示されます。

     

    ドメインレジストラでの A レコードの設定

    後もう一つ残っているのは、Terraform が Azure で作成したパブリック IP とDomain Registrar 近ソースのドメイン名の A レコードでバインディングを作成することです。

    クラスタに接続しましょう。

    $ az aks get-credentials --resource-group demo --name demo

    ingress-nginx で公開されるパブリック IP アドレスを定義します。

    $ kubectl -n ingress-nginx get service ingress-nginx-controller -ojsonpath='{.spec.loadBalancerIP}'x.x.x.x

    次のようにして、この IP をドメインレジストラ(GoDaddyRoute53GoogleDomains など)に設定します。

    YOUR_DOMAIN_NAME = x.x.x.x

    ここで、DNS の変更が世界中に伝搬されるまでしばらく待ってから、結果を確認します。

    $ dig +short YOUR_DOMAIN_NAME

    応答は x.x.x.x となります。

    テスト

    ドメイン名を demo-iris.myardyas.club と仮定して、手動テストを実施します。 Let's Encrypt のステージング用証明書を使用しているため、ここでは、証明書の確認は省略しましょう。 本番環境では、lets-encrypt-production(こちら)に置き換える必要があります。 また、メールアドレス(こちら)を example@gmail.com ではないものに設定することもお勧めします。

    $ curl -sku Bill:ChangeMe https://demo-iris.myardyas.club/crudall/_spec | jq .

    人(ユーザー)を作成する:

    $ curl -ku John:ChangeMe -XPOST -H "Content-Type: application/json"  https://demo-iris.myardyas.club/crud/persons/ -d '{"Name":"John Doe"}'

    人が作成されたかどうかを確認する:

    $ curl -sku Bill:ChangeMe https://demo-iris.myardyas.club/crud/persons/all | jq .[  {    "Name": "John Doe"  }...

    まとめ

    これで以上です! Terraform と CircleCI ワークフローを使用して、Azure クラウドに Kubernetes クラスタを作成する方法を確認しました。 IRIS インストールでは、最も簡単な Helm チャートを使用しました。 本番環境では、このチャートを拡張し、少なくともデプロイを Statefulset に置き換えるか、IKO を使用することをお勧めします。

    作成したリソースが不要になったら、忘れずに削除しましょう。 Azure には、無料利用枠が用意されており、AKS は無料ですが、AKS クラスタの実行用に設計されたリソースは有料です。

    0
    0 350
    InterSystems公式 Toshihiko Minamoto · 3月 21, 2022

    InterSystems Kubernetes Operation (IKO) バージョン 3.3 が WRC ダウンロードページ やInterSystems Container Registry よりダウンロード可能となりました。

    IKO は、使いやすい irisClusterリソース定義を提供することにより、Kubernetesにおける InterSystems IRIS やInterSystems IRIS for Health との連携を簡素化します。 簡単なシャーディング、ミラーリング、ECP構成などの機能一覧は ドキュメント をご覧ください。

    IKO 3.3の主な修正点:

    • InterSystems IRIS 、 IRIS for Healthのバージョン 2021.2 、 2022.1 のサポート
    • Kuberentes 1.21のサポート
    • irisCluster の一部として共通のSystem Alerting and Monitoring (SAM) コンフィグレーションのデプロイ
    • irisCluster の一部として InterSystems API Manager (IAM) のデプロイや管理
    • ミラーリングのアクティブ側の自動タグ付けにより、サービスは常にアクティブなミラーメンバーを指すことができます。
    0
    0 122
    記事 Makiko Kokubun · 7月 14, 2021 1m read

    *この動画は、2021年2月に開催された「InterSystems Japan Virtual Summit 2021」のアーカイブです。
     

    本動画では、コンテナを開発・デモ環境で使用されている方を対象に、プロダクション環境をコンテナで運用することを目的として Kubernetes の基本及び IRIS data platform の運用を支える仕組みをデモを交えながらご紹介します。

    この他にも、開発者コミュニティでは、コンテナやKubernetesに関連する技術情報を公開しています。
    こちらも合わせてご覧ください。

    0
    0 134
    記事 Tomohiro Iwamoto · 5月 18, 2021 18m read

    目的

    Japan Virtual Summit 2021で、Kubernetesに関するセッションを実施させていただいたのですが、AzureのアカウントやIRIS評価用ライセンスキーをお持ちの方が対象になっていました。もう少し手軽に試してみたいとお考えの開発者の方もおられると思いますので、本記事では仮想環境でも利用可能なk8sの軽量実装であるmirok8sで、IRIS Community Editionを稼働させる手順をご紹介いたします。

    2022/1/7 若干の加筆・修正しました

    マルチノード化する手順はこちらに記載しています。

    参考までに私の環境は以下の通りです。

    用途O/SホストタイプIP
    クライアントPCWindows10 Pro物理ホスト172.X.X.30/24, (vmware NAT)192.168.11.1/24
    mirok8s環境ubuntu 20.04.1 LTS上記Windows10上の仮想ホスト(vmware)192.168.11.49/24

    ubuntuは、ubuntu-20.04.1-live-server-amd64.isoを使用して、最低限のサーバ機能のみをインストールしました。

    概要

    IRIS Community EditionをKubernetesのStatefulSetとしてデプロイする手順を記します。 IRISのシステムファイルやユーザデータベースを外部保存するための永続化ストレージには、microk8s_hostpathもしくはLonghornを使用します。
    使用するコードはこちらにあります。

    インストレーション

    microk8sをインストール・起動します。

    $ sudo snap install microk8s --classic --channel=1.20
    $ sudo usermod -a -G microk8s $USER
    $ microk8s start
    $ microk8s enable dns registry storage metallb
      ・
      ・
    Enabling MetalLB
    Enter each IP address range delimited by comma (e.g. '10.64.140.43-10.64.140.49,192.168.0.105-192.168.0.111'):192.168.11.110-192.168.11.130
    

    ロードバランサに割り当てるIPのレンジを聞かれますので、適切な範囲を設定します。私の環境はk8sが稼働しているホストのCIDRは192.168.11.49/24ですので適当な空いているIPのレンジとして、[192.168.11.110-192.168.11.130]と指定しました。

    この時点で、シングルノードのk8s環境が準備されます。

    $ microk8s kubectl get pods -A
    NAMESPACE            NAME                                      READY   STATUS    RESTARTS   AGE
    metallb-system       speaker-gnljw                             1/1     Running   0          45s
    metallb-system       controller-559b68bfd8-bkrdz               1/1     Running   0          45s
    kube-system          hostpath-provisioner-5c65fbdb4f-2z9j8     1/1     Running   0          48s
    kube-system          calico-node-bwp2z                         1/1     Running   0          65s
    kube-system          coredns-86f78bb79c-gnd2n                  1/1     Running   0          57s
    kube-system          calico-kube-controllers-847c8c99d-pzvnb   1/1     Running   0          65s
    container-registry   registry-9b57d9df8-bt9tf                  1/1     Running   0          48s
    $ microk8s kubectl get node
    NAME     STATUS   ROLES    AGE   VERSION
    ubuntu   Ready    <none>   10d   v1.20.7-34+df7df22a741dbc
    

    kubectl実行時に毎回microk8sをつけるのは手間なので、下記コマンドでエリアスを設定しました。以降の例ではmicrok8sを省略しています。

    注意 すでに"普通の"kubectlがインストールされていると、そちらが優先されてしまいますので、alias名をkubectl2にするなど衝突しないようにしてください。

    $ sudo snap alias microk8s.kubectl kubectl
    $ kubectl get node
    NAME     STATUS   ROLES    AGE   VERSION
    ubuntu   Ready    <none>   10d   v1.20.7-34+df7df22a741dbc
    

    元の状態に戻すには sudo snap unalias kubectl

    環境が正しく動作することを確認するためにIRISを起動してみます。下記コマンドの実行で、USERプロンプトが表示されるはずです。

    $ kubectl run iris --image=intersystemsdc/iris-community:2022.1.0.209.0-zpm
    $ watch kubectl get pod
    $ kubectl exec -ti iris -- iris session iris
    USER>
    

    今後の作業に備えて、作成したPODを削除しておきます。

    $ kubectl delete pod iris
    

    起動

    $ kubectl apply -f mk8s-iris.yml
    

    IRIS Community版なので、ライセンスキーもコンテナレジストリにログインするためのimagePullSecretsも指定していません。

    しばらくするとポッドが2個作成されます。これでIRISが起動しました。

    $ kubectl get pod
    NAME     READY   STATUS    RESTARTS   AGE
    data-0   1/1     Running   0          107s
    data-1   1/1     Running   0          86s
    $ kubectl get statefulset
    NAME   READY   AGE
    data   2/2     3m32s
    $ kubectl get service
    NAME         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)           AGE
    kubernetes   ClusterIP      10.152.183.1     <none>           443/TCP           30m
    iris         ClusterIP      None             <none>           52773/TCP         8m55s
    iris-ext     LoadBalancer   10.152.183.137   192.168.11.110   52773:31707/TCP   8m55s
    

    この時点で、下記コマンドでirisのREST/APIで提供されているメトリックスを取得できるはずです。

    $ curl http://192.168.11.110:52773/api/monitor/metrics
    

    ポッドのSTATUSがrunningにならない場合、下記コマンドでイベントを確認できます。イメージ名を間違って指定していてPullが失敗したり、なんらかのリソースが不足していることが考えられます。

    $ kubectl describe pod data-0
    
    Events:
      Type     Reason            Age                From               Message
      ----     ------            ----               ----               -------
      Warning  FailedScheduling  4m (x3 over 4m3s)  default-scheduler  0/1 nodes are available: 1 pod has unbound immediate PersistentVolumeClaims.
      Normal   Scheduled         3m56s              default-scheduler  Successfully assigned default/data-0 to ubuntu
      Normal   Pulling           3m56s              kubelet            Pulling image "containers.intersystems.com/intersystems/iris-community:2021.1.0.215.3"
      Normal   Pulled            69s                kubelet            Successfully pulled image "containers.intersystems.com/intersystems/iris-community:2021.1.0.215.3" in 2m46.607639152s
      Normal   Created           69s                kubelet            Created container iris
      Normal   Started           68s                kubelet            Started container iris
    

    下記コマンドでirisにO/S認証でログインできます。

    $ kubectl exec -it data-0 -- iris session iris
    Node: data-0, Instance: IRIS
    USER>
    

    下記で各IRISインスタンスが使用するPVCが確保されていることが確認できます。

    $ kubectl get pvc
    NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS        AGE
    dbv-mgr-data-0    Bound    pvc-fbfdd797-f90d-4eac-83a8-f81bc608d4bc   5Gi        RWO            microk8s-hostpath   12m
    dbv-data-data-0   Bound    pvc-b906a687-c24c-44fc-acd9-7443a2e6fec3   5Gi        RWO            microk8s-hostpath   12m
    dbv-mgr-data-1    Bound    pvc-137b0ccf-406b-40ac-b8c5-6eed8534a6fb   5Gi        RWO            microk8s-hostpath   9m3s
    dbv-data-data-1   Bound    pvc-4f2be4f1-3691-4f7e-ba14-1f0461d59c76   5Gi        RWO            microk8s-hostpath   9m3s
    

    dfを実行すると、データべースファイルを配置するための/vol-dataがマウント対象に表示されていなくて、一瞬、?となりますが、--all指定すると表示されます。

    irisowner@data-0:~$ df --all
    Filesystem     1K-blocks     Used Available Use% Mounted on
      ・
      ・
    /dev/sda2      205310952 26925908 167883056  14% /iris-mgr
    /dev/sda2      205310952 26925908 167883056  14% /vol-data
    /dev/sda2      205310952 26925908 167883056  14% /irissys/cpf
    /dev/sda2      205310952 26925908 167883056  14% /etc/hosts
    /dev/sda2      205310952 26925908 167883056  14% /dev/termination-log
    /dev/sda2      205310952 26925908 167883056  14% /etc/hostname
    /dev/sda2      205310952 26925908 167883056  14% /etc/resolv.conf
      ・
      ・
    

    /dev/sda2はコンテナ内のデバイスではなく、ホスト上のデバイスなので、microk8s-hostpathの仕組み上、そのような表示になるのでしょう。

    個別のポッド上のIRISの管理ポータルにアクセスする

    下記コマンドで各ポッドの内部IPアドレスを確認します。

    $ kubectl get pod -o wide
    NAME     READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
    data-0   1/1     Running   0          46m   10.1.243.202   ubuntu   <none>           <none>
    data-1   1/1     Running   0          45m   10.1.243.203   ubuntu   <none>           <none>
    $
    

    私の仮想環境のLinuxはGUIがありませんので、下記のコマンドを実行することで、Windowsのブラウザから管理ポータルにアクセスできるようにしました。

    $ kubectl port-forward data-0 --address 0.0.0.0 9092:52773
    $ kubectl port-forward data-1 --address 0.0.0.0 9093:52773
    
    対象URLユーザパスワード
    ポッドdata-0上のIRIShttp://192.168.11.49:9092/csp/sys/%25CSP.Portal.Home.zenSuperUserSYS
    ポッドdata-1上のIRIShttp://192.168.11.49:9093/csp/sys/%25CSP.Portal.Home.zenSuperUserSYS

    パスワードはCPFのPasswordHashで指定しています

    データベースの構成を確認してください。下記のデータベースがPV上に作成されていることを確認できます。

    データベース名path
    IRISSYS/iris-mgr/IRIS_conf.d/mgr/
    TEST-DATA/vol-data/TEST-DATA/

    停止

    作成したリソースを削除します。

    $ kubectl delete -f mk8s-iris.yml --wait
    

    これで、IRISのポッドも削除されますが、PVCは保存されたままになっていることに留意ください。これにより、次回に同じ名前のポッドが起動した際には、以前と同じボリュームが提供されます。つまり、ポッドのライフサイクルと、データベースのライフサイクルの分離が可能となります。次のコマンドでPVCも削除出来ます(データベースの内容も永久に失われます)。

    $ kubectl delete pvc -l app=iris
    

    O/Sをシャットダウンする際には下記を実行すると、k8s環境を綺麗に停止します。

    $ microk8s stop
    

    O/S再起動後には下記コマンドでk8s環境を起動できます。

    $ microk8s start
    

    microk8s環境を完全に消去したい場合は、microk8s stopを「実行する前」に下記を実行します。(やたらと時間がかかりました。日頃は実行しなくて良いと思います)

    $ microk8s reset --destroy-storage
    

    観察

    ストレージの場所

    興味本位の観察ではありますが、/iris-mgr/はどこに存在するのでしょう?microk8sはスタンドアロンで起動するk8s環境ですので、storageClassNameがmicrok8s-hostpathの場合、ファイルの実体は同ホスト上にあります。まずはkubectl get pvで、作成されたPVを確認します。

    $ kubectl apply -f mk8s-iris.yml
    $ kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                               STORAGECLASS        REASON   AGE
    pvc-ee660281-1de4-4115-a874-9e9c4cf68083   20Gi       RWX            Delete           Bound    container-registry/registry-claim   microk8s-hostpath            37m
    pvc-772484b1-9199-4e23-9152-d74d6addd5ff   5Gi        RWO            Delete           Bound    default/dbv-data-data-0             microk8s-hostpath            10m
    pvc-112aa77e-2f2f-4632-9eca-4801c4b3c6bb   5Gi        RWO            Delete           Bound    default/dbv-mgr-data-0              microk8s-hostpath            10m
    pvc-e360ef36-627c-49a4-a975-26b7e83c6012   5Gi        RWO            Delete           Bound    default/dbv-mgr-data-1              microk8s-hostpath            9m55s
    pvc-48ea60e8-338e-4e28-9580-b03c9988aad8   5Gi        RWO            Delete           Bound    default/dbv-data-data-1             microk8s-hostpath            9m55s
    

    ここで、data-0ポッドのISC_DATA_DIRECTORYに使用されている、default/dbv-mgr-data-0 をdescribeします。

    $ kubectl describe pv pvc-112aa77e-2f2f-4632-9eca-4801c4b3c6bb
      ・
      ・
    Source:
        Type:          HostPath (bare host directory volume)
        Path:          /var/snap/microk8s/common/default-storage/default-dbv-mgr-data-0-pvc-112aa77e-2f2f-4632-9eca-4801c4b3c6bb
    

    このpathが実体ファイルのありかです。

    $ ls /var/snap/microk8s/common/default-storage/default-dbv-mgr-data-0-pvc-112aa77e-2f2f-4632-9eca-4801c4b3c6bb/IRIS_conf.d/
    ContainerCheck  csp  dist  httpd  iris.cpf  iris.cpf_20210517  _LastGood_.cpf  mgr
    $
    

    storageClassNameにhostpathは使用しないでください。microk8s_hostpathとは異なり、同じフォルダに複数IRISが同居するような状態(破壊された状態)になってしまいます。

    ホスト名の解決

    StatefulSetでは、各ポットにはmetadata.nameの値に従い、data-0, data-1などのユニークなホスト名が割り当てられます。 ポッド間の通信に、このホスト名を利用するために、Headless Serviceを使用しています。

    kind: StatefulSet
    metadata:
      name: data
    
    kind: Service
    spec:
      clusterIP: None # Headless Service
    
    

    この特徴は、ノード間で通信をするShardingのような機能を使用する際に有益です。本例では直接の便益はありません。

    nslookupを使いたいのですが、kubectlやk8sで使用されているコンテナランタイム(ctr)にはdockerのようにrootでログインする機能がありません。また、IRISのコンテナイメージはセキュリティ上の理由でsudoをインストールしていませんので、イメージのビルド時以外のタイミングで追加でソフトウェアをapt install出来ません。ここではbusyboxを追加で起動して、そこでnslookupを使ってホスト名を確認します。

    $ kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
    / # nslookup data-0.iris
    Server:    10.152.183.10
    Address 1: 10.152.183.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      data-0.iris
    Address 1: 10.1.243.202 data-0.iris.default.svc.cluster.local
    / #
    

    10.152.183.10はk8sが用意したDNSサーバです。data-0.irisには10.1.243.202というIPアドレスが割り当てられていることがわかります。FQDNはdata-0.iris.default.svc.cluster.localです。同様にdata-1.irisもDNSに登録されています。

    独自イメージを使用する場合

    現在のk8sはDockerを使用していません。ですので、イメージのビルドを行うためには別途Dockerのセットアップが必要です。

    k8sはあくまで運用環境のためのものです

    サンプルイメージを使用する場合

    イメージはどんな内容でも構いませんが、ここでは例としてsimpleを使用します。このイメージはMYAPPネームスペース上で、ごく簡単なRESTサービスを提供します。データの保存場所をコンテナ内のデータベース(MYAPP-DATA)から外部データベース(MYAPP-DATA-EXT)に切り替えるために、cpfのactionにModifyNamespaceを使用しています。
    mk8s-simple.ymlとしてご用意しました(mk8s-iris.ymlとほとんど同じです)。これを使用して起動します。

    自分でイメージをビルドする場合

    ご自身でビルドを行いたい場合は、下記の手順でmicrok8sが用意した組み込みのコンテナレジストリに、イメージをpushします。

    内容のわからない非公式コンテナイメージって...ちょっと気持ち悪いかも、ですよね。

    (Docker及びdocker-composeのセットアップが済んでいること)

    $ git clone https://github.com/IRISMeister/simple.git
    $ cd simple
    $ ./build.sh
    $ docker tag dpmeister/simple:latest localhost:32000/simple:latest
    $ docker push localhost:32000/simple:latest
    

    このイメージを使用するように、ymlを書き換えます。

    mk8s-simple.ymlを編集
    前)        image: dpmeister/simple:latest
    後)        image: localhost:32000/simple
    

    起動方法

    既にポッドを起動しているのであれば、削除します。

    $ kubectl delete -f mk8s-iris.yml
    $ kubectl delete pvc -l app=iris
    
    $ kubectl apply -f mk8s-simple.yml
    $ kubectl get svc
    NAME         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)           AGE
    kubernetes   ClusterIP      10.152.183.1     <none>           443/TCP           3h36m
    iris         ClusterIP      None             <none>           52773/TCP         20m
    iris-ext     LoadBalancer   10.152.183.224   192.168.11.110   52773:30308/TCP   20m
    $
    $ curl -s -H "Content-Type: application/json; charset=UTF-8" -H "Accept:application/json" "http://192.168.11.110:52773/csp/myapp/get" --user "appuser:SYS" | python3 -mjson.tool
    {
        "HostName": "data-1",
        "UserName": "appuser",
        "Status": "OK",
        "TimeStamp": "05/17/2021 19:34:00",
        "ImageBuilt": "05/17/2021 10:06:27"
    }
    

    curlの実行を繰り返すと、HostName(RESTサービスが動作したホスト名)がdata-0だったりdata-1だったりしますが、これは(期待通りに)ロードバランスされているためです。

    まれにログインに失敗したり、待たされることがあります。Community EditionはMAX 5セッションまでですが、以前の何らかの操作によりその上限を超えてしまっている可能性があります。その場合、ライセンス上限を超えた旨のメッセージがログされます。

    $ kubectl logs data-0
      ・
      ・
    05/17/21-19:21:17:417 (2334) 2 [Generic.Event] License limit exceeded 1 times since instance start.
    

    Longhornを使用する場合

    分散KubernetesストレージLonghornについては、こちらを参照ください。

    IRISのようなデータベース製品にとってのメリットは、クラウド環境でアベーラビリティゾーンをまたいだデータベースの冗長構成を組めることにあります。アベーラビリティゾーン全体の停止への備えは、ミラー構成を組むことで実現しますが、Kubernetes環境であれば、分散Kubernetesストレージの採用という選択子が増えます。

    ミラー構成とは異なり、データベース以外のファイルも保全できるというメリットもあります。ただしパフォーマンスへの負のインパクトには要注意です。

    起動方法

    longhornを起動し、すべてのポッドがREADYになるまで待ちます。

    $ kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.3/deploy/longhorn.yaml
    $ kubectl -n longhorn-system get pods
    NAME                                       READY   STATUS    RESTARTS   AGE
    longhorn-ui-5b864949c4-72qkz               1/1     Running   0          4m3s
    longhorn-manager-wfpnl                     1/1     Running   0          4m3s
    longhorn-driver-deployer-ccb9974d5-w5mnz   1/1     Running   0          4m3s
    instance-manager-e-5f14d35b                1/1     Running   0          3m28s
    instance-manager-r-a8323182                1/1     Running   0          3m28s
    engine-image-ei-611d1496-qscbp             1/1     Running   0          3m28s
    csi-attacher-5df5c79d4b-gfncr              1/1     Running   0          3m21s
    csi-attacher-5df5c79d4b-ndwjn              1/1     Running   0          3m21s
    csi-provisioner-547dfff5dd-pj46m           1/1     Running   0          3m20s
    csi-resizer-5d6f844cd8-22dpp               1/1     Running   0          3m20s
    csi-provisioner-547dfff5dd-86w9h           1/1     Running   0          3m20s
    csi-resizer-5d6f844cd8-zn97g               1/1     Running   0          3m20s
    csi-resizer-5d6f844cd8-8nmfw               1/1     Running   0          3m20s
    csi-provisioner-547dfff5dd-pmwsk           1/1     Running   0          3m20s
    longhorn-csi-plugin-xsnj9                  2/2     Running   0          3m19s
    csi-snapshotter-76c6f569f9-wt8sh           1/1     Running   0          3m19s
    csi-snapshotter-76c6f569f9-w65xp           1/1     Running   0          3m19s
    csi-attacher-5df5c79d4b-gcf4l              1/1     Running   0          3m21s
    csi-snapshotter-76c6f569f9-fjx2h           1/1     Running   0          3m19s
    

    mk8s-iris.ymlの全て(2箇所あります)のstorageClassNameをlonghornに変更してください。 もし、microk8s_hostpathで既に起動しているのであれば、ポッド、PVCともに全て削除したうえで、上述の手順を実行してください。つまり...

    $ kubectl delete -f mk8s-iris.yml --wait
    $ kubectl delete pvc -l app=iris
    
    mk8s-iris.yml編集
    前)storageClassName: microk8s-hostpath
    後)storageClassName: longhorn
    
    $ kubectl apply -f mk8s-iris.yml
    

    マウントしたLonghorn由来のボリュームのオーナがrootになっていたのでsecurityContext:fsGroupを指定しています。これ無しでは、データベース作成時にプロテクションエラーが発生します。
    fsGroup指定なしの場合

    $ kubectl exec -it data-0 -- ls / -l
    drwxr-xr-x   3 root      root         4096 May 18 15:40 vol-data
    

    fsGroup指定ありの場合

    $ kubectl exec -it data-0 -- ls / -l
    drwxrwsr-x   4 root      irisowner     4096 Jan  5 17:09 vol-data
    

    2021.1まではfsGroup:52773を指定すると動きましたが、2022.1以後はfsGroup:51773を指定すると動きました。

    下記を実行すれば、Windowsのブラウザから、Longhorn UIを参照できます。

    $ microk8s enable ingress
    $ kubectl apply -f longhorn-ui-ingress.yml
    

    ポート80を他の用途に使ってる場合、下記のようにport-forwardを使う方法もあります。この場合ポートは8080なので、URLはこちらになります。

    $ kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80 --address 0.0.0.0
    

    UIで、VolumeのStateが"Degraded"になっていますが、これはReplicaの数がnumberOfReplicasの既定値3を満たしていないためです。

    以降の操作は、同様です。不要になれば削除します。

    $ kubectl delete -f mk8s-iris.yml
    $ kubectl delete pvc --all
    

    削除方法

    Longhorn環境が不要になった場合は、下記のコマンドで削除しておくと良いようです(いきなりdeleteしてはダメ)。

    $ kubectl create -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.3/uninstall/uninstall.yaml
    $ kubectl get job/longhorn-uninstall -n default -w
    NAME                 COMPLETIONS   DURATION   AGE
    longhorn-uninstall   1/1           79s        97s
    ^C
    $ kubectl delete -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.3/deploy/longhorn.yaml
    $ kubectl delete -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.3/uninstall/uninstall.yaml
    

    apply時のエラー

    Longhornの前回の使用時に綺麗に削除されなかった場合に、apply時に下記のようなエラーが出ることがあります。

    $ kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
      ・
      ・
    Error from server (Forbidden): error when creating "https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml": serviceaccounts "longhorn-service-account" is forbidden: unable to create new content in namespace longhorn-system because it is being terminated
    Error from server (Forbid
    

    上記のuninstall.yamlを使った削除手順をもう一度実行したら回復しました。

    その他気づいた事

    storageClassにmicrok8s_hostpathを指定した場合、マルチノード環境ではsecurityContext:fsGroupが正しく機能しないようです。その結果、下記のようなエラーが発生して、データベースの作成に失敗します(Error=-13はPermission denieです)。longhornは問題なく動作しました。

    01/07/22-23:11:32:729 (1205) 1 [Utility.Event] ERROR #503: Error executing [Actions] section in file /iris-mgr/IRIS_conf.d/merge_actions.cpf
    01/07/22-23:11:32:729 (1205) 1 [Utility.Event] ERROR #507: Action 'CreateDatabase' failed at line 2, Method Config.CPF:CreateOneDatabase(), Error=ERROR #5032: Cannot create directory '/vol-data/db/TEST-DATA/, Error=-13'
    

    InterSystems Kubernetes Operator

    IKOもmicrok8sで動作しますが、Community向けの機能ではないので、今回のご紹介は見送りました。

    0
    0 4769
    InterSystems公式 Toshihiko Minamoto · 4月 24, 2021

    みなさん、こんにちは。
    今回はInterSystems Container Registryを発表できることをうれしく思います。 これはコンテナベースのリリースやプレビューにアクセスする新たな配布チャンネルです。すべてのコミュニティエディションのイメージはログイン不要の公開リポジトリにあります。すべてのリリースイメージ(IRIS, IRIS for Health, Health Connect, System Alerting and Monitoring, InterSystems Cloud Manager) やユーティリティイメージ(アービター、 Web Gateway、PasswordHash等) にはWRCアカウントの認証情報から生成されるログイントークンが必要です。

    0
    0 383
    記事 Toshihiko Minamoto · 3月 17, 2021 31m read

    この記事では、従来のIRISミラーリング構成の代わりに、Kubernetesの Deploymentと分散永続ストレージを使って高可用性IRIS構成を構築します。 このデプロイでは、ノード、ストレージ、アベイラビリティーゾーンといったインフラストラクチャ関連の障害に耐えることが可能です。 以下に説明する方法を使用することで、RTOがわずかに延長されますが、デプロイの複雑さが大幅に軽減されます。

    図1 - 従来のミラーリング構成と分散ストレージを使ったKubernetesの比較

    この記事に記載されているすべてのソースコードは、https://github.com/antonum/ha-iris-k8s より入手できます。
    要約

    3ノードクラスタを実行しており、Kubernetesにいくらか詳しい方は、このまま以下のコードを使用してください。

    kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
    kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml

    上記の2行の意味がよくわからない方、またはこれらを実行できるシステムがない場合は、「高可用性要件」のセクションにお進みください。 説明しながら進めていきます。

    最初の行は、Longhornというオープンソースの分散Kubernetesストレージをインストールしています。 2行目は、DurableSYS用にLonghornベースのボリュームを使って、InterSystems IRISデプロイをインストールしています。

    すべてのポッドが実行状態になるまで待ちます。 kubectl get pods -A

    上記の手順を済ませると、http://<IRIS Service Public IP>:52773/csp/sys/%25CSP.Portal.Home.zenにあるIRIS管理ポータル(デフォルトのパスワードは「SYS」)にアクセスできるようになります。また、次のようにしてIRISコマンドラインも使用できるようになります。

    kubectl exec -it iris-podName-xxxx -- iris session iris

    障害をシミュレートする

    では、実際に操作してみましょう。 ただし、操作の前に、データベースにデータを追加して、IRISがオンラインになったときに存在することを確認してください。

    kubectl exec -it iris-6d8896d584-8lzn5 -- iris session iris
    USER>set ^k8stest($i(^k8stest))=$zdt($h)_" running on "_$system.INetInfo.LocalHostName()
    USER>zw ^k8stest
    ^k8stest=1
    ^k8stest(1)="01/14/2021 14:13:19 running on iris-6d8896d584-8lzn5"

    ここからが「カオスエンジニアリング」の始まりです。

    # IRISを停止 - コンテナは自動的に再開します
    kubectl exec -it iris-6d8896d584-8lzn5 -- iris stop iris quietly
     
    # ポッドを削除 - ポッドが再作成されます
    kubectl delete pod iris-6d8896d584-8lzn5
     
    # irisポッドの配信によりノードを「強制ドレイン」 - ポッドは別のノードで再作成されます
    kubectl drain aks-agentpool-29845772-vmss000001 --delete-local-data --ignore-daemonsets --force
     
    # ノードを削除 - ポッドは別のノードで再作成されます
    # ただし、kubectlでは削除できないので、 そのインスタンスまたはVMを見つけて、強制終了します。
    マシンにアクセスできる場合は、電源を切るかネットワークケーブルを外します。 冗談ではありません!

    高可用性要件

     

    以下の障害に耐えられるシステムを構築しています。

    • コンテナ/VM内のIRISインスタンス。 IRIS - レベル障害
    • ポッド/コンテナの障害
    • 個々のクラスタノードの一時的な使用不能。 アベイラビリティーゾーンが一時的にオフラインになる場合などが挙げられます。
    • 個々のクラスタノードまたはディスクの永久的障害。

    基本的に、「障害をシミュレートする」セクションで試したシナリオです。

    上記のいずれかの障害が発生すると、システムは人間が手をいれなくてもオンラインになり、データも失われません。 データの永続性が保証する範囲には一応制限がありますが、 ジャーナルサイクルとアプリケーション内のトランザクションの使用状況に応じて、IRISで実現されます(https://docs.intersystems.com/irisforhealthlatestj/csp/docbook/Doc.View.cls?KEY=GCDI_journal#GCDI_journal_writecycle)。いずれにしても、RPO(目標復旧ポイント)は2秒未満です。

    システムの他のコンポーネント(Kubernetes APIサービス、etcdデータベース、ロードバランサーサービス、DNSなど)はスコープ外であり、通常、Azure AKSやAWS EKSなどのマネージドKubernetesサービスで管理されます。

    別の言い方をすれば、個別の計算コンポーネントやストレージコンポーネントの障害はユーザーが処理するものであり、その他のコンポーネントの障害はインフラストラクチャ/クラウドプロバイダーが対処するものと言えます。

    アーキテクチャ

    InterSystems IRISの高可用性について言えば、これまでミラーリングの使用が勧められてきました。 ミラーリングでは、データは、常時オンライン状態にある2つのIRISインスタンスが同期的に複製されます。 各ノードにはデータベースの完全なコピーが維持され、プライマリノードがオフラインになると、ユーザーはバックアップノードに接続し直すことができます。 基本的に、ミラーリング手法では、計算とストレージの両方の冗長性は、IRISが管理するものです。

    ミラーをさまざまなアベイラビリティーゾーンにデプロイしたミラーリングにより、計算処理とストレージの障害に備えた必要な冗長性を実現し、わずか数秒という優れたRTO(目標復旧時間または障害後にシステムがオンラインに復旧するまでにかかる時間)を達成することができます。 AWSクラウドでミラーリングされたIRISのデプロイテンプレートは、https://jp.community.intersystems.com/node/486206から入手できます。

    一方で、ミラーリングには、設定やバックアップ/復旧手順が複雑で、セキュリティ設定とローカルのデータベース以外のファイルの複製機能が不足しているという欠点があります。

    コンテナオーケストレータ―にはKubernetesなど(今や2021年。ほかにオーケストレーターってありますか?!)がありますが、これらは、障害時に、Deploymentオブジェクトが障害のあるIRISポッド/コンテナを自動的に再起動することで、計算冗長性を実現しています。 Kubernetesアーキテクチャ図に、実行中のIRISノードが1つしかないのはこのためです。 2つ目のIRISノードを常時実行し続ける代わりに、計算可用性をKubernetesにアウトソースしています。 Kubernetesは、元のIRISポッドが何らかの理由でエラーとなった場合に、IRISポッドの再作成を確保します。

    図2 フェイルオーバーのシナリオ

    ここまでよろしいでしょうか。 IRISノードに障害が発生すると、Kubernetesは単に新しいノードを作成します。 クラスタによって異なりますが、計算障害が発生してからIRISがオンラインになるまでには、10~90秒かかります。 ミラーリングではわずか2秒で復旧されるわけですから、これはダウングレードとなりますが、万が一サービス停止となった場合に許容できる範囲であれば、複雑さが緩和されることは大きなメリットと言えます。 ミラーリングの構成は不要です。 セキュリティ設定やファイル複製を気にする必要もありません。

    率直に言うと、KubernetesでIRISを実行し、コンテナにログインしても、高可用性環境で実行していることには気づくこともないでしょう。 単一インスタンスのIRISデプロイと全く同じように見え、何ら感触にも変化はありません。

    では、ストレージはどうでしょうか。 データベースを扱っているわけですから気になります。 どのようなフェイルオーバーのシナリオであっても、システムはデータの永続性も処理する必要があります。 ミラーリングは、IRISノードのローカルである計算ノードに依存しているため、 ノードが停止するか、一時的に使用不可になってしまえば、そのノードのストレージも停止してしまいます。 ミラーリング構成では、IRISがIRISレベルでデータベースを複製するのはこれに対処するためです。

    コンテナの再起動時に元の状態を維持したデータベースを使用できるだけでなく、ノードやネットワークのセグメント全体(アベイラビリティーゾーン)が停止するといったイベントに備えて、冗長性を提供できるストレージが必要です。 ほんの数年前、これを解決できる簡単な答えは存在しませんでした。 上の図から推測できるように、今ではその答えを得ています。 分散コンテナストレージです。

    分散ストレージは、基盤のホストボリュームを抽象化し、k8sクラスタのすべてのノードが使用できる共同の1つのクラスターとして提示します。 この記事では、インストールが非常に簡単なLonghorn(https://longhorn.io)という無料のオープンソースのストレージを使用しますが、 OpenEBS、Porworx、StorageOSといったほかのストレージも利用できます。同じ機能が提供されています。 CNCF IncubatingプロジェクトであるRook Cephも検討する価値があるでしょう。 この種のハイエンドとしては、NetAppやPureStorageといったエンタープライズ級のストレージソリューションがあります。

    手順

    「要約」セクションでは、1回にすべてをインストールしました。 インストールと検証の手順の説明は、付録Bをご覧ください。

    Kubernetesストレージ

    まずは、コンテナとストレージ全般について、またIRISがどこに適合するのかについて説明しましょう。

    デフォルトでは、コンテナ内のすべてのデータは揮発性であるため、 コンテナが停止すればデータは消失します。 Dockerでは、ボリュームの概念を使用することができるため、 基本的に、ホストOSのディレクトリをコンテナに公開することができます。

    docker run --detach
      --publish 52773:52773
      --volume /data/dur:/dur
      --env ISC_DATA_DIRECTORY=/dur/iconfig
      --name iris21 --init intersystems/iris:2020.3.0.221.0

    上記の例では、IRISコンテナを起動し、host-localの「/data/dur」ディレクトリを、「/dur」マウントポイントのコンテナからアクセスできるようにしています。 つまり、コンテナがこのディレクトリに何かを格納している場合、それは保持され、コンテナの次回起動時に使用できるようになります。

    IRIS側では、ISC_DATA_DIRECTORYを指定することで、IRISに、コンテナの再起動時に損失してはいけないすべてのデータを特定のディレクトリに格納するように指示することができます。 ドキュメントの「Durable SYS」というIRISの機能をご覧ください( https://docs.intersystems.com/irisforhealthlatestj/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_durable_running)。

    Kubernetesでの構文は異なりますが、概念は同じです。

    IRISの基本的なKubernetes Deploymentは以下のようになります。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: iris
    spec:
      selector:
        matchLabels:
          app: iris
      strategy:
        type: Recreate
      replicas: 1
      template:
        metadata:
          labels:
            app: iris
        spec:
          containers:
          - image: store/intersystems/iris-community:2020.4.0.524.0
            name: iris
            env:
            - name: ISC_DATA_DIRECTORY
              value: /external/iris
            ports:
            - containerPort: 52773
              name: smp-http
            volumeMounts:
            - name: iris-external-sys
              mountPath: /external
          volumes:
          - name: iris-external-sys
            persistentVolumeClaim:
              claimName: iris-pvc

     

    上記のデプロイ仕様では、「volumes」の部分にストレージボリュームがリストされています。 このボリュームには、「iris-pvc」などのpersistentVolumeClaimを介して、コンテナの外部からアクセスできます。 volumeMountsは、このボリュームをコンテナ内に公開します。 「iris-external-sys」は、ボリュームマウントを特定のボリュームに関連付ける識別子です。 実際には複数のボリュームが存在する可能性があり、ほかのボリュームと区別するためにこの名前が使用されるわけですから、 「steve」と名付けることも可能です。

    すでにお馴染みのISC_DATA_DIRECTORYは、IRISが特定のマウントポイントを使用して、コンテナの再起動後も存続する必要のあるすべてのデータを格納するように指示する環境変数です。

    では、persistentVolumeClaimのiris-pvcを見てみましょう。

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: iris-pvc
    spec:
      storageClassName: longhorn
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi

     

    かなりわかりやすいと思います。 「longhorn」ストレージクラスを使って、1つのノードでのみ読み取り/書き込みとしてマウント可能な、10ギガバイトを要求しています。

    ここで重要なのは、storageClassName: longhornです。

    AKSクラスタで利用できるストレージクラスを確認してみましょう。

    kubectl get StorageClass
    NAME                             PROVISIONER                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    azurefile                        kubernetes.io/azure-file        Delete          Immediate           true                   10d
    azurefile-premium                kubernetes.io/azure-file        Delete          Immediate           true                   10d
    default (default)                kubernetes.io/azure-disk        Delete          Immediate           true                   10d
    longhorn                         driver.longhorn.io              Delete          Immediate           true                   10d
    managed-premium                  kubernetes.io/azure-disk        Delete          Immediate           true                   10d

    Azureのストレージクラスはほとんどありませんが、これらは、デフォルトでインストールされたクラスと、以下の一番最初のコマンドの一部でインストールしたLonghornのクラスです。

    kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml

    永続ボリュームクレームの定義に含まれる#storageClassName: longhornをコメントアウトすると、現在「default」としてマークされているストレージクラスが使用されます。これは、通常のAzureディスクです。

    分散ストレージが必要な理由を説明するために、この記事の始めに説明した、longhornストレージを使用しない「カオスエンジニアリング」実験をもう一度行ってみましょう。 最初の2つのシナリオ(IRISの停止とポッドの削除)は正常に完了し、システムは稼働状態に回復します。 ノードをドレインまたは強制終了しようとすると、システムは障害状態になります。

    #forcefully drain the node
    kubectl drain aks-agentpool-71521505-vmss000001 --delete-local-data --ignore-daemonsets
    

    kubectl describe pods ...   Type     Reason            Age                  From               Message   ----     ------            ----                 ----               -------   Warning  FailedScheduling  57s (x9 over 2m41s)  default-scheduler  0/3 nodes are available: 1 node(s) were unschedulable, 2 node(s) had volume node affinity conflict.

    基本的に、KubernetesはクラスタのIRISポッドを再起動しようとしますが、最初に起動されていたノードは利用できないため、ほかの2つのノードに「ボリュームノードアフィニティの競合」が発生しています。 このストレージタイプでは、基本的にボリュームはノードホストで使用可能なディスクに関連付けられているため、最初に作成されたノードでしか使用できないのです。

    ストレージクラスにlonghornを使用すると、「強制ドレイン」と「ノードの強制終了」の2つの実験は正常に完了し、間もなくしてIRISポッドの動作が再開します。 これを実現するために、Longhornは3ノードクラスタをで使用可能なストレージを制御し、3つのすべてのノードにデータを複製しています。 ノードの1つが永久的に使用不可になると、Longhornは迅速にクラスタストレージを修復します。 「ノードの強制終了」シナリオでは、IRISポッドはほかの2つのボリュームレプリカを使ってすぐに別のノードで再起動されます。 するとAKSは失われたノードに置き換わる新しいノードをプロビジョニングし、準備ができたら、Longhorn がそのノードに必要なデータを再構築します。 すべては自動的に行われるため、ユーザーが手を入れる必要はありません。

    図3 置換されたノードにボリュームレプリカを再構築するLonghorn

    k8sデプロイについて

    デプロイの他の側面をいくつか見てみましょう。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: iris
    spec:
      selector:
        matchLabels:
          app: iris
      strategy:
        type: Recreate
      replicas: 1
      template:
        metadata:
          labels:
            app: iris
        spec:
          containers:
          - image: store/intersystems/iris-community:2020.4.0.524.0
            name: iris
            env:
            - name: ISC_DATA_DIRECTORY
              value: /external/iris
            - name: ISC_CPF_MERGE_FILE
              value: /external/merge/merge.cpf
            ports:
            - containerPort: 52773
              name: smp-http
            volumeMounts:
            - name: iris-external-sys
              mountPath: /external
            - name: cpf-merge
              mountPath: /external/merge
            livenessProbe:
              initialDelaySeconds: 25
              periodSeconds: 10
              exec:
                command:
                - /bin/sh
                - -c
                - "iris qlist iris | grep running"
          volumes:
          - name: iris-external-sys
            persistentVolumeClaim:
              claimName: iris-pvc
          - name: cpf-merge
            configMap:
              name: iris-cpf-merge

     

    strategy: Recreateとreplicas: 1では、Kubernetesに、常にIRISポッドの1つのインスタンスのみを実行し続けることを指示しています。 これが「ポッドの削除」シナリオを処理する部分です。

    livenessProbeのセクションでは、IRISが常時、コンテナ内で稼働し、「IRIS停止」シナリオを処理できるようにしています。 initialDelaySecondsは、IRISが起動するまでの猶予期間です。 IRISがデプロイを起動するのにかなりの時間が掛かっている場合は、これを増やすと良いでしょう。

    IRISのCPF MERGE機能を使用すると、コンテナの起動時に、iris.cpf構成ファイルのコンテンツを変更することができます。 関連するドキュメントについては、 https://docs.intersystems.com/irisforhealthlatestj/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_mergeをご覧ください。 この例では、Kubernetesの構成図を使って、マージファイルのコンテンツを管理しています( https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml)。ここでは、IRISインスタンスが使用するグローバルバッファとgmheapの値を調整していますが、iris.cpfファイルにあるものはすべて調整できます。 デフォルトのIRISパスワードでさえも、CPFマージファイルの「PasswordHash」フィールドで変更可能です。 詳細については、https://docs.intersystems.com/irisforhealthlatestj/csp/docbook/Doc.View.cls?KEY=ADOCK#ADOCK_iris_images_password_authをご覧ください。

    永続ボリュームクレーム(https://github.com/antonum/ha-iris-k8s/blob/main/iris-pvc.yaml)デプロイ(https://github.com/antonum/ha-iris-k8s/blob/main/iris-deployment.yaml)とCPFマージコンテンツを使った構成図(https://github.com/antonum/ha-iris-k8s/blob/main/iris-cpf-merge.yaml)のほかに、デプロイには、IRISデプロイをパブリックインターネットに公開するサービスが必要です(https://github.com/antonum/ha-iris-k8s/blob/main/iris-svc.yaml)。

    kubectl get svc
    NAME         TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)           AGE
    iris-svc     LoadBalancer   10.0.18.169   40.88.123.45   52773:31589/TCP   3d1h
    kubernetes   ClusterIP      10.0.0.1      <none>          443/TCP           10d

    iris-svcの外部IPは、http://40.88.123.45:52773/csp/sys/%25CSP.Portal.Home.zenを介してIRIS管理ポータルにアクセスするために使用できます。 デフォルトのパスワードは「SYS」です。

    ストレージのバックアップ/復元とスケーリング

    Longhornには、ボリュームの構成と管理を行えるウェブベースのUIがあります。

    kubectlを使用して、ポッドや実行中のlonghorn-uiコンポーネントを特定し、ポート転送を確立できます。

    kubectl -n longhorn-system get pods
    # longhorn-uiのポッドIDに注意してください。
    

    kubectl port-forward longhorn-ui-df95bdf85-gpnjv 9000:8000 -n longhorn-system

    Longhorn UIは、http://localhost:9000で利用できるようになります。

    図4 LonghornのUI

    Kubernetesコンテナストレージのほとんどのソリューションでは、高可用性のほか、バックアップ、スナップショット、および復元のための便利なオプションが用意されています。 詳細は実装によって異なりますが、一般的には、バックアップをVolumeSnapshotに関連付ける方法です。 この方法はLonghornでも利用できます。 使用しているKubernetesのバージョンとプロバイダーによっては、ボリュームスナップショット機能( https://github.com/kubernetes-csi/external-snapshotter)もインストールする必要があります。

    そのようなボリュームショットの例には、「iris-volume-snapshot.yaml」が挙げられます。 使用する前に、バックアップを、LonghornのS3バケットまたはNFSボリュームに構成する必要があります。 https://longhorn.io/docs/1.0.1/snapshots-and-backups/backup-and-restore/set-backup-target/

    # IRISボリュームのクラッシュコンシステントなバックアップを取得する
    kubectl apply -f iris-volume-snapshot.yaml

    IRISでは、バックアップ/スナップショットを取得する前にExternal Freezeを実行し、後でThawを実行することをお勧めします。 詳細については、https://docs.intersystems.com/irisforhealthlatestj/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=Backup.General#ExternalFreezeをご覧ください。  

    IRISボリュームのサイズを増やすには、IRISが使用する、永続ボリュームクレーム(iris-pvc.yamlファイル)のストレージリクエストを調整します。

    ...
      resources:
        requests:
          storage: 10Gi #change this value to required

    そして、pvcの仕様を再適用します。 Longhornは、実行中のポッドにボリュームが接続されている場合、この変更を実際に適用することはできません。 そのため、ボリュームサイズが増えるように、デプロイでレプリカ数を一時的にゼロに変更します。

    高可用性 - 概要

    この記事の冒頭で、高可用性の基準を設定しました。 このアーキテクチャでは、次のようにそれを実現します。

    障害の領域

    自動的に緩和処置を行う機能

    コンテナ/VM内のIRISインスタンス。 IRIS - レベル障害

    IRISが機能停止となった場合、デプロイのLiveness Probeによってコンテナが再起動されます。

    ポッド/コンテナの障害

    デプロイによってポッドが再作成されます。

    個々のクラスタノードの一時的な使用不能。 アベイラビリティーゾーンがオフラインになる場合などが挙げられます。

    デプロイによって別のノードにポッドが再作成されます。 Longhornによって、別のノードでデータが使用できるようになります。

    個々のクラスタノードまたはディスクの永久的障害。

    上記と同様かつ、k8sクラスタオートスケーラーによって、破損したノードが新しいノードに置き換えられます。 Longhornによって、新しいノードにデータが再構築されます。

    ゾンビやその他の考慮事項

    DockerコンテナでのIRISの実行を理解している方であれば、「--init」フラグを使用したことがあるでしょう。

    docker run --rm -p 52773:52773 --init store/intersystems/iris-community:2020.4.0.524.0

    このフラグは、「ゾンビプロセス」の形成を防止することを目的としています。 Kubernetesでは、「shareProcessNamespace: true」を使用する(セキュリティの考慮事項が適用されます)か、コンテナで「tini」を使用することができます。 以下に、tiniを使用したDockerfileの例を示します。

    FROM iris-community:2020.4.0.524.0
    ...
    # Tiniを追加します
    USER root
    ENV TINI_VERSION v0.19.0
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
    RUN chmod +x /tini
    USER irisowner
    ENTRYPOINT ["/tini", "--", "/iris-main"]

    2021年以降、すべてのInterSystemsが提供するコンテナイメージには、デフォルトでtiniが含まれています。

    いくつかのパラメーターを調整することで、「ノードの強制ドレイン/ノードの強制終了」シナリオのフェイルオーバー時間をさらに短縮することができます。

    Longhornのポッド削除ポリシー(https://longhorn.io/docs/1.1.0/references/settings/#pod-deletion-policy-when-node-is-down)およびkubernetes taintベースのエビクション機能(https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/#taint-based-evictions)をご覧ください。

    免責事項

    InterSystemsに勤務する者として、この内容を記載しておく必要があります。この記事では、分散型Kubernetesブロックストレージの一例としてLonghornを使用しています。 InterSystemsは、個々のストレージソリューションや製品を検証ないしは公式なサポート声明を発行していません。 特定のストレージソリューションがニーズに適合しているかどうかは、ユーザー自身がテストして検証する必要があります。

    分散ストレージには、ノードローカルストレージとは大きく異なるパフォーマンス特性も備わっています。 特に書き込み操作の場合、データが永続化された状態とみなされるには、データを複数の場所に書き込む必要があります。 必ずワークロードをテストし、CSIドライバが提供する特定の動作とオプションを理解するようにしてください。

    InterSystemsでは基本的に、Longhornなどの特定のストレージjソリューションを検証あるいは承認していません。これは、個々のHDDブランドやサーバーハードウェアメーカーを検証しないのと同じです。 個人的には、Longhornは扱いやすいと感じています。プロジェクトのGitHubページ(https://github.com/longhorn/longhorn)では、その開発チームは積極的に応答し、よく助けていただいています。 

    まとめ

    Kubernetesエコシステムは、過去数年間で大きな進化を遂げました。今日では、分散ブロックストレージソリューションを使用することで、IRISインスタンスやクラスタノード、さらにはアベイラビリティーゾーンの障害を維持できる高可用性構成を構築できるようにもなっています。

    計算とストレージの高可用性をKubernetesコンポーネントにアウトソースできるため、従来のIRISミラーリングに比べ、システムの構成と管理が大幅に単純化しています。 ただしこの構成では、ミラーリング構成ほどのRTOとストレージレベルのパフォーマンスは得られないことがあります。

    この記事では、Azure AKSをマネージドKubernetesとLonghorn分散ストレージシステムとして使用し、可用性の高いIRIS構成を構築しました。 ほかにも、AWS EKS、マネージドK8s向けのGoogle Kubernetes Engine、StorageOS、Portworx、OpenEBSなどの様々な分散コンテナストレージを探ってみると良いでしょう。エンタープライズ級のストレージソリューションには、NetApp、PureStorage、Dell EMCなどもあります。

    付録A: クラウドでのKubernetesクラスタの作成

    パブリッククラウドプロバイダーが提供するマネージドKubernetesサービスを使うと、このセットアップに必要なk8sクラスタを簡単に作成できます。 この記事で説明したデプロイには、AzureのAKSデフォルト構成をそのまますぐに使用することができます。

    3ノードで新しいAKSクラスタを作成します。 それ以外はすべてデフォルトのままにします。

    図5 AKSクラスタの作成

    ローカルコンピュータにkubectlをインストールします。https://kubernetes.io/docs/tasks/tools/install-kubectl/

    ローカルkubectlにASKクラスタを登録します。

     

    図6 kubectlにAKSクラスタを登録

    登録が済んだら、この記事の最初に戻って、longhornとIRISデプロイをインストールします。

    AWS EKSでのインストールは、もう少し複雑です。 ノードグループのすべてのインスタンスにopen-iscsiがインストトールされていることを確認する必要があります。

    sudo yum install iscsi-initiator-utils -y

    GKEでのLonghornのインストールには追加の手順があります。https://longhorn.io/docs/1.0.1/advanced-resources/os-distro-specific/csi-on-gke/をご覧ください。

    付録B: インストール手順

    手順1 - Kubernetesクラスタとkubectl

    3ノードk8sクラスタが必要です。 Azureでのクラスタの構成方法は付録Aをご覧ください。

    $ kubectl get nodes
    NAME                                STATUS   ROLES   AGE   VERSION
    aks-agentpool-29845772-vmss000000   Ready    agent   10d   v1.18.10
    aks-agentpool-29845772-vmss000001   Ready    agent   10d   v1.18.10
    aks-agentpool-29845772-vmss000002   Ready    agent   10d   v1.18.10

    手順2 - Longhornをインストールする

    kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml

    「longhorn-system」ネームスペースのすべてのポッドが実行状態であることを確認してください。 これには数分かかる場合があります。

    $ kubectl get pods -n longhorn-system
    NAME                                       READY   STATUS    RESTARTS   AGE
    csi-attacher-74db7cf6d9-jgdxq              1/1     Running   0          10d
    csi-attacher-74db7cf6d9-l99fs              1/1     Running   1          11d
    ...
    longhorn-manager-flljf                     1/1     Running   2          11d
    longhorn-manager-x76n2                     1/1     Running   1          11d
    longhorn-ui-df95bdf85-gpnjv                1/1     Running   0          11d

    詳細とトラブルシューティングについては、Longhornインストールガイド(https://longhorn.io/docs/1.1.0/deploy/install/install-with-kubectl)をご覧ください。

    手順3 - GitHubリポジトリのクローンを作成する

    $ git clone https://github.com/antonum/ha-iris-k8s.git
    $ cd ha-iris-k8s
    $ ls
    LICENSE                   iris-deployment.yaml      iris-volume-snapshot.yaml
    README.md                 iris-pvc.yaml             longhorn-aws-secret.yaml
    iris-cpf-merge.yaml       iris-svc.yaml             tldr.yaml

    手順4 - コンポーネントを1つずつデプロイして検証する

    tldr.yamlファイルには、デプロイに必要なすべてのコンポーンネントが1つのバンドルとして含まれています。 ここでは、コンポーネントを1つずつインストールし、それぞれのセットアップを個別に検証します。

    # 以前にtldr.yamlを適用したことがある場合は、削除します。
    $ kubectl delete -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml
    

    永続ボリュームクレームを作成します

    $ kubectl apply -f iris-pvc.yaml persistentvolumeclaim/iris-pvc created

    $ kubectl get pvc NAME       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE iris-pvc   Bound    pvc-fbfaf5cf-7a75-4073-862e-09f8fd190e49   10Gi       RWO            longhorn       10s

    構成図を作成します

    $ kubectl apply -f iris-cpf-merge.yaml

    $ kubectl describe cm iris-cpf-merge Name:         iris-cpf-merge Namespace:    default Labels:       <none> Annotations:  <none>

    Data

    merge.cpf:

    [config] globals=0,0,800,0,0,0 gmheap=256000 Events:  <none>

    irisデプロイを作成します

    $  kubectl apply -f iris-deployment.yaml deployment.apps/iris created

    $ kubectl get pods                     NAME                    READY   STATUS              RESTARTS   AGE iris-65dcfd9f97-v2rwn   0/1     ContainerCreating   0          11s

    ポッド名を書き留めます。 この名前は、次のコマンドでポッドに接続する際に使用します。

    $ kubectl exec -it iris-65dcfd9f97-v2rwn   -- bash

    irisowner@iris-65dcfd9f97-v2rwn:~$ iris session iris Node: iris-65dcfd9f97-v2rwn, Instance: IRIS

    USER>w $zv IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2020.4 (Build 524U) Thu Oct 22 2020 13:04:25 EDT

    h<enter>でIRISシェルを終了

    exit<enter>でポッドを終了

    IRISコンテナのログにアクセスします

    $ kubectl logs iris-65dcfd9f97-v2rwn ... [INFO] ...started InterSystems IRIS instance IRIS 01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Private webserver started on 52773 01/18/21-23:09:11:312 (1173) 0 [Utility.Event] Processing Shadows section (this system as shadow) 01/18/21-23:09:11:321 (1173) 0 [Utility.Event] Processing Monitor section 01/18/21-23:09:11:381 (1323) 0 [Utility.Event] Starting TASKMGR 01/18/21-23:09:11:392 (1324) 0 [Utility.Event] [SYSTEM MONITOR] System Monitor started in %SYS 01/18/21-23:09:11:399 (1173) 0 [Utility.Event] Shard license: 0 01/18/21-23:09:11:778 (1162) 0 [Database.SparseDBExpansion] Expanding capacity of sparse database /external/iris/mgr/iristemp/ by 10 MB.

    irisサービスを作成します

    $ kubectl apply -f iris-svc.yaml    service/iris-svc created

    $ kubectl get svc NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)           AGE iris-svc     LoadBalancer   10.0.214.236   20.62.241.89   52773:30128/TCP   15s

    手順5 - 管理ポータルにアクセスする

    最後に、サービスの外部IP(http://20.62.241.89:52773/csp/sys/%25CSP.Portal.Home.zen)を使って、IRISの管理ポータルに接続します。ユーザー名は「_SYSTEM」、パスワードは「SYS」です。 初回ログイン時にパスワードの変更が求められます。

     

    0
    0 1217
    記事 Toshihiko Minamoto · 1月 6, 2021 25m read

    データ分析のために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 つ作成し、それを実行します。そして 職場でボーナスをゲットします。

    次第に、マイクロサービスの時代に Docker を使わないのはもったいない、InterSystems がそのドキュメンテーションを提供してくれることを考えるとなおさらだ、という結論に至ります。 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 iris
    USER>zpm
    zpm: USER>install samples-bi

    ブラウザーを http://localhost:52773/csp/user/_DeepSee.UserPortal.Home.zen?$NAMESPACE=USER, に変更したら、もう一度上司の所に戻り、良い仕事のご褒美として1 日休みをもらいます。

    あなたはその時、“docker run” は単なる第一歩であり、少なくとも docker-compose を使う必要があるでは?と考え始めます。 問題ありません:

    $ cat docker-compose.yml
    version: "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 を使うという方法があります。 私個人的には、Kubernetes のマニフェストファイルをマニュアルからコピーし、自分で編集する方が好きなのですが、kompose でも小さなタスクを完了させるだけなら問題ありません。

    $ kompose convert -f docker-compose.yml
    INFO Kubernetes file "irisce-service.yaml" created
    INFO Kubernetes file "irisce-deployment.yaml" created

    これで、Kubernetes のクラスターに送信できるデプロイファイルとサービスファイルができました。 ここであなたは、minikube をインストールすれば、シングルノードの Kubernetes クラスターを実行できること、そしてこの段階ではまさにそれが必要なことを知ります。 minikube のサンドボックスを 1 日か 2 日使ってみた後、ライブの Kubernetes を実際に AWS クラウドのどこかでデプロイする準備が整いました。

    セットアップ

    それでは、一緒にやりましょう。 この時点で、前提にしておきたい点がいくつかあります。

    第一に、あなたが AWS のアカウントを持っていること、その ID を知っていること、そしてルートユーザーの認証情報を使用していないということです。 管理者権限とプログラムによるアクセス権だけを持つ IAM ユーザー (「my-user」とでも呼びましょう) を作成し、その認証情報を保管します。 また、「terraform」と名付けた IAM ユーザーも作成し、同じアクセス権を与えます。

    このユーザーの代わりに、Terraform はあなたの AWS アカウントにアクセスし、必要なリソースを作成、削除します。 これはデモなので、これらのユーザーに広範な権限を与えています。 両方の IAM ユーザーの認証情報はローカルに保存します。

    $ cat ~/.aws/credentials
    [terraform]
    aws_access_key_id = ABCDEFGHIJKLMNOPQRST
    aws_secret_access_key = ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123
    [my-user]
    aws_access_key_id = TSRQPONMLKJIHGFEDCBA
    aws_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 コンソールでお試しください。

    飽きてしまったらクリーンアップしましょう。

    $ terraform destroy

    そして、次のレベルに移動し、Terraform EKS モジュールの使用を開始します。これは、同じ EKS Introduction がベースになっているためでもあります。 その使い方は、「examples/」ディレクトリで確認してください。 そこには、他の例も用意してあります。

    例はある程度簡素化してあります。 こちらが、VPC の作成モジュールと EKS の作成モジュールが呼び出されるメインファイルです。

    $ cat /terraform/main.tf
    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"
      }
    }

    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-bucket
    An 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-remember
    An 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-1
    make_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

    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.tf
    variable "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.tf
    output "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.yml
    version: "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.yaml
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      labels:
        io.kompose.service: samples-bi
      name: samples-bi
    spec:
      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.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        io.kompose.service: samples-bi
      name: samples-bi
    spec:
      ports:
      - name: "52773"
        port: 52773
        targetPort: 52773
      selector:
        io.kompose.service: samples-bi
      type: LoadBalancer

    そうです、今回は「デフォルト」のネームスペースでデプロイします。デモですからね。

    _何_を_どこ_で実行するかが分かったところで、 最後は、_どのように_実行するか、を見ます。

    GitHub Actions のワークフロー

    すべてを一から作るのではなく、GitHub Actions を使って GKE に InterSystems IRIS Solution をデプロイする に記載されているワークフローに似たものを作成します。 今回は、コンテナを構築する必要はありません。 GKE 特有の箇所は EKS 特有の箇所に置き換えられます。 太文字の箇所は、コミットメッセージを受けて条件付きステップで使用することに関する記述です。

    $ cat /.github/workflows/workflow.yaml
    name: 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 がそのシークレットを使用することを許可する必要があります。

    ワークフローの強調表示されている箇所に注目してください。 これらは、“[destroy eks]” というフレーズを持つコミットメッセージをプッシュし、EKS クラスターを破壊することを可能にします。 私たちはそのようなコミットメッセージを持つ “kubernetes apply” は実行しません。
    パイプラインを実行します。でも最初に .gitignore ファイルを作成します。

    $ cat /.gitignore
    .DS_Store
    terraform/.terraform/
    terraform/*.plan
    terraform/*.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-context
    dev-cluster

    $ kubectl get nodes
    NAME                                                                               STATUS   ROLES      AGE          VERSION
    ip-10-42-1-125.eu-west-1.compute.internal   Ready          6m20s     v1.14.8-eks-b8860f

    $ kubectl get po
    NAME                                                       READY        STATUS      RESTARTS   AGE
    samples-bi-756dddffdb-zd9nw    1/1               Running    0                      6m16s

    $ kubectl get svc
    NAME                   TYPE                        CLUSTER-IP        EXTERNAL-IP                                                                                                                                                         PORT(S)                    AGE
    kubernetes        ClusterIP               172.20.0.1                                                                                                                                                                                443/TCP                    11m
    samples-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 _(リンクを自分の External-IP と入れ替えてください) に移動し、“_system”、“SYS” と順に入力して、デフォルトのパスワードを変更します。 たくさんの BI ダッシュボードが表示されるはずです。

    細かく確認するには、それぞれの矢印をクリックします。

    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 の有効化については触れていません。 それは、また近いうちに取り上げたいと思います。

    0
    0 428
    記事 Tomohiro Iwamoto · 12月 10, 2020 17m read

    この記事は、GitHub Actions を使って GKE に InterSystems IRIS Solution をデプロイするの継続記事で、そこではGitHub Actions パイプラインを使って、 Terraform で作成された Google Kubernetes クラスタにzpm-registry をデプロイしています。 繰り返しにならないよう、次の項目を満たしたものを開始点とします。

    訳者注) 上記の記事を読まれてから、本記事に進まれることをお勧めしますが、GKE上のサービスにドメイン名を紐づける方法を解説した単独記事としてもお読みいただけます。

    0
    0 389
    記事 Shintaro Kaminaka · 12月 10, 2020 20m read

    以前紹介した記事 (お読みいただいたでしょうか) では、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 には無料のティアがありますが、Google クラウドプラットフォームは有料ですのでご注意ください。 経費の管理にはお気を付けください。

    ワークフローの基本

    それでは、始めましょう。 

    以下はシンプルですが使いものにならないワークフローの例です。

    $ cd <root_repo_dir>
    $ mkdir -p .github/workflows
    $ cat <root_repo_dir>/.github/workflows/workflow.yaml          
    name: Traditional Hello World
    on: [push]
    jobs:
      courtesy:
        name: Greeting
        runs-on: ubuntu-latest
        steps:
        - name: Hello world
          run: echo "Hello, world!

    リポジトリにプッシュするときは、「Greeting」と名付けたジョブを実行する必要があります。このジョブは唯一のステップとしてウェルカムフレーズを出力します。 このジョブは、GitHub でホストされる仮想マシン「Runner」で実行されます。同マシンには Ubuntu の最新バージョンがインストールされています。
    このファイルをリポジトリにプッシュした後に「Code GitHub」タブを開けば、すべて順調に実行されたことを確認できるはずです。

    ジョブが失敗したら、緑のチェックマークの代わりに赤の「X」が表示されます。 詳細を見るには、緑のチェックマークと「Details」を順にクリックします。 もしくは、直接「Actions」タブに移動することもできます。

    ワークフローの構文に関する完全な詳細は、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’ branch
    on:
      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 nodes
    NAME                                                                                                   STATUS ROLES   AGE        VERSION
    gke-dev-cluster-dev-cluster-node-pool-98cef283-dfq2 Ready    <none> 8m51s   v1.13.11-gke.23

    $ kubectl -n iris get po
    NAME                     READY   STATUS     RESTARTS   AGE
    zpm-registry-0   1/1         Running    0                      8m25s

    他の内容の確認は、Running ステータスになるまで待機することをおすすめします。

    $ kubectl -n iris get sts
    NAME                 READY   AGE
    zpm-registry   1/1          8m25s

    $ kubectl -n iris get svc
    NAME                 TYPE                      CLUSTER-IP       EXTERNAL-IP    PORT(S)                       AGE
    zpm-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 iris
    USER>write ##class(Math.Math).Factorial(5)
    <CLASS DOES NOT EXIST> *Math.Math
    USER>zpm
    zpm: USER>list
    zpm: USER>repo -list
    registry
        Source:     https://pm.community.intersystems.com
        Enabled?    Yes
        Available?    Yes
        Use for Snapshots?    Yes
        Use for Prereleases?    Yes
    zpm: USER>repo -n registry -r -url http://104.199.6.32:52773/registry/ -user _system -pass SYS
    zpm: 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 registry
    objectscript-math 0.0.4
    zpm: USER>install objectscript-math
    [objectscript-math]    Reload START
    ...
    [objectscript-math]    Activate SUCCESS


    zpm: USER>quit
    USER>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 はどうすれば再実行できるか?) という質問をご覧ください。

    0
    0 491
    記事 Toshihiko Minamoto · 11月 26, 2020 17m read

    前回は GKE サービスを使用して IRIS アプリケーションを Google Cloud 上で起動しました。

    また、クラスターを手動で(または gcloud を介して)作成するのは簡単ですが、最新の Infrastructure-as-Code(IaC)手法では、Kubernetesクラスターの説明もコードとしてリポジトリに格納する必要があります。 このコードの記述方法は、IaC に使用されるツールによって決まります。

    Google Cloud の場合は複数のオプションが存在し、その中には Deployment ManagerTerraform があります。 どちらが優れているかにつては意見が分かれています。詳細を知りたい場合は、この Reddit のスレッド「Opinions on Terraform vs. Deployment Manager?」と Medium の記事「Comparing GCP Deployment Manager and Terraform」を参照してください。 

    この記事では特定のベンダーとの結びつきが少なく、さまざまなクラウドプロバイダーで IaC を使用できる Terraform を選択します。

    ここでは過去の記事を読み、Googleアカウントを作成し、前回の記事と同様に「開発」という名前のプロジェクトを作成しているものと仮定します。 この記事ではその ID は <PROJECT_ID> として表示されます。 以下の例では、それを自分のプロジェクトの IDに変更してください。 

    Google には無料枠がありますが、無料ではないことに注意してください。 必ず出費をコントロールするようにしてください。

    また、ここではすでに元のリポジトリをフォークしていることを前提にしています。 この記事全体を通してこのフォークを「my-objectscript-rest-docker-template」と呼び、そのルートディレクトリを「<root_repo_dir>」として参照します。

    コピーと貼り付けを簡単にするため、すべてのコードサンプルをこのリポジトリに格納しています。

    次の図では、デプロイプロセス全体を 1 つの図で表しています。

    では、次のように執筆時点での Terraform の最新バージョンをインストールしましょう。

    $ terraform version
    Terraform v0.12.17

    インターネット上の多くの例では旧バージョンが使用されており、0.12 では多くの変更が加えられているため、ここではバージョンが重要になります。

    ここでは GCP アカウントで Terraform に特定のアクションを実行(特定の API を使用)させたいと考えています。 これを可能にするには「terraform」という名前のサービスアカウントを作成し、Kubernetes Engine API を有効にしてください。 その実施方法についてはご心配なく。この記事を読み進めるだけで、あなたの疑問は解消します。

    Web Console を使用することもできますが、ここでは gcloud ユーティリティを使った例を試してみましょう。

    次の例では、数種類のコマンドを使用します。 これらのコマンドや機能の詳細については、次のドキュメントのトピックを参照指定してください。

  • gcloud iam service-accounts create
  • 特定のリソースのサービスアカウントへの役割の付与
  • gcloud iam service-accounts keys create
  • Google Cloud プロジェクトでの API の有効化
  • それでは、例を見ていきましょう。

    $ gcloud init

    前回の記事で gcloud を取り上げましたので、ここではセットアップの詳細は説明しません。 この例では、次のコマンドを実行します。

    $ cd <root_repo_dir>
    $ mkdir terraform; cd terraform
    $ gcloud iam service-accounts create terraform --description "Terraform" --display-name "terraform"

    次に、「Kubernetes Engine Admin」(container.admin)の他にいくつかのロールを terraform サービスアカウントに追加しましょう。 これらのロールは今後役に立つことでしょう。

    $ gcloud projects add-iam-policy-binding <PROJECT_ID> \
      --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \
      --role roles/container.admin

    $ gcloud projects add-iam-policy-binding <PROJECT_ID> \
      --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \
      --role roles/iam.serviceAccountUser

    $ gcloud projects add-iam-policy-binding <PROJECT_ID> \
      --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \
      --role roles/compute.viewer

    $ gcloud projects add-iam-policy-binding <PROJECT_ID> \
      --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \
      --role roles/storage.admin

    $ gcloud iam service-accounts keys create account.json \
    --iam-account terraform@<PROJECT_ID>.iam.gserviceaccount.com

    最後の入力では、account.json ファイルを作成していることに注意してください。 このファイルは必ず秘密にしてください。

    $ gcloud projects list
    $ gcloud config set project <PROJECT_ID>
    $ gcloud services list --available | grep 'Kubernetes Engine'
    $ gcloud services enable container.googleapis.com
    $ gcloud services list --enabled | grep 'Kubernetes Engine'
    container.googleapis.com Kubernetes Engine API

    次に、Terraform の HCL 言語で GKE クラスターを記述しましょう。 ここではいくつかのプレースホルダーを使用していますが、これらは実際の値に置き換えてください。

    <td style="text-align: center;">
      意味
    </td>
    
    <td style="text-align: center;">
      例
    </td>
    
    <td>
        GCP のプロジェクト ID
    </td>
    
    <td>
        possible-symbol-254507
    </td>
    
    <td>
        Terraform のステート/ロック用のストレージ(一意である必要があります)
    </td>
    
    <td>
        circleci-gke-terraform-demo
    </td>
    
    <td>
        リソースが作成されるリージョン
    </td>
    
    <td>
        europe-west1
    </td>
    
    <td>
        リソースが作成されるゾーン
    </td>
    
    <td>
        europe-west1-b
    </td>
    
    <td>
        GKE クラスター名
    </td>
    
    <td>
        dev-cluster
    </td>
    
    <td>
        GKE ワーカーノードのプール名
    </td>
    
    <td>
        dev-cluster-node-pool
    </td>
    
    プレースホルダー
      <PROJECT_ID>
      <BUCKET_NAME>
      <REGION>
      <LOCATION>
      <CLUSTER_NAME>
      <NODES_POOL_NAME>

     

    以下に実際のクラスターの HCL 構成を示します。

    $ cat main.tf
    terraform {
      required_version = "~> 0.12"
      backend "gcs" {
        bucket = "<BUCKET_NAME>"
        prefix = "terraform/state"
        credentials = "account.json"
      }
    }

    provider "google" {
      credentials = file("account.json")
      project = "<PROJECT_ID>"
      region = "<REGION>"
    }

    resource "google_container_cluster" "gke-cluster" {
      name = "<CLUSTER_NAME>"
      location = "<LOCATION>"
      remove_default_node_pool = true
      # In regional cluster (location is region, not zone) 
      # this is a number of nodes per zone 
      initial_node_count = 1
    }

    resource "google_container_node_pool" "preemptible_node_pool" {
      name = "<NODES_POOL_NAME>"
      location = "<LOCATION>"
      cluster = google_container_cluster.gke-cluster.name
      # In regional cluster (location is region, not zone) 
      # this is a number of nodes per zone
      node_count = 1

      node_config {
        preemptible = true
        machine_type = "n1-standard-1"
        oauth_scopes = [
          "storage-ro",
          "logging-write",
          "monitoring"
        ]
      }
    }

    HCL コードを適切に整形できるよう、Terraform には次の便利な整形コマンドが用意されています。

    $ terraform fmt

    上記のコードスニペットは、作成されたリソースが Google によって提供され、リソース自体は google_container_cluster と google_container_node_pool であることを示しています。また、ここではコスト削減のために preemptible を指定しています。 また、デフォルトの代わりに独自のプールを作成しています。

    次の設定を簡単に説明します。

    terraform {
      required_version = "~> 0.12"
      backend "gcs" {
        Bucket = "<BUCKET_NAME>"
        Prefix = "terraform/state"
        credentials = "account.json"
      }
    }

    Terraform はすべての実行結果をステータスファイルに書き込み、このファイルを他の作業に使用します。 このファイルは共有しやすいように、離れた場所に保存することをお勧めします。 一般的にはGoogle バケットに保存されます。

    このバケットを作成しましょう。 プレースホルダー <BUCKET_NAME> の代わりに自分のバケットの名前を使用してください。 バケットを作成する前に、<BUCKET_NAME> が使用できるかどうかを次のコマンドで確認してください。すべての GCP で一意である必要があるためです。

    $ gsutil acl get gs://<BUCKET_NAME>

    期待する応答:

    BucketNotFoundException: 404 gs://<BUCKET_NAME> bucket does not exist

    「Busy」という応答があった場合、別の名前を選択する必要があります。

    AccessDeniedException: 403 <YOUR_ACCOUNT> does not have storage.buckets.get access to <BUCKET_NAME>

    Terraform の推奨どおりにバージョン管理も有効にしましょう。

    $ gsutil mb -l EU gs://<BUCKET_NAME>

    $ gsutil versioning get gs://<BUCKET_NAME>
    gs://<BUCKET_NAME>: Suspended

    $ gsutil versioning set on gs://<BUCKET_NAME>

    $ gsutil versioning get gs://<BUCKET_NAME>
    gs://<BUCKET_NAME>: Enabled

    Terraform はモジュール方式であり、GCP で何かを作成するにはGoogle Provider プラグインを追加する必要があります。 これを行うには、次のコマンドを使用します。

    $ terraform init

    Terraform が GKE クラスターを作成する際の実行計画を見てみましょう。

    $ terraform plan -out dev-cluster.plan

    コマンドの出力には、計画の詳細が含まれています。 特に問題なければ、次のコマンドでこの計画を実行しましょう。

    $ terraform apply dev-cluster.plan

    ちなみに、Terraform によって作成されたリソースを削除するには、このコマンドを <root_repo_dir>/terraform/ ディレクトリから実行してください。

    $ terraform destroy -auto-approve

    しばらくクラスターから離れて先に進みましょう。 ただし、何もかもリポジトリにプッシュされないように、先にいくつかのファイルを例外に追加しましょう。

    $ cat <root_repo_dir>/.gitignore
    .DS_Store
    terraform/.terraform/
    terraform/*.plan
    terraform/*.json

    Helm の使用

    前回の記事では、Kubernetes のマニフェストを yaml ファイルとして <root_repo_dir>/k8s/ ディレクトリに保存し、それを「kubectl apply」コマンドを使用してクラスターに送信しました。 

    今回は別の手法を試してみましょう。最近バージョン 3にアップデートされた Kubernetes のパッケージマネージャーである Helm を使用します。 バージョン 2 には Kubernetes 側のセキュリティの問題があったため、バージョン 3 以降を使用してください(詳細については、Running Helm in production: Security best practices を参照してください)。 まず、Kubernetes のマニフェストを k8s/ ディレクトリからチャートとして知られる Helm パッケージにまとめます。 Kubernetes にインストールされている Helm チャートはリリースと呼ばれます。 最小構成では、チャートは次のような複数のファイルで構成されます。

    $ mkdir <root_repo_dir>/helm; cd <root_repo_dir>/helm
    $ tree <root_repo_dir>/helm/
    helm/
    ├── Chart.yaml
    ├── templates
    │   ├── deployment.yaml
    │   ├── _helpers.tpl
    │   └── service.yaml
    └── values.yaml

    これらのファイルの目的は、公式サイトで詳細に説明されています。 独自チャートを作成するためのベストプラクティスは、Helm ドキュメントの The Chart Best Practices Guide に記載されています。 

    次にファイルの内容を示します。

    $ cat Chart.yaml
    apiVersion: v2
    name: iris-rest
    version: 0.1.0
    appVersion: 1.0.3
    description: Helm for ObjectScript-REST-Docker-template application
    sources:
    - https://github.com/intersystems-community/objectscript-rest-docker-template
    - https://github.com/intersystems-community/gke-terraform-circleci-objectscript-rest-docker-template
    $ cat templates/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: {{ template "iris-rest.name" . }}
      labels:
        app: {{ template "iris-rest.name" . }}
        chart: {{ template "iris-rest.chart" . }}
        release: {{ .Release.Name }}
        heritage: {{ .Release.Service }}
    spec:
      replicas: {{ .Values.replicaCount }}
      strategy:
        {{- .Values.strategy | nindent 4 }}
      selector:
        matchLabels:
          app: {{ template "iris-rest.name" . }}
          release: {{ .Release.Name }}
      template:
        metadata:
          labels:
            app: {{ template "iris-rest.name" . }}
            release: {{ .Release.Name }}
        spec:
          containers:
          - image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
            name: {{ template "iris-rest.name" . }}
            ports:
            - containerPort: {{ .Values.webPort.value }}
              name: {{ .Values.webPort.name }}
    $ cat templates/service.yaml
    {{- if .Values.service.enabled }}
    apiVersion: v1
    kind: Service
    metadata:
      name: {{ .Values.service.name }}
      labels:
        app: {{ template "iris-rest.name" . }}
        chart: {{ template "iris-rest.chart" . }}
        release: {{ .Release.Name }}
        heritage: {{ .Release.Service }}
    spec:
      selector:
        app: {{ template "iris-rest.name" . }}
        release: {{ .Release.Name }}
      ports:
      {{- range $key, $value := .Values.service.ports }}
        - name: {{ $key }}
    {{ toYaml $value | indent 6 }}
      {{- end }}
      type: {{ .Values.service.type }}
      {{- if ne .Values.service.loadBalancerIP "" }}
      loadBalancerIP: {{ .Values.service.loadBalancerIP }}
      {{- end }}
    {{- end }}
    $ cat templates/_helpers.tpl
    {{/* vim: set filetype=mustache: */}}
    {{/*
    Expand the name of the chart.
    */}}

    {{- define "iris-rest.name" -}}
    {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
    {{- end -}}

    {{/*
    Create chart name and version as used by the chart label.
    */}}
    {{- define "iris-rest.chart" -}}
    {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
    {{- end -}}

    $ cat values.yaml
    namespaceOverride: iris-rest

    replicaCount: 1

    strategy: |
      type: Recreate

    image:
      repository: eu.gcr.io/iris-rest
      tag: v1

    webPort:
      name: web
      value: 52773

    service:
      enabled: true
      name: iris-rest
      type: LoadBalancer
      loadBalancerIP: ""
      ports:
        web:
          port: 52773
          targetPort: 52773
          protocol: TCP

    Helm チャートを作成するには、Helm クライアント kubectl コマンドラインユーティリティをインストールします。

    $ helm version
    version.BuildInfo{Version:"v3.0.1", GitCommit:"7c22ef9ce89e0ebeb7125ba2ebf7d421f3e82ffa", GitTreeState:"clean", GoVersion:"go1.13.4"}

    iris というネームスペースを作成します。 デプロイ中にこれが作成されていれば良かったのですが、現時点ではその動作は実装されていません

    まず、Terraform によって作成されたクラスターの資格情報を kube-config に追加します。

    $ gcloud container clusters get-credentials <CLUSTER_NAME> --zone <LOCATION> --project <PROJECT_ID>
    $ kubectl create ns iris

    Helm が Kubernetes で以下を作成することを(実際にデプロイを開始せずに)確認します。

    $ cd <root_repo_dir>/helm
    $ helm upgrade iris-rest \
      --install \
      . \
      --namespace iris \
      --debug \
      --dry-run

    ここでは、出力(Kubernetes のマニフェスト)をスペースを確保するために省略しています。 特に問題がなければ、デプロイしましょう。

    $ helm upgrade iris-rest --install . --namespace iris
    $ helm list -n iris --all
    Iris-rest  iris  1  2019-12-14 15:24:19.292227564  +0200  EET  deployed    iris-rest-0.1.0  1.0.3

    Helm がアプリケーションをデプロイしたことはわかりますが、Docker イメージ eu.gcr.io/iris-rest:v1 をまだ作成していないため、Kubernetes がそのイメージをプルすることはできません(ImagePullBackOff)。

    $ kubectl -n iris get po
    NAME                                           READY  STATUS                       RESTARTS  AGE
    iris-rest-59b748c577-6cnrt 0/1         ImagePullBackOff  0                    10m

    とりあえず、今はここで終わっておきましょう。

    $ helm delete iris-rest -n iris

    CircleCI 側

    Terraform と Helm クライアントを試しましたので、それらを CircleCI 側のデプロイプロセスで使用できるようにしましょう。

    $ cat <root_repo_dir>/.circleci/config.yml
    version: 2.1

    orbs:
      gcp-gcr: circleci/gcp-gcr@0.6.1

    jobs:
      terraform:
        docker:
        # Terraform image version should be the same as when
        # you run terraform before from the local machine
          - image: hashicorp/terraform:0.12.17
        steps:
          - checkout
          - run:
              name: Create Service Account key file from environment variable
              working_directory: terraform
              command: echo ${TF_SERVICE_ACCOUNT_KEY} > account.json
          - run:
              name: Show Terraform version
              command: terraform version
          - run:
              name: Download required Terraform plugins
              working_directory: terraform
              command: terraform init
          - run:
              name: Validate Terraform configuration
              working_directory: terraform
              command: terraform validate
          - run:
              name: Create Terraform plan
              working_directory: terraform
              command: terraform plan -out /tmp/tf.plan
          - run:
              name: Run Terraform plan
              working_directory: terraform
              command: terraform apply /tmp/tf.plan
      k8s_deploy:
        docker:
          - image: kiwigrid/gcloud-kubectl-helm:3.0.1-272.0.0-218
        steps:
          - checkout
          - run:
              name: Authorize gcloud on GKE
              working_directory: helm
              command: |
                echo ${GCLOUD_SERVICE_KEY} > gcloud-service-key.json
                gcloud auth activate-service-account --key-file=gcloud-service-key.json
                gcloud container clusters get-credentials ${GKE_CLUSTER_NAME} --zone ${GOOGLE_COMPUTE_ZONE} --project ${GOOGLE_PROJECT_ID}
          - run:
              name: Wait a little until k8s worker nodes up
              command: sleep 30 # It’s a place for improvement
          - run:
              name: Create IRIS namespace if it doesn't exist
              command: kubectl get ns iris || kubectl create ns iris
          - run:
              name: Run Helm release deployment
              working_directory: helm
              command: |
                helm upgrade iris-rest \
                  --install \
                  . \
                  --namespace iris \
                  --wait \
                  --timeout 300s \
                  --atomic \
                  --set image.repository=eu.gcr.io/${GOOGLE_PROJECT_ID}/iris-rest \
                  --set image.tag=${CIRCLE_SHA1}
          - run:
              name: Check Helm release status
              command: helm list --all-namespaces --all
          - run:
              name: Check Kubernetes resources status
              command: |
                kubectl -n iris get pods
                echo
                kubectl -n iris get services
    workflows:
      main:
        jobs:
          - terraform
          - gcp-gcr/build-and-push-image:
              dockerfile: Dockerfile
              gcloud-service-key: GCLOUD_SERVICE_KEY
              google-compute-zone: GOOGLE_COMPUTE_ZONE
              google-project-id: GOOGLE_PROJECT_ID
              registry-url: eu.gcr.io
              image: iris-rest
              path: .
              tag: ${CIRCLE_SHA1}
          - k8s_deploy:
              requires:
                - terraform
                - gcp-gcr/build-and-push-image

    CircleCI 側のプロジェクトに次のようないくつかの環境変数 を追加する必要があります。

    GCLOUD_SERVICE_KEY は CircleCI のサービスアカウントキーであり、TF_SERVICE_ACCOUNT_KEY は Terraform のサービスアカウントキーです。 サービスアカウントキーが account.json ファイル全体の内容であることを思い出してください。

    次に、変更をリポジトリにプッシュしましょう。

    $ cd <root_repo_dir>
    $ git add .circleci/ helm/ terraform/ .gitignore
    $ git commit -m "Add Terraform and Helm"
    $ git push

    CircleCI UI ダッシュボードには、次のようにすべてが正常であることが示されているはずです。

    Terraform は冪等性のあるツールであり、GKE クラスターが存在する場合、「terraform」ジョブは何も実行しません。 クラスターが存在しない場合は、Kubernetes をデプロイする前に作成されます。
    最後に、IRIS の可用性を確認しましょう。

    $ gcloud container clusters get-credentials <CLUSTER_NAME> --zone <LOCATION> --project <PROJECT_ID>

    $ kubectl -n iris get svc
    NAME        TYPE                     CLUSTER-IP     EXTERNAL-IP   PORT(S)                     AGE
    Iris-rest    LoadBalancer  10.23.249.42    34.76.130.11    52773:31603/TCP   53s

    $ curl -XPOST -H "Content-Type: application/json" -u _system:SYS 34.76.130.11:52773/person/ -d '{"Name":"John Dou"}'

    $ curl -XGET -u _system:SYS 34.76.130.11:52773/person/all
    [{"Name":"John Dou"},]

    まとめ

    Terraform と Helm は標準の DevOps ツールであり、IRIS のデプロイと緊密に統合する必要があります。

    これらはある程度の学習を必要としますが、何度か実践した後は大幅に時間と労力を節約できるようになります。

    0
    0 838
    記事 Mihoko Iijima · 7月 7, 2020 11m read

    私たちのほとんどは、多かれ少なかれDockerに慣れ親しんでいます。 Dockerを使用している人たちは、ほとんどのアプリケーションを簡単にデプロイし遊んで、何かを壊してしまってもDockerコンテナを再起動するだけでアプリケーションを復元できる点を気に入っています。  

    InterSystems も Docker を気に入っています。

    InterSystems OpenExchange プロジェクトには、InterSystems IRISのイメージを簡単にダウンロードして実行できるDockerコンテナで実行するサンプルが多数掲載されています。また、Visual Studio IRISプラグインなど、その他の便利なコンポーネントもあります 。

    特定のユースケース用の追加コードを使ってDockerでIRISを実行するのは簡単ですが、ソリューションを他のユーザーと共有する場合は、コマンドを実行し、コードを更新するたびに繰り返し実行するための何らかの方法が必要になります。 この記事では、継続的インテグレーション継続的デリバリー (CI / CD)を使ってそのプロセスを簡素化する方法を説明します。

    セットアップ

    0
    0 244
    記事 Mihoko Iijima · 7月 6, 2020 14m read

    前回シンプルなIRISアプリケーション をGoogleクラウドにデプロイしました。 今回は、同じプロジェクトを Amazon Web Services(アマゾンウェブサービス) のElastic Kubernetes Service (EKS)を使って、デプロイします。

    IRISプロジェクトをあなた自身のプライベート・リポジトリにすでにFORKしていると想定します。この記事では<username>/my-objectscript-rest-docker-templateという名前にしています。 <root_repo_dir>は、そのルートディレクトリです。

    開始する前に、 AWSコマンドラインインターフェースと、Kubernetesクラスタ作成用のシンプルなCLIユーティリティeksctlをインストールします。 AWSの場合 aws2 の使用を試すことができますが、ここで説明するようにkube設定ファイルでaws2の使用法を設定する必要があります 。  

    AWS EKS 

    0
    0 674