医療系AIチャットボットをKubernetesへ展開するHelm v3チャート構成とGitOpsパイプライン

病院のEMR連携およびOpenAI APIを利用するAIチャットボットを、Helm v3とArgoCDを用いてKubernetes上に安全かつ宣言的に構築する手法を解説します。

医療機関向けシステムのコンテナ運用において、院内電子カルテ(EMR)APIや外部AIエンジンとの連携を行うマイクロサービスをデプロイする場合、環境ごとの設定差分管理や証明書のライフサイクル制御、安全な認証情報の配布が極めて重要な課題となります。手動でマニフェストを書き換える手法や個別の仮想ホスト設定は、ノード拡張やマルチ環境展開時に人為的な操作ミスによる障害を引き起こすリスクが高まります。

本稿では、Helm v3を用いた医療系AIチャットボット(ai-chatbot)のパッケージングおよび抽象化手法、インメモリ型トークン共有アーキテクチャ、そしてArgoCDによるGitOps継続的デプロイメントの構成仕様について解説します。

システムアーキテクチャとトポロジ構造

対象のワークロードは、クラスタ内部のEMRエンドポイント(https://emr.myhospital.co.kr/api)および外部のOpenAI APIエンジンと通信します。本構成ではインフラ階層を明確に分離し、TLS終端を担うIngress、クラスタ内ルーティングを行うService(ClusterIP)、実体を構成するPodレプリカ群をHelmによって宣言的に制御します。

ai-chatbot/
├── Chart.yaml             # チャートメタデータおよび semantic versioning 定義
├── values.yaml            # デフォルト変数および実行時パラメータ
└── templates/             # Kubernetes マニフェストテンプレート
    ├── deployment.yaml    # ポッドオーケストレーション制御
    ├── service.yaml       # Layer 4 内部ネットワーク抽象化 (ClusterIP)
    ├── ingress.yaml       # Layer 7 外部ルーティングおよび TLS 終端設定
    └── configmap.yaml     # 外部 API 設定およびシステムプロンプト定義

コアテンプレート設計仕様

1. メタデータ定義 (Chart.yaml)

Helm 3との互換性を保証するため apiVersion: v2 を指定し、アプリケーションのセマンティックバージョニングを設定します。

apiVersion: v2
name: ai-chatbot
description: 病院向けAIチャットボットHelmチャート
type: application
version: 0.1.0
appVersion: "1.0.0"

2. 環境変数およびリソース定義 (values.yaml)

稼働環境に応じたパラメータ変更を容易にするため、CPU/メモリ要求量、レプリカ数、Ingressホスト名を values.yaml に統一管理します。

replicaCount: 2

image:
  repository: myregistry/ai-chatbot
  tag: latest
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  hostname: chatbot.myhospital.co.kr
  tls: true

resources:
  limits:
    cpu: "500m"
    memory: "512Mi"
  requests:
    cpu: "250m"
    memory: "256Mi"

env:
  EMR_API_URL: "https://emr.myhospital.co.kr/api"
  CHATBOT_LANG: "ko"

3. ワークロード制御定義 (templates/deployment.yaml)

リソース制限や環境変数の動的挿入を行います。Helmテンプレート関数の nindent を正しく適用し、YAMLインデントの破損を防止します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-chatbot
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: ai-chatbot
  template:
    metadata:
      labels:
        app: ai-chatbot
    spec:
      containers:
        - name: chatbot
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: 3000
          env:
            - name: EMR_API_URL
              value: {{ .Values.env.EMR_API_URL | quote }}
            - name: CHATBOT_LANG
              value: {{ .Values.env.CHATBOT_LANG | quote }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

4. 内部サービスおよび Ingress 露出 (templates/service.yaml, templates/ingress.yaml)

service.yaml により80番ポートからコンテナの3000番ポートへトラフィックを転送し、ingress.yaml によりNGINX Ingress Controller経由のSSLリダイレクトおよびTLS終端を適用します。

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-chatbot
spec:
  type: {{ .Values.service.type }}
  selector:
    app: ai-chatbot
  ports:
    - protocol: TCP
      port: {{ .Values.service.port }}
      targetPort: 3000

```yaml
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-chatbot
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
rules:
- host: {{ .Values.ingress.hostname }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}-chatbot
port:
number: {{ .Values.service.port }}
{{- if .Values.ingress.tls }}
tls:
- hosts:
- {{ .Values.ingress.hostname }}
secretName: chatbot-tls
{{- end }}
{{- end }}

5. OpenAI 設定用 ConfigMap (templates/configmap.yaml)

モデル識別子やプロンプトパラメータをアプリケーション本体から切り離して外部化します。

apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-chatbot-config
labels:
app: ai-chatbot
data:
OPENAI_MODEL: "gpt-4o"
OPENAI_MAX_TOKENS: "2048"
OPENAI_TEMPERATURE: "0.2"
SYSTEM_PROMPT: |
You are an AI assistant designed for hospital settings.
You assist medical personnel and patients by answering queries based on the hospital's EMR dataset.
Always operate under medical protocol standards.

EMR 認証トークン統合パターン

医療データ(PHI)の流出を防止するため、EMRアクセストークンは永続ストレージではなくメモリ上にのみ保持する設計を採用します。

  • Sidecar/InitContainer パターン: 認証サイドカーがEMR認証サーバー(https://emr.myhospital.co.kr)から短寿命JWTトークンを取得します。
  • In-Memory ボリューム (tmpfs): 取得したトークンは medium: Memory で構成された emptyDir ボリューム(/var/run/secrets/emr)に書き込まれ、メインのチャットボットコンテナから参照されます。
  • 動的トークン更新: チャットボットアプリケーション層のOAuth2クライアントモジュールが定期的にトークンの有効期限を検証し、Podを再起動することなくメモリ上のJWTを透過的に更新します。

GitOps パイプライン構成 (argocd-application.yaml)

ArgoCDを用いてGitリポジトリの状態とクラスタの実体を自動同期します。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ai-chatbot-prod
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: 'https://github.com/myhospital-org/helm-charts.git'
targetRevision: HEAD
path: ai-chatbot
helm:
valueFiles:
- values.yaml
destination:
server: 'https://kubernetes.default.svc'
namespace: ai-services
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

トラブルシューティング 🛠️

1. nindent 関数適用不備による YAML パースエラー

  • 現象: helm template 実行時に error converting YAML to JSON: yaml: line X: did not find expected key が発生します。
  • 原因: deployment.yaml 内の resources: ブロック直下で nindent のインデント数が合致していない場合、構文エラーが発生します。
  • 対策: {{- toYaml .Values.resources | nindent 12 }} のように、resources キーの配下に入る正確なスペース数(12スペース)を正しく指定します。

2. Ingress の TLS Secret 不整合による 502/SSL エラー

  • 現象: 外部からHTTPSアクセス時にデフォルトのKubernetes Ingress Fake Certificateが返却され、接続が拒否されます。
  • 原因: values.yaml で定義した chatbot-tls Secretが対象のネームスペースに存在しない、またはIngressのhost名と証明書のSAN(Subject Alternative Name)が一致していません。
  • 対策: 証明書管理コンポーネント(cert-manager等)を用いて、ターゲットネームスペース(ai-services)内に正しく chatbot-tls が生成されているかを検証します。

3. メモリバックド emptyDir の書き込み権限エラー

  • 現象: サイドカーコンテナからトークンファイル書き込み時に Permission denied が発生し、Podが CrashLoopBackOff に陥ります。
  • 原因: セキュリティコンテキストで非rootユーザー実行を定義している際、マウントされた emptyDir ボリュームのオーナー権限が一致していません。
  • 対策: securityContext の fsGroup 設定をPod仕様に追加し、書き込み権限を持つGIDを明示的に割り当てます。

動作検証および検証ログ

デプロイメント完了後、以下の検証用ターミナルコマンドを実行してオブジェクトの正常性を確認します。

$ helm status chatbot -n ai-services
NAME: chatbot
LAST DEPLOYED: Sun Sep 27 10:15:22 2026
NAMESPACE: ai-services
STATUS: deployed
REVISION: 1
TEST SUITE: None

$ kubectl get pods -n ai-services -l app=ai-chatbot -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP            NODE           NOMINATED NODE   READINESS GATES
chatbot-ai-chatbot-67f9b87c-x92zk   1/1     Running   0          2m    10.244.1.45   k8s-worker-01   <none> <none>
chatbot-ai-chatbot-67f9b87c-z4l8p   1/1     Running   0          2m    10.244.2.89   k8s-worker-02   <none> <none>

$ kubectl get ingress -n ai-services
NAME                 CLASS   HOSTS                   ADDRESS        PORTS     AGE
chatbot-ai-chatbot   nginx   chatbot.myhospital.co.kr   192.168.1.100  80, 443   2m

$ curl -I -sS https://chatbot.myhospital.co.kr/
HTTP/2 200
server: nginx/1.25.3
date: Sun, 27 Sep 2026 10:17:45 GMT
content-type: text/html; charset=utf-8
strict-transport-security: max-age=15724800; includeSubDomains

設定上の留意事項 💡

  1. リソース制限の調整: EMR応答の解析処理によりスパイクアクセスが発生する場合、values.yaml の resources.limits.cpu をノードのコア数に応じて適切にチューニングしてください。
  2. プロンプト情報の管理: ConfigMap内のシステムプロンプトの更新はPodの自動再起動を誘発しないため、設定反映にはArgoCDによるローリングアップデートまたはPodの再作成を伴う運用を推奨します。
  3. ネットワークポリシー: 医療データを取り扱う性質上、ai-services ネームスペースに対してEMR認証エンドポイントおよびIngress以外の外部アウトバウンド通信を制限するNetworkPolicyの併用を推奨します。
Hugo で構築されています。
テーマ Stack は Jimmy によって設計されています。
Privacy Policy Disclaimer Contact