0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TKG2.1での証明書の埋め込み方&アップグレード後の挙動を検証する

Last updated at Posted at 2023-04-06

Tanzu Kubernetes GridでHarborをオレオレ証明書で構築後、このHarborの証明書をノードに入れないとイメージのpullが出来ない。
例えばnginxを使って起動しようとすると、以下のようなエラーになる。

  Normal   Pulling    7s (x2 over 19s)  kubelet            Pulling image "harbor.10-220-14-115.sslip.io/library/nginx"
  Warning  Failed     7s (x2 over 19s)  kubelet            Failed to pull image "harbor.10-220-14-115.sslip.io/library/nginx": rpc error: code = Unknown desc = failed to pull and unpack image "harbor.10-220-14-115.sslip.io/library/nginx:latest": failed to resolve reference "harbor.10-220-14-115.sslip.io/library/nginx:latest": failed to do request: Head "https://harbor.10-220-14-115.sslip.io/v2/library/nginx/manifests/latest": x509: certificate signed by unknown authority

これを回避するために、ノードにHarborの証明書を埋め込むのだが、TKG2.1ではplan-basedとclass-basedという2パターンのクラスタタイプが用意されており、それぞれで証明書のインストール方法が異なる。またアップデート後の挙動も異なるため、それを検証した時のメモとなる。

前準備

テスト用のイメージをPrivate Harborに入れておく。docker loginするためにPrivate HarborのSecretのharbor-tlsをdocker daemonに読み込ませる。

export HARBOR_DOMAIN="harbor.10-151-200-240.sslip.io"
sudo mkdir -p /etc/docker/certs.d/$HARBOR_DOMAIN
kubectl get secret -n tanzu-system-registry -o yaml harbor-tls -o jsonpath="{.data.ca\.crt}" | base64 -d | sudo tee /etc/docker/certs.d/$HARBOR_DOMAIN/ca.crt
sudo systemctl restart docker

Private Harborにdocker loginし、テストイメージとしてnginxをpushする。

export HARBOR_ID=admin
export HARBOR_PW=xxxx
echo $HARBOR_PW | docker login $HARBOR_DOMAIN -u $HARBOR_ID --password-stdin
docker pull nginx
docker tag nginx ${HARBOR_DOMAIN}/library/nginx
docker push ${HARBOR_DOMAIN}/library/nginx

新規・既存クラスタへの証明書の埋め込み

既存クラスタへの埋め込み

Private Harborをデプロイしたクラスタ自身からHarborを参照したい場合など、既存のクラスタでPrivate Harborを利用したい場合などはこちらの手順を利用する。
オフィシャルドキュメントの手順はこちらになる。
なお、ドキュメントだと何故かKubeadmControlPlaneリソースへの埋め込みがスキップされている。KubeadmControlPlaneに入れないとControlPlaneに反映されない。
ただ、ControlPlaneでPrivate Harborからpullするケースは今回想定していないため、ドキュメントに合わせてWorkerノードにのみ証明書を導入する。

最初にHarborの証明書を取得しデコードしたものを控えておく。

kubectl get secret -n tanzu-system-registry -o yaml harbor-tls -o jsonpath="{.data.ca\.crt}" | base64 -d

Management Clusterにコンテキストを切り替える。

kubectl config use-context tkg21mc

最初にWorkerノードを管理するkubeadmconfigtemplateリソースを書き換える。
オブジェクト名はClusterClassリソースの名前が付いているものを指定する。

kubectl edit kubeadmconfigtemplate tkg-vsphere-default-v1.0.0-md-config

kubeadmconfigtemplateリソースはspec.template.spec.filesに定義したファイルをノード作成時に生成してくれる。
既存のfilesに追記する形で以下を追記する。

      - content: |
          -----BEGIN CERTIFICATE-----
          MIIDKDCCAhCgAwIBAgIQHmZgcfI/GYpB0qIz0BwmRTANBgkqhkiG9w0BAQsFADAU
      :(省略)
          XcSmQr62lueUki8kqLcIDGnI2p3j+YmzO+fZxKjmIn+XySPy8TGcOwz9tjs=
          -----END CERTIFICATE-----
        owner: root:root
        path: /etc/ssl/certs/tkg-custom-ca.pem
        permissions: "0644"

また、spec.template.spec.preKubeadmCommandsに以下を追加する。

- '! which rehash_ca_certificates.sh 2>/dev/null || rehash_ca_certificates.sh'
- '! which update-ca-certificates 2>/dev/null || (mv /etc/ssl/certs/tkg-custom-ca.pem
   /usr/local/share/ca-certificates/tkg-custom-ca.crt && update-ca-certificates)'

ノードデプロイ時、中でkubadmによるノードのクラスタ追加が実施されるが、preKubeadmCommandsはその前に実行するコマンドを指定するオプションであり、ここでは証明書のインストールコマンドを叩いている。。 PhotonOSとUbuntuでパスが異なるため、両方対応出来るよう2種類のインストールコマンドを記載しており、ubuntuではupdate-ca-certificatesを実行して上記で作成した証明書を読み込ませている。
元々ある設定値を含めると、preKubeadmCommandsは以下のようになった。

      preKubeadmCommands:
      - hostname "{{ ds.meta_data.hostname }}"
      - echo "::1         ipv6-localhost ipv6-loopback" >/etc/hosts
      - echo "127.0.0.1   localhost" >>/etc/hosts
      - echo "127.0.0.1   {{ ds.meta_data.hostname }}" >>/etc/hosts
      - echo "{{ ds.meta_data.hostname }}" >/etc/hostname
      - '! which rehash_ca_certificates.sh 2>/dev/null || rehash_ca_certificates.sh'
      - '! which update-ca-certificates 2>/dev/null || (mv /etc/ssl/certs/tkg-custom-ca.pem
         /usr/local/share/ca-certificates/tkg-custom-ca.crt && update-ca-certificates)'

次にWorkerノードを再作成する。MachineDeploymentリソースはannotationに変更を加えるとWorkerノードを再作成する。annotationに以下のpatchコマンドで変更を加えてノードに証明書が入った状態にする。

kubectl patch machinedeployments  tkg21wc-md-0-8jpw5 --type merge -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}"

しばらくするとWorkerノードのみ再作成される。
再作成された後、動作確認する。
pullする際の認証情報をSecretとして作成し、defaultのServiceAccountに紐付ける。ここではkubectl patchで紐付けているが、既存のimagePullSecret設定があるならkubectl editで代用すること。

kubectl create secret docker-registry --docker-server=$HARBOR_DOMAIN --docker-username=$HARBOR_ID --docker-password=$HARBOR_PW myharbor-cred
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myharbor-cred"}]}'

テスト用のイメージを起動する。

kubectl run --image ${HARBOR_DOMAIN}/library/nginx nginx

無事起動すればOK。

$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          43s

class-basedの新規クラスタへの埋め込み

実は特に作業はない。
従来だと、既存のクラスタへのカスタム CA 証明書の信頼の追加(スタンドアローン MC)の手順にて追加するが、後述の余談の節に記載しているようにclass-basedだと自動でtkg-vsphere-default-v1.0.0-md-configをベースとしたkubeadmconfigtemplatesが作成されるので対応不要。

試しに新規にクラスタを作成する。

tanzu cluster create -f tkg21wc-2.yaml -v9

構築後、コンテキストを切り換えてPrivate Harborのnginxを起動する。

tanzu cluster kubeconfig get --admin tkg21wc-2
kubectl config use-context tkg21wc-2-admin@tkg21wc-2
kubectl run --image $HARBOR_DOMAIN/library/nginx nginx

無事起動している。

$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          31s

plan-basedの新規クラスタへの埋め込み

plan-basedの場合はkubeadmconfigtemplatesの元となるものなく新規に作成される。新規に作成されるkubeadmconfigtemplatesをカスタマイズしたい場合、リソースに修正をかけるytt overlayを~/.config/tanzu/tkg/providers/ytt/03_customizations以下に設置しておく必要がある。
公式ドキュメントをベースにytt overlayを作成する。

最初にコンテキストをWorkload Clusterに切り替えておく。

kubectl config use-context tkg21wc

ytt overlayファイルを作成する。

export YTT_DIR=~/.config/tanzu/tkg/providers/ytt/03_customizations
kubectl get secret -n tanzu-system-registry -o yaml harbor-tls -o jsonpath="{.data.ca\.crt}" | base64 -d > ${YTT_DIR}/tkg-custom-ca.pem
cat << 'EOF' > ${YTT_DIR}/add-custom-ca.yaml
#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:data", "data")

#@overlay/match by=overlay.subset({"kind":"KubeadmConfigTemplate"}), expects="1+"
---
spec:
  template:
    spec:
      #@overlay/match missing_ok=True
      files:
        #@overlay/append
        - content: #@ data.read("tkg-custom-ca.pem")
          owner: root:root
          permissions: "0644"
          path: /etc/ssl/certs/tkg-custom-ca.pem
      #@overlay/match missing_ok=True
      preKubeadmCommands:
        #! For Photon OS
        #@overlay/append
        - '! which rehash_ca_certificates.sh 2>/dev/null || rehash_ca_certificates.sh'
        #! For Ubuntu
        #@overlay/append
        - '! which update-ca-certificates 2>/dev/null || (mv /etc/ssl/certs/tkg-custom-ca.pem /usr/local/share/ca-certificates/tkg-custom-ca.crt && update-ca-certificates)'
EOF

plan-basedの設定ファイルを利用してWorkloadクラスタを作成する

tanzu config set features.cluster.allow-legacy-cluster true
tanzu cluster create -f tkg21wc-addcert-planbased.yaml -v9

コンテキストを切り替えてPrivate Harborのイメージを起動する。

tanzu cluster kubeconfig get --admin tkg21wc-addcert
kubectl config use-context tkg21wc-addcert-admin@tkg21wc-addcert
kubectl run --image $HARBOR_DOMAIN/library/nginx nginx

こちらでも問題なくpullできることが分かる。

$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          61s

TKGのバージョンアップ後の証明書

今回、バージョンアップの検証もしたかったので、TKG2.1.0でManagement Clusterを構築した。これを2.1.1にバージョンアップして、証明書が再度取り込まれるかを確認する。

Management ClusterをTKG2.1.1にアップグレードする。公式ドキュメントはこちら
tanzu cliをv0.28.0からv0.28.1に変更する。

$ tanzu version
version: v0.28.1
buildDate: 2023-03-07
sha: 0e6704777-dirty

v0.28.1用のプラグインをインストールする。

tanzu init

Kubernetesバージョンv1.24.10+vmware.1のOVAを転送してテンプレート化し、upgradeを実行する。

tanzu mc upgrade

アップデート後、ClusterClassに紐付いたkubeadmconfigtemplatesリソースのtkg-vsphere-default-v1.0.0-md-configが変更されたままかを確認する。

$ kubectl get kubeadmconfigtemplates -o yaml tkg-vsphere-default-v1.0.0-md-config
:(省略)
      preKubeadmCommands:
      - hostname "{{ ds.meta_data.hostname }}"
      - echo "::1         ipv6-localhost ipv6-loopback" >/etc/hosts
      - echo "127.0.0.1   localhost" >>/etc/hosts
      - echo "127.0.0.1   {{ ds.meta_data.hostname }}" >>/etc/hosts
      - echo "{{ ds.meta_data.hostname }}" >/etc/hostname
      useExperimentalRetryJoin: true
:(省略)

filesに書いた証明書もpreKubeadmCommandsに書いた証明書のインストールコマンドも消えている。
これにより、class-basedだとTKGのバージョンアップ後は追記した証明書情報はリセットされることが分かる。

次に、class-basedとplan-basedのクラスタをそれぞれupgradeしてみる。

tanzu cluster upgrade tkg21wc
tanzu cluster upgrade tkg21wc-addcert

それぞれkubeadmconfigtemplatesリソースは作成され直すが、plan-basedで作成していたクラスタはTKG2.1.0と同じytt overlayを利用してデプロイされるため、証明書や証明書のインストールコマンドは入っている。

      preKubeadmCommands:
      - hostname "{{ ds.meta_data.hostname }}"
      - echo "::1         ipv6-localhost ipv6-loopback" >/etc/hosts
      - echo "127.0.0.1   localhost" >>/etc/hosts
      - echo "127.0.0.1   {{ ds.meta_data.hostname }}" >>/etc/hosts
      - echo "{{ ds.meta_data.hostname }}" >/etc/hostname
      - '! which rehash_ca_certificates.sh 2>/dev/null || rehash_ca_certificates.sh'
      - '! which update-ca-certificates 2>/dev/null || (mv /etc/ssl/certs/tkg-custom-ca.pem
        /usr/local/share/ca-certificates/tkg-custom-ca.crt && update-ca-certificates)'
      useExperimentalRetryJoin: true

一方でplan-basedで作成していたクラスタは更新後のtkg-vsphere-default-v1.0.0-md-configを元にしたkubeadmconfigtemplatesで再作成されるため、証明書や証明書のインストールコマンドは消える。

      preKubeadmCommands:
      - hostname "{{ ds.meta_data.hostname }}"
      - echo "::1         ipv6-localhost ipv6-loopback" >/etc/hosts
      - echo "127.0.0.1   localhost" >>/etc/hosts
      - echo "127.0.0.1   {{ ds.meta_data.hostname }}" >>/etc/hosts
      - echo "{{ ds.meta_data.hostname }}" >/etc/hostname
      useExperimentalRetryJoin: true

更新後の環境でサンプルのnginxを起動した結果はこちら。
■plan-based

$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          4s

■class-based

$ kubectl get pod
NAME    READY   STATUS         RESTARTS   AGE
nginx   0/1     ErrImagePull   0          2s

結論

class-basedでインストールした環境だと、Private Harborの証明書の適用はクラスタ内のリソースを更新するだけでよく、ytt overlayのファイルを用意せずに他のクラスタに伝搬できるため、その点は便利である。
一方で、TKGのバージョンアップを行うと、設定が消えてしまう。
現時点ではManagement Clusterの更新のタイミングで設定ファイルを読ませる事も出来ないため、アップグレード後に手動で設定し直す必要が現時点ではありそうだ。

一方、plan-basedだとアップグレード後の修正コストが発生しないが、ytt overlayファイルを用意する必要があったり、デプロイ後の環境に対して適用する場合は個々にkubectl editを実施する煩雑さが出てくる

class-basedがデフォルトとなり、plan-basedが廃れていきそうな感じはあるが、現時点ではユースケースに合わせて使い分けるのが良さそう。

余談:class-basedとplan-basedのkubeadmconfigtemplates

TKG2.1でクラスタ構築時に使う定義ファイルの書き方がclass-basedが新たに追加され、そちらがデフォルトとなり、従来の定義ファイルはplan-basedというタイプに分類され、レガシー扱いされるようになった。
これ、書き方が変わっただけのように見えなくもないが、実際は動作も異なっている。
以下、class-basedとplan-basedでクラスタを作成した状態でkubeadmconfigtemplatesを取得した結果である。

$ kubectl get kubeadmconfigtemplates
NAME                                   AGE
tkg-vsphere-default-v1.0.0-md-config   2d16h ★ClusterClassと紐付き
tkg21wc-addcert-md-0                   8m22s ★plan-based
tkg21wc-md-0-bootstrap-ckrrb           9h   ★class-based

class-basedの方は語尾にランダムな文字が付与されている。これはclass-basedだとkubeadmconfigtemplatesが都度自動生成されることを意味する。
MachineDeploymentリソースを修正すると、class-basedで作成したクラスタの場合、class-basedのkubeadmconfigtemplatesの種であるtkg-vsphere-default-v1.0.0-md-configをベースに新規にkubeadmconfigtemplatesリソースが作成される。
そのため、従来型に慣れてる人でkubeadmconfigtemplatesを修正していた人は手順が変わるため注意が必要である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?