アプリの開発
アプリのワークロードを IBM Cloud® Kubernetes Service にデプロイするための構成を開発します。 Kubernetes は、特定の言語またはアプリに依存しない、拡張可能なコンテナー・オーケストレーション・プラットフォームです。そのため、任意の言語で作成されたステートレス・アプリ、ステートフル・アプリ、データ処理アプリなど、さまざまなワークロードを実行できます。
YAML ファイルでのアプリ要件の指定
Kubernetes では、Kubernetes オブジェクトの構成を宣言する YAML ファイルにアプリを記述します。 その後 Kubernetes API サーバーは、その YAML ファイルを処理し、オブジェクトの構成と必要な状態を etcd データ・ストアに保管します。 Kubernetes スケジューラーがクラスター内のワーカー・ノードにワークロードをスケジュールします。このとき、YAML ファイル内の仕様、管理者が設定するクラスター・ポリシー、使用可能なクラスター・キャパシティーが考慮されます。
『 YAMLファイル全体 』のコピーを確認してください。 その後、以下のセクションを確認して、どのようにすればアプリ・デプロイメントを拡張できるかを理解してください。
デプロイメントで Kubernetes オブジェクトがどのように連携するかについて詳しく説明します。 アプリのための Kubernetes オブジェクトを理解するを確認してください。
基本デプロイメント・メタデータ
デプロイする Kubernetes オブジェクトの種類に適した API バージョンを使用してください。 Kubernetes オブジェクトに対してサポートされる機能のうち使用できる機能は、API バージョンによって決まります。 メタデータで指定する名前はオブジェクトの名前であり、ラベルではありません。 この名前は、オブジェクトと対話するときに使用します
(kubectl get deployment <name>など)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
レプリカ・セット
アプリの可用性を高めるために、デプロイメントでレプリカ・セットを指定することができます。 レプリカ・セットで、デプロイするアプリ・インスタンスの数を定義します。 レプリカ・セットは Kubernetes デプロイメントによって管理され、モニターされます。 あるアプリ・インスタンスがダウンすると、Kubernetes は新しいアプリ・インスタンスを自動的に開始して、指定された数のアプリ・インスタンスを維持します。
spec:
replicas: 3
ラベル
ラベルを使用すると、クラスター内のさまざまなタイプのリソースに同じ key: value ペアのマークを付けることができます。 その後、このラベルに突き合わせるセレクターを指定することによって、これらの他のリソースに基づいて処理を進めることができます。 アプリをパブリックに公開することを計画している場合は、サービスで指定するセレクターと一致するラベルを使用する必要があります。
この例では、デプロイメント仕様がラベルに一致するテンプレートを使用しています。 app: wasliberty.
クラスター内のラベル付きオブジェクトを取得して、staging コンポーネントや production コンポーネントを調べるなどの作業を行うことができます。 例えば、クラスター内のすべての名前空間における env: production ラベルの付いたリソースをすべてリストします。 注: このコマンドを実行するには、すべての名前空間に対するアクセス権限が必要です。
kubectl get all -l env=production --all-namespaces
- ラベルに関する詳細については、『 Kubernetes 』のドキュメントを参照してください。
- ワーカー・ノードにラベルを適用します。
- より詳しい例については、ラベルを使用した特定のワーカー・ノードへのアプリのデプロイを参照してください。
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
アフィニティー
ポッドがどのワーカーノードにスケジューリングされるかをより細かく制御したい場合は、アフィニティ(コロケーション)を指定してください。 アフィニティーはスケジューリング時にのみポッドに影響を与えます。 例えば、同じノードにポッドがスケジュールされるようにするのではなく、各ワーカー・ノード全体にデプロイメントを分散させるには、標準クラスターで podAntiAffinity オプションを使用します。 「優先」と「必須」という 2 つのタイプのポッド・アンチアフィニティーを定義できます。
詳細については、 Kubernetes のドキュメント「ポッドをノードに割り当てる」を参照してください。
- 必須アンチアフィニティー
- ワーカー・ノードと同数のレプリカのみをデプロイできます。 例えば、クラスターに 3 つのワーカー・ノードがある場合は、YAML ファイルに 5 つのレプリカを定義しても、3 つのレプリカしかデプロイされません。 各レプリカは異なるワーカー・ノード上に存在します。 残りの 2 つのレプリカは保留中のままです。 別のワーカー・ノードをクラスターに追加すると、残りのレプリカのうち 1 つが新しいワーカー・ノードに自動的にデプロイされます。 ワーカー・ノードに障害が発生しても、アフィニティー・ポリシーが必要になるため、ポッドはスケジュール変更されません。 「required」が指定されたYAMLの例については、「 required」ポッドのアンチアフィニティを設定したLibertyアプリを参照してください。
- 優先アンチアフィニティー
- 使用可能なキャパシティーを備えたノードにポッドをデプロイできるので、ワークロードの柔軟性が向上します。 ポッドはそれぞれ、できるだけ異なるワーカー・ノードにスケジュールされます。 例えば、十分なキャパシティーを備えたワーカー・ノードがクラスターに 3 つある場合は、これらのノード全体に 5 つのレプリカ・ポッドをスケジュールできます。 ただし、クラスタにワーカーノードをさらに2つ追加した場合でも、アフィニティルールによって、既存のノード上で実行中の2つの追加ポッドが、利用可能なノードへ再スケジューリングされることはありません。
- ワーカー・ノード・アフィニティー
- ベアメタルなどの特定のワーカー・ノードでのみ実行されるようにデプロイメントを構成できます。 詳しくは、ラベルを使用した特定のワーカー・ノードへのアプリのデプロイを参照してください。
優先アンチアフィニティーの例
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
コンテナー・イメージ
コンテナーに使用するイメージ、イメージのロケーション、イメージ・プル・ポリシーを指定します。 イメージ・タグを指定しない場合、デフォルトではlatestというタグが付いたイメージがプルされます。
実動ワークロードに latest タグを使用するのは避けてください。 Docker Hub や IBM Cloud Container Registry などのパブリック・リポジトリーまたは共有リポジトリーを使用している場合は、ワークロードを最新イメージでテストしていない可能性があります。
例えば、パブリック IBM イメージのタグをリストするには、以下のようにします。
- グローバル・レジストリー・リージョンに切り替えます。
ibmcloud cr region-set global - IBM イメージをリストします。
ibmcloud cr images --include-ibm
デフォルトの imagePullPolicy が IfNotPresent に設定され、ローカルに存在しない場合のみイメージがプルされるようになります。 コンテナーが開始されるたびにイメージがプルされるようにするには、imagePullPolicy: Always を指定します。
containers:
- name: wasliberty
image: icr.io/ibm/liberty:webProfile8
imagePullPolicy: Always
アプリのサービス用のポート
アプリのサービスを開くために使用するコンテナー・ポートを選択します。 どのポートを開く必要があるかを確認するには、アプリ仕様または Dockerfile を参照してください。 プライベート・ネットワークからポートにアクセスできますが、パブリック・ネットワーク接続からはアクセスできません。 アプリをパブリックに公開するには、NodePort サービス、ロード・バランサー・サービス、または Ingress サービスを作成する必要があります。 Service オブジェクトを作成するときは、これと同じポート番号を使用します。
IBM Cloud のすべてのサービスで、ポート 25 はブロックされています。
ports:
- containerPort: 9080
リソースの要求と制限
クラスタ管理者は、クラスタ内の各 Kubernetes ネームスペースに対して ResourceQuota オブジェクトを作成することで、クラスタを共有する各チームが、計算リソース(メモリとCPU)を公平な割り当て分を超えて使用しないようにします。
クラスター管理者がコンピュート・リソース割り当て量を設定した場合は、デプロイメント・テンプレート内のコンテナーごとに、メモリーと CPU に関するリソースの要求と制限を指定する必要があります。そうしないと、ポッド作成は失敗します。
- 名前空間にリソース割り当て量が設定されているかどうかを確認します。
kubectl get quota --namespace=<namespace> - 割り当て量制限を調べます。
kubectl describe quota <quota_name> --namespace=<namespace>
リソース割り当て量が設定されていなくても、デプロイメントにリソースの要求と制限を含めることによって、ワーカー・ノード・リソースの管理を改善することができます。
コンテナーがその制限を超えた場合、コンテナーは再始動するか障害が起こる可能性があります。 コンテナーが要求を超えた場合、ワーカー・ノードが、制限を超えているそのリソースを使い尽くすと、コンテナーのポッドが強制除去される可能性があります。 トラブルシューティングについて詳しくは、ポッドの再始動が繰り返し失敗するまたはポッドが予期せず除去されるを参照してください。
- 要求
- スケジューラーがコンテナー用に予約する最小リソース量。 この量が制限に等しい場合、要求は保証されます。 この量が制限より少ない場合、やはり要求は保証されますが、スケジューラーは要求と制限の差を使用して、他のコンテナーのリソースを充足させることができます。
- 制限
- コンテナーが消費できる最大リソース量。 各コンテナー全体で使用される合計リソース量がワーカー・ノード上の使用可能量を超えると、スペースを解放するためにコンテナーが強制除去されることがあります。 強制除去が行われないようにするには、リソース要求をコンテナーの制限と同じに設定します。 制限が指定されない場合のデフォルトは、ワーカー・ノードのキャパシティーです。
詳細については、『 Kubernetes 』のドキュメントを参照してください。
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
Liveness Probe と Readiness Probe
デフォルトでは、Kubernetes はポッド内のすべてのコンテナーの開始後にトラフィックをアプリ・ポッドに送信し、コンテナーが異常終了するとコンテナーを再始動します。 ただし、サービス・トラフィック・ルーティングの頑強性を向上させるために、ヘルス・チェックを設定できます。
例えば、アプリの起動が遅い場合があります。 アプリ全体の準備が完全に整う前にアプリ・プロセスが開始する場合があります。これは、特に多数のインスタンスにわたるスケールアップ時に、応答に影響を与えることがあります。 ヘルス・チェックを使用すれば、アプリが実行中で要求を受け取る準備ができているかどうかをシステムに知らせることができます。 これらのプローブを設定すると、アプリのローリング更新を実行するときにダウン時間を回避するのにも役立ちます。 Liveness Probe と Readiness Probe の 2 つのタイプのヘルス・チェックを設定できます。
- Liveness Probe
- コンテナーが実行中であるかどうかを検査するには、Liveness Probe をセットアップします。 プローブが失敗した場合、コンテナーは再始動します。 コンテナーで Liveness Probe が指定されていなくても、プローブは成功します。コンテナーは Running 状況のときは活動状態であると見なされるからです。
- Readiness Probe
- コンテナーが要求と外部トラフィックを受け取る準備ができているかどうかを検査するには、Readiness Probe をセットアップします。 プローブが失敗した場合、ポッドに対応するサービスの使用可能 IP アドレスであるポッドの IP アドレスが削除されますが、コンテナーは再始動しません。 アプリの起動に時間がかかる場合は、初期遅延を設定したレディネスプローブを設定することが特に重要です。 初期遅延より前にプローブが開始することはないので、コンテナーに起動時間が与えられます。 コンテナーで Readiness Probe が指定されていなくても、プローブは成功します。コンテナーは Running 状況のときは活動状態であると見なされるからです。
コマンド、HTTP 要求、または TCP ソケットとしてプローブをセットアップできます。 例では、HTTP 要求を使用しています。 Liveness Probe の時間設定は Readiness Probe より長くしてください。 詳細については、『 Kubernetes 』のドキュメントを参照してください。
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
ポッド中断バジェット
アプリの可用性を高めるには、 PodDisruptionBudget オブジェクトを使用して、希望する可用性の種類に応じて、 障害発生時のアプリの動作を制御することができます。
ポッド中断バジェットは、意図的な中断 (ユーザーがアプリのデプロイメントを更新して直接再起動を開始するなど) におけるアプリの動作の計画に使用することも、カーネル・パニックなどの非意図的な中断におけるアプリの動作の計画に使用することもできます。
minAvailable : 中断が発生した後も使用可能でなければならないポッドの数またはパーセンテージを指定できます。
maxUnavailable- 中断が発生した後に使用不可になってもよいポッドの数またはパーセンテージを指定できます。 この例では、
maxUnavailable: 1を使用しています。 selectorPodDisruptionBudgetが適用されるポッドのセットを選択するために、ラベルを入力してください。 他のポッドのデプロイメントでこの同じラベルを使用した場合、そのポッドも対象になるので注意してください。
詳細については、『 Kubernetes 』のドキュメントを参照してください。
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
アプリ・サービスの公開
アプリを公開するサービスを作成できます。 spec セクションで、port とラベルの値を、デプロイメントで使用した値と一致させておきます。 サービスは、ラベル (次の例では app: wasliberty) と一致するオブジェクトを公開します。
- デフォルトでは、サービスは
ClusterIPを使用します。これにより、サービスはクラスタ内からのみアクセス可能となり、クラスタ外からはアクセスできなくなります。 - NodePort サービス、ロード・バランサー・サービス、または Ingress サービスを作成することで、アプリをパブリックに公開できます。 これらのサービスには 2 つの IP (1 つは外部 IP、1 つは内部 IP) があります。 トラフィックが外部 IP で受信されると、内部クラスター IP に転送されます。 次に、内部クラスター IP からアプリのコンテナー IP にトラフィックがルーティングされます。
- この例では、
NodePortを使用して、クラスター外にサービスを公開します。 外部アクセスのセットアップ方法について詳しくは、NodePort サービス、ロード・バランサー・サービス、または Ingress サービスの選択を参照してください。
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
特定のポートで listen するように hostNetwork ポッドをデプロイする必要がある場合、または hostPort を使用してワーカー・ノード上の特定ポートでアプリ・ポッドを公開する必要がある場合は、11000-11200 範囲にあるポートを使用します。IBM Cloud Kubernetes Service は、この目的でワーカー・ノード上の 11000-11200 ポート範囲を指定し、IBM Cloud Kubernetes Service が使用するローカル・ポートや他のポートとの競合を回避します。 hostNetwork ポッドと hostPorts は特定のワーカー・ノードの IP アドレスを参照するので、ポッドはそのワーカー・ノード上に限り実行されるように制限されます。 ワーカー・ノードの削除やリソース不足など、予期しないことが発生した場合、ポッドのスケジュールを変更できません。
ワーカー・ノード上でポッドのポートを公開しようとしている場合は、代わりに NodePort サービスを使用することを検討してください。 詳細については、 ベストプラクティ Kubernetes スのドキュメントを参照してください。
コンテナー環境変数のための構成マップ
構成マップは、デプロイメント・ワークロードの非機密構成情報を提供します。
次の例は、デプロイメント YAML のコンテナー仕様セクションで、構成マップの値を環境変数として参照する方法を示しています。 構成マップの値を参照することにより、その構成情報をデプロイメントから分離して、コンテナー化アプリをポータブルにしておくことができます。
- 変数に Kubernetes の
ConfigMapオブジェクトを使用するかSecretオブジェクトを使用するかの判断方法。 - configmap のその他の活用方法については、 Kubernetes のドキュメントを参照してください。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
コンテナー環境変数のためのシークレット
シークレットは、デプロイメント・ワークロードのパスワードなどの機密構成情報を提供します。
次の例は、デプロイメント YAML のコンテナー仕様セクションで、シークレットの値を環境変数として参照する方法を示しています。 ボリュームとしてシークレットをマウントすることもできます。 シークレットの値を参照することにより、その構成情報をデプロイメントから分離して、コンテナー化アプリをポータブルにしておくことができます。
- 変数に Kubernetes の
ConfigMapオブジェクトを使用するかSecretオブジェクトを使用するかの判断方法。 - シークレットを作成するには、『 Kubernetes 』のドキュメントを参照してください。
クラスター全体におけるすべてのシークレットの集中管理、およびアプリケーション・ランタイムでの注入には、IBM Cloud Secrets Manager を試してください。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
...
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
コンテナー・ストレージ用永続ボリューム
永続ボリューム (PV) は、物理ストレージとインターフェースをとり、コンテナー・ワークロード用の永続データ・ストレージを提供します。
次の例は、アプリに永続ストレージを追加する方法を示しています。 永続ストレージをプロビジョンするには、永続ボリューム請求 (PVC) を作成して、用意するファイル・ストレージのタイプとサイズを記述します。 PVC を作成すると、ダイナミックプロビジョニングによって、パーシステントボリュームと物理ストレージが自動的に作成されます。 デプロイメント YAML で PVC を参照することで、アプリ・ポッドにストレージが自動的にマウントされます。 ポッド内のコンテナーが /test マウント・パス・ディレクトリーにデータを書き込むと、データは NFS ファイル・ストレージ・インスタンスに保管されます。 プロビジョンできるその他のタイプのストレージのオプションについては、可用性の高い永続ストレージの計画を参照してください。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi
完全なデプロイメント YAML の例
以下の例は、既にセクションごとに解説されているデプロイメント YAML のコピーです。 また、 GitHub から YAML ファイルをダウンロードすることもできます。
YAML を適用するには、以下のようにします。
kubectl apply -f file.yaml [-n <namespace>]
YAML の例
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
containers:
- name: wasliberty
image: icr.io/ibm/liberty:latest
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
ports:
- containerPort: 9080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
---
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi