Kubernetes(AKS)導入奮闘記〜その２〜

# 目次

 - **試行錯誤の検証段階**
 - **どうやってkubernetesの理解に至ったか？**
 - **導入方針**
 - **導入範囲**
 - **Azureマネージドサービスとの連携**
 - **kubernetes化への既存環境の問題**



# ◆試行錯誤の検証段階
とにかくkubernetesクラスタを作成していろいろ操作してみないと全然イメージできなかったので、Azure Kubernetes Serviceにてテスト用のクラスタを作成。
この段階では以下のような検証シナリオを考えていました。

 - MySQLは開発環境に影響を与えないように、kubernetesクラスタ内部にEmptyDir{}で簡易MySQL Podを作って対処。
 - Redis Cahceもkubernetesクラスタ内部にEmptyDir{}で簡易Redisを作って対処。
 - QueueなどはAzureストレージエミュレーターPodを作成して対処。

要するに、ローカルテスト環境をそのままkubernetesに乗せてテストしようとしたのですが、予想外に難易度が高かったので断念しました。


## 教訓
できるだけ早く実際にコマンドを打って動作させてみるべし。書籍なんて出版された時点で古い情報しか載っていません。ローカルPCなりクラウド上なり使い捨てkubernetesクラスタを構築していじり倒した方がいいです。



# ◆どうやってkubernetesの理解に至ったか？
おそらく今現在でもkubernetesの全機能から見れば半分も使いこなしていないと思いますが。。。
とりあえず以下の機能を使いこなせれば最低限の運用ができると思っています。

 - kubernetesクラスタ。  
    AzureがAKSクラスタで全部面倒見てくれます。GUIでささっと作成。クラウドは便利。クラスタ構築の作業についてとりあえず学習コストは抑えられます。  
 - Docker buildとかpushとかpullとか。Dockerfileの書き方。  
    これは以前の会社からたくさん作業していたので問題ありませんでした。このやり方を知らない人は結構苦労すると思います。特にレジストリにpushする方法が複雑なので慣れるのに時間がかかるかな。  
 - docker-composeでデプロイ(=オーケーストレーション)する仕組みの理解
    初体験だったのですが、docker-compose.yamlを見てだいたい想像して理解できました。未だにdocker-composeの勉強は改まってしたことがありません。  
 - kubernetesのマニフェストファイル=yamlの作成。  
    既存システムであるdocker-compose.yamlから想像して脳内変換して１種類づつ作成していきました。  
    事前に本で勉強していたので、１つpod作成するのにも必ずDeploymentでtemplate定義していきました。セオリーを本で勉強していたので、この点は苦労せずに移行作業することができました。  
    ただ最初は１つのpodに全コンテナを詰め込んでいたのが勘違いで、マイクロサービス的に各Serviceに分割していく設計変更作業が発生します。  




# ◆導入方針
 - helmやkustomizeなどのツールは使用しない。既存メンバーの学習コストを減らすため。
 - 既存ソースコードはできるだけ変更しない。全てを作り変えているといくら時間があっても足りません。

## helmやkustomizeなど
helmは当時から既にKubernetes版標準パッケージマネージャ的な存在でしたが、  

 - cert-managerはAzureアプリケーションゲートウェイの関係でLet's Encryptの完全自動化が困難。  
 - helm-ingressは、これもまたアプリケーションゲートウェイの関係でL7ゲートウェイとしては不採用。  

Azureの場合、helmというツールがあまり活躍の場面がないです。kubernetesのコマンド「kubectl」の使い方をメンバーに教えるだけでも大変なので、ただでさえ学習コストが高いの余計なツールを増やしたくなかったという事情があります。  

テンプレート化もかなり悩みました。helm-templateもkustomizeも高機能なのは分かりますが、複雑すぎるツールで、これらをゼロから学んでメンバーに教えることを考えると憂鬱になります。欲しい機能は「kubenetesのyamlに変数を埋め込みたい」という単純なものだったため、readlineパッケージのおまけ的な `envsubst` を工夫して利用しています。機会があればその運用方法の詳細も後ほど紹介したいと思います。  

## ソースコード変更について
幸いなことに元々docker-composeで複数のDockerコンテナを組み合わせてサービス提供していた進んだ技術の会社だったので、Linuxサーバをコンテナ化する作業は発生しませんでした。全部コンテナ化する作業からやっていたら1人で数ヶ月で移行作業を達成できてはいませんでした。
ただ、このdocker-composeに乗ってる全種類のコンテナを全部kubenrtesの１つのpodにしようと当初勘違いしていたので、後ほどマイクロサービスっぽく構成変更するためにいろいろ悩むことになります。ソースコードを修正してしまえば手っ取り早いのですが、、、移行の暫定環境のためにソース修正は結構面倒で時間がかかり、結局ほとんどの部分をインフラの設定というテクニックを使って回避してしまいました。


# ◆導入範囲
あくまでシステムの一部をkubernetesに移行するのであって、以下のリソースは対象外です。

 - MySQL -> クラウドのマネージドMySQLを使用。
 - Redis -> クラウドのマネージドRedis Cacheを使用。
 - CPUコア数十個やメモリ数十Gバイトを使用する重いバッチ処理 -> Function/Batchなどのクラウドの仕組みで別に構築。
 - 一部のドローン管理サーバ -> コンテナ化が困難な事情があり、ほんのわずかですがVirtual Machineが残っています。

MySQLのMaster/Slave負荷分散をkubernetesで構築しているサンプルが載っている書籍もありますが、面倒なだけで実用的ではありません。素直にクラウドのマネージドサービスを利用した方がいいと思います。


# ◆Azureマネージドサービスとの連携
 - Azure MySQLとの連携は不思議と問題なし。
 - Azure Redis Cacheとの連携はegressを利用しないとできない。

## Azure MySQL
なぜかマネージドMySQLにはファイアーウォールで許可しなくても接続できています。不思議。AKSクラスタ間だけ特別扱いなのでしょうか？突然通信遮断されるかもしれないので怖いです。

## Azure Redis Cache
kubernetesクラスタ内部Podからの接続を受け付ける方法でかなり悩んで、どうしても解決できないので、仕方なくMicrosoftサポートに頼りました。
結局この技術を使わないとRedis Cacheとは連携できませんでした。
[Azure Kubernetes Service (AKS) でエグレス トラフィックに静的パブリック IP アドレスを使用する](https://docs.microsoft.com/ja-jp/azure/aks/egress)
RBAC(Role Based Access Control)も調査して検証したのですが難しく、AKSクラスタのOutgoingトラフィックの固定IP化ができるので、このIPをRedis Cacheファイアーウォールで許可すれば連携できます。



# ◆kubernetes化への既存環境の問題
 - セッションとキャッシュをローカル管理していてスケールできるプログラム構成になっていなかった。
 - DBのmigrationを移行し忘れて後ほど大変なことに。k8sのどこかのコンテナでdocker exec db migrationすればいいのですが、なにしろpod名はランダムでしかもdeploy後状態のコンテナでexecしなければいけないのでかなり苦労しました。kubectl waitはたいして役に立たない機能でした。
 - その他docker-composeを起動するためにいろいろAzureのリソース作成で冪等性を保っていたのですが、k8sになって定期実行するjob/cronjobがリソースもクリーンナップしてくれず非常につかいづらく苦労させられました。


# ◆次回
次回はいよいよ想定外のコンポーネント分割の話です。