本番環境のAPIサーバーをEKS(Kubernetes)に移しました。サーバーをKubernetesに移行するとき、どのような観点で考え、どこが難しかったかという情報が少ないように感じたので、書いてみようと思います。なお、本稿は移行の全体感について書きたいので、個々の要素についての詳細は省略しています。予めご了承ください。
構成図
次のようになっています。なお、APIのPodは、nginx, php-fpm, envoy-proxyの3つのコンテナで構成されています。
なぜKubernetesに移すことにしたのか
運営しているサービスのユーザー数が増え、APIサーバーの負荷が高くなったからです。
もともと、EC2で運営していてサーバを増やすことはできましたが、増えるごとにインフラやアプリのコードをデプロイをしなおす必要があり煩雑になっていました。サービスは一日の中でアクセス量が増減しますが、それにあわせてPOD単位でこまめにコンピューティングリソースを増減させれば、コストの最適化が期待できます。
また、これまではアプリケーション開発者が、PHPなど依存しているソフトウェアのバージョンを上げたいとき、インフラ担当に依頼して上げてもらうなどの必要がありましたが、コンテナ技術を用いれば自分ですぐにstaging環境などに適用できるため、ソフトウェア開発の効率がよくなることも見込んでいます。
オーケストレーションシステムにECSではなくEKSを選択した理由は、Kubernetesは周辺ツールがしているためです。Canary Releaseなど様々なデプロイの方法を実現したり、Helmなどのパッケージマネージャから今回対象としたサーバに限らず今後さまざまなリソースをKubernetes上に移すことがしやすくなるであろうと考えています。
よく言われる技術的なハードルの高さについては、AWS EKSの誕生によってハードルが下がったのではないかと思います。また、Kubernetesのすべての技術を使おうとするのではなく、必要なところだけを限定して利用することで難易度を抑えることができました。
どこが難しかったか
1. CI/CD、監視、ロギングなどの仕組みをすべて1から作る必要があった
コンテナ化をすると、これまでサーバー(EC2)を前提としていたCI/CD、監視、ロギングの仕組みは殆どが使えなくなります。デプロイはDockerのイメージをビルドしてからクラスタにapplyする流れになるし、監視はこれまでのサーバ単位だけでなくクラスタ全体の状態を監視する必要があります。ログもコンテナ内に永続化することはせずに、どこかのサーバに飛ばす必要があります。
色々と検討した結果、ワークフローの実現にGitHub Actions、コンテナのビルドとデプロイの実現にSkaffoldを組み合わせてることにしました。また、productionやstagingなど環境ごとの設定をするためにkustomizeを利用しています。この方式をとることで、構成情報はすべてgitで管理され、オペレーションもすべてgitのコマンドで行えるためとてもシンプルになりました。このような運用方式は、GitOpsの考え方を参考にして作っています。
監視とロギングは、CloudWatch Container Insightsを使用し、fluentdのDaemonSetがログを転送します。これらを使用すると、CloudWatchにメトリクスやログが集約されるため、そこからアラームを作成して問題があれば開発者に通知を飛ばすことができます。一旦CloudWatchにログやメトリクスが飛んでしまえば、あとはCloudWatch Logs Insightsなどを使っていかようにも分析したりアラームを作れるので重宝しています。
2. バックエンドサーバへのアクセス
MySQLやRedisなどバックエンドサーバーへのアクセスは、もともとHAProxyを使っていましたが、その代替となるものが必要です。アクセス方法には外部にプロキシサーバーを立てるなど選択肢が多く悩ましかったのですが、最終的にはenvoyを使用しました。いわゆるアンバサダー・パターンと言われるもので、このようにすることでstagingやproductionなど環境ごとの違いはenvoyの設定で吸収することができます。envoyの設定はkubernetesのConfigMapをgitで管理しているため、アプリケーション開発者が設定することができるようになりました。なお、envoyと一緒によく使用されるIstioは、今回マイクロサービスアーキテクチャを指向しておらず、最小構成で作りたかったため使用していません。
3. CPUリソースの設定
Kubernetesの設定で一番難しかったのが、リソースの設定です。これはPod毎にrequestsで要求量、limitsで制限量を設定することができますが、何が適正値なのかわからず試行錯誤の連続でした。
特に、php-fpmのコンテナのCPUのリソースの決定が難しかったです。kubectl top
コマンドなどを用いてCPUの使用量を観測したのですが、負荷状況によりCPU使用率は大きく変化していました。requestsとlimitsは近い値を設定すれば、予期しないリソースの使用を抑えられ安定的することが見込めますが、CPUの使用率が大きく変化するようなPODの場合、limitsによって上限が制約されてしまうため、他のPODの余ったリソースを使用することができません。試行錯誤の結果、今回はrequestを平常時のCPU利用率にし、limitsをその倍程度とることにしました。
4. いきなり切り替えることへの不安感への対策
EKSのクラスタにAPIサーバを移行するにしても、事前にテストはするもののいきなり全部切り替えることには不安が残ります。トラブル対応の方法など一定期間運用してみないとわからない部分もあるため、少しつずつ試しながら運用したいものです。そこで、次のような手順で切り替えました。
- staging環境をEKSに切り替えて開発者に1ヶ月程度使ってもらう
- production環境を旧来のEC2サーバに70%、EKSに30%のトラフィックを配分し、2ヶ月程度使用する
- production環境をEKSに100%のトラフィックで配分する
staging環境での運用によって、クラスタの設定の問題点や、ログや監視の使いづらさなどの問題はほとんど抽出することができましたproduction環境での、7:3で30%だけ導入したことにより、実際のトラフィックの中での問題(特にリソースの配分)を抽出することができました。
参考にした情報
1. Kubernetes完全ガイド impress top gearシリーズ
主にこの本をベースに、Kubernetes公式やAmazon EKS公式を検索しながら構築しました。
2. Kubernetes on AWS ~アプリケーションエンジニア 本番環境へ備える
この本は、構築がほぼ終わる頃に読んだのですが、構築時に悩んだことが網羅されており、先に読んでおけばよかったと思った一冊です。
3. Kubernetes ベスト プラクティス 6 選
ブログも沢山読みましたが、印象に残っているのはこの記事です。GKE向けですが、EKSでも利用できる知見が詰まっていました。
結果どうだったか
リリース後、特にトラブルもなく安定稼働しています。
まとめ
このような実例の情報がもっとあれば楽だったのになと思い今回書くことにしました。また、今回はなるべく最小の構成にすることを目指したので、最初構成の例にもなるかと思います。これから移行を考えている方の参考になりましたら幸いです。