This the multi-page printable view of this section. Click here to print.
設定
- 1: 設定のベストプラクティス
- 2: ConfigMap
- 3: Secrets
- 4: コンテナのリソース管理
1 - 設定のベストプラクティス
このドキュメントでは、ユーザーガイド、入門マニュアル、および例を通して紹介されている設定のベストプラクティスを中心に説明します。
このドキュメントは生ものです。このリストには載っていないが他の人に役立つかもしれない何かについて考えている場合、IssueまたはPRを遠慮なく作成してください。
一般的な設定のTips
構成を定義する際には、最新の安定したAPIバージョンを指定してください。
設定ファイルは、クラスターに反映される前にバージョン管理システムに保存されるべきです。これによって、必要に応じて設定変更を迅速にロールバックできます。また、クラスターの再作成や復元時にも役立ちます。
JSONではなくYAMLを使って設定ファイルを書いてください。これらのフォーマットはほとんどすべてのシナリオで互換的に使用できますが、YAMLはよりユーザーフレンドリーになる傾向があります。
意味がある場合は常に、関連オブジェクトを単一ファイルにグループ化します。多くの場合、1つのファイルの方が管理が簡単です。例としてguestbook-all-in-one.yamlファイルを参照してください。
多くの
kubectl
コマンドがディレクトリに対しても呼び出せることも覚えておきましょう。たとえば、設定ファイルのディレクトリでkubectl apply
を呼び出すことができます。不必要にデフォルト値を指定しないでください。シンプルかつ最小限の設定のほうがエラーが発生しにくくなります。
よりよいイントロスペクションのために、オブジェクトの説明をアノテーションに入れましょう。
"真っ裸"のPod に対する ReplicaSet、Deployment、およびJob
可能な限り、"真っ裸"のPod(ReplicaSetやDeploymentにバインドされていないPod)は使わないでください。Nodeに障害が発生した場合、これらのPodは再スケジュールされません。
明示的に
restartPolicy: Never
を使いたいシーンを除いて、DeploymentはPodを直接作成するよりもほとんど常に望ましい方法です。Deploymentには、希望する数のPodが常に使用可能であることを確認するためにReplicaSetを作成したり、Podを置き換えるための戦略(RollingUpdateなど)を指定したりできます。Jobのほうが適切な場合もあるかもしれません。
Service
対応するバックエンドワークロード(DeploymentまたはReplicaSet)の前、およびそれにアクセスする必要があるワークロードの前にServiceを作成します。Kubernetesがコンテナを起動すると、コンテナ起動時に実行されていたすべてのServiceを指す環境変数が提供されます。たとえば、fooという名前のServiceが存在する場合、すべてのコンテナは初期環境で次の変数を取得します。
FOO_SERVICE_HOST=<the host the Service is running on> FOO_SERVICE_PORT=<the port the Service is running on>
これは順序付けの必要性を意味します -
Pod
がアクセスしたいService
はPod
自身の前に作らなければならず、そうしないと環境変数は注入されません。DNSにはこの制限はありません。(強くお勧めしますが)クラスターアドオンの1つの選択肢はDNSサーバーです。DNSサーバーは、新しい
Service
についてKubernetes APIを監視し、それぞれに対して一連のDNSレコードを作成します。クラスタ全体でDNSが有効になっている場合は、すべてのPod
が自動的にServices
の名前解決を行えるはずです。どうしても必要な場合以外は、Podに
hostPort
を指定しないでください。PodをhostPort
にバインドすると、Podがスケジュールできる場所の数を制限します、それぞれの<hostIP
、hostPort
、protocol
>の組み合わせはユニークでなければならないからです。hostIP
とprotocol
を明示的に指定しないと、KubernetesはデフォルトのhostIP
として0.0.0.0
を、デフォルトのprotocol
としてTCP
を使います。デバッグ目的でのみポートにアクセスする必要がある場合は、apiserver proxyまたは
kubectl port-forward
を使用できます。ノード上でPodのポートを明示的に公開する必要がある場合は、hostPortに頼る前にNodePortの使用を検討してください。
hostPort
の理由と同じくして、hostNetwork
の使用はできるだけ避けてください。kube-proxy
のロードバランシングが不要な場合は、headless Service(ClusterIP
がNone
)を使用してServiceを簡単に検出できます。
ラベルの使用
{ app: myapp, tier: frontend, phase: test, deployment: v3 }
のように、アプリケーションまたはデプロイメントの__セマンティック属性__を識別するラベルを定義して使いましょう。これらのラベルを使用して、他のリソースに適切なポッドを選択できます。例えば、すべてのtier:frontend
を持つPodを選択するServiceや、app:myapp
に属するすべてのphase:test
コンポーネント、などです。このアプローチの例を知るには、ゲストブックアプリも合わせてご覧ください。
セレクターからリリース固有のラベルを省略することで、Serviceを複数のDeploymentにまたがるように作成できます。 Deploymentにより、ダウンタイムなしで実行中のサービスを簡単に更新できます。
オブジェクトの望ましい状態はDeploymentによって記述され、その仕様への変更が_適用_されると、Deploymentコントローラは制御された速度で実際の状態を望ましい状態に変更します。
- デバッグ用にラベルを操作できます。Kubernetesコントローラー(ReplicaSetなど)とServiceはセレクターラベルを使用してPodとマッチするため、Podから関連ラベルを削除すると、コントローラーによって考慮されたり、Serviceによってトラフィックを処理されたりすることがなくなります。既存のPodのラベルを削除すると、そのコントローラーはその代わりに新しいPodを作成します。これは、「隔離」環境で以前の「ライブ」Podをデバッグするのに便利な方法です。対話的にラベルを削除または追加するには、
kubectl label
を使います。
コンテナイメージ
imagePullPolicyとイメージのタグは、kubeletが特定のイメージをpullしようとしたときに作用します。
imagePullPolicy: IfNotPresent
: ローカルでイメージが見つからない場合にのみイメージをpullします。imagePullPolicy: Always
: kubeletがコンテナを起動する度に、kubeletはコンテナイメージレジストリに問い合わせて、イメージのダイジェストの名前解決を行います。もし、kubeletが同じダイジェストのコンテナイメージをローカルにキャッシュしていたら、kubeletはそのキャッシュされたイメージを利用します。そうでなければ、kubeletは解決されたダイジェストのイメージをダウンロードし、そのイメージを利用してコンテナを起動します。imagePullPolicy
のタグが省略されていて、利用してるイメージのタグが:latest
の場合や省略されている場合、Always
が適用されます。imagePullPolicy
のタグが省略されていて、利用してるイメージのタグはあるが:latest
でない場合、IfNotPresent
が適用されます。imagePullPolicy: Never
: 常にローカルでイメージを探そうとします。ない場合にもイメージはpullしません。
備考: コンテナが常に同じバージョンのイメージを使用するようにするためには、そのコンテナイメージのダイジェストを指定することができます。<image-name>:<tag>
を<image-name>@<digest>
で置き換えます(例:image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
)。このダイジェストはイメージの特定のバージョンを一意に識別するため、ダイジェスト値を変更しない限り、Kubernetesによって更新されることはありません。
備考: どのバージョンのイメージが実行されているのかを追跡するのが難しく、適切にロールバックするのが難しいため、本番環境でコンテナをデプロイするときは:latest
タグを使用しないでください。
備考: ベースイメージのプロバイダーのキャッシュセマンティクスにより、imagePullPolicy:Always
もより効率的になります。たとえば、Dockerでは、イメージがすでに存在する場合すべてのイメージレイヤーがキャッシュされ、イメージのダウンロードが不要であるため、pullが高速になります。
kubectlの使い方
kubectl apply -f <directory>
を使いましょう。これを使うと、ディレクトリ内のすべての.yaml
、.yml
、および.json
ファイルがapply
に渡されます。get
やdelete
を行う際は、特定のオブジェクト名を指定するのではなくラベルセレクターを使いましょう。ラベルセレクターとラベルの効果的な使い方のセクションを参照してください。単一コンテナのDeploymentやServiceを素早く作成するなら、
kubectl create deployment
やkubectl expose
を使いましょう。一例として、Serviceを利用したクラスター内のアプリケーションへのアクセスを参照してください。
2 - ConfigMap
ConfigMapは、 機密性のないデータをキーと値のペアで保存するために使用されるAPIオブジェクトです。Podは、環境変数、コマンドライン引数、またはボリューム内の設定ファイルとしてConfigMapを使用できます。
ConfigMapを使用すると、環境固有の設定をコンテナイメージから分離できるため、アプリケーションを簡単に移植できるようになります。
注意: ConfigMapは機密性や暗号化を提供しません。保存したいデータが機密情報である場合は、ConfigMapの代わりにSecretを使用するか、追加の(サードパーティー)ツールを使用してデータが非公開になるようにしてください。
動機
アプリケーションのコードとは別に設定データを設定するには、ConfigMapを使用します。
たとえば、アプリケーションを開発していて、(開発用時には)自分のコンピューター上と、(実際のトラフィックをハンドルするときは)クラウド上とで実行することを想像してみてください。あなたは、DATABASE_HOST
という名前の環境変数を使用するコードを書きます。ローカルでは、この変数をlocalhost
に設定します。クラウド上では、データベースコンポーネントをクラスター内に公開するKubernetesのServiceを指すように設定します。
こうすることで、必要であればクラウド上で実行しているコンテナイメージを取得することで、ローカルでも完全に同じコードを使ってデバッグができるようになります。
ConfigMapオブジェクト
ConfigMapは、他のオブジェクトが使うための設定を保存できるAPIオブジェクトです。ほとんどのKubernetesオブジェクトにspec
セクションがあるのとは違い、ConfigMapにはアイテム(キー)と値を保存するためのdata
セクションがあります。
ConfigMapの名前は、有効なDNSのサブドメイン名でなければなりません。
ConfigMapとPod
ConfigMapを参照して、ConfigMap内のデータを元にしてPod内のコンテナの設定をするPodのspec
を書くことができます。このとき、PodとConfigMapは同じ名前空間内に存在する必要があります。
以下に、ConfigMapの例を示します。単一の値を持つキーと、Configuration形式のデータ片のような値を持つキーがあります。
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# プロパティーに似たキー。各キーは単純な値にマッピングされている
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
#
# ファイルに似たキー
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
ConfigMapを利用してPod内のコンテナを設定する方法には、次の4種類があります。
- コマンドライン引数をコンテナのエントリーポイントに渡す
- 環境変数をコンテナに渡す
- 読み取り専用のボリューム内にファイルを追加し、アプリケーションがそのファイルを読み取る
- Kubernetes APIを使用してConfigMapを読み込むコードを書き、そのコードをPod内で実行する
これらのさまざまな方法は、利用するデータをモデル化するのに役立ちます。最初の3つの方法では、kubeletがPodのコンテナを起動する時にConfigMapのデータを使用します。
4番目の方法では、ConfigMapとそのデータを読み込むためのコードを自分自身で書く必要があります。しかし、Kubernetes APIを直接使用するため、アプリケーションはConfigMapがいつ変更されても更新イベントを受信でき、変更が発生したときにすぐに反応できます。この手法では、Kubernetes APIに直接アクセスすることで、別の名前空間にあるConfigMapにもアクセスできます。
以下に、Podを設定するためにgame-demo
から値を使用するPodの例を示します。
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: game.example/demo-game
env:
# 環境変数を定義します。
- name: PLAYER_INITIAL_LIVES # ここではConfigMap内のキーの名前とは違い
# 大文字が使われていることに着目してください。
valueFrom:
configMapKeyRef:
name: game-demo # この値を取得するConfigMap。
key: player_initial_lives # 取得するキー。
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# Podレベルでボリュームを設定し、Pod内のコンテナにマウントします。
- name: config
configMap:
# マウントしたいConfigMapの名前を指定します。
name: game-demo
# ファイルとして作成するConfigMapのキーの配列
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
ConfigMapは1行のプロパティの値と複数行のファイルに似た形式の値を区別しません。問題となるのは、Podや他のオブジェクトによる値の使用方法です。
この例では、ボリュームを定義して、demo
コンテナの内部で/config
にマウントしています。これにより、ConfigMap内には4つのキーがあるにもかかわらず、2つのファイル/config/game.properties
および/config/user-interface.properties
だけが作成されます。
これは、Podの定義がvolumes
セクションでitems
という配列を指定しているためです。もしitems
の配列を完全に省略すれば、ConfigMap内の各キーがキーと同じ名前のファイルになり、4つのファイルが作成されます。
ConfigMapを使う
ConfigMapは、データボリュームとしてマウントできます。ConfigMapは、Podへ直接公開せずにシステムの他の部品として使うこともできます。たとえば、ConfigMapには、システムの他の一部が設定のために使用するデータを保存できます。
ConfigMapをPodからファイルとして使う
ConfigMapをPod内のボリュームで使用するには、次のようにします。
- ConfigMapを作成するか、既存のConfigMapを使用します。複数のPodから同じConfigMapを参照することもできます。
- Podの定義を修正して、
.spec.volumes[]
以下にボリュームを追加します。ボリュームに任意の名前を付け、.spec.volumes[].configMap.name
フィールドにConfigMapオブジェクトへの参照を設定します。 - ConfigMapが必要な各コンテナに
.spec.containers[].volumeMounts[]
を追加します。.spec.containers[].volumeMounts[].readOnly = true
を指定して、.spec.containers[].volumeMounts[].mountPath
には、ConfigMapのデータを表示したい未使用のディレクトリ名を指定します。 - イメージまたはコマンドラインを修正して、プログラムがそのディレクトリ内のファイルを読み込むように設定します。ConfigMapの
data
マップ内の各キーが、mountPath
以下のファイル名になります。
以下は、ボリューム内にConfigMapをマウントするPodの例です。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
configMap:
name: myconfigmap
使用したいそれぞれのConfigMapごとに、.spec.volumes
内で参照する必要があります。
Pod内に複数のコンテナが存在する場合、各コンテナにそれぞれ別のvolumeMounts
のブロックが必要ですが、.spec.volumes
はConfigMapごとに1つしか必要ありません。
マウントしたConfigMapの自動的な更新
ボリューム内で現在使用中のConfigMapが更新されると、射影されたキーも最終的に(eventually)更新されます。kubeletは定期的な同期のたびにマウントされたConfigMapが新しいかどうか確認します。しかし、kubeletが現在のConfigMapの値を取得するときにはローカルキャッシュを使用します。キャッシュの種類は、KubeletConfiguration構造体の中のConfigMapAndSecretChangeDetectionStrategy
フィールドで設定可能です。ConfigMapは、監視(デフォルト)、ttlベース、またはすべてのリクエストを直接APIサーバーへ単純にリダイレクトする方法のいずれかによって伝搬されます。その結果、ConfigMapが更新された瞬間から、新しいキーがPodに射影されるまでの遅延の合計は、最長でkubeletの同期期間+キャッシュの伝搬遅延になります。ここで、キャッシュの伝搬遅延は選択したキャッシュの種類に依存します(監視の伝搬遅延、キャッシュのttl、または0に等しくなります)。
Kubernetes v1.18 [alpha]
Kubernetesのアルファ版の機能である イミュータブルなSecretおよびConfigMap は、個別のSecretやConfigMapをイミュータブルに設定するオプションを提供します。ConfigMapを広範に使用している(少なくとも数万のConfigMapがPodにマウントされている)クラスターでは、データの変更を防ぐことにより、以下のような利点が得られます。
- アプリケーションの停止を引き起こす可能性のある予想外の(または望まない)変更を防ぐことができる
- ConfigMapをイミュータブルにマークして監視を停止することにより、kube-apiserverへの負荷を大幅に削減し、クラスターの性能が向上する
この機能を使用するには、ImmutableEmphemeralVolumes
フィーチャーゲートを有効にして、SecretやConfigMapのimmutable
フィールドをtrue
に設定してください。次に例を示します。
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true
備考: 一度ConfigMapやSecretがイミュータブルに設定すると、この変更を元に戻したり、data
フィールドのコンテンツを変更することはできません。既存のPodは削除されたConfigMapのマウントポイントを保持するため、こうしたPodは再作成することをおすすめします。
次の項目
- Secretについて読む。
- Podを構成してConfigMapを使用するを読む。
- コードを設定から分離する動機を理解するためにThe Twelve-Factor Appを読む。
3 - Secrets
KubernetesのSecretはパスワード、OAuthトークン、SSHキーのような機密情報を保存し、管理できるようにします。 Secretに機密情報を保存することは、それらをPodの定義やコンテナイメージに直接記載するより、安全で柔軟です。詳しくはSecretの設計文書を参照してください。
Secretの概要
Secretはパスワード、トークン、キーのような小容量の機密データを含むオブジェクトです。 他の方法としては、そのような情報はPodの定義やイメージに含めることができます。 ユーザーはSecretを作ることができ、またシステムが作るSecretもあります。
Secretを使うには、PodはSecretを参照することが必要です。 PodがSecretを使う方法は3種類あります。
- ボリューム内のファイルとして、Podの単一または複数のコンテナにマウントする
- コンテナの環境変数として利用する
- Podを生成するためにkubeletがイメージをpullするときに使用する
内蔵のSecret
自動的にサービスアカウントがAPIの認証情報のSecretを生成し、アタッチする
KubernetesはAPIにアクセスするための認証情報を含むSecretを自動的に生成し、この種のSecretを使うように自動的にPodを改変します。
必要であれば、APIの認証情報が自動生成され利用される機能は無効化したり、上書きしたりすることができます。しかし、安全にAPIサーバーでアクセスすることのみが必要なのであれば、これは推奨されるワークフローです。
サービスアカウントがどのように機能するのかについては、サービスアカウント のドキュメントを参照してください。
Secretを作成する
kubectl
を利用してSecretを作成する
SecretにはPodがデータベースにアクセスするために必要な認証情報を含むことができます。
例えば、ユーザー名とパスワードからなる接続文字列です。
ローカルマシンのファイル./username.txt
にユーザー名を、ファイル./password.txt
にパスワードを保存することができます。
# この後の例で使用するファイルを作成します
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt
kubectl create secret
コマンドはそれらのファイルをSecretに格納して、APIサーバー上でオブジェクトを作成します。
Secretオブジェクトの名称は正当なDNSサブドメイン名である必要があります。
kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
次のように出力されます:
secret "db-user-pass" created
デフォルトのキー名はファイル名です。[--from-file=[key=]source]
を使って任意でキーを指定することができます。
kubectl create secret generic db-user-pass --from-file=username=./username.txt --from-file=password=./password.txt
備考:
$
、\
、*
、=
、!
のような特殊文字はシェルに解釈されるので、エスケープする必要があります。 ほとんどのシェルではパスワードをエスケープする最も簡単な方法はシングルクォート('
)で囲むことです。 例えば、実際のパスワードがS!B\*d$zDsb=
だとすると、実行すべきコマンドは下記のようになります。kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
--from-file
を使ってファイルからパスワードを読み込む場合、ファイルに含まれるパスワードの特殊文字をエスケープする必要はありません。
Secretが作成されたことを確認できます。
kubectl get secrets
出力は次のようになります。
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
Secretの説明を参照することができます。
kubectl describe secrets/db-user-pass
出力は次のようになります。
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
備考:kubectl get
やkubectl describe
コマンドはデフォルトではSecretの内容の表示を避けます。 これはSecretを誤って盗み見られたり、ターミナルのログへ記録されてしまったりすることがないよう保護するためです。
Secretの内容を参照する方法はSecretのデコードを参照してください。
手動でSecretを作成する
SecretをJSONまたはYAMLフォーマットのファイルで作成し、その後オブジェクトを作成することができます。
Secretオブジェクトの名称は正当なDNSサブドメイン名である必要があります。
Secretは、data
とstringData
の2つの連想配列を持ちます。
data
フィールドは任意のデータの保存に使われ、Base64でエンコードされています。
stringData
は利便性のために存在するもので、機密データをエンコードされない文字列で扱えます。
例えば、data
フィールドを使って1つのSecretに2つの文字列を保存するには、次のように文字列をBase64エンコードします。
echo -n 'admin' | base64
出力は次のようになります。
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
出力は次のようになります。
MWYyZDFlMmU2N2Rm
このようなSecretを書きます。
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
これでSecretをkubectl apply
コマンドで作成できるようになりました。
kubectl apply -f ./secret.yaml
出力は次のようになります。
secret "mysecret" created
状況によっては、代わりにstringData
フィールドを使いたいときもあるでしょう。
このフィールドを使えばBase64でエンコードされていない文字列を直接Secretに書くことができて、その文字列はSecretが作られたり更新されたりするときにエンコードされます。
実用的な例として、設定ファイルの格納にSecretを使うアプリケーションをデプロイすることを考えます。 デプロイプロセスの途中で、この設定ファイルの一部のデータを投入したいとしましょう。
例えば、アプリケーションは次のような設定ファイルを使用するとします。
apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"
次のような定義を使用して、この設定ファイルをSecretに保存することができます。
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
config.yaml: |-
apiUrl: "https://my.api.com/api/v1"
username: {{username}}
password: {{password}}
デプロイツールはkubectl apply
を実行する前に{{username}}
と{{password}}
のテンプレート変数を置換することができます。
stringData
フィールドは利便性のための書き込み専用フィールドです。
Secretを取得するときに出力されることは決してありません。
例えば、次のコマンドを実行すると、
kubectl get secret mysecret -o yaml
出力は次のようになります。
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:40:59Z
name: mysecret
namespace: default
resourceVersion: "7225"
uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data:
config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
username
のようなフィールドをdata
とstringData
の両方で指定すると、stringData
の値が使用されます。
例えば、次のSecret定義からは
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
次のようなSecretが生成されます。
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:46:46Z
name: mysecret
namespace: default
resourceVersion: "7579"
uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
data:
username: YWRtaW5pc3RyYXRvcg==
YWRtaW5pc3RyYXRvcg==
をデコードするとadministrator
になります。
data
やstringData
のキーは英数字または'-'、'_'、'.'からなる必要があります。
備考: シリアライズされたJSONやYAMLの機密データはBase64エンコードされています。 文字列の中の改行は不正で、含まれていてはなりません。 Darwin/macOSのbase64
ユーティリティーを使うときは、長い行を分割する-b
オプションを指定するのは避けるべきです。 反対に、Linuxユーザーはbase64
コマンドに-w 0
オプションを指定するか、-w
オプションが使えない場合はbase64 | tr -d '\n'
のようにパイプすべきです。
ジェネレーターからSecretを作成する
Kubernetes v1.14から、kubectl
はKustomizeを使ったオブジェクトの管理に対応しています。
KustomizeはSecretやConfigMapを生成するリソースジェネレーターを提供します。
Kustomizeのジェネレーターはディレクトリの中のkustomization.yaml
ファイルにて指定されるべきです。
Secretが生成された後には、kubectl apply
コマンドを使用してAPIサーバー上にSecretを作成することができます。
ファイルからのSecretの生成
./username.txtと./password.txtのファイルから生成するようにsecretGenerator
を定義することで、Secretを生成することができます。
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
EOF
Secretを生成するには、kustomization.yaml
を含むディレクトリをapplyします。
kubectl apply -k .
出力は次のようになります。
secret/db-user-pass-96mffmfh4k created
Secretが生成されたことを確認できます。
kubectl get secrets
出力は次のようになります。
NAME TYPE DATA AGE
db-user-pass-96mffmfh4k Opaque 2 51s
kubectl describe secrets/db-user-pass-96mffmfh4k
出力は次のようになります。
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
文字列リテラルからのSecretの生成
リテラルusername=admin
とpassword=secret
から生成するようにsecretGenerator
を定義して、Secretを生成することができます。
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
literals:
- username=admin
- password=secret
EOF
Secretを生成するには、kustomization.yaml
を含むディレクトリをapplyします。
kubectl apply -k .
出力は次のようになります。
secret/db-user-pass-dddghtt9b5 created
備考: Secretが生成されるとき、Secretのデータからハッシュ値が算出され、Secretの名称にハッシュ値が加えられます。 これはデータが更新されたときに毎回新しいSecretが生成されることを保証します。
Secretのデコード
Secretはkubectl get secret
を実行することで取得可能です。
例えば、前のセクションで作成したSecretは次のコマンドを実行することで参照できます。
kubectl get secret mysecret -o yaml
出力は次のようになります。
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: 2016-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
password
フィールドをデコードします。
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
出力は次のようになります。
1f2d1e2e67df
Secretの編集
既存のSecretは次のコマンドで編集することができます。
kubectl edit secrets mysecret
デフォルトに設定されたエディターが開かれ、data
フィールドのBase64でエンコードされたSecretの値を編集することができます。
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: { ... }
creationTimestamp: 2016-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
Secretの使用
Podの中のコンテナがSecretを使うために、データボリュームとしてマウントしたり、環境変数として値を参照できるようにできます。 Secretは直接Podが参照できるようにはされず、システムの別の部分に使われることもあります。 例えば、Secretはあなたに代わってシステムの他の部分が外部のシステムとやりとりするために使う機密情報を保持することもあります。
SecretをファイルとしてPodから利用する
PodのボリュームとしてSecretを使うには、
- Secretを作成するか既存のものを使用します。複数のPodが同一のSecretを参照することができます。
- ボリュームを追加するため、Podの定義の
.spec.volumes[]
以下を書き換えます。ボリュームに命名し、.spec.volumes[].secret.secretName
フィールドはSecretオブジェクトの名称と同一にします。 - Secretを必要とするそれぞれのコンテナに
.spec.containers[].volumeMounts[]
を追加します。.spec.containers[].volumeMounts[].readOnly = true
を指定して.spec.containers[].volumeMounts[].mountPath
をSecretをマウントする未使用のディレクトリ名にします。 - イメージやコマンドラインを変更し、プログラムがそのディレクトリを参照するようにします。連想配列
data
のキーはmountPath
以下のファイル名になります。
これはSecretをボリュームとしてマウントするPodの例です。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
使用したいSecretはそれぞれ.spec.volumes
の中で参照されている必要があります。
Podに複数のコンテナがある場合、それぞれのコンテナがvolumeMounts
ブロックを必要としますが、.spec.volumes
はSecret1つあたり1つで十分です。
多くのファイルを一つのSecretにまとめることも、多くのSecretを使うことも、便利な方を採ることができます。
Secretのキーの特定のパスへの割り当て
Secretのキーが割り当てられるパスを制御することができます。
それぞれのキーがターゲットとするパスは.spec.volumes[].secret.items
フィールドによって指定てきます。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
次のような挙動をします。
username
は/etc/foo/username
の代わりに/etc/foo/my-group/my-username
の元に格納されます。password
は現れません。
.spec.volumes[].secret.items
が使われるときは、items
の中で指定されたキーのみが現れます。
Secretの中の全てのキーを使用したい場合は、items
フィールドに全て列挙する必要があります。
列挙されたキーは対応するSecretに存在する必要があり、そうでなければボリュームは生成されません。
Secretファイルのパーミッション
単一のSecretキーに対して、ファイルアクセスパーミッションビットを指定することができます。
パーミッションを指定しない場合、デフォルトで0644
が使われます。
Secretボリューム全体のデフォルトモードを指定し、必要に応じてキー単位で上書きすることもできます。
例えば、次のようにしてデフォルトモードを指定できます。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
defaultMode: 0400
Secretは/etc/foo
にマウントされ、Secretボリュームが生成する全てのファイルはパーミッション0400
に設定されます。
JSONの仕様は8進数の記述に対応していないため、パーミッション0400を示す値として256を使用することに注意が必要です。 Podの定義にJSONではなくYAMLを使う場合は、パーミッションを指定するためにより自然な8進表記を使うことができます。
kubectl exec
を使ってPodに入るときは、期待したファイルモードを知るためにシンボリックリンクを辿る必要があることに注意してください。
例として、PodのSecretのファイルモードを確認します。
kubectl exec mypod -it sh
cd /etc/foo
ls -l
出力は次のようになります。
total 0
lrwxrwxrwx 1 root root 15 May 18 00:18 password -> ..data/password
lrwxrwxrwx 1 root root 15 May 18 00:18 username -> ..data/username
正しいファイルモードを知るためにシンボリックリンクを辿ります。
cd /etc/foo/..data
ls -l
出力は次のようになります。
total 8
-r-------- 1 root root 12 May 18 00:18 password
-r-------- 1 root root 5 May 18 00:18 username
前の例のようにマッピングを使い、ファイルごとに異なるパーミッションを指定することができます。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 0777
この例では、ファイル/etc/foo/my-group/my-username
のパーミッションは0777
になります。
JSONを使う場合は、JSONの制約により10進表記の511
と記述する必要があります。
後で参照する場合、このパーミッションの値は10進表記で表示されることがあることに注意してください。
Secretの値のボリュームによる利用
Secretのボリュームがマウントされたコンテナからは、Secretのキーはファイル名として、Secretの値はBase64デコードされ、それらのファイルに格納されます。 上記の例のコンテナの中でコマンドを実行した結果を示します。
ls /etc/foo/
出力は次のようになります。
username
password
cat /etc/foo/username
出力は次のようになります。
admin
cat /etc/foo/password
出力は次のようになります。
1f2d1e2e67df
コンテナ内のプログラムはファイルからSecretの内容を読み取る責務を持ちます。
マウントされたSecretの自動更新
ボリュームとして使用されているSecretが更新されると、やがて割り当てられたキーも同様に更新されます。
kubeletは定期的な同期のたびにマウントされたSecretが新しいかどうかを確認します。
しかしながら、kubeletはSecretの現在の値の取得にローカルキャッシュを使用します。
このキャッシュはKubeletConfiguration struct内のConfigMapAndSecretChangeDetectionStrategy
フィールドによって設定可能です。
Secretはwatch(デフォルト)、TTLベース、単に全てのリクエストをAPIサーバーへリダイレクトすることのいずれかによって伝搬します。
結果として、Secretが更新された時点からPodに新しいキーが反映されるまでの遅延時間の合計は、kubeletの同期間隔 + キャッシュの伝搬遅延となります。
キャッシュの遅延は、キャッシュの種別により、それぞれwatchの伝搬遅延、キャッシュのTTL、0になります。
備考: SecretをsubPathを指定してボリュームにマウントしているコンテナには、Secretの更新が反映されません。
Kubernetes v1.18 [alpha]
Kubernetesのアルファ機能である Immutable Secrets and ConfigMaps は各SecretやConfigMapが不変であると設定できるようにします。 Secretを広範に利用しているクラスター(PodにマウントされているSecretが1万以上)においては、データが変更されないようにすることで次のような利点が得られます。
- 意図しない(または望まない)変更によってアプリケーションの停止を引き起こすことを防ぎます
- 不変であると設定されたSecretの監視を停止することにより、kube-apiserverの負荷が著しく軽減され、クラスターのパフォーマンスが改善されます
この機能を利用するには、ImmutableEphemeralVolumes
feature gateを有効にして、SecretまたはConfigMapのimmutable
フィールドにtrue
を指定します。例えば、次のようにします。
apiVersion: v1
kind: Secret
metadata:
...
data:
...
immutable: true
備考: 一度SecretやConfigMapを不変であると設定すると、この変更を戻すことやdata
フィールドの内容を書き換えることは できません 。 Secretを削除して、再生成することだけができます。 既存のPodは削除されたSecretへのマウントポイントを持ち続けるため、Podを再生成することが推奨されます。
Secretを環境変数として使用する
SecretをPodの環境変数として使用するには、
- Secretを作成するか既存のものを使います。複数のPodが同一のSecretを参照することができます。
- Podの定義を変更し、Secretを使用したいコンテナごとにSecretのキーと割り当てたい環境変数を指定します。Secretキーを利用する環境変数は
env[].valueFrom.secretKeyRef
にSecretの名前とキーを指定すべきです。 - イメージまたはコマンドライン(もしくはその両方)を変更し、プログラムが指定した環境変数を参照するようにします。
Secretを環境変数で参照するPodの例を示します。
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
restartPolicy: Never
環境変数からのSecretの値の利用
Secretを環境変数として利用するコンテナの内部では、Secretのキーは一般の環境変数名として現れ、値はBase64デコードされた状態で保持されます。
上記の例のコンテナの内部でコマンドを実行した結果の例を示します。
echo $SECRET_USERNAME
出力は次のようになります。
admin
echo $SECRET_PASSWORD
出力は次のようになります。
1f2d1e2e67df
imagePullSecretsを使用する
imagePullSecrets
フィールドは同一のネームスペース内のSecretの参照のリストです。
kubeletにDockerやその他のイメージレジストリのパスワードを渡すために、imagePullSecrets
にそれを含むSecretを指定することができます。
kubeletはこの情報をPodのためにプライベートイメージをpullするために使います。
imagePullSecrets
の詳細はPodSpec APIを参照してください。
imagePullSecretを手動で指定する
ImagePullSecrets
の指定の方法はコンテナイメージのドキュメントに記載されています。
imagePullSecretsが自動的にアタッチされるようにする
imagePullSecrets
を手動で作成し、サービスアカウントから参照することができます。
サービスアカウントが指定されるまたはデフォルトでサービスアカウントが設定されたPodは、サービスアカウントが持つimagePullSecrets
フィールドを得ます。
詳細な手順の説明はサービスアカウントへのImagePullSecretsの追加を参照してください。
手動で作成されたSecretの自動的なマウント
手動で作成されたSecret(例えばGitHubアカウントへのアクセスに使うトークンを含む)はサービスアカウントを基に自動的にアタッチすることができます。 詳細な説明はPodPresetを使ったPodへの情報の注入を参照してください。
詳細
制限事項
Secretボリュームは指定されたオブジェクト参照が実際に存在するSecretオブジェクトを指していることを保証するため検証されます。 そのため、Secretはそれを必要とするPodよりも先に作成する必要があります。
Secretリソースはnamespaceに属します。 Secretは同一のnamespaceに属するPodからのみ参照することができます。
各Secretは1MiBの容量制限があります。 これはAPIサーバーやkubeletのメモリーを枯渇するような非常に大きなSecretを作成することを避けるためです。 しかしながら、小さなSecretを多数作成することも同様にメモリーを枯渇させます。 Secretに起因するメモリー使用量をより網羅的に制限することは、将来計画されています。
kubeletがPodに対してSecretを使用するとき、APIサーバーから取得されたSecretのみをサポートします。
これにはkubectl
を利用して、またはレプリケーションコントローラーによって間接的に作成されたPodが含まれます。
kubeletの--manifest-url
フラグ、--config
フラグ、またはREST APIにより生成されたPodは含まれません
(これらはPodを生成するための一般的な方法ではありません)。
環境変数として使われるSecretは任意と指定されていない限り、それを使用するPodよりも先に作成される必要があります。 存在しないSecretへの参照はPodの起動を妨げます。
Secretに存在しないキーへの参照(secretKeyRef
フィールド)はPodの起動を妨げます。
SecretをenvFrom
フィールドによって環境変数へ設定する場合、環境変数の名称として不適切なキーは飛ばされます。
Podは起動することを認められます。
このとき、reasonがInvalidVariableNames
であるイベントが発生し、メッセージに飛ばされたキーのリストが含まれます。
この例では、Podは2つの不適切なキー1badkey
と2alsobad
を含むdefault/mysecretを参照しています。
kubectl get events
出力は次のようになります。
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON
0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.
SecretとPodの相互作用
Kubernetes APIがコールされてPodが生成されるとき、参照するSecretの存在は確認されません。 Podがスケジューリングされると、kubeletはSecretの値を取得しようとします。 Secretが存在しない、または一時的にAPIサーバーへの接続が途絶えたことにより取得できない場合、kubeletは定期的にリトライします。 kubeletはPodがまだ起動できない理由に関するイベントを報告します。 Secretが取得されると、kubeletはそのボリュームを作成しマウントします。 Podのボリュームが全てマウントされるまでは、Podのコンテナは起動することはありません。
ユースケース
ユースケース: コンテナの環境変数として
Secretの作成
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
USER_NAME: YWRtaW4=
PASSWORD: MWYyZDFlMmU2N2Rm
kubectl apply -f mysecret.yaml
envFrom
を使ってSecretの全てのデータをコンテナの環境変数として定義します。
SecretのキーはPod内の環境変数の名称になります。
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- secretRef:
name: mysecret
restartPolicy: Never
ユースケース: SSH鍵を持つPod
SSH鍵を含むSecretを作成します。
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
出力は次のようになります。
secret "ssh-key-secret" created
SSH鍵を含むsecretGenerator
フィールドを持つkustomization.yaml
を作成することもできます。
注意: 自身のSSH鍵を送る前に慎重に検討してください。クラスターの他のユーザーがSecretにアクセスできる可能性があります。 Kubernetesクラスターを共有しているユーザー全員がアクセスできるようにサービスアカウントを使用し、ユーザーが安全でない状態になったらアカウントを無効化することができます。
SSH鍵のSecretを参照し、ボリュームとして使用するPodを作成することができます。
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
labels:
name: secret-test
spec:
volumes:
- name: secret-volume
secret:
secretName: ssh-key-secret
containers:
- name: ssh-test-container
image: mySshImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
コンテナのコマンドを実行するときは、下記のパスにて鍵が利用可能です。
/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey
コンテナーはSecretのデータをSSH接続を確立するために使用することができます。
ユースケース: 本番、テスト用の認証情報を持つPod
あるPodは本番の認証情報のSecretを使用し、別のPodはテスト環境の認証情報のSecretを使用する例を示します。
secretGenerator
フィールドを持つkustomization.yaml
を作成するか、kubectl create secret
を実行します。
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
出力は次のようになります。
secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
出力は次のようになります。
secret "test-db-secret" created
備考:
$
、\
、*
、=
、!
のような特殊文字はシェルに解釈されるので、エスケープする必要があります。 ほとんどのシェルではパスワードをエスケープする最も簡単な方法はシングルクォート('
)で囲むことです。 例えば、実際のパスワードがS!B\*d$zDsb=
だとすると、実行すべきコマンドは下記のようになります。kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
--from-file
によってファイルを指定する場合は、そのパスワードに含まれる特殊文字をエスケープする必要はありません。
Podを作成します。
cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
apiVersion: v1
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: prod-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
- kind: Pod
apiVersion: v1
metadata:
name: test-db-client-pod
labels:
name: test-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: test-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
EOF
同じkustomization.yamlにPodを追記します。
cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF
下記のコマンドを実行して、APIサーバーにこれらのオブジェクト群を適用します。
kubectl apply -k .
両方のコンテナはそれぞれのファイルシステムに下記に示すファイルを持ちます。ファイルの値はそれぞれのコンテナの環境ごとに異なります。
/etc/secret-volume/username
/etc/secret-volume/password
2つのPodの仕様の差分は1つのフィールドのみである点に留意してください。 これは共通のPodテンプレートから異なる能力を持つPodを作成することを容易にします。
2つのサービスアカウントを使用すると、ベースのPod仕様をさらに単純にすることができます。
prod-user
とprod-db-secret
test-user
とtest-db-secret
簡略化されたPod仕様は次のようになります。
apiVersion: v1
kind: Pod
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
serviceAccount: prod-db-client
containers:
- name: db-client-container
image: myClientImage
ユースケース: Secretボリューム内のdotfile
キーをドットから始めることで、データを「隠す」ことができます。
このキーはdotfileまたは「隠し」ファイルを示します。例えば、次のSecretはsecret-volume
ボリュームにマウントされます。
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: k8s.gcr.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
このボリュームは.secret-file
という単一のファイルを含み、dotfile-test-container
はこのファイルを/etc/secret-volume/.secret-file
のパスに持ちます。
備考: ドットから始まるファイルはls -l
の出力では隠されるため、ディレクトリの内容を参照するときにはls -la
を使わなければなりません。
ユースケース: Podの中の単一コンテナのみが参照できるSecret
HTTPリクエストを扱い、複雑なビジネスロジックを処理し、メッセージにHMACによる認証コードを付与する必要のあるプログラムを考えます。 複雑なアプリケーションロジックを持つため、サーバーにリモートのファイルを読み出せる未知の脆弱性がある可能性があり、この脆弱性は攻撃者に秘密鍵を晒してしまいます。
このプログラムは2つのコンテナに含まれる2つのプロセスへと分割することができます。 フロントエンドのコンテナはユーザーとのやりとりやビジネスロジックを扱い、秘密鍵を参照することはできません。 署名コンテナは秘密鍵を参照することができて、単にフロントエンドからの署名リクエストに応答します。例えば、localhostの通信によって行います。
この分割する手法によって、攻撃者はアプリケーションサーバーを騙して任意の処理を実行させる必要があるため、ファイルの内容を読み出すより困難になります。
ベストプラクティス
Secret APIを使用するクライアント
Secret APIとやりとりするアプリケーションをデプロイするときには、RBACのような認可ポリシーを使用して、アクセスを制限すべきです。 Secretは様々な種類の重要な値を保持することが多く、サービスアカウントのトークンのようにKubernetes内部や、外部のシステムで昇格できるものも多くあります。個々のアプリケーションが、Secretの能力について推論することができたとしても、同じネームスペースの別のアプリケーションがその推定を覆すこともあります。
これらの理由により、ネームスペース内のSecretに対するwatch
やlist
リクエストはかなり強力な能力であり、避けるべきです。Secretのリストを取得することはクライアントにネームスペース内の全てのSecretの値を調べさせることを認めるからです。クラスター内の全てのSecretに対するwatch
、list
権限は最も特権的な、システムレベルのコンポーネントに限って認めるべきです。
Secret APIへのアクセスが必要なアプリケーションは、必要なSecretに対するget
リクエストを発行すべきです。管理者は全てのSecretに対するアクセスは制限しつつ、アプリケーションが必要とする個々のインスタンスに対するアクセス許可を与えることができます。
get
リクエストの繰り返しに対するパフォーマンスを向上するために、クライアントはSecretを参照するリソースを設計し、それをwatch
して、参照が変更されたときにSecretを再度リクエストすることができます。加えて、個々のリソースをwatch
することのできる"bulk watch" APIが提案されており、将来のKubernetesリリースにて利用可能になる可能性があります。
セキュリティ特性
保護
Secretはそれを使用するPodとは独立に作成されるので、Podを作ったり、参照したり、編集したりするワークフローにおいてSecretが晒されるリスクは軽減されています。 システムは、可能であればSecretの内容をディスクに書き込まないような、Secretについて追加の考慮も行っています。
Secretはノード上のPodが必要とした場合のみ送られます。
kubeletはSecretがディスクストレージに書き込まれないよう、tmpfs
に保存します。
Secretを必要とするPodが削除されると、kubeletはSecretのローカルコピーも同様に削除します。
同一のノードにいくつかのPodに対する複数のSecretが存在することもあります。 しかし、コンテナから参照できるのはPodが要求したSecretのみです。 そのため、あるPodが他のPodのためのSecretにアクセスすることはできません。
Podに複数のコンテナが含まれることもあります。しかし、Podの各コンテナはコンテナ内からSecretを参照するためにvolumeMounts
によってSecretボリュームを要求する必要があります。
これはPodレベルでのセキュリティ分離を実装するのに便利です。
ほとんどのKubernetesディストリビューションにおいては、ユーザーとAPIサーバー間やAPIサーバーからkubelet間の通信はSSL/TLSで保護されています。 そのような経路で伝送される場合、Secretは保護されています。
Kubernetes v1.13 [beta]
保存データの暗号化を有効にして、Secretがetcdに平文で保存されないようにすることができます。
リスク
- APIサーバーでは、機密情報はetcdに保存されます。
そのため、
- 管理者はクラスターデータの保存データの暗号化を有効にすべきです(v1.13以降が必要)。
- 管理者はetcdへのアクセスを管理ユーザに限定すべきです。
- 管理者はetcdで使用していたディスクを使用しなくなったときにはそれをワイプするか完全消去したくなるでしょう。
- クラスターの中でetcdが動いている場合、管理者はetcdのピアツーピア通信がSSL/TLSを利用していることを確認すべきです。
- Secretをマニフェストファイル(JSONまたはYAML)を介して設定する場合、それはBase64エンコードされた機密情報を含んでいるので、ファイルを共有したりソースリポジトリに入れることは秘密が侵害されることを意味します。Base64エンコーディングは暗号化手段では なく 、平文と同様であると判断すべきです。
- アプリケーションはボリュームからSecretの値を読み取った後も、その値を保護する必要があります。例えば意図せずログに出力する、信用できない相手に送信するようなことがないようにです。
- Secretを利用するPodを作成できるユーザーはSecretの値を見ることができます。たとえAPIサーバーのポリシーがユーザーにSecretの読み取りを許可していなくても、ユーザーはSecretを晒すPodを実行することができます。
- 現在、任意のノードでルート権限を持つ人は誰でも、kubeletに偽装することで 任意の SecretをAPIサーバーから読み取ることができます。 単一のノードのルート権限を不正に取得された場合の影響を抑えるため、実際に必要としているノードに対してのみSecretを送る機能が計画されています。
4 - コンテナのリソース管理
Podを指定する際に、コンテナが必要とする各リソースの量をオプションで指定することができます。 指定する最も一般的なリソースはCPUとメモリ(RAM)ですが、他にもあります。
Pod内のコンテナのリソース要求を指定すると、スケジューラはこの情報を使用して、どのNodeにPodを配置するかを決定します。コンテナに制限ソースを指定すると、kubeletはその制限を適用し、実行中のコンテナが設定した制限を超えてリソースを使用することができないようにします。また、kubeletは、少なくともそのシステムリソースのうち、要求の量を、そのコンテナが使用するために特別に確保します。
要求と制限
Podが動作しているNodeに利用可能なリソースが十分にある場合、そのリソースの要求
が指定するよりも多くのリソースをコンテナが使用することが許可されます
ただし、コンテナはそのリソースの制限
を超えて使用することはできません。
たとえば、コンテナに256MiBのメモリー
要求を設定し、そのコンテナが8GiBのメモリーを持つNodeにスケジュールされたPod内に存在し、他のPodが存在しない場合、コンテナはより多くのRAMを使用しようとする可能性があります。
そのコンテナに4GiBのメモリー
制限を設定すると、kubelet(およびコンテナランタイム) が制限を適用します。ランタイムは、コンテナーが設定済みのリソース制限を超えて使用するのを防ぎます。例えば、コンテナ内のプロセスが、許容量を超えるメモリを消費しようとすると、システムカーネルは、メモリ不足(OOM)エラーで、割り当てを試みたプロセスを終了します。
制限は、違反が検出されるとシステムが介入するように事後的に、またはコンテナーが制限を超えないようにシステムが防ぐように強制的に、実装できます。 異なるランタイムは、同じ制限を実装するために異なる方法をとることができます。
リソースタイプ
CPUとメモリーはいずれもリソースタイプです。リソースタイプには基本単位があります。 CPUは計算処理を表し、Kubernetes CPUsの単位で指定されます。 メモリはバイト単位で指定されます。 Kubernetes v1.14以降を使用している場合は、huge pageリソースを指定することができます。 Huge PageはLinux固有の機能であり、Nodeのカーネルはデフォルトのページサイズよりもはるかに大きいメモリブロックを割り当てます。
たとえば、デフォルトのページサイズが4KiBのシステムでは、hugepages-2Mi: 80Mi
という制限を指定できます。
コンテナが40を超える2MiBの巨大ページ(合計80 MiB)を割り当てようとすると、その割り当ては失敗します。
備考:hugepages-*
リソースをオーバーコミットすることはできません。 これはmemory
やcpu
リソースとは異なります。
CPUとメモリーは、まとめてコンピュートリソースまたは単にリソースと呼ばれます。 コンピューティングリソースは、要求され、割り当てられ、消費され得る測定可能な量です。 それらはAPI resourcesとは異なります。 PodやServicesなどのAPIリソースは、Kubernetes APIサーバーを介して読み取りおよび変更できるオブジェクトです。
Podとコンテナのリソース要求と制限
Podの各コンテナは、次の1つ以上を指定できます。
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory
spec.containers[].resources.limits.hugepages-<size>
spec.containers[].resources.requests.cpu
spec.containers[].resources.requests.memory
spec.containers[].resources.requests.hugepages-<size>
要求と制限はそれぞれのコンテナでのみ指定できますが、このPodリソースの要求と制限の関係性について理解すると便利です。 特定のリソースタイプのPodリソース要求/制限は、Pod内の各コンテナに対するそのタイプのリソース要求/制限の合計です。
Kubernetesにおけるリソースの単位
CPUの意味
CPUリソースの制限と要求は、cpu単位で測定されます。 Kuberenetesにおける1つのCPUは、クラウドプロバイダーの1 vCPU/コアおよびベアメタルのインテルプロセッサーの1 ハイパースレッドに相当します。
要求を少数で指定することもできます。
spec.containers[].resources.requests.cpu
が0.5
のコンテナは、1CPUを要求するコンテナの半分のCPUが保証されます。
0.1
という表現は100m
という表現と同等であり、100ミリCPU
と読み替えることができます。
100ミリコア
という表現も、同じことを意味しています。
0.1
のような小数点のある要求はAPIによって100m
に変換され、1m
より細かい精度は許可されません。
このため、100m
の形式が推奨されます。
CPUは常に相対量としてではなく、絶対量として要求されます。 0.1は、シングルコア、デュアルコア、あるいは48コアマシンのどのCPUに対してでも、同一の量を要求します。
メモリーの意味
メモリー
の制限と要求はバイト単位で測定されます。
E、P、T、G、M、Kのいずれかのサフィックスを使用して、メモリーを整数または固定小数点数として表すことができます。
また、Ei、Pi、Ti、Gi、Mi、Kiのような2の累乗の値を使用することもできます。
たとえば、以下はほぼ同じ値を表しています。
128974848, 129e6, 129M, 123Mi
例を見てみましょう。 次のPodには2つのコンテナがあります。 各コンテナには、0.25cpuおよび64MiB(226バイト)のメモリー要求と、0.5cpuおよび128MiBのメモリー制限があります Podには0.5cpuと128MiBのメモリー要求があり、1cpuと256MiBのメモリ制限があると言えます。
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
env:
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
リソース要求を含むPodがどのようにスケジュールされるか
Podを作成すると、KubernetesスケジューラーはPodを実行するNodeを選択します。 各Nodeには、リソースタイプごとに最大容量があります。それは、Podに提供できるCPUとメモリの量です。 スケジューラーは、リソースタイプごとに、スケジュールされたコンテナのリソース要求の合計がNodeの容量より少ないことを確認します。 Node上の実際のメモリーまたはCPUリソースの使用率は非常に低いですが、容量チェックが失敗した場合、スケジューラーはNodeにPodを配置しないことに注意してください。 これにより、例えば日々のリソース要求のピーク時など、リソース利用が増加したときに、Nodeのリソース不足から保護されます。
リソース制限のあるPodがどのように実行されるか
kubeletがPodのコンテナを開始すると、CPUとメモリーの制限がコンテナランタイムに渡されます。
Dockerを使用する場合:
spec.containers[].resources.requests.cpu
は、潜在的に小数であるコア値に変換され、1024倍されます。docker run
コマンドの--cpu-shares
フラグの値は、この数値と2のいずれか大きい方が用いられます。spec.containers[].resources.limits.cpu
はミリコアの値に変換され、100倍されます。 結果の値は、コンテナが100ミリ秒ごとに使用できるCPU時間の合計です。 コンテナは、この間隔の間、CPU時間の占有率を超えて使用することはできません。備考: デフォルトのクォータ期間は100ミリ秒です。 CPUクォータの最小分解能は1ミリ秒です。spec.containers[].resources.limits.memory
は整数に変換され、docker run
コマンドの--memory
フラグの値として使用されます。
コンテナがメモリー制限を超過すると、終了する場合があります。 コンテナが再起動可能である場合、kubeletは他のタイプのランタイム障害と同様にコンテナを再起動します。
コンテナがメモリー要求を超過すると、Nodeのメモリーが不足するたびにそのPodが排出される可能性があります。
コンテナは、長時間にわたってCPU制限を超えることが許可される場合と許可されない場合があります。 ただし、CPUの使用量が多すぎるために、コンテナが強制終了されることはありません。
コンテナをスケジュールできないか、リソース制限が原因で強制終了されているかどうかを確認するには、トラブルシューティングのセクションを参照してください。
コンピュートリソースとメモリーリソースの使用量を監視する
Podのリソース使用量は、Podのステータスの一部として報告されます。
オプションの監視ツールがクラスターにおいて利用可能な場合、Podのリソース使用量はメトリクスAPIから直接、もしくは監視ツールから取得できます。
ローカルのエフェメラルストレージ
Kubernetes v1.10 [beta]
Nodeには、ローカルに接続された書き込み可能なデバイス、または場合によってはRAMによってサポートされるローカルのエフェメラルストレージがあります。 "エフェメラル"とは、耐久性について長期的な保証がないことを意味します。
Podは、スクラッチ領域、キャッシュ、ログ用にエフェメラルなローカルストレージを使用しています。
kubeletは、ローカルのエフェメラルストレージを使用して、Podにスクラッチ領域を提供し、emptyDir
ボリュームをコンテナにマウントできます。
また、kubeletはこの種類のストレージを使用して、Nodeレベルのコンテナログ、コンテナイメージ、実行中のコンテナの書き込み可能なレイヤーを保持します。
注意: Nodeに障害が発生すると、そのエフェメラルストレージ内のデータが失われる可能性があります。 アプリケーションは、ローカルのエフェメラルストレージにパフォーマンスのサービス品質保証(ディスクのIOPSなど)を期待することはできません。
ベータ版の機能として、Kubernetesでは、Podが消費するローカルのエフェメラルストレージの量を追跡、予約、制限することができます。
ローカルエフェメラルストレージの設定
Kubernetesは、Node上のローカルエフェメラルストレージを構成する2つの方法をサポートしています。
この構成では、さまざまな種類のローカルのエフェメラルデータ(emptyDir
ボリュームや、書き込み可能なレイヤー、コンテナイメージ、ログなど)をすべて1つのファイルシステムに配置します。
kubeletを構成する最も効果的な方法は、このファイルシステムをKubernetes(kubelet)データ専用にすることです。
kubeletはNodeレベルのコンテナログも書き込み、これらをエフェメラルなローカルストレージと同様に扱います。
kubeletは、設定されたログディレクトリ(デフォルトでは/var/log
)内のファイルにログを書き出し、ローカルに保存された他のデータのベースディレクトリ(デフォルトでは/var/lib/kubelet
)を持ちます。
通常、/var/lib/kubelet
と/var/log
はどちらもシステムルートファイルシステムにあり、kubeletはそのレイアウトを考慮して設計されています。
Nodeには、Kubernetesに使用されていない他のファイルシステムを好きなだけ持つことができます。
Node上にファイルシステムがありますが、このファイルシステムは、ログやemptyDir
ボリュームなど、実行中のPodの一時的なデータに使用されます。
このファイルシステムは、例えばKubernetesに関連しないシステムログなどの他のデータに使用することができ、ルートファイルシステムとすることさえ可能です。
また、kubeletはノードレベルのコンテナログを最初のファイルシステムに書き込み、これらをエフェメラルなローカルストレージと同様に扱います。
また、別の論理ストレージデバイスでバックアップされた別のファイルシステムを使用することもできます。 この設定では、コンテナイメージレイヤーと書き込み可能なレイヤーを配置するようにkubeletに指示するディレクトリは、この2番目のファイルシステム上にあります。
最初のファイルシステムは、コンテナイメージレイヤーや書き込み可能なレイヤーを保持していません。
Nodeには、Kubernetesに使用されていない他のファイルシステムを好きなだけ持つことができます。
kubeletは、ローカルストレージの使用量を測定できます。 これは、以下の条件で提供されます。
LocalStorageCapacityIsolation
フィーチャーゲートが有効になっています。(デフォルトでオンになっています。)- そして、ローカルのエフェメラルストレージ用にサポートされている構成の1つを使用してNodeをセットアップします。
別の構成を使用している場合、kubeletはローカルのエフェメラルストレージにリソース制限を適用しません。
備考: kubeletは、tmpfs
のemptyDirボリュームをローカルのエフェメラルストレージとしてではなく、コンテナメモリーとして追跡します。
ローカルのエフェメラルストレージの要求と制限設定
ローカルのエフェメラルストレージを管理するためには_ephemeral-storage_パラメーターを利用することができます。 Podの各コンテナは、次の1つ以上を指定できます。
spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage
ephemeral-storage
の制限と要求はバイト単位で記します。
ストレージは、次のいずれかの接尾辞を使用して、通常の整数または固定小数点数として表すことができます。
E、P、T、G、M、K。Ei、Pi、Ti、Gi、Mi、Kiの2のべき乗を使用することもできます。
たとえば、以下はほぼ同じ値を表しています。
128974848, 129e6, 129M, 123Mi
次の例では、Podに2つのコンテナがあります。 各コンテナには、2GiBのローカルのエフェメラルストレージ要求があります。 各コンテナには、4GiBのローカルのエフェメラルストレージ制限があります。 したがって、Podには4GiBのローカルのエフェメラルストレージの要求と、8GiBのローカルのエフェメラルストレージ制限があります。
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
ephemeral-storage: "2Gi"
limits:
ephemeral-storage: "4Gi"
エフェメラルストレージを要求するPodのスケジュール方法
Podを作成すると、KubernetesスケジューラーはPodを実行するNodeを選択します。 各Nodeには、Podに提供できるローカルのエフェメラルストレージの上限があります。 詳細については、Node割り当て可能を参照してください。
スケジューラーは、スケジュールされたコンテナのリソース要求の合計がNodeの容量より少なくなるようにします。
エフェメラルストレージの消費管理
kubeletがローカルのエフェメラルストレージをリソースとして管理している場合、kubeletはストレージの使用量を測定します
- tmpfs
emptyDir
ボリュームを除くemptyDir
ボリューム - Nodeレベルのログを保持するディレクトリ
- 書き込み可能なコンテナレイヤー
Podが許可するよりも多くのエフェメラルストレージを使用している場合、kubeletはPodの排出をトリガーするシグナルを設定します。
コンテナレベルの分離の場合、コンテナの書き込み可能なレイヤーとログ使用量がストレージの制限を超えると、kubeletはPodに排出のマークを付けます。
Podレベルの分離の場合、kubeletはPod内のコンテナの制限を合計し、Podの全体的なストレージ制限を計算します。
このケースでは、すべてのコンテナからのローカルのエフェメラルストレージの使用量とPodのemptyDir
ボリュームの合計がPod全体のストレージ制限を超過する場合、
kubeletはPodをまた排出対象としてマークします。
kubeletはPodストレージの使用状況を測定するさまざまな方法をサポートしています
kubeletは、emptyDir
ボリューム、コンテナログディレクトリ、書き込み可能なコンテナレイヤーをスキャンする定期的なスケジュールチェックを実行します。
スキャンは、使用されているスペースの量を測定します。
備考:このモードでは、kubeletは削除されたファイルのために、開いているファイルディスクリプタを追跡しません。
あなた(またはコンテナ)が
emptyDir
ボリューム内にファイルを作成した後、何かがそのファイルを開き、そのファイルが開かれたままの状態でファイルを削除した場合、削除されたファイルのinodeはそのファイルを閉じるまで残りますが、kubeletはそのスペースを使用中として分類しません。
Kubernetes v1.15 [alpha]
プロジェクトクォータは、ファイルシステム上のストレージ使用量を管理するためのオペレーティングシステムレベルの機能です。
Kubernetesでは、プロジェクトクォータを有効にしてストレージの使用状況を監視することができます。
ノード上のemptyDir
ボリュームをバックアップしているファイルシステムがプロジェクトクォータをサポートしていることを確認してください。
例えば、XFSやext4fsはプロジェクトクォータを提供しています。
備考: プロジェクトクォータはストレージの使用状況を監視しますが、制限を強制するものではありません。
Kubernetesでは、1048576
から始まるプロジェクトIDを使用します。
使用するプロジェクトIDは/etc/projects
と/etc/projid
に登録されます。
この範囲のプロジェクトIDをシステム上で別の目的で使用する場合は、それらのプロジェクトIDを/etc/projects
と/etc/projid
に登録し、
Kubernetesが使用しないようにする必要があります。
クォータはディレクトリスキャンよりも高速で正確です。 ディレクトリがプロジェクトに割り当てられると、ディレクトリ配下に作成されたファイルはすべてそのプロジェクト内に作成され、カーネルはそのプロジェクト内のファイルによって使用されているブロックの数を追跡するだけです。 ファイルが作成されて削除されても、開いているファイルディスクリプタがあれば、スペースを消費し続けます。 クォータトラッキングはそのスペースを正確に記録しますが、ディレクトリスキャンは削除されたファイルが使用するストレージを見落としてしまいます。
プロジェクトクォータを使用する場合は、次のことを行う必要があります。
kubelet設定で、
LocalocalStorpactionCapactionIsolationFSQuotaMonitoring=true
フィーチャーゲートを有効にします。ルートファイルシステム(またはオプションのランタイムファイルシステム))がプロジェクトクォータを有効にしていることを確認してください。 すべてのXFSファイルシステムはプロジェクトクォータをサポートしています。 ext4ファイルシステムでは、ファイルシステムがマウントされていない間は、プロジェクトクォータ追跡機能を有効にする必要があります。
# ext4の場合、/dev/block-deviceがマウントされていません sudo tune2fs -O project -Q prjquota /dev/block-device
ルートファイルシステム(またはオプションのランタイムファイルシステム)がプロジェクトクォータを有効にしてマウントされていることを確認してください。 XFSとext4fsの両方で、マウントオプションは
prjquota
という名前になっています。
拡張リソース
拡張リソースはkubernetes.io
ドメインの外で完全に修飾されたリソース名です。
これにより、クラスタオペレータはKubernetesに組み込まれていないリソースをアドバタイズし、ユーザはそれを利用することができるようになります。
拡張リソースを使用するためには、2つのステップが必要です。 第一に、クラスタオペレーターは拡張リソースをアドバタイズする必要があります。 第二に、ユーザーはPodで拡張リソースを要求する必要があります。
拡張リソースの管理
Nodeレベルの拡張リソース
Nodeレベルの拡張リソースはNodeに関連付けられています。
デバイスプラグイン管理のリソース
各Nodeにデバイスプラグインで管理されているリソースをアドバタイズする方法については、デバイスプラグインを参照してください。
その他のリソース
新しいNodeレベルの拡張リソースをアドバタイズするには、クラスタオペレータはAPIサーバにPATCH
HTTPリクエストを送信し、クラスタ内のNodeのstatus.capacity
に利用可能な量を指定します。
この操作の後、ノードのstatus.capacity
には新しいリソースが含まれます。
status.allocatable
フィールドは、kubeletによって非同期的に新しいリソースで自動的に更新されます。
スケジューラはPodの適合性を評価する際にNodeのstatus.allocatable
値を使用するため、Nodeの容量に新しいリソースを追加してから、そのNodeでリソースのスケジューリングを要求する最初のPodが現れるまでには、短い遅延が生じる可能性があることに注意してください。
例:
以下は、curl
を使用して、Masterがk8s-master
であるNodek8s-node-1
で5つのexample.com/foo
リソースを示すHTTPリクエストを作成する方法を示す例です。
curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
備考: 上記のリクエストでは、~1
はパッチパス内の文字/
のエンコーディングです。 JSON-Patchの操作パス値は、JSON-Pointerとして解釈されます。 詳細については、IETF RFC 6901, section 3を参照してください。
クラスターレベルの拡張リソース
クラスターレベルの拡張リソースはノードに関連付けられていません。 これらは通常、リソース消費とリソースクォータを処理するスケジューラー拡張機能によって管理されます。
スケジューラーポリシー構成では。スケジューラー拡張機能によって扱われる拡張リソースを指定できます。
例:
次のスケジューラーポリシーの構成は、クラスターレベルの拡張リソース"example.com/foo"がスケジューラー拡張機能によって処理されることを示しています。
- スケジューラーは、Podが"example.com/foo"を要求した場合にのみ、Podをスケジューラー拡張機能に送信します。
ignoredByScheduler
フィールドは、スケジューラがそのPodFitsResources
述語で"example.com/foo"リソースをチェックしないことを指定します。
{
"kind": "Policy",
"apiVersion": "v1",
"extenders": [
{
"urlPrefix":"<extender-endpoint>",
"bindVerb": "bind",
"managedResources": [
{
"name": "example.com/foo",
"ignoredByScheduler": true
}
]
}
]
}
拡張リソースの消費
ユーザーは、CPUやメモリのようにPodのスペックで拡張されたリソースを消費できます。 利用可能な量以上のリソースが同時にPodに割り当てられないように、スケジューラーがリソースアカウンティングを行います。
APIサーバーは、拡張リソースの量を整数の値で制限します。
有効な数量の例は、3
、3000m
、3Ki
です。
無効な数量の例は、0.5
、1500m
です。
備考: 拡張リソースは不透明な整数リソースを置き換えます。 ユーザーは、予約済みのkubernetes.io
以外のドメイン名プレフィックスを使用できます。
Podで拡張リソースを消費するには、コンテナ名のspec.containers[].resources.limits
マップにキーとしてリソース名を含めます。
備考: 拡張リソースはオーバーコミットできないので、コンテナスペックに要求と制限の両方が存在する場合は等しくなければなりません。
Podは、CPU、メモリ、拡張リソースを含むすべてのリソース要求が満たされた場合にのみスケジュールされます。
リソース要求が満たされない限り、PodはPENDING
状態のままです。
例:
下のPodはCPUを2つ、"example.com/foo"(拡張リソース)を1つ要求しています。
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: myimage
resources:
requests:
cpu: 2
example.com/foo: 1
limits:
example.com/foo: 1
トラブルシューティング
failedSchedulingイベントメッセージが表示され、Podが保留中になる
スケジューラーがPodが収容されるNodeを見つけられない場合、場所が見つかるまでPodはスケジュールされないままになります。 スケジューラーがPodの場所を見つけられないたびに、次のようなイベントが生成されます。
kubectl describe pod frontend | grep -A 3 Events
Events:
FirstSeen LastSeen Count From Subobject PathReason Message
36s 5s 6 {scheduler } FailedScheduling Failed for reason PodExceedsFreeCPU and possibly others
前述の例では、"frontend"という名前のPodは、Node上のCPUリソースが不足しているためにスケジューリングに失敗しています。 同様のエラーメッセージは、メモリー不足による失敗を示唆することもあります(PodExceedsFreeMemory)。 一般的に、このタイプのメッセージでPodが保留されている場合は、いくつか試すべきことがあります。
- クラスタにNodeを追加します。
- 不要なポッドを終了して、保留中のPodのためのスペースを空けます。
- PodがすべてのNodeよりも大きくないことを確認してください。
例えば、すべてのNodeが
cpu: 1
の容量を持っている場合、cpu: 1.1
を要求するPodは決してスケジューリングされません。
Nodeの容量や割り当て量はkubectl describe nodes
コマンドで調べることができる。
例えば、以下のようになる。
kubectl describe nodes e2e-test-node-pool-4lw4
Name: e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
cpu: 2
memory: 7679792Ki
pods: 110
Allocatable:
cpu: 1800m
memory: 7474992Ki
pods: 110
[ ... lines removed for clarity ...]
Non-terminated Pods: (5 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
kube-system fluentd-gcp-v1.38-28bv1 100m (5%) 0 (0%) 200Mi (2%) 200Mi (2%)
kube-system kube-dns-3297075139-61lj3 260m (13%) 0 (0%) 100Mi (1%) 170Mi (2%)
kube-system kube-proxy-e2e-test-... 100m (5%) 0 (0%) 0 (0%) 0 (0%)
kube-system monitoring-influxdb-grafana-v4-z1m12 200m (10%) 200m (10%) 600Mi (8%) 600Mi (8%)
kube-system node-problem-detector-v0.1-fj7m3 20m (1%) 200m (10%) 20Mi (0%) 100Mi (1%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
680m (34%) 400m (20%) 920Mi (12%) 1070Mi (14%)
前述の出力では、Podが1120m以上のCPUや6.23Gi以上のメモリーを要求した場合、そのPodはNodeに収まらないことがわかります。
Pods
セクションを見れば、どのPodがNode上でスペースを占有しているかがわかります。
システムデーモンが利用可能なリソースの一部を使用しているため、Podに利用可能なリソースの量はNodeの容量よりも少なくなっています。
allocatable
フィールドNodeStatusは、Podに利用可能なリソースの量を与えます。
詳細については、ノード割り当て可能なリソースを参照してください。
リソースクォータ機能は、消費できるリソースの総量を制限するように設定することができます。 名前空間と組み合わせて使用すると、1つのチームがすべてのリソースを占有するのを防ぐことができます。
コンテナが終了した
コンテナはリソース不足のため、終了する可能性があります。
コンテナがリソース制限に達したために強制終了されているかどうかを確認するには、対象のPodでkubectl describe pod
を呼び出します。
kubectl describe pod simmemleak-hra99
Name: simmemleak-hra99
Namespace: default
Image(s): saadali/simmemleak
Node: kubernetes-node-tf0f/10.240.216.66
Labels: name=simmemleak
Status: Running
Reason:
Message:
IP: 10.244.2.75
Replication Controllers: simmemleak (1/1 replicas created)
Containers:
simmemleak:
Image: saadali/simmemleak
Limits:
cpu: 100m
memory: 50Mi
State: Running
Started: Tue, 07 Jul 2015 12:54:41 -0700
Last Termination State: Terminated
Exit Code: 1
Started: Fri, 07 Jul 2015 12:54:30 -0700
Finished: Fri, 07 Jul 2015 12:54:33 -0700
Ready: False
Restart Count: 5
Conditions:
Type Status
Ready False
Events:
FirstSeen LastSeen Count From SubobjectPath Reason Message
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {scheduler } scheduled Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD pulled Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD created Created with docker id 6a41280f516d
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} implicitly required container POD started Started with docker id 6a41280f516d
Tue, 07 Jul 2015 12:53:51 -0700 Tue, 07 Jul 2015 12:53:51 -0700 1 {kubelet kubernetes-node-tf0f} spec.containers{simmemleak} created Created with docker id 87348f12526a
上記の例では、Restart Count:5
はPodのsimmemleak
コンテナが終了して、5回再起動したことを示しています。
-o go-template=...
オプションを指定して、kubectl get pod
を呼び出し、以前に終了したコンテナのステータスを取得できます。
kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}' simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]
reason:OOM Killed
が原因でコンテナが終了したことがわかります。OOM
はメモリー不足を表します。
次の項目
コンテナとPodへのメモリーリソースの割り当てハンズオンを行う
コンテナとPodへのCPUリソースの割り当てハンズオンを行う
要求と制限の違いの詳細については、リソースQoSを参照する
コンテナAPIリファレンスを読む
リソース要求APIリファレンスを読む
XFSのプロジェクトクォータについて読む