JapanTaxiアプリが利用するサーバのインフラを昨年Kubernetesに切り替えました。Kubernetesにより苦労のない安定的な運用方法を紹介したいと思います。
JapanTaxiでは様々な言語(Ruby, Scala, Go…)や様々なクラウドサービス(AWS, Microsoft Azure, GCP…)を利用しています。去年、JapanTaxiアプリが使っているサーバのインフラをリニューアルしたタイミングにインフラやアプリケーションの管理を統一させるためKubernetesを導入しました。
Kubernetesを選択した理由の一部は下記のようになります。
Kubernetesは割と複雑な技術ですし使い方が複数あって混乱しやすいです。今回はJapanTaxiならではのやり方を紹介したいと思います。
Kubernetes (k8s)はコンテナ化されたアプリケーションの管理、デプロイ、スケーリングを自動的に行うために設計されたオープンソースのプラットフォームです。
Kubernetesのマネージドサービスを利用して必要なマシンスペックとホスト(いわゆるノード)の数を指定することだけでKubernetesのクラスターを立ち上げることができます。クラスターを自分で作るために様々なツールが存在しているためOn-Premiseでも使えます。メインのクラウドサービスはKubernetesを対応していてあるクラウドサービスとの依存がなくなるのもいいところです:Amazon Elastic Container Service for Kubernetes (Amazon EKS)、Azure Kubernetes Service (AKS)、 Google Kubernetes Engine (GKE)。
予め作成されたKubernetesクラスターでアプリケーションを動かすことはとても簡単です。kubectlと言うKubernetesのCLIは使えます。
例えば下記のコマンドだけでNGINXを起動することはできます。
kubectl run my-application --image=nginx:1.14
NGINXにリクエストを送信するため、コンテナにアクセスできるようにしましょう。
kubectl expose deployment my-application --name=my-application --port 80
クラスターの中から、http://my-application/ にリクエストを送るとNGINXまで届きます!
アプリケーションをスケールさせるため、レプリカをもう一つ追加しましょう。
kubectl scale deployment/my-application --replicas=2
Nginxの新しいバージョンをデプロイしましょう。
kubectl set image deployment.v1.apps/my-application my-application=nginx:1.15
Kubernetesを最初に使う時に上記のように kubectlを使ってアプリケーションを起動しても良いですが、Kubernetesを本格的に使おうとすると、クラスターの設定もアプリケーションの設定もSVCで管理する必要があります。
クラスターの設定に関してはTerraformで簡単に管理ができます。AWSもAzureもGCPもサポートしています。
アプリケーションのKubernetes設定の管理方法は複数あります。一番シンプルなやり方はKubernetesのYAMLファイルをそのまま書いてgitで管理する。上記のNGINXの例ですと:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-application
labels:
app: my-application
spec:
replicas: 1
selector:
matchLabels:
app: my-application
template:
metadata:
labels:
app: my-application
spec:
containers:
- name: nginx
image: nginx:1.14
ports:
- containerPort: 80
# service.yaml
kind: Service
apiVersion: v1
metadata:
name: my-application
spec:
selector:
app: my-application
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
deployment.yamlとservice.yamlをgitで管理すると、ファイルを編集してkubectl applyコマンドを実行することでNGINXを起動したりスケールさせたりすることができます。それに、kubectl applyをCIから実行するようにすると、Gitにプッシュすることだけでアプリケーションの管理ができます。
# NGINXを起動する
kubectl apply -f deployment.yaml
# アクセスできるようにする
kubectl apply -f service.yaml
# レプリカを追加する
# deployment.yamlを開いて、`replicas: 1`を`replicas: 2`に変更する
kubectl apply -f deployment.yaml
# NGINXの新しいバージョンをデプロイする
# deployment.yamlを開いて、`image: nginx:1.14`を`image: nginx:1.15`に変更する
kubectl apply -f deployment.yaml
KubernetesのYAMLファイルをそのまま書くことはとてもシンプルで使いやすいですが柔軟性は足りていないです。複数のクラスターや複数の環境(開発、ステージング、本番)を管理するには、共通な設定を用意した上で環境別の設定を追加したいことが多いと思います。その場合、設定ファイルをテンプレート化する必要がありますがKubernetesはまだそれを対応していないです。一方、サービスが進化していくと関係性の近いアプリケーションを同時にデプロイしたり、デプロイする前に特定な処理を実行したりすることがありますがKubernetesではそういう処理のタイミングはまだコントロールできないです。
そう言う問題を解決するためHelmというツールが使えます!Kubernetesのパッケージマネージャーと呼ばれているHelmでは様々なテンプレート化されたYAMLファイルを一つのパッケージにまとめて、GitHubやAWS S3などに保存してからクラスターにデプロイできます。とても複雑なアプリケーションでも簡単にデプロイやロールバックできます。
例えば、先ほどのNGINXデプロイメントは環境によってCURRENT_ENVIRONMENT環境変数の値をセットしたい場合、下記のように書けます:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-application
labels:
app: my-application
spec:
replicas: 1
selector:
matchLabels:
app: my-application
template:
metadata:
labels:
app: my-application
spec:
containers:
- name: nginx
image: nginx:1.14
ports:
- containerPort: 80
env:
- name: CURRENT_ENVIRONMENT
value: {{ .Values.environment | quote }}
# development-values.yaml
environment: "development"
# production-values.yaml
environment: "production"
開発環境にデプロイするには:
helm upgrade -i my-application . -f development-values.yaml
本番環境にデプロイするには:
helm upgrade -i my-application . -f production-values.yaml
Helmではhookの仕組みもあって、デプロイする前またはデプロイしてから特定な処理を実行することができます。データベースのバックアップや秘密情報の取得などに使えて便利な仕組みです。
Kubernetesでアプリケーションを管理する3つのやり方をまとめますと:
JapanTaxiではHelmを使うことにしましたがそれでもいくつかの課題がありました。
アプリケーションと関係なく必ず必要な機能があります。アプリケーション毎に設定することは悩ましいです。
そういった問題を解決するためJapanTaxiのSREチームはJapanTaxi Kubernetes Ecosystemというプラットフォームを用意しました。
JapanTaxi Kubernetes Ecosystem (JKE)はJapanTaxiのSREチームが開発したKubernetesでアプリケーションを簡単にデプロイしたり管理したりするための共通なインフラ基盤です。
インフラやアプリケーションの管理は下記の流れで行います:
SREチームはKubernetesクラスター共通な機能を開発してHelmのパッケージ(いわゆるHelmチャート)として提供している。その機能の一部:
各チームのインフラ担当は自分が担当しているクラスターの設定YAMLファイルを変更してansibleで適用している。作業自体はシンプルですし、全てのクラスターの管理方法は統一しているため知識は共有しやすいです。
JKEを使うために必要とされているプログラムや設定方法などはSREチームが提供したdockerイメージに含まれていて、docker-composeとAWS/Azure/GCPのアカウントさえあればアプリケーションへの接続やデプロイなどができます。それに、クラウドサービスと関係なく同様に作業ができるため知識は共有しやすいです。
一方、アプリケーションの基本設定はSREチームが提供しているHelmチャートにパッケージ化されていて、アプリケーションエンジニアが書かないといけない設定はとても簡単になります。
先ほどのNGINXの例ですと:
# values.yaml
application:
name: my-application
# SREチームが提供した基本設定が入っているベースチャート
baseChart:
name: "application"
version: 0.1.0
image:
repository: nginx:1.14
# コンテナの定義
containers:
nginx:
enabled: true
ports:
- name: http
containerPort: 80
protocol: TCP
env:
CURRENT_ENVIRONMENT: "development"
上記の設定だけで、KubernetesのDeployment(ローリングアップデートデプロイ、コンテナの自動再起動)、Service(アクセス)、HorizontalPodAutoscaler(オートスケール)、Secret(秘密情報)、ConfigMap(一般ファイル)、Ingress(ルーティング設定)、PodDisruptionBudgetなどが作成されます。アプリケーションエンジニアはKubernetesの設定を理解しなくても上記のような単純なYAMLファイルで全て設定できます。
JapanTaxiがKubernetesを導入するとともに開発したJapanTaxi Kubernetes Ecosystem (JKE)というプラットフォームを紹介しました。
JapanTaxiならではの運用のやり方であって、管理方法の統一とアプリケーションのセットアップの簡単化を強調しました。
これからもJKEに機能を追加してより安定しているサービスを提供していきたいと思います。
Mobility Technologies では共に日本のモビリティを進化させていくエンジニアを募集しています。話を聞いてみたいという方は、是非 募集ページ からご相談ください!