road288の日記

S3にあるALBログの調査はAthenaよりDuckDBのほうが簡単

AWSのALB(Application Load Balancer)のログはS3に置かれるが、この中身をサクッと調べたいとき、Athenaを使う方法が標準的で、下記で案内されているようにパーティション射影(Partition Projection)でテーブルを作ってAthenaからクエリする。

パーティション射影を使用して Athena で ALB アクセスログ用テーブルを作成する - Amazon Athena

私も従来はその方法を使っていたが、Athenaはブラウザから使うと動作がもっさりしているし、決まったクエリを1回きり実行して結果を取得したいだけのときならまだしも、探索的にクエリを何発も実行したいときには使い勝手が悪い。

最近他のプロジェクトでDuckDBを使うようになって、使い勝手の良さに感動していたが、DuckDBはALBのログを探索的に調べたいときにもめっちゃ使えると思った。

DuckDBのインストールは公式 あたりを参照。S3のファイルをクエリする準備としてはAWS Extension – DuckDBS3 API Support – DuckDBを参照。 端的にいうと、シェルでS3にアクセスできるクレデンシャルの環境変数がロードされている状態でDuckDBを起動して、

INSTALL aws;
LOAD aws;
INSTALL httpfs;
LOAD httpfs;
CREATE SECRET (
    TYPE S3,
    PROVIDER CREDENTIAL_CHAIN
);

これでロードしてあるクレデンシャルがアクセス権限を持つS3のファイルに対してクエリできるようになる。 例えば2024年11月のログをロードするなら下記のようにする。Blobのパターンを変えれば任意の期間のデータをロードできる。 この定義にあたってはパーティション射影を使用して Athena で ALB アクセスログ用テーブルを作成する - Amazon Athena にある定義を参考にした。

CREATE TABLE alb_log_202411 AS
SELECT *
FROM read_csv(
    's3://[YOUR_S3_BUCKET_NAME]/AWSLogs/[YOUR_ACCOUNT_ID]/elasticloadbalancing/[YOUR_REGION]/2024/11/**/*.log.gz',
    columns={
        'type': 'VARCHAR',
        'timestamp': 'TIMESTAMP',
        'elb': 'VARCHAR',
        'client_ip_port': 'VARCHAR',
        'target_ip_port': 'VARCHAR',
        'request_processing_time': 'DOUBLE',
        'target_processing_time': 'DOUBLE',
        'response_processing_time': 'DOUBLE',
        'elb_status_code': 'INTEGER',
        'target_status_code': 'VARCHAR',
        'received_bytes': 'BIGINT',
        'sent_bytes': 'BIGINT',
        'request': 'VARCHAR',
        'user_agent': 'VARCHAR',
        'ssl_cipher': 'VARCHAR',
        'ssl_protocol': 'VARCHAR',
        'target_group_arn': 'VARCHAR',
        'trace_id': 'VARCHAR',
        'domain_name': 'VARCHAR',
        'chosen_cert_arn': 'VARCHAR',
        'matched_rule_priority': 'VARCHAR',
        'request_creation_time': 'TIMESTAMP',
        'actions_executed': 'VARCHAR',
        'redirect_url': 'VARCHAR',
        'error_reason': 'VARCHAR',
        'target_port_list': 'VARCHAR',
        'target_status_code_list': 'VARCHAR',
        'classification': 'VARCHAR',
        'classification_reason': 'VARCHAR',
        'conn_trace_id': 'VARCHAR'
    },
    delim=' ',
    quote='"',
    escape='"',
    header=False,
    auto_detect=False
);

S3の対象ファイル数が多い場合テーブルができるまでにそこそこ時間はかかるが、いったんローカルにロードされればあとは速い。

  • テーブルの存在を確認
D show tables;
┌────────────────┐
│      name      │
│    varchar     │
├────────────────┤
│ alb_log_202411 │
└────────────────┘
D describe alb_log_202411;
┌──────────────────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│       column_name        │ column_type │  null   │   key   │ default │  extra  │
│         varchar          │   varchar   │ varchar │ varchar │ varchar │ varchar │
├──────────────────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ type                     │ VARCHAR     │ YES     │         │         │         │
│ timestamp                │ TIMESTAMP   │ YES     │         │         │         │
│ elb                      │ VARCHAR     │ YES     │         │         │         │
│ client_ip_port           │ VARCHAR     │ YES     │         │         │         │
│ target_ip_port           │ VARCHAR     │ YES     │         │         │         │
│ request_processing_time  │ DOUBLE      │ YES     │         │         │         │
│ target_processing_time   │ DOUBLE      │ YES     │         │         │         │
│ response_processing_time │ DOUBLE      │ YES     │         │         │         │
│ elb_status_code          │ INTEGER     │ YES     │         │         │         │
│ target_status_code       │ VARCHAR     │ YES     │         │         │         │
│ received_bytes           │ BIGINT      │ YES     │         │         │         │
│ sent_bytes               │ BIGINT      │ YES     │         │         │         │
│ request                  │ VARCHAR     │ YES     │         │         │         │
│ user_agent               │ VARCHAR     │ YES     │         │         │         │
│ ssl_cipher               │ VARCHAR     │ YES     │         │         │         │
│ ssl_protocol             │ VARCHAR     │ YES     │         │         │         │
│ target_group_arn         │ VARCHAR     │ YES     │         │         │         │
│ trace_id                 │ VARCHAR     │ YES     │         │         │         │
│ domain_name              │ VARCHAR     │ YES     │         │         │         │
│ chosen_cert_arn          │ VARCHAR     │ YES     │         │         │         │
│ matched_rule_priority    │ VARCHAR     │ YES     │         │         │         │
│ request_creation_time    │ TIMESTAMP   │ YES     │         │         │         │
│ actions_executed         │ VARCHAR     │ YES     │         │         │         │
│ redirect_url             │ VARCHAR     │ YES     │         │         │         │
│ error_reason             │ VARCHAR     │ YES     │         │         │         │
│ target_port_list         │ VARCHAR     │ YES     │         │         │         │
│ target_status_code_list  │ VARCHAR     │ YES     │         │         │         │
│ classification           │ VARCHAR     │ YES     │         │         │         │
│ classification_reason    │ VARCHAR     │ YES     │         │         │         │
│ conn_trace_id            │ VARCHAR     │ YES     │         │         │         │
├──────────────────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┤
│ 30 rows                                                              6 columns │
└────────────────────────────────────────────────────────────────────────────────┘

あとはいろいろクエリしてお好きなように。

  • select * from alb_log_202411 where elb_status_code != 200 LIMIT 1;
  • select timestamp, request, elb_status_code, target_status_code, domain_name from alb_log_202411 where elb_status_code != 200 LIMIT 100
  • select timestamp, request, elb_status_code, target_status_code, domain_name from alb_log_202411 where elb_status_code != 200 and domain_name != 'foobar.com' LIMIT 100

みたいに探索的に調べるのが高速にやれるのが最高。

※補足

30カラムもあるので、デフォルトではコマンドラインで全部のカラムを表示できない。 なので、SELECT句でカラムを絞るか、 .mode line とか .mode box とかで全カラム表示できる。 デフォルトに戻すには .mode duckbox。 どういう選択肢があるかはOutput Formats – DuckDB見ると分かる。

※補足2

ググったら似たことしている記事あったのでリンクしとく

Analysing AWS Application Load Balancer Logs with DuckDB: Unleashing Performance Insights

Rustのglibc依存問題

Rustはコンパイルできれば動くと思いきや、そうでもない。 cargo-lambdaを使って、ローカル環境で動作確認したlambda関数をデプロイしたら、動かなかった。

/var/task/bootstrap: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by /var/task/bootstrap)`

Amazon Linux2(AL2)とAmazon Linux2023(AL2023)のそれぞれのglibcのバージョンは ツールチェーンの改善: gcc、binutils、 glibc - Amazon Linux 2023 に記載のとおり、2.26, 2.34なので、AL2だとエラーが出たということだった。AL2023でデプロイしなおしたら問題なく動いた。

aws-lambda-rust-runtimeのREADMEを見ると al2023を使うことを勧めている。

この記事もみつけた。 cargo lambda deployした関数が `GLIBC_2.28' not found でうごかない

ビルド時に--target を指定することで解決できるようだ。

cargo lambda build --release --target aarch64-unknown-linux-gnu.2.26

下記の記事ではいろいろな選択肢がリストアップされているが、結局のところどれが良いかはアプリの実装内容や、環境や運用の方法によって異なりそう。

Rust でバイナリを配布する

時系列+もう一つの何らかの属性で検索することがほとんどのデータは、S3に置いてs3 selectが有用

www.infoq.com

この記事が話題になってて( なお元記事はここっぽい Moving from DynamoDB to tiered storage with MySQL+S3 | by Shane Hender | Zendesk Engineering )、かつて似たことやってAWS Summit Tokyoのミニステージで登壇したことがあったので忘れないうちに補足記事を書こうと思った。

www.nileworks.co.jp

PDFファイルの直接のリンクは以下になる。

ドローン運行監視 ーAmazon AthenaのBucketingとAmazon S3 Selectの活用ー

このスライドの概要としては、S3に保存されているドローンの飛行ログデータを、AthenaのCTAS + Bucketingを使って1日1回任意のまとまりでS3のファイルを適切な階層構造に配置しなおし、APIサーバーから取得できるようにしたという事例紹介となる。

この場合は飛行ログなので、機体のIDと、時系列で検索できればほとんどの場合十分だ。 このように時系列+何らかの属性のみで検索することがほとんどのデータがけっこうたくさんあると思っていて、そういうときにはこのパターンは適していると思う。

S3でファイルを置くならFirehoseとかで事足りそうに思うかもしれないが、Firehoseでファイルを置くとファイル数が増えてしまうので、そのままではS3 selectでクエリするのに適さないので、適切な粒度でファイルをまとめなおすという一手間を加えている。

このシステムでは実際にこの仕組みを使って、フロントエンドのNext.jsから、APIサーバーに任意の機体の任意の時間の飛行ログを問い合わせる仕組みを作って、過去の飛行履歴をすべて振り替えれるようにしている。

S3はExpress One Zoneもリリースされたので、より高速にレスポンスするようになっただろうから、S3はデータストレージとして更に有望になったと思う。システム要件によるが、REST APIなどでS3に置いたデータをクエリしてレスポンスするといったことは十分現実的だと思う。

最近はDuckDBに注目している。これもS3にあるログを短時間で読み込むことができる。

Obsidianのvimのjump to linkで英単語だけでなく日本語でもジャンプしたい

最近Obsidianを使って個人の知識データベースを作っている。日々学んだこと・見た記事などをdaily noteにしつつ、ストックとなる記事を貯めていっている。 これまでもEvernote・Notionや個人ブログ等で似たことをやろうとしていたが、Obsidianはローカルで使うmarkdownのエディタとして普通に日々のメモツール(これまではSlackとかにメモしていた)の延長で使えるので、メモとストックの垣根を無くせるのが良くて、常用するツールになりそう。

エディタを快適に使うときに自分にとって欠かせない要素なのがvimキーバインドで、Obsidianにも公式にvimキーバインドがあるので使っている。vimの設定はあまりカスタマイズしないほうだが、ジャンプ機能を使いたいと思って、

📗vim-easymotionのように入力した文字から始まる単語にジャンプしたい - Minerva

ここで知ったjump to linkを入れた。 普段はjump to anywhereをよく使うが、デフォルトの設定の正規表現\b\w{3,}\bだと日本語の位置にジャンプできない。そこで日本語でも兼用できる正規表現にしたいといくつか試行錯誤した結果、[。、,.\s]\S{4}で自分にとって満足のいく結果が出るようになった。

超速で成果を出す アジャイル仕事術

たまたま手にとったので読んだ。

著者の方はソフトウェア開発のマネジメント経験があって、タイトルの「アジャイル」もソフトウェア開発の「アジャイル」の文脈に沿っている。 内容的には特別新しい観点が書かれているわけではないが、体系だてて整理して述べられている印象はあった。

本書の中では仕事術として重要なスキルとして「構想力」「俊敏力」「適応力」「連携力」「共創力」の5つが挙げられているが、自分は最後の「共創力」が特に弱いと感じる。直近4年間ほどで本業2社(サイバーエージェント、ナイルワークス)、副業7社の9つの会社で仕事をしてきた。どの会社でも、個人としてはそこそこアウトプットは出せる。だけどチームとして、異なる専門性を持った個人が集まった集団として、単なる個人のかけあわせ以上の仕事の成果が出せてきたかでいうとあまり実現できていない。 個人としてスキルを磨くだけではなくて、他人と共創して自分一人×時間では決してなし得ない仕事をできるようになりたいと思っている。 その課題感にあったヒントがあったように思った。 ポイントを要約すると「互いに興味を持ち、質より量で情報を共有しあい、対話によって価値観の違いを理解しあい、ビジョンと行動原則に落とし込むこと」と理解した。 リモートで最低限のタスク管理だけしているとこれは実現できない。

terraformでrandom_passwordで作ったパスワードを参照する

またあとで自分で調べそうなのでメモ。

terraformにはrandom_passwordという便利なリソースがあって、ランダムパスワードを生成してRDSの起動時などに使うことができる。

https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password

このパスワードを一時的に参照したいときは、backendのS3のファイルを直接参照するなどすればもちろん見れるわけだけど、CLIだけで済ませたい。

outputとnonsensitiveの組み合わせでいける。

output "master_password" {
  value = nonsensitive(random_password.master.result)
}

これでterraform planすることで表示される。

Changes to Outputs:
  + master_password = "xxxxxxxxxxxx"

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

terraform applyはせずに消す。

terraform stateに含まれることには違いないので、初期構築時にだけ使い、あとでパスワードは必ず変更する。

複数のGitHubアカウントでgit と ghコマンドをそれぞれ使い分ける

例えば ~/git/work/以下で仕事、~/git/personal以下で個人のGitHubアカウントで作業したいということがある。

gitコマンドにおいてGitHubアカウントを使い分ける情報自体はそこそこ豊富にある。その中でディレクトリベースで使い分ける方法を選んだ。 gitconfigのincludeIfディレクティブで切りかえられる。

ディレクトリベースで GitHub アカウントを切り替える https://zenn.dev/ytk6565/articles/github-accounts-based-on-directories

ただこれだとgitレポジトリであるディレクトリにしか作用しないので、レポジトリをcloneする前だと設定が有効にならない。

そもそも私はGitHubからのcloneのときはghコマンドを使っている。そこでghコマンドも複数アカウントで使い分けられるようにする方法がないか調べた。

いろいろググった結果、この方法がうまくいった。

GH CLI multi-account switch · GitHub

gh work とか gh personal と打つとアクティブなアカウントが切り替わる。