あれ、本番環境のKubernetes Podがいなくなっちゃったよ
はじめに
とある日、本番環境のKubernetes Pod数が0になってしまう事態が発生しました。この記事はそのストーリーを語っています。
Kubernetes運用の現状
私たちのプロダクトはリリースから10周年を迎えており、溜まった技術負債を脱却すべく、インフラとアプリケーションのリプレイス中です。
- インフラはオンプレミスからAWSクラウド(コンピューティングはEKS)
- アプリケーションはVBScript+jQueryからRails+Next.js
リプレイスはまさに過渡期であります。段階的に移行しているため、日に日に、Kubernetes Podを増やしていました。
chapter1. アラートは突然に
そんなある日、「502エラーめっちゃ出てるくね!? てか、リリース時と被ってんじゃん」
この原因はすぐに分かりました。私たちのチームでは、デプロイ戦略としてローリングアップデートを採用しています。Deploymentのデフォルト設定では、起動中のPod数の25%ごと入れ替わるため、仮に50台で運用しているとしたら 13台のPodが一気に減少してしまいます。
なんと!これはいかん。常時起動しているPod数が増加していくにつれて、ローリングアップデート時に減少するPodも増えてしまいます。
spec:
replicas: 50
selector:
matchLabels:
app: rails
strategy:
type: RollingUpdate
そこで、maxSurge: 100%
、maxUnavailable: 0%
とすることで解決しました。つまり、replicasで指定したPod数が減少することなく、Pod数を一気に倍に増加させることで一時的にリソースは倍になるが安全にリリースできるようになります。
spec:
replicas: 50
selector:
matchLabels:
app: rails
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 100%
maxUnavailable: 0%
これで新しいPodが完全に立ち上がるまで古いPodたちは残ってくれる...
chapter2. HPAが悪さをする
安心したのもつかの間、またリリース時に502エラーが出る現象が発生してしまいました。直したのはずなのに...。よく見るとまたPod数が減っている。なぜ?
結論から言うと、これはHPAが関係ありました。
Deploymentでは、replicas: 50
と設定し、HPAでは以下のマニフェストのように設定していました。リリースのタイミングはちょうどピークタイムであったこともあり、HPAによってreplicas
が上書きされ、Pod数が70まで上昇していました。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: rails
namespace: rails
spec:
minReplicas: 30
maxReplicas: 70
...
この状態では、リリースによって以下の挙動になってしまいます。
- リリース前に70個のPodが起動
- リリースによって、HPAでスケールした分の20個のPodが削除され、50個のPodでローリングアップデートが開始
- Pod数の減少により負荷が高まり、HPAによってスケールアウトし元に戻る
スケールアウトした分も含めてローリングアップデートしたいですよね。そこで調べてみると、どうやら「Deploymentのreplicasフィールドを削除して、HPAのみにreplicasをコントロールさせると良い」という情報が。
chapter3. 俺はreplicasの存在を消したい
俺はreplicasの存在を消したい。そのことに必死でした。そして、replicas: 50
を消し、Pull Requestを作成しました。
spec:
selector:
matchLabels:
app: rails
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 100%
maxUnavailable: 0
よし、Approveされた。いざ、staging、productionに適用するぜよ。
chapter4. さらば愛するPodたち
言うことはありません。タイトルの通りです。一応、念の為リリースの様子をArgo CDダッシュボードで確認していましたが、愛するPodたちはあっという間にkillされてしまいました。
Podは1個残りましたが、今度はそのPodにアクセスが集中したためヘルスチェックに引っかかりDownしてしまいました。
真っ赤に染まるアラートと、鳴り響くPargerDuty
chapter5. なぜ愛するPodたちは出ていったの
そもそも、kubectl applyコマンドは「適用するマニフェスト」と「前回適用したマニフェスト」を比較して、その差分を変更として適用します。
適用前のマニフェストの状態は、last-applied-configurationです。last-applied-configurationにreplicas: 50
が残っていると、replicasフィールドを削除しただけではデフォルトの1が適用されてしまい。Podが1個になってしまいます。
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{... spec":{"replicas":50,"selector":{"matchLabels": { ...
chapter6. どうすれば愛するPodは出ていかないの
事前にlast-applied-configurationからreplicasフィールドを削除しておけば良いです。そうすることで、「適用するマニフェスト」と「前回適用したマニフェスト」にreplicasの設定がないので、マニフェスト適用時に変更されなくなります。
おわりに
ふざけたタイトルを付けていますが、私たちインフラエンジニアは常に危険と隣り合わせです。作業の影響範囲を知り、動作確認をきちんと行い、失敗してもロールバックの準備をしておかなければなりません。それらを怠ると取り返しのつかないことになってしまいます。僕のようにならないように気をつけて仕事しましょう。
Discussion