この記事は Akatsuki Advent Calendar 2020 4日目の記事です。
前回の記事は tomotaka-yamasaki さんの 開発エンジニアとQAの心理の違いとは何か? でした。
はじめに
私の所属するチームでは日々、安全な運用を継続しつつ運用業務改善を行っています。
今日はその中で AWS CloudFormation(以下Cfn) において、 kumogata2 を使った運用をご紹介します。
AWS CloudFormation とは
詳しくは AWS の公式ドキュメントをご確認ください。
Infrastructure as Code(以下IaC) を使用した、クラウドプロビジョニングサービスです。
スタックという単位で管理されており、スタックごと下記のようなyaml形式のテンプレートを作成して、 AWS リソースをコードで管理することができます。
1つのスタックで、VPC、AutoScaling、EC2の構成など複数サービスの依存関係を含めて表現することができます。
AWSTemplateFormatVersion: '2010-09-09'
Description: Test Instance
Resources:
TestInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-abcdexxxxxx
InstanceType: t3.micro
Tags:
- Key: Name
Value: test
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: gp2
VolumeSize: '32'
例)Cfn で EC2 インスタンスを作成する
メリット
- 環境を複製できる
- インフラの変更をレビューすることができる
- ログを残して変更経緯を追うことができる
- 手動によるオペレーションミス・漏れがなくなる
何年も運用していくと検証・開発環境への要望も変わってくることもありますが、そういった要望にも柔軟に対応することができます。
また、作成・削除を簡単に行えるため、テンポラリな環境を作成することにも役立ちます。
デメリット
強いてあげるとしたら
- 変更に手間が増える
がありますが Cfn 管理していても一時的な変更であれば手動で変更して確認できたら元に戻せば良いので、あまりデメリットとは言えないかもしれません
kumogata2 とは
Cfn のスタックを作成することができるツールです。
特徴としては
- Rubyでかける
- Cfn スタックの作成・更新・削除を CLI で完結させられる
- dry-run で依存関係を含めて更新箇所を確認することができる
でしょうか。
dry-run で依存関係を含めて更新箇所を確認することができる
依存関係がわかることによって、
「インスタンスのボリュームだけを変更するつもりが、インスタンスごと入れ替わってしまった」
といったことを事前に回避できます。
また、私のプロジェクトではkumogata2-plugin-rubyも合わせて使っています。
使い方
TestInstance do
Type "AWS::EC2::Instance"
Properties do
ImageId 'ami-abcdexxxxxx'
InstanceType 't3.micro'
Tags [
_{
Key "Name"
Value "test"
}
]
BlockDeviceMappings [
_{
DeviceName "/dev/xvda"
Ebs do
VolumeType "gp2"
VolumeSize '32'
end
}
]
end
end
新規作成
このように書くことで最初に紹介した test.yml
の形式に kumogata2 が変換してスタックを作成してくれます。
$ kumogata2 create test.rb test
を実行することによって、 Cfn 上に test
というスタックが作成され、インスタンスが立ち上がります。
更新
また、スタックのVolumeSize
を 32 → 64に変更を行いたい際は
dry-runを実行することによって、更新する際に変更される差分のみ確認できます。
$ kumogata2 dry-run test.rb test
[
{
"Action": "Modify",
"LogicalResourceId": "TestInstance",
"PhysicalResourceId": "ami-abcdexxxxxx",
"ResourceType": "AWS::EC2::Instance",
"Details": [
{
"attribute": "Properties",
"name": "BlockDeviceMappings"
}
]
}
]
困っていたこと
BlockDeviceMappings
に変更が入るのみ
ということまでは確認できていたのですが、このままでは本当に 32 → 64 という変更が行われているのかがわかりません。
プロジェクトでは複数人が Cfn の管理を行っているので、適用時にBlockDeviceMappings
にはVolumeSize
以外のパラメータもあるので、不要なものまで適用されてしまうかもしれません。
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: gp2
VolumeSize: '32'
そのため私のプロジェクトでは、
$ kumogata2 convert test.rb
でローカルで更新したいテンプレートフォーマットを作成したものと、
Cfn上のテンプレートを取得して、
変更差分がVolumeSize
のみであることを確認してから適用していました。
diffのコマンドがほしい
さすがに毎回確認をするのが大変だったので、diffコマンドを探しました。
Kumogata2 Usageにコマンド自体はあったのですが、そのまま適用するとパースでエラーになってしまったり、必要以上に機能があったのでプロジェクトに合わせて変更を加えました。
今回の要件としては、スタックの更新する際の差分が確認したい。
だったので
https://github.com/kumogata/kumogata2/blob/master/lib/kumogata2/client.rb
にオーバーライドする形で以下を追記しました。
また、Lambda や CloudWatch で日本語を使っていたのですが、 CloudFormation API がマルチバイトに対応していなかったため、diffを取る前に API の結果と同じになるようにコンバートしています。
def export_template(stack_name)
stack = get_resource.stack(stack_name)
stack.stack_status
stack.client.get_template(stack_name: stack_name).template_body
end
def diff(path_or_url, stack_name)
remote_stack = export_template(stack_name)
local_stack = convert(path_or_url)
# 現状CloudFormation APIがマルチバイト文字を?に変換してしまうため、同様の変換をかけてからdiffをとる
File.write("/tmp/remote_stack.txt", remote_stack.gsub(/[^\x00-\x7f]/, '?'))
File.write("/tmp/local_stack.txt", local_stack.gsub(/[^\x00-\x7f]/, '?'))
system("diff -u /tmp/remote_stack.txt /tmp/local_stack.txt")
end
結果
$ kumogata2 diff test.rb test
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: gp2
- VolumeSize: '32'
+ VolumeSize: '64'
これでVolumeSize
のみに差分があることを確認してから適用できるようになりました。
さいごに
今回差分を出せるようにしたことによって
- 差分を見る手間が減った
- 誰が差分確認しても同じ確認をできるようになった
- 自動化/コマンド化されていない手動の運用は作業者によって手順が異なる可能性があります。
- 差分を確認したつもりが手順を間違えていたため、きちんとした差分確認ができず不要な対応を適用してしまうことがなくなります。
が達成できました。
少人数で運用を回しつつ新しい挑戦をするにはこういった小さな改善が必要不可欠なので今後も続けていきたいと思います!