EC2のマイクロサービスをKubernetesに移行した話

これは TECHSCORE Advent Calendar 2018 の23日目の記事です。
本記事では、EC2で稼働しているマイクロサービスアプリケーションをKubernetesに移行した顛末をご紹介します。

背景

大きな目的は、EC2に散在する各マイクロサービスを順次Kubernetes(以降k8s)に移していくことによるコストの削減です。
少し掘り下げると、k8s化により以下の2種類のコストが削減できると期待しています。

  • 構成管理や環境構築の工数を削減し、開発効率を向上 → 人的コストの削減
  • メモリやCPUなど計算リソース集約による効率化 → インフラコストの削減

構成を刷新するついでにフレームワークやライブラリなどのバージョンアップもスコープに含め、半年程の時間をかけて段階的にリリースしていきました。
周辺ツール含めた新旧構成の比較はこちらです。

実行環境 ソース管理 フレームワーク ビルド CI/CD 構成管理
EC2 GitLab SpringBoot 1.x Gradle Jenkins Ansible
k8s (EC2) GitLab SpringBoot 2.x Gradle + jib GitLabCI kustomize

Kubernetes移行に伴いデプロイパイプラインを大きく変更しており、このあたりのツール選定で考慮した点などをまとめてみました。

ソース管理: GitLab

ソースコード管理については、以前からGitLabを社内で構築して利用しており、特に不満もなかったのでそのまま利用しました。
ただ、かなり古いバージョンを利用していたため、後述のCI/CDなどを利用するためにも最新バージョンに移行しています。

ビルド: Gradle + jib

移行作業当初はアプリケーションごとにDockerfileを記述し、イメージをビルドをしていました。
今回移行対象は4種類のサーバで構成されており、トライアンドエラーの度にDockerfileやビルド手順を修正していく作業が面倒でした。
そこで、途中からDockerイメージのビルドにjib-gradle-pluginを利用するように切り替えました。
SpringBootのような単独で起動できるJavaアプリケーションなら、Gradleのpluginとして導入し少しの設定で拍子抜けするぐらい簡単にDockerイメージを作成する事ができます。

jibにおけるデフォルトのベースイメージは軽量なdistrolessなのですが、調査などの際にexecを使ってコンテナ内ででシェルを実行することを考え、OpenJDKベースのカスタムイメージをベースイメージとしています。

CI/CDパイプライン: GitLabCI

長らくJenkinsを愛用していたのですが、GitLabCIに移行しました。
移行の決め手は、GitLabCIはリポジトリに.gitlab-ci.yamlを配置するだけで動作するため、設定の管理がリポジトリ内で一元化される点です。
また、以前は複数のチームで一つのJenkinsを共用しており、プラグインのバージョンアップ時に互換性問題が度々発生して頭を抱えていました。
GitLabCIではプラグインを用いずDockerを使って各々のプロジェクトで自動化を組んでいくことになるため、GitLabのバージョンアップによる影響を受けにくいのも魅力です。

マニフェスト管理: kustomize

一番悩んだのはk8sのマニフェスト管理についてです。
k8sへのアプリケーションのデプロイでは、yaml(かjson)で記述されたマニフェストの登録/更新を行うことになります。
開発、ステージング、本番それぞれの環境ごとに、リソース割り当てや細かい設定などの差異が生まれるため、共通部分と環境差分を管理しながらマニフェストの生成/登録を行う必要があり、kubectlだけでカバーするのは厳しいかと思います。
何らかのツールのサポートが欲しくなり、以下のツールが候補となりました。

  • kustomize
    k8sのマニフェスト構文そのままに、継承やパッチなどで設定をインジェクトできる。
    kubernetes-SIG管轄なのもポイント。

  • helm
    CNCFの一員にもなり、すでにデファクトっぽい気がするk8sのパッケージマネージャ。
    広く公開するためのパッケージ管理ツールで、k8s版のyumやaptのようなイメージ。
    定義を自前で用意するのは正直しんどい。

  • kompose
    docker-composeのyamlをKubernetesのマニフェストに変換してくれる。
    docker-compose資産を移行するのがメインの用途かと。

  • kedge
    k8sのyamlをシンプルに定義できるようにしたシンタックスシューガー的なもの。
    冗長な記述を減らせるのはメリット、デメリットは標準の構文から外れる点か。

  • ksonnet
    jsonnetというJSONのテンプレート言語がベース。
    再利用性が重視されており、コンポーネント、モジュール、パッケージなど細かく細分化されている。
    使ってみて便利だとは思ったものの、構成も複雑で、jsonnetもかなり癖の強い言語なので学習コストが高め。

  • kubecfg
    ksonnetのように重厚な仕組みではなく、k8s向けのjsonnetラッパー的なツール。
    jsonnetに抵抗がなければシンプルに使えそう。

  • envsubst
    他とは違いk8sとは関係ないgettext付属のツールで、シンプルなテンプレート展開コマンド。
    対象ファイルに埋め込んだ環境変数を展開してくれる。

最終的に、k8s標準のマニフェスト構文が利用でき、比較的自由度も高く、公式コミュニティ製であることなどの理由からkustomizeを選択しました。
今のところ使用感には満足しており、社内でk8sマニフェストを管理するツールとしてはファーストチョイスと考えています。

まとめ

記事に書ききれない紆余曲折も有ったりするのですが、アプリケーションコードには一切手を加えずに済み「案外スムーズに移行できた」というのが本音です。
移行してみて、デプロイまでの時間短縮と、アプリ周辺のインフラ設定の記述量が大幅に削減できたのは実感として感じています。
運用面や自動化の改善の余地はまだまだ大きいと思うので、来年も引き続き改善していかねばと思っています。

Comments are closed, but you can leave a trackback: Trackback URL.