sorta kinda...

主にAWS関連ですが、これに限らずいろいろ勉強したことや思ったことを書いていきます。

システム監視の目的は障害を検知すること、だけではない

敗北しない設計に憧れます、那須です。

システム監視って皆さんされてますか? 障害発生したらピューン!ってメールとかが飛んでくるアレですね。 障害検知したらドキュメント見て初期対応したり関係各所に連絡入れたりして運用担当者は大忙しです。 それが 1,2 件とかならまだいいんですよ。 問題はそれが一気にやってきたり、運用手順がよくわからなかったりして、運用に支障をきたす場面ってありますよね。 本気で運用業務の負荷を下げたい。 その一心で考えてみたことを書き残します。

 

システム監視導入の目的とは?

ここでは、1 秒でも早く障害状態から正常状態もしくは縮退運転状態にしてサービスに影響がない状態にすること、と定義しましょう。 障害発生したら運用担当者が気づけること、ではないと私は思っています。 気づいてどうするの?って視点がないと運用で苦しむことになりますから。 なので、アラートが飛んできてから正常状態になるまでの時間をいかに短くできるかが大切だと思います。

その目的を達成するために何をしないといけないのか考えてみましょう。

 

アラート内容の工夫

アラートの内容ってカスタマイズできるサービスが今は一般的かなと思います。 Datadog でも Mackerel でも Zabbix でもできますよね。 で、何も考えずにアラートを設定すると、まあこうなります。

mm/dd hh:ss、○○で××が発生しました。
しきい値:100
検知した値:200

的な。 いや、わかるんですよ。 いつ、どこで、何が起こっているのかがわかるんで十分って声もわかります。

でもね、一度ここでさっき確認したシステム監視の目的に立ち返ってみましょう。

1 秒でも早く障害状態から正常状態もしくは縮退運転状態にしてサービスに影響がない状態にすること

さっきのアラートが自分のところに飛んできたとして、1 秒でも早く対応しないといけない。 そんな状況でこのアラートだけが飛んできたら。。。 目的達成できますか? もっと事前にできることあるんじゃないでしょうか?

あくまで案ですが、アラートの情報には、発生日時や対象、障害内容以外にもこんな情報を付け加えましょう。

  • 障害対応手順
  • 運用ドキュメントへのリンク
  • 復旧目標時間

Slack への通知例だとこんな感じですね。

f:id:nasrinjp1:20190531113513p:plain

アラートの中に対応手順が書かれていれば、ドキュメントを探す時間がなくなります。 手順がものすごく長いとかなら、そのドキュメントへのリンクをアラートに書くだけでも手順実施までの時間が短くなります。 あとは、運用契約で目標復旧時間がある場合は、障害発生からいつまでに復旧させる必要があるのかまで書きましょう。 これがあれば、状況や運用担当のスキル等を基に障害発生してから○分経過しても状況が変わってなければエスカレーションするってフローが作りやすくなりますよね。

手順等をアラートの中に書くって運用にすれば、監視設計している時にそれぞれの監視項目での一次対応を決めることになります。 一次対応が書けないなら、そもそもそれを監視する意味ってあるの?って気づきも得ることができますね。 不要なアラートを飛ばすのはやめましょう。 運用の負荷が上がるだけですから。

このような工夫をすることで、場合によっては復旧までの時間が大幅に短縮されることもあります。 アラートはただの通知ではありません。 よくできたアラートは、よくできたガイドにもなり得ます。

 

障害対応の工夫

アラートで手順を書くようにすれば、一次対応はすぐに着手することができるようになります。 でもその一次手順、絶対に人間がやらないといけないですか? 自動化できる余地があるなら自動化しましょう。

たとえば、サーバで何かのメトリクスの異常を検知したとしましょう。 サーバでコマンド叩くのもいいですが、調査系のコマンドであれば障害発生をトリガーに自動実行してその結果もアラートに続いて送るようにしましょう。 AWS 等のクラウドなら案外簡単にできますよね? 復旧するためのコマンドも叩けるなら一緒に自動実行するようにすれば、人間が関与することなく復旧まで持っていくことができます。

調査や復旧を自動化すれば、かなり時間短縮になりますし、人間のオペレーションによるオペミスの発生確率もグンと下げることもできますね。 でも自動化したものが必ず想定した動きをするとは限りません。 なので、アラートにはどんな場合でも対応手順は書くようにしましょう。

 

最後に

この記事を書くきっかけとなったツイートがあります。

設計で敗北すると最終的に運用でも敗北するんですよね。 お互い疲弊するだけですので、最初から「運用でカバー」ありきの設計はやめましょう。

のらから@運用屋さん (@norakara512) | Twitter さん、HATANO Hirokazu (@tcsh) | Twitter さん、いつもありがとうございます。

最小限の手間で最新の AMI から EC2 インスタンスを起動したい

めんどくさいことは嫌いですがめんどくさい運用はもっと嫌いです、那須です。

AWS ではあまりないと思いますが、なんらかの事情で最新の AMI から EC2 インスタンスを起動しないといけない場合ってありますよね? オペレーションミスで必要なデータやファイルを削除してしまったり上書きしてしまったり。 ハードウェア障害等でかなりの確率で EC2 インスタンスを stop/start すれば復旧するのになぜか復旧できなかったり。 いろんなことが起こりえます。

その時に、最新の AMI ID ってどれだっけ? って探してる余裕があればいいですが、まあそんな精神的余裕はありません。 手順化されていて落ち着いて作業できる人はいいですが、私はまあ無理ですね。

で、どれが最新の AMI なのかをその時に探すよりは、事前に「これが現時点での最新の AMI ID やで!」ってすぐに分かるようにしておけばみんなハッピーになるんじゃないかと思って、その仕組みを考えてみました。

 

前提条件

AMI 作成は、AWS 標準のものではなくてサーバーワークスさんから提供されている Cloud Automator を使います(サーバーワークスさんいつもありがたく使わせていただいてます

 

全体的な流れ

ざっくり書くとこんな感じです。 AMI 作成の直後にその AMI ID をパラメータストアに入れれば 1 と 2 は同じコードに書けたりするんですが、Cloud Automator で AMI 作成対象をタグで制御してるので、どんくさいかもしれませんがこんな感じで分けました。

  1. Cloud Automator で定期的に AMI を作成する
  2. 特定の EC2 インスタンスの最新 AMI ID を Systems Manager のパラメータストアに入れる
  3. AMI ID をパラメータストアから取ってきて EC2 インスタンスを作成する CloudFormation テンプレートを準備する
  4. (いざという時に) CloudFormation テンプレートからスタック作成して EC2 インスタンスを作成(復旧)する

f:id:nasrinjp1:20190527232150p:plain

今回は上記の 2 と 3 の部分にフォーカスして書いていきますね。

 

最新 AMI ID を Systems Manager のパラメータストアに入れる

とりあえず入れるだけなので簡単です。 Cloud Automator がつけてくれるタグを基に、対象の EC2 インスタンスを指定してその最新 AMI ID を取得してパラメータストアに入れましょう。

この例では、Cloud Automator Source Instance Name タグの値が「ほんばんき」になっている AMI 一覧から作成日が一番新しい AMI ID を取得します。 その後、/prod/ami-id パラメータとしてさきほど取ってきた AMI ID を上書きして書き込みます。

これらの動きを Lambda で実装しましょう。

from logging import getLogger, INFO
import boto3
from operator import itemgetter

logger = getLogger()
logger.setLevel(INFO)

def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    response = ec2_client.describe_images(
        Filters=[
            {
                'Name': 'tag:Cloud Automator Source Instance Name',
                'Values': [
                    'ほんばんき',
                ]
            },
        ],
        Owners=[
            'self'
        ]
    )
    image_details = sorted(response['Images'], key=itemgetter('CreationDate'), reverse=True)
    ami_id = image_details[0]['ImageId']
    logger.info(f'The Latest AMI ID is {ami_id}.')

    ssm_client = boto3.client('ssm')
    response = ssm_client.put_parameter(
        Name='/prod/ami-id',
        Value=ami_id,
        Type='String',
        Overwrite=True
    )
    logger.info('The Latest AMI ID is put to parameter store successfully.')

 

最新 AMI ID が入ったパラメータから値を取ってきて CloudFormation テンプレートで使う

↓このドキュメントの最後の方にある「AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> タイプ」の項目を参考に適当に作っています。

docs.aws.amazon.com

実際はパラメータを定義して柔軟に対応できるようにするべきですが、テンプレートが長くなるのでいい感じに読み替えてください。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  AMIID:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /prod/ami-id

Resources:
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: EC2Role
      ImageId: !Ref AMIID
      InstanceType: m5.large
      KeyName: honyalara
      SecurityGroupIds: 
        - sg-xxxxxxxxxxxxxxxxx
      SubnetId: subnet-xxxxxxxxxxxxxxxxx
      Tags:
      - Key: Name
        Value: リストアインスタンス

これでスタックを作成すると最新の AMI から EC2 インスタンスが作成されますよ。 ここまでの準備作業はそれなりにかかりますが、運用開始後の突発的な作業項目はかなり減らすことができますね。 何かあっても EC2 のコンソールは見なくてもよくて、CloudFormation からスタック作成するだけです。

 

運用が楽になるように設計しよう

運用は手順書を揃えたり自動化したりしますが、どうせ作るならオペミスが起こりにくい構造にしたいですよね。 今回は AMI にフォーカス当てましたが、他にもいろんな検討ポイントがあると思います。 AWS のサービスをうまく使って楽にかつオペミスが少なくなるような運用を心がけましょう。