K8sデビューしてから4日で小さな本番プロダクトをリリースした話

JX通信社Advent Calendar 2019」24日目の記事です.

昨日は@kimihiro_nさんによる, 「Immutable Python ~ NamedTuple で書く副作用のないプログラム」の話でした.

こんにちは. JX通信社でシニア・エンジニア&データ基盤エンジニアおよび野球*1をしています, @shinyorke(しんよーく)と申します.

JX通信社では, データ駆動での意思決定および施策実施をより円滑に進めるため, データ基盤の構築・運用を進めています.

その過程でいろんなFrameworkやサービスを試して...の一つの成果として以前こんな話をしました.*2

tech.jxpress.net

このエントリーではもうちょっとプラットフォーム・基盤に寄り添ったテーマで,

同僚氏に「K8sいいぞ!」と布教活動を受けた結果, 4日で本番運用までこぎつけた話

をしたいと思います.

TL;DR

  • はじめて使うものは「急がば回れ」で基礎から学ぶことが良さそう
  • kubectlが楽すぎてGCRとかの認証がうっかり気が付かなくてハマることもあるので気をつけよう.
  • 欲張らず小さい構成で試しながらやるのオススメ, いきなりガッツリ作らなくてもいい.

あらすじ - 私のK8sスキル

かなりざっくりいうと,

  • 前提としてDockerは使いこなしてる. 何があっても独力で解決できるレベル.
  • クラウドインフラとかの知識・経験も同様.
  • なお, K8sは過去2回挑戦して2回放置挫折.

といったレベルです(ちなみに本職はデータ基盤エンジニアメインのなんでも屋ですが, インフラは専門ってわけでもない*3).

なお, Dockerは自分のブログで何度かチャレンジしてそこそこヒットする記事も出してるのですが,

  • K8sはHello world的に動かしたりしたしその場では動いてるの確認してる
  • だがしかし, 用語とか混乱するしわからん
  • というのと, ローカルだったらdocker-compose, STG/PRDならシンプルにAWS ECSとかGAEで良いのでは説*4

という思いもあり, 正直K8sにチャレンジするモチベはあまりありませんでした.*5

そう, 社内で布教されるまでは.

K8sデビューしてから4日で本番リリースするまで

というわけでこっからいろんな記録(Slackから社内ブログまで)をサルベージして4日でやった記録を述べます.

やりたかったこと・やったこと

その前にやりたかったことを絵にしました.

※ブログ用に書き起こした絵図です(ので色々端折ってます)

f:id:shinyorke:20191208181114p:plain
やったことの絵

やりたいことは一言で言うと「ETL*6の"L"にあたるバッチプロセスを作りたい」でした.

  • GCS(Google Cloud Storage), 上図の「App Logs」に溜まったログファイル(hourlyでダンプされる, json.tgz)をBigQuery「Datasets」にimport
  • GCS -> BigQuery ImportをPythonのGCS + BigQueryのスクリプトとして開発*7*8
  • 上記の「オレオレBigQueryスクリプト」をhourly実行. 上図の「Backend」の部分.

と思いつき, 絵にしたら実にシンプルだったので早速やることにしました.

なお, 環境はお絵かきの通りGCPと決まっており, GCS(App Logs)とBigQuery(Datasets)はすでに存在しており, 残すBackendを

  • 普通のDocker Containerとして動かす前提で開発. hourly実行はcronとかで上手くやる
  • ちょっと調べたらK8sで行けそう?別件で作ってたWorkflowもCloud Composer*9だしせっかくだったら揃えたい

という2択になり, チームのSlackチャンネルでつぶやいた所,

K8s布教マン「それK8sのJobで行けるのでは?」

というレスが来たので, 速攻でK8sでやる場合の手順などを検証することにしました.

1日目 - K8sに慣れ親しむ

というわけで初日はK8sに入門(通算3回目)をすることにしました.

題材は, おすすめされたこちらの書籍を使いました(会社の書棚にあった).

gihyo.jp

こちらを読みながら色々お試ししていたのですが, こちらの絵を見てすべてを解釈しました.

f:id:shinyorke:20191223235459p:plain
【図】K8sと実際の環境の差(参考書籍を元に描いた)

その時自分が思わず自分のSlackに一言

K8sの仕組みがあんまわかってなかったもやもやが午前中で吹き飛んだ

ローカル環境(Macbook Pro上のDocker Desktopでクラスタ作ってやった)上での移植および, 通常のJobを用いた単体での動作確認が初日でアッサリ終わりました.

2日目・3日目

2日目・3日目は,

  • Backendの動作環境である, GKE上でクラスタを立ち上げる
  • その前に, GCRにApplicationのImageをPushする
  • STG確認して問題なかったら本番!

に注力しました.

今回は新規プロダクト・新規開発かつ, 「ログをBigQueryに放り込むバッチ」という小さいプロダクトだったのでSTG上手く行ったら即本番は既定路線でした.

が, ここで一つトラブルが.

ワイ「GCRからpullできないンゴ...」

dockerコマンドを用いてpush/pullは通るのですが, kubectl上でコマンドを叩いてもSTG(GKE)でpullに失敗してJobが起動しない...orz

自分でも調べつつ, 周りの手も借りたのですが中々いい感じに行かなかったのですが見つけたこちらのサイトに助けられました.

blog.container-solutions.com

要約すると,

  • GCR用のSecretを定義しないとあかんよ
  • 使うべきSecret指定してね!

っていうことでした.

念の為書くとこんな感じです.

GCR用のSecret作成

$ kubectl create namespace local-dev    # namespace作る
# 鍵を保管するSecretを作る
$ kubectl create secret docker-registry gcr-key \
 --docker-server=https://gcr.io \
 --docker-username=_json_key \
 --docker-email=hoge@example.com \
--docker-password="$(cat ../credentinal-hoge.json)"  # 予めcredential.jsonを作っておく

Secretの指定.

# imagePullSecretsに鍵とそのパスを指定
$ kubectl get serviceaccount default -o yaml                   
apiVersion: v1
imagePullSecrets:
- name: gcr-key
kind: ServiceAccount
metadata:
  creationTimestamp: "2019-11-12T01:12:19Z"
  name: default
  namespace: local-dev
  resourceVersion: "38601"
  selfLink: /api/v1/namespaces/local-dev/serviceaccounts/default
  uid: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
secrets:
- name: default-token-hogehoge
- name: gcr-key

Patchを当てて使えるようになったらおしまい.

$ kubectl patch serviceaccount default \    
-p '{"imagePullSecrets": [{"name": "gcr-key"}]}'
serviceaccount/default patched
$ kubectl config set-context --namespace=local-dev docker-desktop

これで無事にクラスタもでき, STG確認もうまくいきました.

また, バッチ化に伴い今までJobで動かしていたものをCronJobに移行しました(これは瞬殺で終わった).

ここでハマった知見・ノウハウを社内ブログを書いたのを含め, ここまで3日使いました.

4日目 - 本番リリース

こうしてすべての課題がクリアし, K8sデビューしてから丸4日で本番にリリースができました.

リリース自体はSecretおよびConfigの向け先を変更(環境変数を変えた), 祈るようにモニタリングしてたぐらいなので楽でした.

運用から一ヶ月ちょっと, サービスのログを1時間に一度, 割と多い量を転送していますが,

  • (社内で広く使われている)Sentryの警告が鳴らない限り放置でOK*10
  • 予防線として, Slackでの通知も入れている(流量が極端に多い・少ないときにに備えて)
  • バッチの再実行・時刻変更およびbugfixのリリースなどのときもkubectlでチョチョイと終わる

などかなり快適な感じになりました.

今後やりたいこと&結び

というわけで, 3度目の挑戦にして成功したK8s入門*11および, 4日で実戦投入の話はこれにておしまいです.

今回の勝因は色々ありますが,

  • 急がば回れ, で基礎から攻めた結果の爆速キャッチアップとリリースにつながった.のと, K8s推進派のたっち(TatchNicolas)さんが技術的にも気持ち的にも色々サポートしてくれたのに感謝.
  • 自分自身が構想していたサービスが小さくかつシンプルであり, 導入に関して障壁が少なかった*12
  • 既存プロダクトのリプレースではなく, ゼロイチでの新規立ち上げだったのでやり方についてある程度自由度があった*13

といった所が大きかったです.

とはいえいくつか課題・やりたいこともあり,

  • CronJobで動かしてるが全体最適って意味ではCloud Composer(Airflow)のWorkflow管理にしたい. K8sクラスタをAirflowにKickさせる感じ.
  • 今回のK8sバッチがオレオレBatchとして風化しないように似たようなバックエンドをK8sに寄せるのと, ちゃんとCIもやりたい...*14
  • 何よりも, マイクロサービスが多い弊社でK8sが最適な場面は多そうなのでそこにContributeしていく(もしくは周りがContributeするような楽しい感じにする)

というのが今後のアクションかなと思っていますし, そんなK8sの良さを伝えてくれたりサポートしてくれた たっち(TatchNicolas)さんの用にいい感じにK8sを使いこなせるようになりたいなと思いました, この記事とかあの記事とか.

JX通信社Advent Calendar 2019」のクローザーはつよつよエンジニア統括な@YAMITZKYさんです.

皆さん良いメリクリを.

*1:元々野球のデータを扱うお仕事をしていました&Pythonを使った野球データサイエンスは18日の記事(類似性スコア)でやりました. 余談ですがプレーする方は全くもって苦手です.

*2:PySpark MLlibはきっといつかやります(小声)

*3:ですがベアメタルからサーバー一台作って納品程度の仕事は過去にしてました.

*4:過去に経験した技術スタックって意味で

*5:前にいたチームにもK8s普及マンがいてついてやっていい感じになったこともあるけど結局仕事で使えずぐぬぬってのがありました.

*6:Extract Transform Loadの略で, 要するに「データを取得して加工して吐き出す」バッチのこと. 今回はEとTの部分は別に作っていました.

*7:やってることは公式のBigQueryおよびGCSクライアントを繋いでPython3.7でいい感じに開発したスクリプトです. 実装は割愛.

*8:今思えばembulkでも良かった説.

*9:データ基盤のWorkflowとして活用, これは作った人にブログ書いてほしいかもなお気持ち.

*10:Pythonスクリプトの内部にimportして使ってます. Sentryが何者かを知りたい方は公式サイトを御覧ください.Pythonクライアントはこれ.

*11:もう入門することはないやろってぐらい普段着的に使おうなってお気持ちはあります.

*12:これは私見ですが, K8s = 大規模構成なサービスに合ってる, と誤解してましたが小さくやるのはまあまあアリだと思いました. とはいえ使い所難しいのもあるので技術選定でK8s選ぶ時は気をつけたほうが良さげ.

*13:本題からちょっと外れますが最悪K8s使わずにGAEとかで動かしても良かったわけで.っていう逃げ道も考えてました.要するに選択肢・余白は重要です.

*14:リリース頻度が多くない, ローカルからshell叩いておしまいとはいえCIしときたいなと.