Upload
-
View
777
Download
1
Embed Size (px)
Citation preview
No SSHKMC関東例会 at Cybozu
野島 裕輔 (@nojima)
自己紹介
@nojima (Yusuke Nojima)
blog: nojima.hatenablog.com
github: github.com/nojima
twitter: twitter.com/nojima
Cybozu でインフラエンジニアをやっています。
元競プロ勢。
インフラの自動化について話します
そもそも何で自動化しないといけないのか?
前提:
サービスが成長していく
サービスが成長していくと…
• 使用するメモリ量、ディスクIO、CPU使用量などが
増加していく。
• そして、いつかサーバーの物理的な限界に到達する。
スケーリング
• スケールアップ
• より強いサーバーを投入することで、より多くの計算資源を確保する。
• 一定の性能を超えると、性能あたりの値段が指数関数的に増大する。
• スケールアウト
• サーバーを大量に並べることで、必要な計算資源を確保する。
• 値段は全体の性能に比例する。
• 大量のサーバーを並べることになるので、管理が大変
サーバー管理
• サーバーの投入のために、サーバーの役割に応じて
パッケージのインストールや設定の配置を行う。
• サービスのリリースのために、APサーバーの
ローリングアップデートを行う。
• 脆弱性対応のために、インストールされたパッケージを
更新する。
• などなど
サーバー職人の朝は早い
• 感覚的には、サーバーが10台ぐらいなら手動でも管理できる。
• しかし、サーバーが1000台になると、手動では絶対に無理。
• したがって、スケールアウトには自動化が必須。
どうやって自動化する?
ナイーブなやり方:
1. 手動での手順を確立する。
2. (Optional) 手順を手順書に起こす。
3. 手順をシェルスクリプトやPythonで書く。
DB
例1: AP のローリングアップデート
• アプリケーションの新しいバージョンをリリースしたい。
• アプリケーションは次のような3層構成になっているとする。
nginx
nginx
AP
AP
AP
User
# AP サーバをローリングアップデートする
for ap in ${AP_SERVERS}; do
# ロードバランシング対象から外す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} sudo detach-from-load-balancer ${ap}
done
# APサーバの更新
cat ${ARTIFACT_TGZ} | ssh ${ap} sudo tar -z -x -C /
ssh ${ap} sudo systemctl restart ap-service
# ロードバランシング対象に戻す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} sudo attach-to-load-balancer ${ap}
done
done
例2: ロードバランサの更新
• 脆弱性対応とかでロードバランサに入っている
パッケージを更新したいとする。
• Ubuntu の場合は apt dist-upgrade した後にサーバーを
再起動する手順になる。
# ロードバランサの更新を行う# (本当は L4LB からの切り離しを行うべきだけど煩雑なので省略)
for nginx in ${NGINX_SERVERS}; do
# パッケージの更新
ssh ${nginx} sudo apt update
ssh ${nginx} sudo apt dist-upgrade -y
# 再起動
ssh ${nginx} sudo reboot
(${nginx} が起動するまで待つ)
done
この調子で自動化していけば
いくらでもスケールアウトできる?
残念ながら
No
例: APとLBの並列更新
オペレータAがAPをローリングアップデートしている最中に、
別のオペレータBがロードバランサを更新しようとしたとする。
# AP をローリングアップデートする
for ap in ${AP_SERVERS}; do
# ロードバランシング対象から外す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} ...
done
# APサーバの更新
cat ${ARTIFACT} | ssh ${ap} ...
ssh ${ap} sudo systemctl ...
# ロードバランシング対象に戻す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} ...
done
done
# ロードバランサの更新を行う
for nginx in ${NGINX_SERVERS}; do
# パッケージの更新
ssh ${nginx} sudo apt update
ssh ${nginx} sudo apt dist-upgrade
# 再起動
ssh ${nginx} sudo reboot
(${nginx} が起動するまで待つ)
done
単体では問題なく実施できるオペレーションでも
並列に実行すると壊れる場合がある
どうすればいい?
• 同時に実行しないように人が注意する
⇐ チームの規模が大きくなってくると無理
• 2つのスクリプトは「ロードバランサ」という共有リソースを
操作していた。
• 同時に実行してはいけない操作が競合し、問題となった。
• じゃあ排他すればいいじゃん!!
(
flock -x 9
# AP サーバをローリングアップデートする
for ap in ${AP_SERVERS}; do
# ロードバランシング対象から外す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} sudo ...
done
# APサーバの更新
cat ${ARTIFACT_TGZ} | ssh ${ap} ...
ssh ${ap} sudo ...
# ロードバランシング対象に戻す
for nginx in ${NGINX_SERVERS}; do
ssh ${nginx} sudo ...
done
done
) 9> /var/lock/nginx-operation
(
flock -x 9
# ロードバランサの更新を行う
for nginx in ${NGINX_SERVERS}; do
# パッケージの更新
ssh ${nginx} sudo apt update
ssh ${nginx} sudo apt dist-upgrade
# 再起動
ssh ${nginx} sudo reboot
(${nginx} が起動するまで待つ)
done
) 9> /var/lock/nginx-operation
( flock -x 9 ... ) 9> /var/lock/nginx-operation って何?
( ... )
サブシェル。
別のプロセスを起動して、その中で括弧の中のコマンドを実行する。
9> filename
リダイレクト。
この場合、filename を書き込みモードで open し、9番の FD に割り当てる。
flock -x 9
9番の FD を使って排他ロックを行う。
ロックはファイルが閉じられるまで保持される。
• これで、AP の更新スクリプトとロードバランサの
更新スクリプトが衝突する系を救えた。
• しかし、ロードバランサに対して行う操作は他にもある。
• static コンテンツのアップロード
• nginx 設定ファイルの更新
• SSL証明書の更新
• …
• 全部調停しないといけない
調停による依存関係の爆発
Operation5
nginx
nginx
Operation 1
Operation4
Operation3
Operation2
どうすればよかったのか?
• 各スクリプト同士を直接調停させようとすると、
依存関係により破綻する。
• よって、サービスを提供する側(この場合は nginx)で
調停すべき。
やりたいことのイメージ
Operation5
nginx
nginx
Operation 1
Operation4
Operation3
Operation2
nginxoperatorservice
ここで調停
しかし、現在のシェルスクリプトには、
調停者を差し込む余地がない。
# ロードバランサの更新を行う# (本当は L4LB からの切り離しを行うべきだけど煩雑なので省略)
for nginx in ${NGINX_SERVERS}; do
# パッケージの更新
ssh ${nginx} sudo apt update
ssh ${nginx} sudo apt dist-upgrade -y
# 再起動
ssh ${nginx} sudo reboot
(${nginx} が起動するまで待つ)
done
どうしようもない
No SSH
# 再起動
ssh ${nginx} sudo reboot
そもそも、ここがダメなのでは?
ssh はカプセル化を破壊する
• ssh するとリモートサーバー上のあらゆるファイルを読み書きできる。
• ssh するとリモートサーバー上のあらゆるデーモンを起動・停止できる。
• ssh するとリモートサーバー上のあらゆる状態を取得できる。
ssh は OOP において private 変数を直接読み書きしているようなもの!!
ssh を許可すると、
そのサーバーのあらゆる実装上の詳細を
クライアントに露呈させることになる
ssh をやめて、インターフェイスで会話しよう
• ssh によるオペレーション自動化を禁止。
• 代わりに、サービスを管理するためのエージェントを作る。
• エージェントの API が、そのサービスの公開インターフェイスとなる。
• オペレーションスクリプトは、公開されたインターフェイスを
叩くだけにする。
インターフェイスをどう設計するか
• 実装上の詳細ができるだけ露呈しないようにする。
• そのサービスを構成するサーバーのリスト、
サーバーのファイルシステムのレイアウト、
そのサービスの実装が利用している外部サービスの仕様
などをクライアントが意識しなくてもよいようにする。
• クライアント同士で調停しなくてもいいようにする。
• 共有リソースの操作はサービス側で適切に排他して行う。
nginx の例だと…
• 「APの切り離し」や「ロードバランサの更新」などが
公開 API になる。
• API を提供するエージェントは Virtual IP などを使って
真の IP アドレスを隠蔽しておく。
• オペレーションの並列度を制御するために、queue + worker
方式で nginx に対するオペレーションを行う。
• 並列度の制御方法はいろいろあるので、別の方法でもいい。
こんな感じ
nginx
nginx
Operation 1
Operation2
nginxoperatorservice
公開インターフェイスでオペレーションを依頼
queue worker
なんかいい感じにやる
Virtual IP でサービスを提供
まとめ
ssh に頼らず
インターフェイスでオペレーションしよう!!