第2章 ノードの管理

2.1. 概要

インスタンスにあるノードは、CLI を使用して管理することができます。

ノードの管理操作を実行すると、CLI は実際のノードホストの表現であるノードオブジェクトと対話します。マスターはノードオブジェクトの情報を使用し、ヘルスチェックでノードの検証を実行します。

2.2. ノードの一覧表示

マスターに認識されるすべてのノードを一覧表示するには、以下を実行します。

$ oc get nodes
NAME                   STATUS    ROLES     AGE       VERSION
master.example.com     Ready     master    7h        v1.9.1+a0ce1bc657
node1.example.com      Ready     compute   7h        v1.9.1+a0ce1bc657
node2.example.com      Ready     compute   7h        v1.9.1+a0ce1bc657

単一ノードについての情報のみを一覧表示するには、<node> を完全なノード名に置き換えます。

$ oc get node <node>

これらのコマンドの出力にある STATUS 列には、ノードの以下の状態が表示されます。

表2.1 ノードの状態

状態説明

Ready

ノードは StatusOK を返し、マスターから実行されるヘルスチェックをパスしています。

NotReady

ノードはマスターから実行されるヘルスチェックをパスしていません。

SchedulingDisabled

ノードへの Pod の配置をスケジュールできません。

注記

STATUS 列には、CLI でノードの状態を検索できない場合にノードについて Unknown が表示されます。

現在の状態の理由を含む特定ノードについての詳細情報を取得するには、以下を実行します。

$ oc describe node <node>

以下に例を示します。

$ oc describe node node1.example.com
Name:			node1.example.com
Labels:			kubernetes.io/hostname=node1.example.com
CreationTimestamp:	Wed, 10 Jun 2015 17:22:34 +0000
Conditions:
  Type		Status	LastHeartbeatTime			LastTransitionTime			Reason					Message
  Ready 	True 	Wed, 10 Jun 2015 19:56:16 +0000 	Wed, 10 Jun 2015 17:22:34 +0000 	kubelet is posting ready status
Addresses:	127.0.0.1
Capacity:
 memory:	1017552Ki
 pods:		100
 cpu:		2
Version:
 Kernel Version:		3.17.4-301.fc21.x86_64
 OS Image:			Fedora 21 (Twenty One)
 Container Runtime Version:	docker://1.6.0
 Kubelet Version:		v0.17.1-804-g496be63
 Kube-Proxy Version:		v0.17.1-804-g496be63
ExternalID:			node1.example.com
Pods:				(2 in total)
  docker-registry-1-9yyw5
  router-1-maytv
No events.

2.3. ノードの追加

ノードを既存の OpenShift Container Platform クラスターに追加するには、ノードコンポーネントのインストール、必要な証明書の生成およびその他の重要な手順を処理する Ansible Playbook を実行できます。Playbook を直接実行する方法については、通常インストール (Advanced installation)方式を参照してください。

またはクイックインストール方式を使用する場合は、インストーラーを再実行してノードを追加すると、同じ手順を実行できます。

2.4. ノードの削除

CLI を使用してノードを削除する場合、ノードオブジェクトは Kubernetes で削除されますが、ノード自体にある Pod は削除されません。レプリケーションコントローラーでサポートされないベア Pod は OpenShift Container Platform からアクセスできなくなり、レプリケーションコントローラーでサポートされる Pod は他の利用可能なノードにスケジュール変更されます。ローカルのマニフェスト Pod は手動で削除する必要があります。

OpenShift Container Platform クラスターからノードを削除するには、以下を実行します。

  1. 削除しようとしているノードからPod を退避します。
  2. ノードオブジェクトを削除します。

    $ oc delete node <node>
  3. ノードがノード一覧から削除されていることを確認します。

    $ oc get nodes

    Pod は、Ready 状態にある残りのノードに対してのみスケジュールされます。

  4. すべての Pod およびコンテナーを含む、OpenShift Container Platform のすべてのコンテンツをノードホストからアンインストールする必要がある場合は、ノードのアンインストールを継続し、uninstall.yml Playbook を使用する手順に従います。この手順では、Ansible を使用した通常インストール (Advanced installation) 方式についての全般的な理解があることが前提となります。

2.5. ノードのラベルの更新

ノードでラベルを追加し、更新するには、以下を実行します。

$ oc label node <node> <key_1>=<value_1> ... <key_n>=<value_n>

詳細な使用法を表示するには、以下を実行します。

$ oc label -h

2.6. ノード上の Pod の一覧表示

1 つ以上のノードにすべてまたは選択した Pod を一覧表示するには、以下を実行します。

$ oc adm manage-node <node1> <node2> \
    --list-pods [--pod-selector=<pod_selector>] [-o json|yaml]

選択したノードのすべてまたは選択した Pod を一覧表示するには、以下を実行します。

$ oc adm manage-node --selector=<node_selector> \
    --list-pods [--pod-selector=<pod_selector>] [-o json|yaml]

2.7. ノードをスケジュール対象外 (Unschedulable) またはスケジュール対象 (Schedulable) としてマークする

デフォルトで、Ready ステータスの正常なノードはスケジュール対象としてマークされます。つまり、新規 Pod をこのノードに配置することができます。手動でノードをスケジュール対象外としてマークすると、新規 Pod のノードでのスケジュールがブロックされます。ノード上の既存 Pod には影響がありません。

1 つまたは複数のノードをスケジュール対象外としてマークするには、以下を実行します。

$ oc adm manage-node <node1> <node2> --schedulable=false

以下に例を示します。

$ oc adm manage-node node1.example.com --schedulable=false
NAME                 LABELS                                        STATUS
node1.example.com    kubernetes.io/hostname=node1.example.com      Ready,SchedulingDisabled

現時点でスケジュール対象外のノードをスケジュール対象としてマークするには、以下を実行します。

$ oc adm manage-node <node1> <node2> --schedulable

または、特定のノード名 (例: <node1> <node2>) を指定する代わりに、--selector=<node_selector> オプションを使用して選択したノードをスケジュール対象またはスケジュール対象外としてマークすることができます。

2.8. Pod のノードからの退避

Pod を退避することで、すべての Pod または選択した Pod を 1 つまたは複数の指定ノードから移行できます。Pod の退避を実行するには、まずノードをスケジュール対象外としてマークする必要があります。

レプリケーションコントローラーでサポートされる Pod のみを退避できます。レプリケーションコントローラーは他のノードで新規 Pod を作成し、既存の Pod を指定したノードから削除します。デフォルトで、ベア Pod (レプリケーションコントローラーでサポートされていない Pod) はこの影響を受けません。

1 つ以上のノードですべてまたは選択した Pod を退避するには、以下を実行します。

$ oc adm drain <node1> <node2> [--pod-selector=<pod_selector>]

--force オプションを使用すると、ベア Pod の削除を強制的に実行できます。true に設定されると、Pod がレプリケーションコントローラー、ReplicaSet、ジョブ、daemonset、または StatefulSet で管理されていない場合でも削除が続行されます。

$ oc adm drain <node1> <node2> --force=true

--grace-period を使用して、各 Pod を正常に終了するための期間 (秒単位) を設定できます。負の値の場合には、Pod に指定されるデフォルト値が使用されます。

$ oc adm drain <node1> <node2> --grace-period=-1

--ignore-daemonsets を使用し、これを true に設定すると、Deamonset で管理された Pod を無視できます。

$ oc adm drain <node1> <node2> --ignore-daemonset=true

--timeout を使用すると、中止する前の待機期間を設定できます。値 0 は無限の時間を設定します。

$ oc adm drain <node1> <node2> --timeout=5s

--delete-local-data を使用し、これを true に設定すると、Pod が emptyDir (ノードがドレイン (解放)) される場合に削除されるローカルデータ) を使用する場合でも削除が続行されます。

$ oc adm drain <node1> <node2> --delete-local-data=true

退避を実行せずに移行するオブジェクトを一覧表示するには、--dry-run オプションを使用し、これを true に設定します。

$ oc adm drain <node1> <node2>  --dry-run=true

特定のノード名 (例: <node1> <node2>) を指定する代わりに、--selector=<node_selector> オプションを使用し、選択したノードで Pod を退避することができます。

2.9. ノードの再起動

プラットフォームで実行されるアプリケーションを停止せずにノードを再起動するには、まず Pod の退避を実行する必要があります。ルーティング階層によって可用性が高くされている Pod については、何も実行する必要はありません。ストレージ (通常はデータベース) を必要とするその他の Pod については、それらが 1 つの Pod が一時的にオフラインになっても作動したままになることを確認する必要があります。ステートフルな Pod の回復性はアプリケーションごとに異なりますが、いずれの場合でも、ノードの非アフィニティー (node anti-affinity) を使用して Pod が使用可能なノード間に適切に分散するようにスケジューラーを設定することが重要になります。

別の課題として、ルーターやレジストリーのような重要なインフラストラクチャーを実行しているノードを処理する方法を検討する必要があります。同じノードの退避プロセスが適用されますが、一部のエッジケースについて理解しておくことが重要です。

2.9.1. インフラストラクチャーノード

インフラストラクチャーノードは、OpenShift Container Platform 環境の一部を実行するためにラベルが付けられたノードです。現在、ノードの再起動を管理する最も簡単な方法として、インフラストラクチャーを実行するために利用できる 3 つ以上のノードを確保することができます。以下のシナリオでは、2 つのノードのみが利用可能な場合に OpenShift Container Platform で実行されるアプリケーションのサービスを中断しかねないよくある問題を示しています。

  • ノード A がスケジュール対象外としてマークされており、すべての Pod の退避が行われている。
  • このノードで実行されているレジストリー Pod がノード B に再デプロイされる。これは、ノード B が両方のレジストリー Pod を実行していることを意味します。
  • ノード B はスケジュール対象外としてマークされ、退避が行われる。
  • ノード B の 2 つの Pod エンドポイントを公開するサービスは、それらがノード A に再デプロイされるまでの短い期間すべてのエンドポイントを失う。

3 つのインフラストラクチャーノードを使用する同じプロセスではサービスの中断が生じません。しかし、Pod のスケジューリングにより、退避してローテーションに戻された最後のノードはゼロ (0) レジストリーを実行していることになり、他の 2 つのノードは 2 つのレジストリーと 1 つのレジストリーをそれぞれ実行します。最善の解決法として、Pod の非アフィニティーを使用できます。これは現在テスト目的で利用できる Kubernetes のアルファ機能ですが、実稼働ワークロードに対する使用はサポートされていません。

2.9.2. Pod の非アフィニティーの使用

Pod の非アフィニティーは、ノードの非アフィニティーとは若干異なります。ノードの非アフィニティーの場合、Pod のデプロイ先となる適切な場所がほかにない場合には違反が生じます。Pod の非アフィニティーの場合は required (必須) または preferred (優先) のいずれかに設定できます。

docker-registry Pod をサンプルとして使用すると、この機能を有効にするための最初の手順として、Pod に scheduler.alpha.kubernetes.io/affinity を設定します。この Pod はデプロイメント設定を使用するため、アノテーションを追加するのに適している場所は Pod テンプレートのメタデータになります。

$ oc edit dc/docker-registry -o yaml

...
  template:
    metadata:
      annotations:
        scheduler.alpha.kubernetes.io/affinity: |
          {
            "podAntiAffinity": {
              "requiredDuringSchedulingIgnoredDuringExecution": [{
                "labelSelector": {
                  "matchExpressions": [{
                    "key": "docker-registry",
                    "operator": "In",
                    "values":["default"]
                  }]
                },
                "topologyKey": "kubernetes.io/hostname"
              }]
            }
          }
重要

scheduler.alpha.kubernetes.io/affinity は、コンテンツが JSON の場合でも文字列として内部に保存されます。上記の例では、この文字列を YAML デプロイメント設定にアノテーションとして追加する方法を示しています。

この例では、Docker レジストリー Pod に docker-registry=default のラベルがあることを想定しています。Pod の非アフィニティーでは任意の Kubernetes の一致式を使用できます。

最後に必要となる手順として、/etc/origin/master/scheduler.jsonMatchInterPodAffinity スケジューラーの述語を有効にします。これが有効にされると、2 つのインフラストラクチャーノードのみが利用可能で、1 つのノードが再起動される場合に、Docker レジストリー Pod が他のノードで実行されることが禁じられます。oc get pods は、適切なノードが利用可能になるまでこの Pod を unready (準備ができていない) として報告します。ノードが利用可能になると、すべての Pod は Ready (準備ができている) 状態に戻り、次のノードを再起動することができます。

2.9.3. ルーターを実行するノードの処理

ほとんどの場合、OpenShift Container Platform ルーターを実行する Pod はホストのポートを公開します。PodFitsPorts スケジューラーの述語により、同じポートを使用するルーター Pod が同じノードで実行されないようにし、Pod の非アフィニティーが適用されます。ルーターの高可用性を維持するために IP フェイルオーバー を利用している場合には、他に実行することはありません。高可用性を確保するために AWS Elastic Load Balancing などの外部サービスを使用するルーター Pod の場合は、そのような外部サービスがルーター Pod の再起動に対して対応します。

ルーター Pod でホストのポートが設定されていないということも稀にあります。この場合は、インフラストラクチャーノードについての推奨される再起動プロセスに従う必要があります。

2.10. ノードリソースの設定

ノードのリソースは、kubelet 引数をノード設定ファイル (/etc/origin/node/node-config.yaml) に追加して設定することができます。kubeletArguments セクションを追加し、必要なオプションを組み込みます。

kubeletArguments:
  max-pods: 1
    - "40"
  resolv-conf: 2
    - "/etc/resolv.conf"
  image-gc-high-threshold: 3
    - "90"
  image-gc-low-threshold: 4
    - "80"
1
2
コンテナー DNS 解決設定のベースとして使用されるリゾルバーの設定ファイル。
3
イメージのガべージコレクションが常に実行される場合のディスク使用量のパーセント。デフォルト: 90%
4
イメージのガべージコレクションが一度も実行されない場合のディスク使用量のパーセント。デフォルト: 80%

利用可能なすべての kubelet オプションを表示するには、以下を実行します。

$ kubelet -h

この設定は、通常インストール (Advanced installation)の実行時に openshift_node_kubelet_args 変数を使用して実行できます。以下は例になります。

openshift_node_kubelet_args={'max-pods': ['40'], 'resolv-conf': ['/etc/resolv.conf'],  'image-gc-high-threshold': ['90'], 'image-gc-low-threshold': ['80']}

2.10.1. ノードあたりの最大 Pod 数の設定

注記

OpenShift Container Platform の各バージョンでサポートされている最大の制限については、「Cluster Limits」のページを参照してください。

/etc/origin/node/node-config.yaml ファイルでは、 pods-per-core および max-pods の 2 つのパラメーターがノードにスケジュールできる Pod の最大数を制御します。いずれのオプションも使用されている場合、2 つの内の小さい方の値でノードの Pod 数が制限されます。これらの値を超えると、以下の状況が発生します。

  • OpenShift Container Platform と Docker の両方で CPU 使用率が増加する。
  • Pod のスケジューリングの速度が遅くなる。
  • メモリー不足のシナリオが生じる可能性がある (ノードのメモリー量によって異なる)。
  • IP アドレスのプールを消費する。
  • リソースのオーバーコミットが起こり、アプリケーションのパフォーマンスが低下する。
注記

Kubernetes では、単一コンテナーを保持する Pod は実際には 2 つのコンテナーを使用します。2 つ目のコンテナーは実際のコンテナーの起動前にネットワークを設定するために使用されます。そのため、10 の Pod を使用するシステムでは、実際には 20 のコンテナーが実行されていることになります。

pods-per-core は、ノードのプロセッサーコア数に基づいてノードが実行できる Pod 数を設定します。たとえば、4 プロセッサーコアを搭載したノードで pods-per-core10 に設定される場合、このノードで許可される Pod の最大数は 40 になります。

kubeletArguments:
  pods-per-core:
    - "10"
注記

pods-per-core を 0 に設定すると、この制限が無効になります。

max-pods は、ノードのプロパティーにかかわらず、ノードが実行できる Pod 数を固定値に設定します。「Cluster Limits」では max-pods のサポートされる最大の値について説明しています。

kubeletArguments:
  max-pods:
    - "250"

上記の例では、pods-per-core のデフォルト値は 10 であり、max-pods のデフォルト値は 250 です。これは、ノードにあるコア数が 25 以上でない限り、デフォルトでは pods-per-core が制限を設定することになります。

2.11. Docker ストレージの再設定

Docker イメージをダウンロードし、コンテナーを実行して削除する際、Docker は常にマップされたディスク領域を解放する訳ではありません。結果として、一定の時間が経過するとノード上で領域不足が生じる可能性があり、これにより OpenShift Container Platform で新規 Pod を作成できなくなるか、または Pod の作成に時間がかかる可能性があります。

たとえば、以下は 6 分が経過しても ContainerCreating 状態にある Pod を示しており、イベントログは FailedSync イベントについて示しています。

$ oc get pod
NAME                               READY     STATUS              RESTARTS   AGE
cakephp-mysql-persistent-1-build   0/1       ContainerCreating   0          6m
mysql-1-9767d                      0/1       ContainerCreating   0          2m
mysql-1-deploy                     0/1       ContainerCreating   0          6m

$ oc get events
LASTSEEN   FIRSTSEEN   COUNT     NAME                               KIND                    SUBOBJECT                     TYPE      REASON                         SOURCE                                                 MESSAGE
6m         6m          1         cakephp-mysql-persistent-1-build   Pod                                                   Normal    Scheduled                      default-scheduler                                      Successfully assigned cakephp-mysql-persistent-1-build to ip-172-31-71-195.us-east-2.compute.internal
2m         5m          4         cakephp-mysql-persistent-1-build   Pod                                                   Warning   FailedSync                     kubelet, ip-172-31-71-195.us-east-2.compute.internal   Error syncing pod
2m         4m          4         cakephp-mysql-persistent-1-build   Pod                                                   Normal    SandboxChanged                 kubelet, ip-172-31-71-195.us-east-2.compute.internal   Pod sandbox changed, it will be killed and re-created.

この問題に対する 1 つの解決法として、Docker ストレージを再設定し、Docker で不要なアーティファクトを削除することができます。

Docker ストレージを再起動するノードで、以下を実行します。

  1. 以下のコマンドを実行して、ノードをスケジュール対象外としてマークします。

    $ oc adm manage-node <node> --schedulable=false
  2. 以下のコマンドを実行して Docker および atomic-openshift-node サービスをシャットダウンします。

    $ systemctl stop docker atomic-openshift-node
  3. 以下のコマンドを実行してローカルのボリュームディレクトリーを削除します。

    $ rm -rf /var/lib/origin/openshift.local.volumes

    このコマンドは、ローカルイメージのキャッシュをクリアします。その結果、ose-* イメージを含むイメージが再度プルする必要があります。これにより、イメージストアは回復しますが、Pod の起動時間が遅くなる可能性があります。

  4. /var/lib/docker ディレクトリーを削除します。

    $ rm -rf /var/lib/docker
  5. 以下のコマンドを実行して Docker ストレージを再設定します。

    $ docker-storage-setup --reset
  6. 以下のコマンドを実行して Docker ストレージを再作成します。

    $ docker-storage-setup
  7. /var/lib/docker ディレクトリーを再作成します。

    $ mkdir /var/lib/docker
  8. 以下のコマンドを実行して Docker および atomic-openshift-node サービスを再起動します。

    $ systemctl start docker atomic-openshift-node
  9. 以下のコマンドを実行してノードをスケジュール対象としてマークします。

    $ oc adm manage-node <node> --schedulable=true

2.12. ノードトラフィックインターフェースの変更

デフォルトで DNS はすべてのノードトラフィックをルーティングします。ノードの登録時に、マスターはノード IP アドレスを DNS 設定から受信するため、DNS 経由でノードにアクセスすることが、ほとんどのデプロイメントにおいて最も柔軟な解決策となります。

お使いのデプロイメントでクラウドプロバイダーを使用している場合、ノードはクラウドプロバイダーから IP 情報を取得します。ただし openshift-sdn は、 (設定されている場合は) nodeName や (nodeName が設定されていない場合は) システムホスト名の DNS ルックアップを含む、各種の方式を使用して IP の判別を試行します。

ノードトラフィックインターフェースには変更が必要になる場合があります。たとえば、以下の場合がこれに該当します。

  • OpenShift Container Platform が、内部ホスト名がすべてのホストで設定されていない/解決可能でないクラウドプロバイダーにインストールされている。
  • マスターから見たノードの IP とノード自体から見たノードの IP が同じではない。

openshift_set_node_ip Ansible 変数を設定すると、デフォルトのネットワークインターフェース以外のインターフェースでのノードトラフィックが強制的に実行されます。

ノードトラフィックインターフェースを変更するには、以下を実行します。

  1. openshift_set_node_ip Ansible 変数を true に設定します。
  2. openshift_ip を、設定するノードの IP アドレスに設定します。
注記

openshift_set_node_ip は本セクションで記載したケースの回避策として有効ですが、通常は実稼働環境には適していません。ノードが新規 IP アドレスを受信すると適切に機能しなくなるためです。