レッドハットの林です。
本記事は赤帽エンジニアAdvent Calendar 2018の11日目です。(時間オーバー)
OpenShiftはv3.11からAWSのAuto Scaling Groupに対応しています。つまり、下記のようなことが実現できます。
- Auto Scaling Groupのインスタンス数を増減させるだけでワーカーノードを追加・削除できる
- Pod数が増え、リソースが逼迫すると自動的にワーカーノードが追加される
商用版のOpenShiftはCore数に応じた年単位の料金モデルなのでメリットを完全には活かしきれないですが、それでも簡単にワーカーノードを増減できるというのは機能として嬉しいポイントでしょう。
利用方法は以下にドキュメント化されていますが、この手順に従っても成功しない(何でや)ので、解説記事を書いておきます。
https://docs.openshift.com/container-platform/3.11/admin_guide/cluster-autoscaler.html
OpenShiftにおける新規ノード追加
OpenShiftでは、v3.9まではノード追加のためにansible
コマンドを実行する必要がありました。
v3.10からは、前準備さえできていればワーカーノードを起動するだけでクラスタにノードを追加できます。
このあたりを深掘りするため、OpenShiftがワーカーノードをクラスタに追加するためのクライアント認証について振り返ってみます。
OpenShiftで新規ノードを認証する流れ
Kubernetesでは、ワーカーノードがKubernetesのAPIと通信する際、クライアント証明書で認証することを推奨しています。
OpenShiftでももちろん、クライアント証明書を使ってワーカーノードの通信に認証をかけていますが、OpenShift v3.9まではこの証明書をansibleで生成してノードに配布していました。
- 新規ノードの追加
- クラスタ内部の証明書更新
の際、ansibleのplaybookを実行する必要があったわけですね。
しかしv3.10からは、ワーカーノードの証明書作成、更新はKubernetesのTLS bootstrappingという方法で自動化されています。
TLS Bootstrappingの簡単な流れは以下です。
- ワーカーノードでkubeletが起動する
- ワーカーノードは、bootstrap用のkubeconfigファイルを探す
- bootstrap用のkubeconfigを読み込み、KubernetesのAPIに制限された権限で接続する
- kubeletは証明書用のCSRを生成し、APIに送る
- APIサーバーにあるcontroller managerによりCSRが承認されれば、証明書がワーカーノードに発行される
最後のCSRの承認には二通りの方法があり、
- CSRを自動的に承認するカスタムコントローラー(Pod)を導入する
- 手動(kubectlコマンド)でクラスタ管理者が承認する
OpenShiftでは、上記の方法をそれぞれ以下のように対応しています。
CSRを自動的に承認するカスタムコントローラー(Pod)を導入する
openshift-ansible
実行時に、openshift_master_bootstrap_auto_approve
変数をtrue
にする(デフォルトfalse
)
https://docs.openshift.com/container-platform/3.11/install/configuring_inventory_file.html
これにより、CSRを承認してくれるPod、bootstrap-autoapprover
がクラスタにデプロイされます。
手動で承認する
oc get csr
コマンドで未承認のCSRを確認して、oc adm certificate approve csr-xxx
で承認する
つまり、OpenShift v3.10からは、ノードを追加するのにansibleコマンドを実行する必要は無く、準備ができたワーカーノードを立ち上げるだけでよい、ということですね。
OpenShiftで、ワーカーノードを簡単に追加できる仕組みを作る
ノードを立ち上げるだけでOpenShiftクラスタに参加させるには、かなりの前準備が必要です。
- OpenShiftがインストールされたAMI(Primed AMIと呼ぶらしい)を用意する
- 上記AMIからVMを起動する際、
cloud-init
でkubeconfigを配置したり適切なサービスをsystemctl
で起動したりして、VM起動時にノードがクラスタに参加できるようにする - OpenShiftクラスタに
bootstrap-autoapprover
をインストールして、起動したワーカーノードを自動的に承認できるようにする
さらに、次回の記事に譲りますが以下も実施することでより弾力性のあるクラスタを実現できます。
cluster-autoscaler
を導入して、リソースが逼迫したときにAuto Scaling Groupから自動的に必要なだけのワーカーノードが起動されるようにする
OpenShiftがあらかじめインストールされたAMI(Primed AMI)を作成する
まず、OpenShiftがインストールされたVMイメージを用意する必要があります。
OpenShiftでは、そのためにPlaybookが用意されているので、それを実行するだけです。
...といいたいところですが、このPlaybookを実行するにはAWSのAPIにアクセスするためのライブラリが必要です。
RHELでは、以下でインストールできます。
- curl -kL https://bootstrap.pypa.io/get-pip.py | python - pip install boto3 --upgrade - pip install boto --upgrade
AMI作成Playbook、build_ami.yml
は以下のように実行します。
export LANG=C ansible-playbook -i </path/to/inventory/file> \ /usr/openshift-ansible/playbooks/aws/openshift-cluster/build_ami.yml \ -e @build-ami-provisioning-vars.yaml
ここで重要なことは、export LANG=C
でシェルの環境をデフォルトにしておくことです。
上記Playbookの中でOpenShiftをインストールするためにsubscription-manager
を使ってサブスクリプションを登録しに行っているのですが、登録済みか否かを判定するために標準出力のメッセージを利用しており、これが日本語メッセージだと上手く判定できないようです。
そのため、OpenShiftをAMIにインストールする際に失敗します。
ここで利用するインベントリはシンプルに以下のようにします。
[OSEv3:children] masters nodes etcd [OSEv3:vars] openshift_deployment_type=openshift-enterprise ansible_ssh_user=ec2-user openshift_clusterid=xxxxx # 参加させたいクラスタのクラスタ名 ansible_become=yes [masters] [etcd] [nodes]
また、変数ファイルは以下のようにします。既存のクラスタが稼働している環境を設定してください。
openshift_deployment_type: openshift-enterprise # 参加させたいクラスタのクラスタ名 openshift_aws_clusterid: xxx # 既存のクラスタを実行中のAWSリージョン名 openshift_aws_region: ap-northeast-1 # VPCを新しく作成するか openshift_aws_create_vpc: false # クラスタ実行中のVPC名 openshift_aws_vpc_name: xx # クラスタ実行中のサブネット(一つで良い) openshift_aws_subnet_az: ap-northeast-1a openshift_aws_create_security_groups: false # ansible-playbook実行中のマシンからsshログインできるkeypair openshift_aws_ssh_key_name: xxx # RHEL Gold Image (RHEL-7.5_HVM_GA-20180322-x86_64-1-Access2-GP2) openshift_aws_base_ami: ami-2a0f5d4c # s3バケットの作成有無 openshift_aws_create_s3: false # AMIビルドに作成するEC2インスタンスに設定するセキュリティグループ # インターネット経由でPublic IPに向けてSSH接続に行くので、アクセスを受け付けられるようなセキュリティグループを設定する openshift_aws_build_ami_group: xxx # 既存のクラスタが実行されているVPCとサブネットの情報 openshift_aws_vpc: name: "{{ openshift_aws_vpc_name }}" cidr: 10.0.0.0/16 subnets: ap-northeast-1: - cidr: 10.0.0.0/19 az: "ap-northeast-1a" - cidr: 10.0.32.0/19 az: "ap-northeast-1c" - cidr: 10.0.64.0/19 az: "ap-northeast-1d" container_runtime_docker_storage_type: overlay2 container_runtime_docker_storage_setup_device: /dev/xvdb openshift_aws_ami_build_set_gquota_on_slashfs: true # Red Hatアカウントの認証情報 rhsub_user: xxxx rhsub_pass: xxxx # OpenShiftのサブスクリプション Pool ID rhsub_pool: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上記のファイルを作成してPlaybookを実行すると、ワーカーノードのAMIが作成されます。
ワーカーノード起動時にクラスタに参加できるようcloud-initを設定する
上記手順で作成したAMIを使うと、OpenShiftがインストールされた状態でVMを起動できます。
しかし、実際に既存のクラスタにそのVMをワーカーノードとして参加させるためには、OpenShift masterへの接続情報を設定し、OpenShiftのワーカーサービスをsystemctl
で起動しなければなりません。
さらに、OpenShift v3.11から、OpenShiftの実行に必要はシステムコンテナは、認証が掛かっている registry.redhat.io
からダウンロードされます。
そのため、その認証情報もVM起動時に設定する必要があります。
以下は、VM起動時に設定するuserdata
を作成する例です。これを設定してVMを起動することで、既存のクラスタにワーカーノードを参加させることができます。
# ワーカーノード起動時にAPI接続するためのkubeconfigのダウンロード ansible -i <OpenShiftインストール時に/使った/inventoryへの/パス> masters -m fetch -a "src=/etc/origin/master/bootstrap.kubeconfig dest=./bootstrap.kubeconfig flat=yes" # ワーカーノードでシステムコンテナをredhat.ioからダウンロードするためのdocker credentialのダウンロード ansible -i <OpenShiftインストール時に/使った/inventoryへの/パス> masters -m fetch -a "src=/var/lib/origin/.docker/config.json dest=./dockerconfig.json flat=yes" cat <<EOF > user-data.txt #cloud-config write_files: - path: /root/openshift_bootstrap/openshift_settings.yaml owner: 'root:root' permissions: '0640' content: | openshift_node_config_name: node-config-compute - path: /etc/origin/node/bootstrap.kubeconfig owner: 'root:root' permissions: '0640' encoding: b64 content: | `base64 ./bootstrap.kubeconfig | sed '2,$s/^/ /'` - path: /var/lib/origin/.docker/config.json owner: 'root:root' permissions: '0644' encoding: b64 content: | `base64 ./dockerconfig.json | sed '2,$s/^/ /'` runcmd: - [ mkdir, -p, /etc/origin/cloudprovider ] - [ touch, /etc/origin/cloudprovider/aws.conf ] - [ ansible-playbook, /root/openshift_bootstrap/bootstrap.yml] - [ systemctl, restart, systemd-hostnamed] - [ systemctl, restart, NetworkManager] - [ systemctl, enable, atomic-openshift-node] - [ systemctl, start, atomic-openshift-node] EOF
VMを起動して、OpenShiftにワーカーノードとして登録されることを確認する
上記で作成したuser-data.txt
の内容を指定して、EC2インスタンスを起動してみましょう。
このとき、既存のワーカーノードと同じサブネット、セキュリティグループを選択して、master
とネットワークが繋がるように気をつけます。
これで、待っていればクラスタにノードが追加されるはずです。
watch oc get nodes
されませんね。。。
ワーカーノードの承認
ワーカーノードは、前述の通り、クラスタに追加するためにCSRを承認する必要があります。
以下コマンドで、未承認のCSRの一覧を見ることができます。
$ oc get csr NAME AGE REQUESTOR CONDITION node-csr-xxx-xxxxx 2m system:serviceaccount:openshift-infra:node-bootstrapper Pending
ここでは、とりあえず手動でこのCSRを承認します。
$ oc adm certificate approve node-csr-xxx-xxxxx certificatesigningrequest.certificates.k8s.io/node-csr-xxx-xxxxx approved
あらためて、ノードを確認すると。。
$ oc get nodes NAME STATUS ROLES AGE VERSION ip-x-x-xx-xx.ap-northeast-1.compute.internal Ready compute 1m v1.11.0+d4cacc0
ノードが追加されました!
ワーカーノード承認の自動化
最終的には、クラスタの利用状況に応じて動的にノードを追加したり削除したりしたいわけです。
なので、手動でいちいちノードを承認するわけにはいきません。
そこで、OpenShiftではこのノードのCSRを自動的に承認してくれるカスタムコントローラー(Pod)を導入できます。
OpenShiftインストール時に使用したinventoryに以下変数を追加してします。
[OSEv3:vars] # ... openshift_master_bootstrap_auto_approve=true
このinventoryを使ってOpenShiftのインストール用Playbookを再実行すれば、自動承認用のPodがクラスタにインストールされます。
$ cd /usr/share/ansible/openshift-ansible $ ansible-playbook -i <inventoryファイルへの/パス> playbooks/deploy_cluster.yml
実行が終わったら、Podがインストールされているか確認します。
$ oc get pods -n openshift-infra NAME READY STATUS RESTARTS AGE bootstrap-autoapprover-0 1/1 Running 0 10s
これにより、oc adm certificate approve
コマンドをいちいち実行しなくても自動的にノードのCSRが承認される状態となりました。
Auto Scaling Groupでワーカーノードを追加する
さて、先ほどと同様に、EC2インスタンスをダイレクトに使ってもワーカーノードを追加できるのですが、せっかくなのでAuto Scaling Groupを使ってみます。
さらに、spotインスタンスを使ってコストを抑えてみましょう。
起動テンプレートの作成
Auto Scaling Groupを起動するためのテンプレートを以下より作成します。
<Launch Templates> => <起動テンプレートの作成>を選択し、必要事項を埋めていきます。
以下を参考にしてください。
- AMI IDをPrimed AMIのIDにする
- セキュリティグループは既存のワーカーノードと同じものを設定する
- ネットワークインターフェース、ストレージは指定不要
- Purchasing optionでチェックを入れる(Spotインスタンス利用)
- 「ユーザーデータ」に上記で生成したものを入れる
起動テンプレートからAuto Scaling Groupを作成する
起動テンプレートを作成したら、そのテンプレートからAuto Scaling Groupを作成しましょう。
- Auto Scaling GroupのタグでKubernetesのクラスタタグ
kubernetes.io/cluster/<クラスタ名>
をowned
にする - スケーリングポリシーを「このグループを初期のサイズに維持する」に設定する
インスタンス数を増減させ、OpenShiftのノードに反映されることを確認する
では、Auto Scaling Groupのインスタンスを増減させて、ノードが増えるか確認してみます。
watch oc get nodes Every 2.0s: oc get nodes NAME STATUS ROLES AGE VERSION ip-xx-xx-xx-xx.ap-northeast-1.compute.internal Ready compute 11d v1.11.0+d4cacc0 ip-xx-xx-xx-xx.ap-northeast-1.compute.internal Ready infra,master 11d v1.11.0+d4cacc0 ip-xx-xx-xx-xx.ap-northeast-1.compute.internal Ready compute 11d v1.11.0+d4cacc0 ip-xx-xx-xx-xx.ap-northeast-1.compute.internal Ready compute 1m v1.11.0+d4cacc0
増えましたね!
これで、Auto Scaling Groupのインスタンス数を増減させるだけでクラスタのサイズを変更できるようになりました。
まとめ
次回は、cluster-autoscalerを導入し、クラスタのリソースが逼迫したときに自動的にクラスタのサイズを増やす、というのをやってみます。
cluster-autoscalerは、Podのデプロイがリソース不足によりPendingになっているときに、指定のAuto Scaling Groupのインスタンス数を適切な数まで増やしてくれるものです。
このような機構を使いこなせば、クラスタの管理もどんどん楽になっていきそうですね。