本番のサーバーにログインして設定ファイルを 1 行直す作業は、手軽で、その場ではきちんと効きます。
ただ、同じことを何台ものサーバーで何年も続けると、どのサーバーに何が入っているのかを誰も説明できなくなっていきます。
イミュータブルインフラストラクチャは、稼働中のサーバーには手を入れず、変更のたびに新しいサーバーを作って古いものと置き換える考え方です。
作り直す側に振ると何が楽になり、代わりに何が必要になるのかが分かると、コンテナやブルーグリーンデプロイの形にも説明がつきます。
直し続けたサーバーはスノーフレークになる
手で直し続けたサーバーの行き着く先を、Martin Fowler 氏は 2012 年に書いた bliki の記事で「スノーフレークサーバー」と呼びました。
雪の結晶のように、同じものが二つとない本番サーバーという意味です。
Fowler 氏によると、こうしたサーバーは同じ役割のサーバーをもう 1 台立てるのも、検証用に本番の複製を作るのも難しくなります。
中身はすぐに分かりにくく手を入れにくいものになり、ソフトウェアを一部上げただけで思わぬところに影響が出るとも書いていました。
詳しくは SnowflakeServer(martinfowler.com) で読めます。
こうなる原因は、記録されないまま入った変更で設定が少しずつずれていく構成ドリフトです。
構成ドリフトをコードの管理で減らす考え方は、IaC 入門の記事で整理しました。
稼働中のサーバーをその場で直すやり方には、もう一つ弱点があります。
HashiCorp の共同創業者 Armon Dadgar 氏が挙げているのは、アップグレードが途中で一部だけ失敗したときの問題です。
その場で新しい版へ上げていると、失敗した時点で、どの版とも言えない状態のサーバーが残ります。
氏の例では、Web サーバーの新しい版は入ったのに、NGINX のインストールだけが失敗していました。
こうしてできるのは、誰もテストしていない「バージョン 1.56」のようなサーバーです。
解説の動画と書き起こしは What is Mutable vs. Immutable Infrastructure?(HashiCorp) にあります。
イミュータブルは変更を新しいイメージで持ち込む
この考え方の出発点としてよく挙げられるのが、Chad Fowler 氏が 2013 年 6 月に書いたエッセイです。
氏は関数型言語の不変なデータ構造になぞらえ、アップグレードが必要なら新しいシステムを作って古い方を捨てるよう勧めていました。
エッセイの題は Trash Your Servers and Burn Your Code(Chad Fowler) です。
同じ月には、martinfowler.com に Kief Morris 氏の「ImmutableServer」も載りました。
Morris 氏はイミュータブルサーバーを、一度デプロイしたら手を加えず、新しいインスタンスに置き換えるだけのサーバーと定義しています。
変更はまずベースになるイメージに入れ、テストしてから新しいインスタンスとして展開する流れです。
Chef や Puppet のような構成管理ツールで、動いているサーバーを仕様に合わせ続ける運用にも触れています。
実際にはサーバーの構成を管理しきることはできず、構成ドリフトの余地が大きく残るという指摘でした。
原文は ImmutableServer(martinfowler.com) です。
AWS の Well-Architected フレームワークも、信頼性の柱のベストプラクティス REL08-BP04 でこの考え方を取り上げています。
そこでの定義は、本番のワークロードに更新・セキュリティパッチ・設定変更をその場で当てないというものです。
変更が必要になったら新しい一式を今の環境と並べて立て、自動で検証できたら少しずつトラフィックを移すのが望ましい姿とされています。
同じページが挙げている利点は、次のとおりです。
- 既知の構成から作り直すので、構成ドリフトが減る
- デプロイが「全部成功する」か「何も変わらない」かのどちらかになる
- 前の版に手を入れていないので、問題が出たらそこへ戻せる
- サーバーを直接いじらないので、SSH などのリモートアクセスを閉じられる
このページは REL08-BP04 Deploy using immutable infrastructure(AWS) で読めます。
二つのやり方を並べると、違いは次のようになります。
- 変更の入れ方 … ミュータブルは稼働中のサーバーに直接当てる/イミュータブルは新しいイメージから作ったサーバーと入れ替える
- 途中で失敗したとき … 中途半端な状態が残る/新しい方を捨ててやり直す
- 戻し方 … 逆向きの変更を当てる/前のイメージから作り直す
- 前提になるもの … サーバーに入れる権限と手順/イメージを作って検証する仕組み
置き換えの単位はイメージ
イミュータブルにするには、サーバーの中身をまるごと固めたイメージを毎回作れることが前提になります。
仮想マシンの場合、OS・ミドルウェア・アプリの設定まで済ませて標準化したマシンイメージは、ゴールデンイメージと呼ばれます。
作る道具の代表が HashiCorp の Packer です。
公式ドキュメントは Packer を、1 つの設定から複数のプラットフォーム向けに同一のマシンイメージを作るツールと説明しています。
説明の元は Introduction to Packer(HashiCorp Developer) です。
公式チュートリアルの AWS 向けテンプレートを縮めると、Ubuntu のイメージに Redis を入れて AMI を作る設定は次のような形になります。
# packer ブロック(プラグインの指定)は省略
source "amazon-ebs" "ubuntu" {
ami_name = "learn-packer-linux-aws-redis"
instance_type = "t2.micro"
region = "us-west-2"
# source_ami_filter(元にする公式 Ubuntu イメージの条件)は省略
ssh_username = "ubuntu"
}
build {
name = "learn-packer"
sources = ["source.amazon-ebs.ubuntu"]
provisioner "shell" {
inline = ["sudo apt-get update", "sudo apt-get install -y redis-server"]
}
}
amazon-ebs ビルダーは、元の AMI から作業用の EC2 インスタンスを立てて手順を流し、そのマシンから新しい AMI を作ります。
AWS のホワイトペーパーは、アプリや依存物を起動のたびに入れることをブートストラップ、AMI にあらかじめ焼き込むことをプリベイクと呼び分けています。
複雑なアプリや大きなダウンロードを起動時にこなすと、デプロイやスケールアウトが遅くなるというのが、プリベイクを勧める理由でした。
一方で両者は組み合わせることもでき、焼き込んだイメージに、環境ごとに違う部分だけを起動時に足す使い方も紹介されています。
呼び分けの原文は Prebaking vs. bootstrapping AMIs(AWS Whitepaper) です。
できた AMI を本番へ届ける仕組みも、AWS には用意されています。
起動テンプレートの AMI を新しいものに差し替え、Auto Scaling グループでインスタンスリフレッシュを始めます。
すると、既存のインスタンスが新しいイメージのものへ順に入れ替わっていきます。
既定のローリング戦略は、インスタンスを何台かずつ終了しては起動する方式です。
minimum healthy percentage を 100% にすれば、先に新しい方を起動してから古い方を終了させられます。
手順の全体は How an instance refresh works(Amazon EC2 Auto Scaling) のページです。
コンテナでも考え方は同じです。
Docker のドキュメントは、イメージは不変で、一度作ると変更できないと明記しています。
直したいときは新しいイメージを作り、コンテナごと入れ替えることになります。
置き換えるときに新旧をどう並べて切り替えるかは、ブルーグリーン・ローリング・カナリアといったデプロイ戦略の話です。
REL08-BP04 も、イミュータブルな環境ではカナリアかブルーグリーンで出すよう勧めていました。
それぞれの違いと使い分けは、デプロイ戦略の記事で扱っています。
状態はサーバーの外に置いておく
作り直す前提に立つと、サーバーのローカルディスクに書いたものは、置き換えと一緒に消えます。
Dadgar 氏も、イミュータブルの代償として、アプリがローカルディスクに大事なデータを書いている場合を挙げていました。
Morris 氏の ImmutableServer も、価値のあるデータはインスタンスの外へ逃がすことを前提にしています。
逃がし先として挙がっていたのは、ログを集める中央の syslog サーバー、共有ファイルシステム、EBS のようなストレージボリューム、データベースなどです。
アプリの作りについては、The Twelve-Factor App が同じことを求めています。
プロセスは状態を持たず互いに何も共有しないものとし、残す必要のあるデータはデータベースなどのバックエンドサービスに置く、という原則です。
メモリやディスクへのキャッシュは 1 回の処理の中だけで使い、次のリクエストでも残っているとは期待しません。
同じ利用者を同じプロセスへ送り続けるスティッキーセッションも、この原則に反するとされています。
原則の本文は VI. Processes(The Twelve-Factor App) です。
もう一つの前提は、いつ止めても、いつ起こしてもよいプロセスであることでした。
Twelve-Factor App はこれを disposability と呼び、次の 3 つを求めています。
- 起動を数秒に抑える
- SIGTERM を受けたら、処理を片付けてから止まる
- 突然落ちても壊れない
こちらは IX. Disposability(The Twelve-Factor App) に書かれています。
置き換えのたびに古いインスタンスは止められるので、静かに止まれて素早く起きるアプリほど、入れ替えを気軽に回せます。
インスタンスごとに違う設定は数も範囲も小さくしておくよう、Morris 氏も勧めていました。
取り入れるときの落とし穴
イミュータブルにすると、変更の通り道が「イメージを作って置き換える」の一本になります。
その一本が細いと、次のようなところで無理が出てきます。
小さな変更にもビルドの時間がかかる
設定を 1 行変えるだけでも、イメージのビルド、検証、入れ替えを一通り流すことになります。
ここが遅いと、急いでいる人ほどサーバーに直接入って直したくなり、スノーフレークへ逆戻りします。
緊急のパッチも同じ経路で流す
REL08-BP04 は、ソフトウェアの更新だけでなくセキュリティパッチも、新しい一式で入れる対象に含めています。
脆弱性への緊急対応だけを例外にして手で当てると、その時点から本番とイメージがずれ始めます。
緊急時にも使えるくらい、ふだんからパイプラインを速く保っておくことが対策です。
イメージは作った時点の OS やライブラリを固めているので、アプリに変更がなくても定期的に作り直さないと古いまま残ります。
AWS の EC2 Image Builder は、元の AMI やセキュリティの更新があったとき、または決めた周期で新しいイメージを作るよう設定できます。
作り直しのきっかけの説明は、EC2 Image Builder FAQs(AWS) の回答にありました。
イメージが大きくなりすぎる
何でもイメージに焼き込むと、ビルドにも転送にも時間がかかるようになります。
先ほどのプリベイクとブートストラップの組み合わせのように、変わりにくいものは焼き込み、環境ごとに変わるものは起動時に渡す分け方が効いてきます。
シークレットをイメージに入れない
マシンイメージにもコンテナイメージにも、シークレットを埋め込むのはリスクだと HashiCorp の Well-Architected Framework は明記しています。
イメージは複数の環境やチームで使い回されるので、焼き込んだパスワードや API キーは、そのイメージを取得できる人の手元にまで渡ります。
同じページは、Vault のような保管庫から実行時に取り出す形を勧めていました。
ページは Create immutable infrastructure(HashiCorp Developer) です。
最後に
イミュータブルインフラストラクチャは、変更をイメージに入れ、サーバーは直さずに作り直して置き換えるという考え方でした。
手で直したサーバーが一台ずつ違っていくスノーフレーク化を、作り直しの繰り返しで止めるのが狙いです。
その代わりに、状態をサーバーの外へ出すことと、イメージを速く作って検証できるパイプラインを持つことが前提になります。
私も DevOps を学んでいる途中なので、次はイメージの中身を決める側として、構成管理ツールと Dockerfile の書き方を調べてみます。
以上です。

















コメントを残す