『Self-managed Kubernetes in GCE』を、Terraform + Ansibleでやってみる



Project Calicoのサンプルとして掲載されている [Self-managed Kubernetes in Google Compute Engine (GCE)](https://projectcalico.docs.tigera.io/getting-started/kubernetes/self-managed-public-cloud/gce) の構成を、Terraform + Ansibleを用いて半自動構築してみる記事です。

Ansibleでk8sクラスタを構築する方法自体は既に多数の記事がありますしkubesprayという素晴らしいツールも準備されていますが、
Raspberry piでのクラスタ構築の記事が多く、GCP上にわざわざ手でクラスタを作成するケースが少なかったので、構築にチャレンジしてみました。

- (自分も、Raspberry piでの構築をしてみたかったのですが、Raspberry piの在庫切れで手に入らず、、、。)

# TL;DR

最終的なコード類はこちら
- [HirokiYoshida837/k8s-gcp-tf: Provisioning k8s cluster's infrastructure on GCP with Terraform](https://github.com/HirokiYoshida837/k8s-gcp-tf)
- [HirokiYoshida837/k8s-kubeadm-ansible: setup k8s cluster's with Ansible nad kubeadm](https://github.com/HirokiYoshida837/k8s-kubeadm-ansible)

構成図はこうなります

![Frame 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407001/d5857a30-62e2-137d-acc5-62c3b9644ecf.png)



# 1. Terraformでのインフラ構築

今回はあくまで学習用と割り切り、以下の方針で構築します。

- 手元の開発用PCから terraform plan -> terraform apply まで行う
- tfstateの保存も手元のPC内とする
- [Terraform Provisioners](https://www.terraform.io/language/resources/provisioners/syntax) は使用せず、インフラ構築の範囲に留める。インスタンス自体のプロビジョニングはAnsibleで行う


Terraformで準備する主なインフラ類は下記のようになります。OSやインスタンスのサイズ・台数、Region等は適宜好みのものとしてください。

- VPC、VPCサブネット
- Kubernetesクラスタ用ホスト
  - controller node 1台 (今回はHA構成にはしない)
  - worker node 2台
- Ansible実行用ホスト
- Ansible実行用のService Account

これらの方針で作成したterraformファイル類はこちらです。

- [HirokiYoshida837/k8s-gcp-tf: Provisioning k8s cluster's infrastructure on GCP with Terraform](https://github.com/HirokiYoshida837/k8s-gcp-tf)

必要な権限を持ったService Accountを作成し、実行してください。


# 2. Ansible + kubeadmでのk8sクラスタ構築

今回は以下の方針でクラスタを構築する

- Ansibleを実行するホストは、GCP内のサーバー（クラスタとは別サブネット）
- Ansible実行用/対象ホストへのSSH接続はServiceAccountを使う
- GCPのDynamic Inventory等は使用しない（将来的にはRaspberry Piで同じことがしたかったので、環境によらないPlaybookとしたかった）
- CRIはDockerを使用する（特に理由はないので、好きなものに変更してもOK。今後を考えるとcri-oの方がよいと思う。）

というわけで作成したPlaybook類はこちらです。

- [HirokiYoshida837/k8s-kubeadm-ansible: setup k8s cluster's with Ansible nad kubeadm](https://github.com/HirokiYoshida837/k8s-kubeadm-ansible)

# 3. アプリケーションを動かしてみる

適当なdeploymentを applyしてサービスをつくって動かせばOK。好きに試してください。


# 気づき事項・今後の課題

## Terraform関連

- Terraformのファイル・ディレクトリ構成が難しい。
  - モジュールは今回は大きく、 IAM関連、 VM関連、 VPC関連として分割してみた。どういう分け方にするのかはビジネスモデルに合わせて考えるべきなのかもしれない。
  - モジュールの分割、依存関係（各モジュールファイルからのoutput等）がいまいち理解できず苦労した。
  - [Files and Directories - Configuration Language | Terraform by HashiCorp](https://www.terraform.io/language/files)

- terraform plan, terraform apply の実行環境を整備したい
  - 実際は terraform plan内容のレビューや diff確認を GitHubのPR上等ですべき。
  - 今回は適当に手元で実行するようにしていたが、CI/CDのpipelineの中でCloud Build内で terraformを実行する例はかなり良かった。
  - 参考資料
    - [Terraform、Cloud Build、GitOps を使用してインフラストラクチャをコードとして管理する  |  Cloud アーキテクチャ センター  |  Google Cloud](https://cloud.google.com/architecture/managing-infrastructure-as-code)
    - [TerraformとCloud BuildでGCP向けのIaC環境を作成してみた](https://zenn.dev/koduki/articles/78a36c07020638)


## Ansible関連

- Terraform provisionersとAnsible
  - 公式ドキュメントにもあるように、provisionersは最終的な手段であり、できるだけ使うべきではない。管理が複雑になりIaCで大切な冪等性が確保しづらい。
    - [Provisioners | Terraform by HashiCorp](https://www.terraform.io/language/resources/provisioners/syntax)


- kubesprayの存在
  - 今回のようにAnsibleでk8sクラスタを構築する方法自体は
  - kubespray自体がAnsible + kubeadmでのk8sクラスタ構築になっている。(過去はkubeadmを使用していなかったが、v2.3 以降からは使用するように変更されている)
  - オプションとして指定できる項目も豊富であり、内部のAnsible-Playbookの内容もかなり参考になる。
    - Caclicoでのネットワークはデフォルトではフルメッシュだが、もroute reflectorsを使用した形式も指定可能。
    - CNIもFlannel, Calicoが選択可能、ランタイムも 指定可能。HA構成の指定も可能であり、k8s、Ansible自体にそれなりの知見があれば非常に強力。
  - [kubernetes-sigs/kubespray: Deploy a Production Ready Kubernetes Cluster](https://github.com/kubernetes-sigs/kubespray)


- Ansible-Playbookの自動テスト・CI
  - Ansibleの学習は、こちらを参考にした。 : [インフラCI実践ガイド Ansible/GitLabを使ったインフラ改善サイクルの実現](https://www.amazon.co.jp/dp/B07D2YCMJ5)
  - 書籍の中ではPlaybook自体の自動テスト、継続メンテナンスについても触れられている。今回はここまでの整備は実施しなかったが、是非試してみたい。
  - kubeadmでの構築に関しては、kubesprayのリポジトリの内容自体も大きく参考になる。


- Cloud-initとAnsible、初期プロビジョニングの境界
  - 資料を探していると、k8sクラスタ用のセットアップをcloud-initを用いて実施している人もいた。
    - 今回Ansibleで行った初期プロビジョニング自体はcloud-initでも問題なくできそう。 (kubeadm join以前の設定類)
    - 同じインスタンスを継続して管理したい、冪等性を重視したい、等を考慮しながら、適宜作業内容によってcloud-initとAnsibleを使い分けるのが良いのかもしれない。
  - クラウド環境等であればPacker等で初期イメージを作成して使い回すほうが良さそうにはみえる。workerのkubeletアップデートが必要になった場合、既存のインスタンスをアップデートするのではなく、新しいイメージを作成し、そこから起動 -> kubeadm join -> 古いインスタンスを停止、とすれば比較的安全にできそう。イメージ自体を使い回せるので事前検証も十分にできるはず。


- shell/command moduleが必要になる場合の冪等性の確保
  - 参考にした資料の中には、各種設定の変更などを sed を使ってゴリゴリにやってる人もいた。
  - 何度Ansible-Playbookを実行しても結果が変わらないようにする工夫が必要。
  - kubeadmの操作自体はshell/commandモジュールから操作する必要がありそうなので、工夫しないと冪等性（厳密には違う気がするけど）が確保できない。


## kubernetes関連

- kubeadmでのクラスタ構築は意外と簡単
  - 手順にしたがってそのままやるだけなら、ある程度の知識があれば超簡単。k8s公式にも[ドキュメント](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)がある。
  - kubernetesの各コンポーネントや、周辺環境についての知識はある程度得られるはず。
  - 実は[minikube](https://kubernetes.io/ja/docs/setup/learning-environment/minikube/) も内部ではkubeadmを使用している。
  - より詳しく学びたい、より愛着を持ったクラスタにしたいのであれば、[kubernetes-the-hard-way](https://github.com/kelseyhightower/kubernetes-the-hard-way)をやってみよう。


- クラスタ自体の管理・アップグレードが厳しそう
  - 各種のトラブルシューティング必要になるので、手ですべて管理するのは厳しそう。（経験しないとトラブルシューティングはできるようにならないと思うが、、、）
  - どんなものに対しても言えることだが、仮に業務で使うことになる場合、十分に調査してから、構成を検討し、根気よくメンテナンス・情報収集していくことが大事そう。
    - [Kubeadmによるクラスタアップグレード・その光と闇 - Speaker Deck](https://speakerdeck.com/jyoshise/kubeadmniyorukurasutaatupuguredosofalseguang-toan)

- アプリケーションのデプロイ、管理
  - 今回はサンプルレベルのアプリケーションのデプロイで終わった。
  - 早く自宅でKubernetesクラスタを構築してHelmの使用、ArgoCDの導入までやってウキウキになりたい。

- 構築したkubernetesクラスタ自体の管理や監視はどうするのか
  - 複数のk8sクラスタがお互いに監視しあう構成の記事を見かけて、非常に面白かった。
  - セルフホスティングなので管理・監視の手間が増えることを考えると、マネージド・サービスの力強さを改めて感じる。

- HA構成について
  - 今回はシングルControllerなので、明らかに単一障害点。
  - controller はHA構成にしても良かったと思う。この場合は Hostの台数を奇数（etcdの分散合意を機能させるため）にするべき、など色々考慮が必要そう。


## GCP関連

- Ansibleを実行するホストから他のホストへどう接続するかにかなり悩んだ。
  - GCPのコンソール画面上から、自分のアカウントでSSH -> 他ホストにSSHでjumpみたいなことを試したりしたが、エラーが出ることがありハマった。
    - コンソール画面から入るときにつかうSSHキーの期限は5分ほどなので、一時的に他のホストにはいれてしまうのが問題だった。最終的に、プロジェクトのsshキーを使用しないように設定変更してSAのOS Loginで接続するようにした。この辺りがややこしかった。インスタンス自体に権限を与えたほうが良かったもしれない。この辺りは詳しい人に相談してみたい。
  - Cloud-initでユーザ作成などをやればよかったかも。

- SAの鍵を GCP Secret Manager にいれてTerraform実行ホストから渡す、みたいなことをしてみた。
  - 適切なやり方なのかは十分に調べられていない。


# まとめ

特に真新しいことをしたわけではかったが、今までほぼTerraform、Ansibleには触れてこなかったが、今回、一から自分で実践してみることで非常に勉強になりました。
特にAnsible-Playbookの作成は、ちょうどよい難易度であり、 (より凝ったものにしようとすれば、いくらでもできる。kubesprayがあるので、そこまでやるか感はあるかも) Kubesprayなどの中身みて、何をしようとしているのかがある程度把握できるようになったのも大きいです。

また、k8sに関しても、内部構成や実装について興味を持つ機会となり、コレをきっかけに知人と勉強会などを開いて輪読やハンズオンなどをやってみています。

年末年始に暇な方はぜひお手製k8sクラスタを構築してニンマリしてみてください。


# 参考資料

- [kubeadmを使用したクラスターの作成 | Kubernetes](https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/)
- 公式ドキュメント

- [Self-managed Kubernetes in Google Compute Engine (GCE)](https://projectcalico.docs.tigera.io/getting-started/kubernetes/self-managed-public-cloud/gce)
  - CNIに使用したCalicoが作成した資料。この手順に従えばkubeadmで簡単にクラスタが作成できる。今回の操作のほとんどはこの資料をベースに作成。今回のtf x ansible化の前に、これを手で実施して内容を把握した。

- [kubernetes-sigs/kubespray: Deploy a Production Ready Kubernetes Cluster](https://github.com/kubernetes-sigs/kubespray)
  - Ansible x kubeadm でクラスタを構築するツール `kubespray` のリポジトリ。playbookの書き方などを一部参考にした。
  - 今回やったことは全部 kubesprayでもできる。


- [Kubernetesクラスタ構築を例に既存の作業をAnsible化するポイント / initialize kubeadm by ansible - Speaker Deck](https://speakerdeck.com/zaki_lknr/initialize-kubeadm-by-ansible)
- [zaki-lknr/initialize-kubeadm-ansible: kubeadmでk8sクラスタデプロイするAnsible Playbook](https://github.com/zaki-lknr/initialize-kubeadm-ansible)
- [[Kubernetes] kubeadmを使ってCentOSへk8sクラスタをデプロイしてみた (firewalld有効版) - zaki work log](https://zaki-hmkc.hatenablog.com/entry/2020/03/19/191534)
  - Playbookの作成にあたって、多く参考にさせていただいた。
  - こちらのリポジトリでは、CRIやCNIの選択も可能になっている。

- [Deploy a Kubernetes Cluster using Ansible - buildVirtual](https://buildvirtual.net/deploy-a-kubernetes-cluster-using-ansible/)
  - Ansibleでk8s構築をしている記事。一部を参考にした。


- [GCPで基本に戻って始める実践 Infrastructure as code再入門#1 | VISASQ TECH BLOG](https://tech.visasq.com/restart-gcp-infrastructure-as-code1/)
  - 1～4を全体の流れの参考にさせていただいた。
  - terraformでGCPのプロビジョニングを行い、Dynamic Inventoryを使ってAnsibleからの実行対象ホストを取得されていた。


- [How to configure OS Login in GCP for Ansible | There is no magic here](https://alex.dzyoba.com/blog/gcp-ansible-service-account/)
- [AnsibleでCloudIAPを使ってSSH接続する | GRIPHONE ENGINEER'S BLOG](https://tech.griphone.co.jp/2020/11/12/ansible-cloudiap/)
  - OSログイン辺りの調査で参考にした。


- [k8s on RaspberryPiでHAクラスタ構築](https://zenn.dev/nkte8/articles/2021-09-26-r01)
- [我が家のおうちKubernetesの成長記録 | IIJ Engineers Blog](https://eng-blog.iij.ad.jp/archives/11900)
- [おうちで「おうち Kubernetes インターン」を実施しました | CyberAgent Developers Blog](https://developers.cyberagent.co.jp/blog/archives/27443/)
  - k8sをRaspberry pi等に構築している記事。
  - 全体の構成、作業の流れの把握に参考にさせていただいた。


- [ansible/workshops: Training Course for Ansible Automation Platform](https://github.com/ansible/workshops)
- [workshops/README.ja.md at devel · ansible/workshops](https://github.com/ansible/workshops/blob/devel/exercises/ansible_rhel/1.3-playbook/README.ja.md)
  - RedHat公式のAnsibleチュートリアル。日本語もあります。

- [インフラCI実践ガイド Ansible/GitLabを使ったインフラ改善サイクルの実現 | 中島 倫明, 佐々木 健太郎, 北山 晋吾, 齊藤 秀喜, 羽深 修 | コンピュータ・IT | Kindleストア | Amazon](https://www.amazon.co.jp/dp/B07D2YCMJ5)
  - Ansibleの基礎や考え方の元に。非常に参考になりました。

- [CalicoによるKubernetesピュアL3ネットワーキング - Yahoo! JAPAN Tech Blog](https://techblog.yahoo.co.jp/infrastructure/kubernetes_calico_networking/)
  - Calicoなど、CNIプラグインについて非常にわかりやすく、かつ詳しく解説されている

- [TerraformとCloud BuildでGCP向けのIaC環境を作成してみた](https://zenn.dev/koduki/articles/78a36c07020638)
  - Terraform実行環境について。