docker log driverでfluentdからelasticsearchにparseしたログを送るのに苦労したお話

f:id:hige_dev:20210702141632p:plain

dockerのログをfluentdに送って、parseしてelasticsearchとkibanaで可視化したい、とおもったけど苦戦した結果断念したので残しとく。 構成は以下のような感じを想定。

f:id:hige_dev:20210702073714p:plain

EKF(Elasticsearch, Kibana, Fluentd)をdocker-composeで起動し、Nginxのログドライバーにfluentdを設定し起動。

docker run -d -p 80:80 --log-driver=fluentd \
    --log-opt fluentd-address=localhost:24224 \
    --log-opt tag=dokcer.{{.Name}} \
    nginx:latest

このとき、fluent.confでparseしたかったけど、 sourceディレクティブで @type forward ではparseできなかった。けど、 @type tail だとどこに吐かれたログをtailするのか、というのも迷った。

  1. dockerホストにログを出して、それをtailする
  2. Nginxのログディレクトリをvolumeマウントして、そのvolumeをfluentdコンテナにもマウントしてtailする
  3. Nginxコンテナ内にfluentd(sender)を起動して、fluentdコンテナ(aggregator) に送る

・・・どれもしっくりこない。 1、2は同一ホストでの起動前提?な気がするし、3は規模が大きければ良さそうだけど、1コンテナ1プロセスと考えるとありなのかな、、とか用意されたlog driverを利用しないのはなんだかなあ、という感じだし。 (浅い思考がばれてしまう)

で、document見てたら、filterディレクティブでparseできることを知った。

docs.fluentd.org

これに倣って設定してみたけど、Nginxのログがparseされない。

<filter *.*>
  @type parser
  key_name log
  <parse>
    @type nginx
  </parse>
</filter>

いつもお世話になってるfluentularでもparseできてない。(生ログはparseできる) fluentular.herokuapp.com

ログを調べてみると、parserプラグインは生ログを対象にしてるけど、今回のdocker-compose上で立ち上げたfluentdコンテナに届く時点で加工されているみたいなので、regexpで拾えなかったから、ということかな、、?

参考: 上がfluentdコンテナに届いたNginxのログ。下がNginxの生ログ。 f:id:hige_dev:20210702135816p:plain

正規表現を上手く作ればいけそうな気がするけど、のちのち使うことなさそうなので、諦めた。

消化不良なのでやってみた。fluentularでparseできるところまでは確認したけど、いざfilterディレクティブのparseプラグインを使ってみても、ログはparseされなかった。

# fluentdコンテナに届くNginxのログのkeyが"log"の値
192.168.80.1 - - [02/Jul/2021:06:23:45 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.29.0\" \"-\"
# fluentularでparseできるけど、fluent.confでparseできなかった正規表現
<filter docker.**>                                                                   
  @type parser
  key_name log
  <parse>
    @type regexp
    expression /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] \\"(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*) \\"(?:(?<referer>[^\"]*))\\" \\"(?<agent>[^"]*)\\" \\"(?:(?<x_forwarded_for>[^ ]+))\\"?$/
    time_format %d/%b/%Y:%H:%M:%S %z
  </parse>
</filter>   

結局上手くできなかったけど、ちょっと寝かせてまた考えよう。

awkのversionを上げ(てしまっ)たらwarningが出てしまう件

f:id:hige_dev:20210605154948p:plain

タイトルの通り。windows(wsl2)での開発環境を構築し始めてて、なんらかのコマンドでawkのversionが上がってしまって、以下のwarnがでるようになってしまった。(たぶん $ apt update -y かな、、?)

warningなので、実行はできるものの、毎回出てくるの気持ち悪い。

調べると、awkは2019年に8年ぶりのメジャーアップデートをしたとのこと。その流れでubuntuのupgradeでversionが上がってしまった、のかな?

awkのversionは、

$ awk -V
GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
...

awkのwarning内容

awk: cmd. line:1: warning: regexp escape sequence `\"' is not a known regexp operator

まさにこれが全てで、ダブルクオートをバックスラッシュでエスケープしないでね、ということらしい。

なので、

"\"asd\": "
↓
""asd": "

にしてね、ということらしい。

対処法

最初は英語のサイトしか出てこなかったからawkのダウングレードしようかと思ったのだけど、(初心者感)

The issue isn't going to go away in a future release. Change your code. warning: regexp escape sequence `\"' is not a known regexp operator

とのことだったので、コードを直すことにした。

コマンドは、以下の自作コマンド。 hige-dev.hatenablog.com

確かにむっちゃ使ってる。

該当部分を抜き出すと、

 awk ' $1 == "\"AccessKeyId\":" { gsub(/\"/,""); gsub(/,/,""); print "export AWS_ACCESS_KEY_ID="$2 } $1 == "\"SecretAccessKey\":" { gsub(/\"/,""); gsub(/,/,""); print "export AWS_SECRET_ACCESS_KEY="$2} $1 == "\"SessionToken\":" { gsub(/\"/,""); gsub(/,/,""); print "export AWS_SESSION_TOKEN="$2 } '

awsコマンドで取得したaccess_keyなどを、環境変数にセットしてくれるやつ。

この中から、ダブルクオートをバックスラッシュでエスケープしてるやつを削除してやる、と上手くいった。

‘‘‘sh awk ' $1 == ""AccessKeyId":" { gsub(/"/,""); gsub(/,/,""); print "export AWS_ACCESS_KEY_ID="$2 } $1 == ""SecretAccessKey":" { gsub(/"/,""); gsub(/,/,""); print "export AWS_SECRET_ACCESS_KEY="$2} $1 == ""SessionToken":" { gsub(/"/,""); gsub(/,/,""); print "export AWS_SESSION_TOKEN="$2 } '

あれ、、思ったよりもスムーズにできてしまった。。書きながら試行錯誤するかと思ったのだけれども。

まあいいか、成長だ。

### TODO
awkとsed、絶対シェルスクリプトで役立つから勉強したいな。。vimtutorみたいなチュートリアルないかな。。

---

あった。ingurisshu。。やったらあ!

[https://www.tutorialspoint.com/awk/awk_overview.htm:embed:cite]

【読書】「成長マインドセット - 心のブレーキの外し方」を読んだ

f:id:hige_dev:20210529163109p:plain

自分は、今が人生で一番成長欲が強い。20代をバンドに費やして、周りよりもスタートが10年遅いハンデを痛感していて、常に焦りを感じている。また、エンジニアとして目に見えてできることが増えて、他者への貢献を実感でき始めていて、それが嬉しいしもっと貢献したい、というのも成長欲が強い要因だと思う。

そういう時に、Prime Readingで「成長マインドセット 心のブレーキの外し方(吉田行宏 著)」をみて、タイトルが気になったので読んでみた。よくある自己啓発本かな〜と思ってあまり期待せず読んだけど、自分の考えとリンクするところもありとても共感できたので残しておく。

成長するための3要素

この本では、成長するための要素として、以下を挙げている。

  1. 成果を生み出す要因を認識し育む
  2. 心のブレーキを外す
  3. 心のアクセルを踏む

この中でも特に「心のブレーキを外す」がとても共感できた。(他も学びがあったけど)

2. 心のブレーキを外す

ここはとても共感できた。というか、自分でもできてるな、と思う。自分の意識が言語化された感じで、スッキリした。転職面接のフィードバックで、「成長が非常に早い」と言っていただけたのは、ここを意識できているからな気がする。

心のブレーキを外すための5つの方法は以下。

ブレーキの存在を知る

解決できるかできないか、と考える前に、自分には悩みがあり、それによって心のブレーキを踏んでしまっていることを認識する。給料が低い、会社の人間関係が嫌だ、など悩みに思考を持ってかれると、成長につながる行動をとる時間が減る。

ブレーキを踏まない覚悟をする

選択する場面で悩むのは当然のこと。しかし、悩んで選択した道は覚悟を持って突き進むべきであり、余計な悩みに思考と時間を使わない。この時、今後ずーっと、と考えると決断が大き過ぎて選択の場面で二の足を踏んでしまうので、「選択したら2年はブレーキを踏まない」と期間を設定することで決断の重さを軽減できる。

他責にしないは100%

全ての責任を負って減給や退職をも辞さない、という意味ではなく、問題を主体的に捉えることで、自身の成長のチャンスに繋がる、という考え方。また、「あいつのせいで・・・」と考えたところで問題は解決せず、それどころか負の感情が生まれて時間を無駄にするだけ、というのも悪の連鎖が起こる。

結果は選択できないが、行動は選択できる

やると決めたら、結果のことを考えずにやることに集中する。結果は選択した手段への取り組み方によってもたらされる。「できないかも、、」と余計な思考はブレーキになるだけ。極論、ダメでも死なないし、全力で取り組んだ姿勢が次に活きる。

関心の輪と影響の輪

悩んでも仕方ないことに悩んで時間を使うのは無駄。過去や他人は変えられない。現在の自分にできる範囲で改善できるところに思考と時間を使うべき。

例えば会社の理念・方針に違和感を感じたとして、耐えられないなら辞める、耐えられるならその方針に則って全力を出す、という思考を持つ方がいい。グジグジと不満を垂れ流して居残るのは一番無駄な時間。自分だけでなく、会社にも無益。(もちろん、会社に本心から共感した上で全力を尽くせるのが一番いいのだけれども)

「ブレーキの外し方」以外の気に入った点

会社のミッション・ビジョン・バリューに心から共感し実践することで、会社だけでなく自分にとっても成長につながるというのは、納得できる。それらは自分とは比べ物にならないぐらい優秀な方たちが、長い時間をかけて作ったものなので、まずは素直に受け止めて反芻して行動に移すことが大事だな、と。

ネガティブな見方をすると「社畜」と言われそうな考え方かもだけど、そうやって斜に構えて行動しないのも心のブレーキの一つなのかな、と思う。

読んだ感想

本から学べることはたくさんあるな、と思ったけど、この本に出てくるようなメンターみたいな人に出会ってみたいなあ。転職して給料がむちゃくちゃ上がる予定なので、技術だけでなく成長や考え方にも投資をして行きたい。

jqのお勉強(aws cli IAM編)

f:id:hige_dev:20210422064813p:plain

概要

先日、IAM userの権限を整理するためにaws cliとjqでいい感じにデータを取得できたので、備忘録として残しておく。

jqとは

JSONから値を取り出すときにとっても便利なツール。例えば、

# user.json
{
  "users": [
    {
      "name": "yamada",
      "age": "19"
    },
    {
      "name": "suzuki",
      "age": "22"
    },
    ...
  ]
}

といった無限に続くJSONから名前だけを取り出したいとき、

$ cat users.json | jq .users[].name
"yamada"
"suzuki"
...

みたいな感じで取得できる。便利。。

特にaws cliはJSONを返す( aws configure での設定次第?)ので、jqとむちゃ相性がいい。

上の例で言うと、usersの配列からキーがnameの値を取り出す 的な感じ。

jqコマンドに -r オプションをつけると、""が外れるので、もっと使いやすい。

$ cat users.json | jq -r .users[].name
yamada
suzuki
...

また、範囲とか条件とかで絞り込みもできる。そういうときは、jqコマンドの引数を ''で括って、 条件をパイプ(|)でつないで指定する。

$ cat users.json | jq -r 'users[] | select(.age < 20) | .name'
yamada
...

上で言うと、usersの配列を取り出して、その中からageが20未満のuserをselectして、そのリストのnameを取り出す、みたいな。

jqのインストール

お使いのパッケージマネージャーでインストール

$ (brew|apt|yum) install jq

簡単。

aws cli iamでjqを使う

本題はここから。今回やったのは、ユーザーのポリシー利用状況を見て、不要なポリシーをアタッチしてないか、を確認する。というもの。(結局使わなかったけど) 細かく書いてくとしんどいので、各コマンドについてコメントしてく。

# IAMユーザーの名前一覧を出す
$ aws iam list-users | jq -r .Users[].UserName


# アクセス履歴を出すために、まずユーザーに対するJOB IDを生成する
$ (aws iam generate-service-last-accessed-details --arn arn:aws:iam::{AWS_ACCOUNT_ID}:user/suzuki
{
    "JobId": "XXXXXXXXX"
}

# そのJOB IDを使って、サービスの名前と最終利用時間をハッシュ(連想配列?)として取得する
$ aws iam get-service-last-accessed-details --job-id ${JOB_ID} | jq -r '.ServicesLastAccessed[] | {ServiceName: .ServiceName, LastAuthenticated:.LastAuthenticated }'

# 2021/4/1移行にアクセスされたサービス一覧
$ aws iam get-service-last-accessed-details --job-id ${JOB_ID} | jq -r '.ServicesLastAccessed[] | select(.LastAuthenticated > "2021-04-01") | .ServiceName'

# アクセス履歴がないサービス一覧
$ aws iam get-service-last-accessed-details --job-id ${JOB_ID} | jq -r '.ServicesLastAccessed[] | select(.LastAuthenticated == null) | .ServiceName'

# 全ユーザーの、使用履歴があるサービス一覧をファイル出力する
USERS=$(aws iam list-users | jq -r .Users[].UserName)

for USER in ${USERS}; do
  JOB_ID=$(aws iam generate-service-last-accessed-details --arn arn:aws:iam::{AWS_ACCOUNT_ID}:user/${USER} | jq -r .JobId)
  aws iam get-service-last-accessed-details --job-id ${JOB_ID} | jq -r '.ServicesLastAccessed[] | select(.LastAuthenticated != null) | .ServiceName' > ${USER}.access.log
done

最後は、本当は一つのファイルにcsv出力したい。。awkとかなのかな、、?要確認。

terraformコマンドメモ

f:id:hige_dev:20210411174310p:plain

目次

確認したversion

$ terraform -v
Terraform v0.14.9
+ provider registry.terraform.io/hashicorp/aws v3.34.0

$ aws --version
aws-cli/2.1.32 Python/3.8.8 Linux/4.15.0-140-generic exe/x86_64.ubuntu.18 prompt/off

概要

AWSにある既存リソースをTerraformで管理する。 新規リソース作成時もTerraformを使うことで、状態の把握が用意になったり、複数人でのリソース管理で設定漏れが起きにくくなったりする。

事前手順

以下の方法などを利用して、MFA認証してaws cliを実行できるようにしておく

hige-dev.hatenablog.com

運用手順

基本的に使うコマンドは以下の5つ。

# 最初に実行するコマンド。main.tfを変更時にも必要になる 
$ terraform init

# 既存のリソースをterraformで管理するためのコマンド
# リソースデータを取得して、各.tfファイルに記載することで長くなるので割愛
# terraformingという便利ツールも併用するといくらか楽
$ terraform import

# 追加、変更した*.tfファイルと、現状との差分を表示
# diffが出た場合、そのように変更されてしまうため、【必ず】確認すること
# (意図しない変更が起きる可能性がある)
$ terraform plan

# 変更した*.tfファイルが反映される
# また、それに合わせてS3で管理してるterraform.tfstateも更新される
$ terraform apply

# tfファイルを「=」の位置で揃えるコマンド。(必須ではないが、やっておくと整形コミットが作られにくそう)
$ terraform fmt

管理するリソースは、terraform state listにあるもののみ。

新規リソースを追加する場合

.tfファイルに追記しplan => apply

既存リソースを編集する場合

terraformで管理してるリソースの場合

.tfファイルを編集してplan => apply

terraformで管理していないリソースの場合

importして.tfファイルを編集し、plan => apply

既存リソースを削除する場合

terraformで管理してるリソースの場合

該当の.tfファイルから削除しplan => apply

※ terraformで管理しているリソースを、コンソールなどから手動で削除した場合、apply実行時に再度作られてしまうため、上記の対応が必要

terraformで管理していないリソースの場合

手動で削除

terraform管理対象から外す場合

terraform state rm XXXでリソース名を指定(リソース名はterraform state listのもの)

ファイルについて

ファイル名 概要
terraform.tfstate リソースの状態を管理するファイル。main.tf内に記載のS3バケットで管理
*.tf リソースファイル。EC2、LBなど種類ごとに分けると管理しやすい