cloudwatch 統合エージェントから cloudwatch logs にログ転送する際に `InvalidSequenceTokenException`

cloudwatch logs にエージェントからログ転送する際に以下のような Exception が発生するケースがあります。 せっかく support に問い合わせて聞いたのでログに残しておく。

2020-07-06T09:48:26Z W! cloudwatchlogs: InvalidSequenceTokenException, will search the next token and retry the request: The given sequenceToken is invalid. The next expected sequenceToken is: 49607253157850794113694120142050130960625703845569297394, original error: "InvalidSequenceTokenException: The given sequenceToken is invalid. The next expected sequenceToken is: 49607253157850794113694120142050130960625703845569297394\n{\n  ...., {
  RespMetadata: {
    StatusCode: 400,
    RequestID: "f9c433f6-cf15-4dc3-b75d-eca8067f4fac"
  },
  ExpectedSequenceToken: "49607253157850794113694120142050130960625703845569297394",
  Message_: "The given sequenceToken is invalid. The next expected sequenceToken is: 49607253157850794113694120142050130960625703845569297394"
}

aws 公式ドキュメントを見ると、以下のようなケースで発生するらしい

  • 単一のログストリームへ複数ログ(ファイル名が別々なログ)を転送したケース

複数のログファイルからログを単一のログストリームにプッシュすることはできません。設定を更新して、各ログをログストリーム - ロググループの組み合わせにプッシュします

InvalidSequenceTokenException は、ワイルドカードでファイルパスを指定したケースでも該当することがあります。公式では以下のような挙動をすると記述されております

CloudWatch Logs にプッシュするログファイルを指定します。 ファイルは、特定のファイルまたは複数のファイルを指すことができます(/var/log/system.log* のようにワイルドカードを使用)。 ファイルの変更時間に基づいて、最新のファイルのみが CloudWatch Logs にプッシュされます。 access_log.2014-06-01-01 と access_log.2014-06-01-02 など同じ形式の一連のファイルを指定するにはワイルドカードの使用をお勧めします。 ただし、access_log_80 と access_log_443 のように複数の種類のファイルには使用しないでください。 複数の種類のファイルを指定するには、設定ファイルに別のストリームログのエントリを追加して、各種類のログファイルが異なるログストリームに行くようにします。 圧縮ファイルはサポートされていません。

例えば、以下のような設定がされているとして、この /var/log/messages がログローテートされて /var/log/messages-20200710 にローテートされるとします。

/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/default

    "logs": {
        "logs_collected": {
            "files": {
                "collect_list": [
                    {
                        "file_path": "/var/log/messages",
                        "log_group_name": "/var/log/messages.*",
                        "log_stream_name": "{local_hostname}"
                    }
                ]
            }
        }
    }

cloudwatchエージェントは最新の書き込みがあるログファイルを参照するので、/var/log/messages を参照して、ログ転送を継続しようとしますが、 ローテートされた古い方の /var/log/messages-20200710 にも書き込みがあると、InvalidSequenceTokenException が発生する

ローテートされた時点で、/var/log/messages と /var/log/messages-20200710 を別ファイルとして、認識しているからなのかも

Terraformで一度作成したAWSリソースを後からコードをモジュール化して再度適用したらdestory&createされるよ

最近はAWSリソース構築にterraformを使っているのですが、一からコードを書くのは面倒なのでterraformingを使ってコード生成しております。(他にterraformerとかもあったりしますね)

github.com

terraformingは便利なツールですが、万能ではなく、機械的に抽出されるためかそのままだとコードは冗長なものになりがちです。 私の普段見ているシステムのように作成するリソース数が多いとファイルの行数がトンデモないことになるんじゃないかなと思います。

terraformingで抽出したコードを、そのまま適用してしまってあとで悲しみに暮れている私と同じ運命を辿らないように、terraformingで抽出したコードはしっかりモジュール化を検討した上で適用することをおすすめします(マジで)

流派的にはterraformの機能であるWorkspacesを使ってコード量を少なくするという案もありますが、今回はモジュール化でやってます

それでは本題ですが、あとからterraformコードをモジュール化しようとするとどうなるのかっていうところを実際にやってみました(地獄)

環境

  • Terraform v0.12.0
  • provider.aws v2.12.0

前提

  • terraform実行環境は構築済み
  • cloudwatch agentがインストールされているec2構築済み

検証作業

今回はcloudwatch alarmを作成するケースで試してみる

ディレクトリ構成はこんな感じ

# tree
.
├── cloudwatch
│    ├── backend.tf
│    ├── main.tf
│    ├── provider.tf
│    └── versions.tf
├── module
│   └── cloudwatch_alarm
│       ├── java_process.tf
│       └── variables.tf

まずは愚直にモジュール使ってないmain.tfファイルを作成(内容はjava process監視)

resource "aws_cloudwatch_metric_alarm" "java-process-down" {
    alarm_name          = "java-process-down"
    comparison_operator = "LessThanThreshold"
    evaluation_periods  = "1"
    metric_name         = "procstat_lookup_pid_count"
    namespace           = "CWAgent"
    period              = "60"
    statistic           = "Average"
    threshold           = "1.0"
    alarm_description   = ""
    ok_actions          = ["arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"]
    alarm_actions       = ["arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"]
    dimensions = {
        exe = "java"
        InstanceId = "i-XXXXXXXXXXXXXXX"
        ImageId = "ami-XXXXXXXXXXXXXXXXX"
        pid_finder = "native"
        InstanceType = "t2.micro"
    }
}

terraform apply

# tf apply --auto-approve
aws_cloudwatch_metric_alarm.java-process-down: Creating...
aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

これでcloudwatch alarmが作成されましたね。 続いて先ほど作成したmain.tfをモジュール呼び出すようにして同じ内容になるように修正します

module "cloudwatch_alarm_java_process-down" {
    source = "../module/cloudwatch_alarm/"
    instance_id = "i-XXXXXXXXXXXXXXX"
    image_id = "ami-XXXXXXXXXXXXXXXXX"
    instance_type = "t2.micro"
    slack_notice = "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"
}

モジュールの呼び出し先であるjava_process.tfも作成する

resource "aws_cloudwatch_metric_alarm" "java-process-down" {
    alarm_name          = "java-process-down"
    comparison_operator = "LessThanThreshold"
    evaluation_periods  = "1"
    metric_name         = "procstat_lookup_pid_count"
    namespace           = "CWAgent"
    period              = "60"
    statistic           = "Average"
    threshold           = "1.0"
    alarm_description   = ""
    ok_actions          = ["${var.slack_notice}"]
    alarm_actions       = ["${var.slack_notice}"]
    dimensions = {
        exe = "java"
        InstanceId = "${var.instance_id}"
        ImageId = "${var.image_id}"
        pid_finder = "native"
        InstanceType = "${var.instance_type}"
    }
}

モジュールをインストールするために terraform init してから、再び terraform apply

# tf apply --auto-approve
aws_cloudwatch_metric_alarm.java-process-down: Refreshing state... [id=java-process-down]
aws_cloudwatch_metric_alarm.java-process-down: Destroying... [id=java-process-down]
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creating...
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]
aws_cloudwatch_metric_alarm.java-process-down: Destruction complete after 0s

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

コマンドラインでは Apply complete! と表示されてましたが、マネジメントコンソールから確認してみると作成されていない...

f:id:namio6243:20200619235928p:plain

もう一度 terraform apply

# tf apply --auto-approve
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Refreshing state... [id=java-process-down]
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creating...
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

今度は作成されました。 terraform を使ってると割とよくあるんですけど、コマンドライン上は成功してもAWS上では作成されない事象。再度applyすると作成はされるが、何が原因かはよく分かっておらず...

とりあえず、一度作成したリソースをtfファイルをモジュールを利用するように修正すると、リソース自体が再作成されるので、本番環境だと慎重にやる必要がある

あと、おまけで試してみたけど main.tfファイルを単純に分割するのはリソース再作成にはならない模様(tfファイルが冗長になったら分割したくなるので)

まとめ

  • Terraform は、一度作成したリソースを後からコードをモジュール化(処理の共通化)をして再適用するとリソース再作成(destroy&create)になるので本番環境では注意してやること
  • terraform apply がコマンドライン上は成功してもAWS上には作成されないケースがある(私の環境のせい...?)、その場合は再度 terraform apply すればOKみたい
  • tfファイル分割するのは問題なく可能(リソース再作成はされない)

自身の近況とかリモートワークとか転職の話

皆さま、こんばんわ

コロナのおかげで暇になりすぎて、久々に記事を書きます。

一年半振りの投稿ですが、このブログのアクセス履歴見たら、ちょぼちょぼアクセスあって少し驚いております。見てる人いたのか...

今回は、自身の近況とかリモートとか転職について書いておこうかと思います。ただの駄文ですので、他の方の為になるかはアヤシイです。 空き時間の暇つぶしに見てやろうという寛大な心をお持ちのお方向けの記事になります笑

もし、参考になる部分があれば何かの参考にしてやってくださいmm

近況

  • 前提とか近況をザザッと書く
  • 自社Webサービス企業勤務(Not大手。中堅くらい)
  • 最近はオンプレ->クラウド移行を担当
  • 開発しているWebサービスがコロナ影響を受けやすいもので、売上に甚大な被害
  • 全社的に休業状態(全体の8割くらい休業?国から助成金が出る)
  • 一部エンジニアはフルリモート勤務(3月末あたりから)
  • 週5出勤ではないので給料が少し減る
  • エンジニア以外の職種は結構出社しているらしい
  • はんこ出社はそれなりにあるみたい
  • コロナの蔓延地帯である新宿怖くて飲み行ってない

リモートワーク

  • エンジニアは基本的にフルリモート勤務
  • 私のチームはフルリモートになっても特に支障なく行えている
  • 元々リモートワークの制度がある+運用をやっているので慣れている
  • コミュニケーションを綿密にとって細かく管理したい人だとフルリモートはキツイと思う
  • チームは、Hangout で毎日30分程度のチームミーティング
  • 近くにいない影響で、雑談などのコミュニケーションは減った気がする。代わりにミーティング時間を少し長めにとって雑談するようにした
  • 以下利用ツール群
  • コミュニケーション: Slack, Hangout
  • タスク管理: Jira,Redmine
  • Wiki: Confluence

転職

  • 現職にJoinして、そろそろ4年なので今後どうするか考えている(エンジニアとしては歴10年以上)
  • 現職はある程度は仕事の裁量あって評価もされているが、効率よりも上層部への見え方を重視するド昭和な企業文化に辟易してきている
  • すぐに転職したい訳ではなく、するなら1年か2年後に転職しようかと思っている。そうなった時の準備だけはしておこうと思う
  • どうやら世間的にもコロナ影響で時間ができて自身のキャリアを見つめ直している人多いみたい
  • コロナが終わったら、みんな一斉に転職しだすのだろうか...
  • 現職は自分ごときが上位の評価をもらえてしまう環境で、社内に優秀なエンジニアが少ない。転職先は優秀な人が多い環境だといいなぁ
  • とりあえずビズリーチ登録してみた。それなりに求人あるけど、豊富ってほどじゃない印象。基本はエージェントに探してもらうスタイルなのかな
  • 昔は会社が嫌になったら転職してーなーとか割とすぐ考えていたけど、最近は先のキャリアとか考えながら転職プランを考えるようになった
  • そうじゃない企業もあるとは思うけど、基本的にシステムインテグレータというビジネスモデルが自分の性に合わないと思っている(昔たくさん経験したし...)
  • ユーザーに価値を届けること、もしくは自社のビジネスに貢献するための仕事に集中できる環境があるといい。役員にゴマ擦って評価上げるとか無理
  • 儲かってない企業には転職しない(そもそも会社が儲かってないと社員の給料が上がらない)
  • ある程度は将来性のありそうな業界に絞ろうかと考えている。検討中。
  • スマホゲームとかSNSなどの流行りのサービスをやっている企業にはあまり関心がない
  • 技術とマネジメントが50:50くらいのポジションだと自身の強みが活かせる気がしている
  • 給料は少しくらい上げたい
  • 仕事は裁量がある方が望ましい。指示されたことをひたすらやり続けるとかだとキツイ...
  • TOEIC受けた事ないから、これから受験してみようかと思ってる