Gyazo と GKE
2022-05-17 (Tue) 19:55 スタート予定 (15m)
準備
Gyazo のメンテや開発をやってます
おもにバックエンド関連 (Rails, golang, etc...)やたまに
DevOps
今日は DevOps のお話
Gyazo と GKE
deploy
branch staging
Gyazo と GKE
GCP とか Kubernetes (k8s) が何かはなんとなくわかっている前提で話をします
staging cluster では branch staging と呼ばれているものが動いている
他にもイントラ系 kibana, grafana などが動いている GKE cluster もある...
なぜ GKE (Kubernetes)?
Gyazo の初期のころは素朴に
レンタルサーバー的なホスティングのマシン上で動いていた
直近では GCE の instance template から instance group 作成
docker for mac が登場して開発者のローカル環境は docker-compose に
rbenv や nvm が必要無いので楽
deploy 先も コンテナ化 or Full managed な PaaS に移行して楽をしたい
ruby 以外にもフロントエンドのビルドには node.js のツールが必要
コンテナにすると最終 image は node.js なしで済む
サーバーホスト管理したくない...
アップロードや画像配信のアプリケーションは分かれていてやや複雑
メインは Rails だけど画像バイナリを扱うところは golang
heroku みたいなシンプルな PasS では難しい
アプリケーションコードをほぼそのまま移行するにはその複雑さを再現できる Kubernetes がちょうどよかった
k8s cluster 内 network でお互いにアクセス
最近は Serverless VPC Access で Cloud Run とかから GCP のネットワークに接続とかできるらしいけど...
pod に複数のコンテナ (nginx, ruby server, fluentd, etc...)
一つのコンテナに突っ込むのは辛そう...
Gyazo の GKE cluster はどんな感じに運用されているのか
production 環境への入り口を制限したい
GCP 外部の CI から deploy するためには service account key などが必要になる
各CIに self-hosted runner とかあるけど...
Cloudbuild の step としては
普通に docker image をビルドして
kubectl apply
で service, deployment などのリソースを更新
deploy で工夫してるところだと...
manifest.yamlkind: Deployment
spec:
template:
metadata:
annotations:
kubernetes.io/change-cause: "${date} TAG=${IMAGE_TAG}"
slack で rollback 方法を通知
構成変更するのに yaml のパッチみたいなの書くのですが冗長だし自由度が低い...
単純なところは bash の heredoc 使ったほうが簡単
シンプルな yaml, json 汎用管理ツールみたいなのに切り替えたい...
branch staging はどういうしくみなのか?
URL ルーティング
scale to zero
branch staging へのリクエスト
Google HTTP Load balancer の backend service として
staging-proxy
という
nginx コンテナの service を指定
nginx.conf で --g
で終わるサブドメインは upstream .default.svc.cluster.local
に流れるように...
e.g. my-branch--g.gyazo.example
-> my-branch--g.default.svc.cluster.local
そうすると cluster network の service my-branch--g
にリクエストが流れる...
branch staging の scale to zero
HPA は pod を 0 にできない
どんどんブランチができると GKE cluster の CPU/memory resources を圧迫
手動で kubectl delete deployment my-branch--g
とかキビシイ
HPA は 0 から復活することもできない
ブランチの数が増えたときに挙動のデバッグが大変であきらめた
今見たら僕の helm3 対応 commit が最後で archive になってる...
すごいw

knative serving (Cloud Run はこれで動いている) でも scale to zero できるみたいだけど...
1 container/ 1 pod という制限があったり (このへん変わるみたいだけど...)
やりたいことは環境によって微妙に違うはず...
k8s API 自体は REST なので難しくない
試しに ruby で実装してみるか... -> branch-manager.rb
branch-manager.rb がやってること
上記 staging-proxy の nginx.conf で --g の svc に upstream するときに branch-manager にもリクエストを mirror
該当する service の deployment の pod が 0 だったら kubectl scale deployment/my-branch--g --replicas=1
相当の API を叩く
各 service の最終アクセス日時を記録しておいて、一定時間アクセスが無い service は scale --replicas=0
基本はコレだけ
branch-manager.rb の実装
100行ぐらいの ruby script
Gemfilegem "sinatra"
gem 'rest-client'
gem 'rufus-scheduler'
gem 'tzinfo-data' # Internally required by rufus-scheduler
gem 'dalli'
rbac で適切な権限を与えていれば以下のようなリクエストヘッダ設定で API アクセスできる
rubydef resource_client
if ENV['KUBECTL_PROXY']
Thread.current[:resource_client] ||= RestClient::Resource.new(
ENV['KUBECTL_PROXY'],
headers: {
'Content-Type' => 'application/strategic-merge-patch+json'
}
)
else
token = File.read('/var/run/secrets/kubernetes.io/serviceaccount/token')
Thread.current[:resource_client] ||= RestClient::Resource.new(
'https://kubernetes.default.svc',
ssl_ca_file: '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt',
headers: {
'Authorization' => "Bearer #{token}",
'Content-Type' => 'application/strategic-merge-patch+json'
}
)
end
end
たとえばこんな感じ
rubyres = resource_client["/api/v1/namespaces/default/pods"].get(params: {labelSelector: 'branch-manager'})
json = JSON.parse(res.body)
API を調べるには kubectl -v8
ローカルでデバッグするときは
shkubectl --cluster=gyazo-staging proxy
outStarting to serve on 127.0.0.1:8001
おわり
こんな感じに Nota では DevOps 系のタスクもひっそりとやってます
インフラ/セキュリティエンジニア募集してます
最初は helpfeel の DevOps を助けて欲しいけど、長期的にはサービス共通基盤みたいなのができるとかっこいいと思ってる