paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

terraformファイルの変数を追う

以前ministackとterraformでサーバレスの疑似APIサービスを作りました。 複数ファイルに分割したものの変数の参照先などが散らかってしまったので整理します。

ちょっと仕事の方で勉強会の名目で触ったのですが、変数が散らかって難しいなという印象でした。
(慣れもあるでしょうが)

構成

前回と同じですが構成ファイルを色々分けています。

work/ministack-apigateway
├── apigateway.tf
├── dynamodb.tf
├── lambda.tf
├── lambda.zip
├── output.tf
├── provider.tf
└── variables.tf

中身は全部書いても仕方ないのでリポジトリ登録しました。

タグジャンプ

変数といえばばまずはタグジャンプということでctagsで作ってみました。
設定行には飛べそうですが、中の定義ファイルの参照ができていません。
これではあまり有効ではないですね。

❯ grep -v \^! tags
api_endpoint    output.tf   /^output "api_endpoint" {$/;"   o
aws   provider.tf /^provider "aws" {$/;" p
aws_region  variables.tf    /^variable "aws_region" {$/;"   v
demo_api  apigateway.tf   /^resource "aws_api_gateway_rest_api" "demo_api" {$/;"  r
demo_deployment apigateway.tf   /^resource "aws_api_gateway_deployment" "demo_deployment" {$/;"  r
demo_lambda   lambda.tf   /^resource "aws_lambda_function" "demo_lambda" {$/;"    r
demo_table  dynamodb.tf /^resource "aws_dynamodb_table" "demo_table" {$/;"   r
dynamodb  lambda/index.py /^dynamodb = boto3.resource($/;"  v
environment variables.tf    /^variable "environment" {$/;"  v
handler   lambda/index.py /^def handler(event, context):$/;"    f
lambda_integration  apigateway.tf   /^resource "aws_api_gateway_integration" "lambda_integration" {$/;"  r
lambda_role   lambda.tf   /^resource "aws_iam_role" "lambda_role" {$/;"   r
lambda_zip  lambda.tf   /^data "archive_file" "lambda_zip" {$/;" d
proxy apigateway.tf   /^resource "aws_api_gateway_resource" "proxy" {$/;" r
proxy_method    apigateway.tf   /^resource "aws_api_gateway_method" "proxy_method" {$/;" r

terraform graph

terraform本体にも参照先を確認できるオプションがあるそうです。 "terraform graph"でそういった参照を出力してくれます。 dot言語というあまり聞いたことない言語ですが、一応参照っぽいリンクがでてきました。
これに可視化できるツールをかますのが一般的なんですかね。
そういった記事が出てきました。

❯ terraform graph
digraph G {
  rankdir = "RL";
  node [shape = rect, fontname = "sans-serif"];
  "data.archive_file.lambda_zip" [label="data.archive_file.lambda_zip"];
  "aws_api_gateway_deployment.demo_deployment" [label="aws_api_gateway_deployment.demo_deployment"];
  "aws_api_gateway_integration.lambda_integration" [label="aws_api_gateway_integration.lambda_integration"];
  "aws_api_gateway_method.proxy_method" [label="aws_api_gateway_method.proxy_method"];
  "aws_api_gateway_resource.proxy" [label="aws_api_gateway_resource.proxy"];
  "aws_api_gateway_rest_api.demo_api" [label="aws_api_gateway_rest_api.demo_api"];
  "aws_dynamodb_table.demo_table" [label="aws_dynamodb_table.demo_table"];
  "aws_iam_role.lambda_role" [label="aws_iam_role.lambda_role"];
  "aws_lambda_function.demo_lambda" [label="aws_lambda_function.demo_lambda"];
  "aws_api_gateway_deployment.demo_deployment" -> "aws_api_gateway_integration.lambda_integration";
  "aws_api_gateway_integration.lambda_integration" -> "aws_api_gateway_method.proxy_method";
  "aws_api_gateway_integration.lambda_integration" -> "aws_lambda_function.demo_lambda";
  "aws_api_gateway_method.proxy_method" -> "aws_api_gateway_resource.proxy";
  "aws_api_gateway_resource.proxy" -> "aws_api_gateway_rest_api.demo_api";
  "aws_lambda_function.demo_lambda" -> "data.archive_file.lambda_zip";
  "aws_lambda_function.demo_lambda" -> "aws_dynamodb_table.demo_table";
  "aws_lambda_function.demo_lambda" -> "aws_iam_role.lambda_role";
}

blast radius

このパッケージが本記事の本命なのですが例によってGeminiに聞いたところVSCodeの拡張でタグジャンプするか、可視化ツールとしてblast radiusを使えと回答がきたので後者を試してみることにしました。

ブラウザベースで描画してくれるパッケージです。

Blast Radius

インストール

仮想環境作ってからpipでインストールします。

python3 -m venv terraform-graph
source terraform-graph/bin/activate
pip install blastradius
sudo apt install graphviz # blastradiusの描画に必要

実行

terraformのディレクトリに移動して以下を実行。

❯ blast-radius --serve .
 * Serving Flask app 'blastradius.server.server'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://192.168.0.32:5000
Press CTRL+C to quit

5000番で立ち上がるのでアクセスします。

画像データダウンロードできるはずですが動かなかったので一旦スクショにします。 するとこんな感じで変数の参照が図面化されます。

しばらく見てわかったのですが、下が一番最初の参照元で上に行くほど上位の参照先となっているようです。

aws_api_gateway関連だけでいうと、 一番下のrest_apiのdemo_api → resouceのproxy → methodのproxy_method → integrationのlambda_integration → deploymentのdemo_deployment

と関連づいています。

なるほど、こう図面化しただけでも頭に入りやすいですね。

まとめ

私はプログラミングは本業ではないのでタグジャンプやそれに類する操作はあまり行ったことがありません。
AWSのクラウドプラットフォームもようやく触り始めたものの本体の構築にガッツリ絡んだわけではないのでterraformのようなファイル構成も読み込めていませんでした。

検索でなんとか頑張っていましたが、この辺も抑えておこうと思い本記事の操作を色々触ってみました。

blast radiusは簡単で便利でしたが、この辺のツールは流行り廃りで入れ替わる可能性があるのでタグジャンプを作成の仕様から抑えたほうがいいんだろうな。

ministackとterraformでサーバレスAPIを作る

案件でAWSを触り始めてから2年半位経ちました。クラウド環境へ移行したものの構成的にインフラがオンプレっぽくなってしまって今どきの開発というものを経験できていません。
本物を触って覚えるのが一番ですが(無料枠はあるといえど)検証でAWSに登録するのものなぁと思っていました。
ある時localstackというローカルで動かせるパッケージを知って試していたのですが、この度似たministackを見つけてこちらのほうが色々触れそうだったので遊んでみることにしました。

ministack.org

クライアントーサーバ構成でレスポンスが帰ってくるものが良いと思い、またマネージドサービスも触ろうということでサーバレスでAPIが返るような環境を作ろうと思います。

ministack

どちらも有名なツールなので詳細は書きませんが、localstackもministackもローカル環境でAWSをシミュレートできるパッケージです。

先発はlocalstackのようですが、無料版は機能制限があるのに対しministackは今のところ完全無料でlocalstackの有料版機能を使えるようにしたというところでしょうか。

実行

docker imangeで提供されておりすぐ動かせます。

❯ docker run -p 4566:4566 ministackorg/ministack
2026-07-22 23:34:27 INFO [ministack]
  __  __ _       _ ____  _             _
 |  \/  (_)_ __ (_) ___|| |_ __ _  ___| | __
 | |\/| | | '_ \| \___ \| __/ _` |/ __| |/ /
 | |  | | | | | | |___) | || (_| | (__|   <
 |_|  |_|_|_| |_|_|____/ \__\__,_|\___|_|\_\

 Local AWS Service Emulator — Port 4566
  Services: S3, SQS, SNS, DynamoDB, Lambda, IAM, STS, SecretsManager, CloudWatch Logs,
           SSM, EventBridge, Kinesis, CloudWatch, SES, SES v2, ACM, WAF v2, Step Functions,
           ECS, RDS, ElastiCache, Glue, Athena, API Gateway, Firehose, Route53,
           Cognito, EC2, EMR, EBS, EFS, ALB/ELBv2, CloudFormation, KMS, ECR, CloudFront,
           AppSync, Cloud Map, S3 Files, RDS Data API, CodeBuild, AppConfig, Transfer, EKS,
           Inspector2, IoT Core

2026-07-22 23:34:27 INFO [ministack] Worker thread pool: 64 threads
2026-07-22 23:34:30 INFO [transfer] SFTP: generated ephemeral host key (PERSIST_STATE=0)
2026-07-22 23:34:30 INFO [asyncssh] Creating SSH listener on 0.0.0.0, port 2222
2026-07-22 23:34:30 INFO [transfer] SFTP: listening on 0.0.0.0:2222 (shared)
2026-07-22 23:34:31 WARNING [ministack] Scheduler startup failed: name 'method' is not defined
2026-07-22 23:34:31 INFO [ministack] Ready — 74 services available on port 4566.
[2026-07-22 23:34:31 +0000] [1] [INFO] Running on http://0.0.0.0:4566 (CTRL + C to quit)

これで準備はOK。 マネジメントコンソール環境はないので(あるかもしれませんが)AWSコマンド等で操作します。 今回はTerraformので作って見るのでコマンド発行は行いません。

AWSコマンド、Terraformインストール

AWSコマンド使わないといってもTerraform経由で実行するのでインストールは行います。 手順は割愛するのでバージョンのみ。

❯ aws --version
aws-cli/2.31.35 Python/3.14.4 Linux/7.0.0-28-generic source/x86_64.ubuntu.26
❯
❯ terraform --version
Terraform v1.15.8
on linux_amd64
+ provider registry.terraform.io/hashicorp/archive v2.8.0
+ provider registry.terraform.io/hashicorp/aws v5.100.0

terraformファイル

EC2とRDSでLAMPでも作ろうかなと思ったのですが、この辺はオンプレでもよくやっているので今どきなサーバレスのサービスで作ってみることにしました。

構成もconfigもGeminiに相談して以下のようにしました。

❯ tree
.
├── apigateway.tf
├── dynamodb.tf
├── lambda
│   └── index.py
├── lambda.tf
├── output.tf
├── provider.tf
└── variables.tf

この手のは細かくファイル分割している記事をよく見るのでそれっぽく分けました。
今回は詳細は追いませんが、次回にでも中身を読んでみようと思います。

デプロイ

実行します。

terraform init

❯ terraform init
Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Finding latest version of hashicorp/archive...
- Installing hashicorp/aws v5.100.0...
- Installed hashicorp/aws v5.100.0 (signed by HashiCorp)
- Installing hashicorp/archive v2.8.0...
- Installed hashicorp/archive v2.8.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

初期化OK。

terraform plan

❯ terraform plan
data.archive_file.lambda_zip: Reading...
data.archive_file.lambda_zip: Read complete after 0s [id=fdb85aeb1d607c5e9c851e1734a51adac1eef51e]

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_api_gateway_deployment.demo_deployment will be created
  + resource "aws_api_gateway_deployment" "demo_deployment" {
      + created_date  = (known after apply)
      + execution_arn = (known after apply)
      + id            = (known after apply)
      + invoke_url    = (known after apply)
      + rest_api_id   = (known after apply)
      + stage_name    = "dev"
    }

(略)
      + tracing_config (known after apply)
    }

Plan: 8 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + api_endpoint = (known after apply)
╷
│ Warning: Argument is deprecated
│
│   with aws_api_gateway_deployment.demo_deployment,
│   on apigateway.tf line 30, in resource "aws_api_gateway_deployment" "demo_deployment":
│   30:   stage_name  = var.environment
│
│ stage_name is deprecated. Use the aws_api_gateway_stage resource instead.
╵

──────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly
these actions if you run "terraform apply" now.

environmentの変数重複のメッセージがでましたがplanは通ったようです。
このままapplyします。

terraform apply

❯ terraform apply
data.archive_file.lambda_zip: Reading...
data.archive_file.lambda_zip: Read complete after 0s [id=fdb85aeb1d607c5e9c851e1734a51adac1eef51e]

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_api_gateway_deployment.demo_deployment will be created
  + resource "aws_api_gateway_deployment" "demo_deployment" {
      + created_date  = (known after apply)
      + execution_arn = (known after apply)
      + id            = (known after apply)
      + invoke_url    = (known after apply)
      + rest_api_id   = (known after apply)
      + stage_name    = "dev"
    }

(略)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_api_gateway_rest_api.demo_api: Creating...
aws_iam_role.lambda_role: Creating...
aws_dynamodb_table.demo_table: Creating...
aws_api_gateway_rest_api.demo_api: Creation complete after 1s [id=638f0651]
(略)

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

Outputs:

api_endpoint = "http://localhost:4566/restapis/638f0651/dev/_user_request_/hello"

api_endpointが発行されました。
デプロイも成功のようです。
アクセスしてみます。

アクセス

まずはブラウザから見てみます。

OKですね。index.pyと同じbodyになってます。

curlでもアクセスしてみます。

❯ curl http://localhost:4566/restapis/638f0651/dev/_user_request_/hello
{"message": "Hello from MiniStack Serverless (Python)!", "timestamp": "2026-07-22T23:59:56.159372Z", "total_visit_count": 2}%
❯
❯ curl http://localhost:4566/restapis/638f0651/dev/_user_request_/hello
{"message": "Hello from MiniStack Serverless (Python)!", "timestamp": "2026-07-23T01:05:51.454585Z", "total_visit_count": 3}%

OK。ちゃんとカウントも増えてますね。
デプロイ成功です!

まとめ

terraformでサーバレスでレスポンスを返す環境を作ってみました。
IaCはDockerで何度か触っているのでサービスを自動で立ち上げるといった経験はあるのですが、インフラを自動で立ち上げられるのはなかなか凄いことですね。

検証用途とはいえ自宅のローカル環境でAWSを模した環境が作れるとはいい時代になったものです。

今回の構成や中身はAIに書いてもらったので、理解するには1つづつ読み解いていかないといけませんが構築だけならあまり悩まずに出来てしまいました。
アーキテクチャをちゃんとするには別途検討しないといけませんが、AI活用もありアイデアだけあればなにかしらのサービス構築はすぐできそうですね。
インフラエンジニア要らなくなってしまいます。

食いっぱぐれないように細かいとこ抑えておこうということで次回はファイルの中身を少し見てみようと思います。

tmuxをsshクライアントツールとして使う

復活したUbuntuですがラボのマシンもこれから操作することが多くsshクライアントツールが欲しくなりました。
以前はターミナルのsshで接続していたのですが、何台もアクセスしたいときツールがあると便利です。
クライアントツールなんで星の数ほどあるのですがマシンスペックかGNOMEの関係か起動がすごく遅かったり起動しないことすらあります。
それなら自分で作ろうと思い色々調べました。

なるべく実装しないツール

とりあえず最低限、2つの機能は欲しいです。

  • 選択してアクセス
  • タブ

作りたいと言いましたがこのためにちゃんとしたコーディングするのもなんだかなと思いGeminiと色々相談しました。
RustやGoが早いだとか最低限ならシェルスクリプトだけでいいとかいろいろ案が出てきましたが、なるべく有りもので済ませられるようtmuxをベースに作ることにしました。
タブのところの実装が面倒そうだったので元から機能が備わっていることからtmuxを使うことにしました。

動作

先に動くところを見せるとこんな感じになります。
tmuxのsshツールということでtsshというコマンドで呼び出します。
tmuxのなかから呼び出しても同じセッション内に新しいタブで展開されます。

gifがアップロードできなかったのでスクショでお送りします。

なかなかいいじゃない。

実装周り

シェルから呼び出せるよう.zshrcに以下を追加しました。

# ツール用セッション名
readonly TOOL_SESSION="ssh-manager"

function tssh() {
    # 1. SSH接続先を選択 (fzf使用)
    local target=$(grep "^Host " ~/.ssh/config | awk '{print $2}' | fzf --height 40% --reverse --prompt="Select Target > ")

    # キャンセル時は終了
    [ -z "$target" ] && return

    # 2. セッションが存在するか確認
    if ! tmux has-session -t "$TOOL_SESSION" 2>/dev/null; then
        # なければ新規作成
        tmux new-session -d -s "$TOOL_SESSION" -n "$target" "ssh $target"
    else
        # あればそのセッションに新しいウィンドウを作成
        tmux new-window -t "$TOOL_SESSION" -n "$target" "ssh $target"
    fi

    # 3. セッションにアタッチ(既にアタッチ中ならウィンドウ移動)
    tmux attach-session -t "$TOOL_SESSION"
}

コンフィグ系

設定の呼び出し元は.ssh/configに書いてます。
接続先情報をgrepしてfzfでリストを読んだあとtmuxコマンドで渡す形ですね。
この手法は割と色々な記事に出てきます。
中身は一旦簡単にIPとユーザ名だけです。

❯ head .ssh/config
Host docker1
    Hostname 10.0.1.1
    User masashi

Host docker2
    Hostname 10.0.1.2
    User masashi

Host docker3
    Hostname 10.0.1.3

まとめ

まとめるほどでもありませんが、コードはほとんど書かずにタブ機能のあるsshクライアントツールが完成しました。
まあtmuxでどんどんタブ増やしてそれぞれにsshさせておけばいいという話もありますが、一発でつながるのは意外と楽なもので作ってよかったと思います。

見た目系はtmuxのコンフィグをいじっていい感じに仕上げたいと思います。

Ubuntu26.04LTSを新規インストール

2年おきにやっているサブ機Ubuntuのアップグレードですが、今回はディスクのセクタ不要により交換を行いました。
そのため新規インストールとなってしまいましたが無事に新しいOSを迎えることができました。

新規インストール

Ubuntu26のデスクトップ版です。 コードネームResolute Raccoonでアライグマらしいです。

インストーラーに沿って進めただけです。今回もパーティションは特にいじらずそのまま。
要求スペックも最小メモリ6GBと高くなってきているので古いNUCは耐えられるでしょうか。

❯ uname -a
Linux masashi-NUC6CAYS 7.0.0-27-generic #27-Ubuntu SMP PREEMPT_DYNAMIC Thu Jun 18 19:13:49 UTC 2026 x86_64 GNU/Linux
❯ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 26.04 LTS
Release:    26.04
Codename:   resolute
❯ df -k
Filesystem     1K-blocks     Used Available Use% Mounted on
tmpfs            1488876     3200   1485676   1% /run
/dev/sda2      479594152 14044600 441113996   4% /
tmpfs            3722188        0   3722188   0% /dev/shm
none                1024        0      1024   0% /run/credentials/systemd-journald.service
tmpfs            3722188        8   3722180   1% /tmp
none                1024        0      1024   0% /run/credentials/systemd-resolved.service
tmpfs             744436      140    744296   1% /run/user/1000
/dev/mmcblk0p2   1929776      512   1812888   1% /run/media/masashi/f087d796-a210-4b6f-8086-f5b3783801eb

インストール完了しまして初期画面です。

GNOMEカスタム

GNOMEで慣れているので今回もこれをカスタムしていきます。 Extention managerをインストールして諸々いれました。

前回使っていた拡張がサポートされなくなったものもあったので大体の物を入れています。

  • Caffeine
  • Dash to Dock
  • Dash to Panel
  • Tiling Shell
  • Vitals

前回と似たような見た目になりました。 今回はしばらくConkyは動かさずに使ってみます。

今回はPop Shellじゃなくて拡張のTiling Shellを入れてみたのですが意外とお洒落で気に入ってます。
ターミナルが純正のままなので変えたいですね。

スクショを何枚か。

デスクトップ。またサムスピの骸流島です。

fastfetchとtuipetというデジモンを育てるアプリです。

というわけでUbuntu26.04インストール完了しました。
細かい点は全然見れてないですが触っていってという感じです。
またDockerでもいれて色々遊びたいですね。

2026上半期見た映画メモ

今年も半年終わってしまいました。
映画の振り返りをします。

今回はUbuntuが壊れた関係でツールではなくNetflixの視聴記録からgrepします。
なのでフォーマットもいつもと違います。
OMDBから引っ張ってきていないので今回は全部邦題です。

本当は記録はこれで十分な気もしますが、
ダウンロードする手間がちょっとあるのと最後まで見てなくてもクリックしたものは記録されてしまうのでやはり自分のツールが使い勝手が良かったりします。

視聴ファイル

$ grep -P '[1-6]\/.+\/26\"$' NetflixViewingHistory\(1\).csv | nl
     1  "熱い賭け ザ・ギャンブラー","6/27/26"
     2  "アンダーカバーDJ","6/24/26"
     3  "アーミー・オブ・シーブズ","6/21/26"
     4  "メリッサ・マッカーシー in ザ・ボス 世界で一番お金が好き!","6/15/26"
     5  "ネイバーズ2","6/10/26"
     6  "ネイバーズ","6/9/26"
     7  "Mr.& Mrs.スミス","6/5/26"
     8  "アドレナリン2 ハイ・ボルテージ","6/1/26"
     9  "ダブル・サスペクト 疑惑の潜入捜査官","5/26/26"
    10  "端くれ賭博人のバラード ","5/18/26"
    11  "バルトコヴィアク","5/14/26"
    12  "エクスペンダブルズ ニューブラッド","5/6/26"
    13  "スティング","5/5/26"
    14  "インモータルズ ー神々の戦いー","5/5/26"
    15  "インファナル・アフェア III 終極無間","4/12/26"
    16  "イコライザー THE FINAL","4/10/26"
    17  "危ない女たち","4/9/26"
    18  "ジュラシック・パーク","4/6/26"
    19  "ディクテーター 身元不明でニューヨーク","3/30/26"
    20  "ジャック・リーチャー NEVER GO BACK","3/22/26"
    21  "アウトロー","3/20/26"
    22  "ゴーストバスターズ","3/14/26"
    23  "ブレイド3","3/9/26"
    24  "ブレイド2","2/28/26"
    25  "パリピ芸人、ロシアへ行く","2/24/26"
    26  "マダム・ウェブ","2/23/26"
    27  "わが拳に復讐を","2/22/26"
    28  "ビバリーヒルズ・ニンジャ","2/16/26"
    29  "バトルランナー","2/15/26"
    30  "エクスペンダブルズ2","2/14/26"
    31  "ツインズ","2/8/26"
    32  "エクスペンダブルズ","2/2/26"
    33  "スーパーヒーローへの道","1/26/26"
    34  "ジョン・ウィック: パラベラム","1/18/26"
    35  "ミッドナイト・エクスプレス","1/12/26"
    36  "オーバーホール","1/11/26"
    37  "セキュリティ・チェック","1/4/26"

今回は37本。週一回以上は見れましたね。
結構テレワークも多かったので平日に見たのも結構あります。

2026上半期ベスト3

  1. スティング (1973年)
    詐欺師の主人公が仲間の仇のためマフィアのボスを嵌めるという話です。
    大がかりな詐欺セット作ったりと入念な計画が成功へ導くという感覚が得られる感じがします。
    古い映画ながら古臭さを感じることはなくストーリーも面白い映画でした。
    章立てになっているのもおしゃれでよかった。
  2. ジュラシックパーク (1993年)
    超有名な恐竜映画です。友人に見たほうがいいと何度も進められたので視聴しました。
    パニックホラーの要素もありつつグロい感じはそんなになかったので楽しめました。
    有名なのであまり語ることもないですがシンプルに面白いです。

  3. メリッサ・マッカーシー in ・ボス 世界で一番お金が好き! (2016) 富豪の主人公が財産を差し押さえられて無一文から再起を図る話です。
    コメディ映画ですがビジネスの起こし方、チャンスを逃さないところなど参考にしたいところがありました。
    成り上がっていく系の話は面白いですね。

次点

  • アーミー・オブ・シーブズ
  • Mr.& Mrs.スミス
  • 端くれ賭博人のバラード
  • セキュリティ・チェック

あたりもよかったです。

まとめ

今回は割と最近見たものがランクに入りました。
たくさん見てるので似たような構成のものは記憶から消えて行ってしまうのでしょうか。
90年代が2つ入ったのも珍しいと思います。しかし昔の映画は話が面白いですね。

さあ後半も頑張ろう。

セクタ不良で起動中のOSがフリーズすることはありえるのか

古いIntel NUCでサブ機で使っているUbuntuですが起動中でのフリーズが頻発するようになりました。
具体的にはSSH接続中に応答がなくなるといった事象です。
応答がなくなるどころかarpも拾えなくなるのでOSごとフリーズしている可能性が高いです。

以前は何ヶ月一回くらいたまーに繋がらなくなるな、といった感じで強制再起動して使っていたのですが最近は繋いでから5分ほどで使えなくなってしまいます。
前はログを見ても大した原因は分からなかったのですが今回また調査しました。

journalctlで調査

大体起動系はdmesgや/var/log配下を調べるのですが一応AIにも聞いてjournalctlも調べました。 -pオプションでログレベル指定できたのか。これは便利だ。

でたログはこちら。

❯ sudo journalctl -b -1 -p err
[sudo] masashi のパスワード: 
 7月 03 15:07:42 ubuntu-nuc6cays kernel: EDAC pnd2: Failed to register device with error -22.
 7月 03 15:08:01 ubuntu-nuc6cays smartd[1608]: Device: /dev/sda [SAT], 2 Currently unreadable (pending) sectors
 7月 03 15:08:08 ubuntu-nuc6cays bluetoothd[1589]: profiles/sap/server.c:sap_server_register() Sap driver initialization failed.
 7月 03 15:08:08 ubuntu-nuc6cays bluetoothd[1589]: sap-server: Operation not permitted (1)
 7月 03 15:08:08 ubuntu-nuc6cays bluetoothd[1589]: Failed to set mode: Failed (0x03)
 7月 03 15:08:27 ubuntu-nuc6cays xrdp[2054]: [ERROR] g_tcp_bind(11, 3389) failed bind IPv6 (errno=98) and IPv4 (errno=22).
 7月 03 15:08:28 ubuntu-nuc6cays xrdp[2054]: [ERROR] trans_listen_address failed
 7月 03 15:08:28 ubuntu-nuc6cays xrdp[2054]: [ERROR] xrdp_listen_main_loop: xrdp_listen_get_port failed
 7月 03 15:08:44 ubuntu-nuc6cays dnsmasq[2577]: cannot read /var/snap/multipass/common/data/multipassd/network/dnsmasq.hosts: Permission denied

家の機器の情報だしこれもAIに丸投げしちゃいました。 すると原因っぽい重大なコメントをもらいました。

ご提示いただいたログの中に、SSHアクセスが突然ハングアップした直接的な原因(または引き金)となり得る重大なエラーが含まれています。特に注目すべきは 2行目のストレージ(SSD/HDD)のエラー です。

怪しい原因:ストレージの不良セクタ
7月 03 15:41:22 ubuntu-nuc6cays smartd[1631]: Device: /dev/sda [SAT], 2 Currently unreadable (pending) sectors
* 状態: システムのメインドライブ(/dev/sda)に、読み取り不可能な不良セクタ(保留中のセクタ)が2つ存在している という警告です。
* ハングアップとの関連: SSHの通信時やバックグラウンド処理中に、OSがこの不良セクタにあるデータ(重要なシステムファイルやライブラリ)を読み込もうとすると、ドライブの応答が途絶えます。Linuxカーネルはディスクの応答を待ち続けるため、コマンドが一切受け付けなくなり、SSHアクセスが完全にハングアップ(フリーズ) します。

システムのディスクにセクターエラーがあってこれを読み込むもうとするとOSがクラッシュするようです。
画面繋いでないのでローカルではどうなっているか分からないのですがキーボードのLEDも反応しなくなったのでクラッシュ説は濃厚となりました。

smartctlで調査

smartctlも取ってねと言われたので提示したら以下の回答が。
このコマンド叩いたことないな。

smartctl のログをご提示いただきありがとうございます。
詳細に確認した結果、HDDが物理的な寿命を遥かに超えて稼働しており、それが原因でSSHのハングアップが引き起こされていることが完全に裏付けられました。

確かに中古で買ってつけっぱなしで運用してました。寿命は仕方ないですね。
指摘された結果はこちらです。

ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
…
  9 Power_On_Hours          0x0032   043   043   000    Old_age   Always       -       23034
…
197 Current_Pending_Sector  0x0032   100   100   000    Old_age   Always       -       2
  • 9 Power_On_Hours(通電時間)のRAW_VALUEが 23034 時間 となっています。これは24時間365日動かし続けて 約2.6年分 に相当します。2.5インチの5400回転HDD(TOSHIBA MK5065GSXF)としては、いつ壊れてもおかしくない経年劣化が進んでいます。
  • 197 Current_Pending_Sector が 2 になっています。前述の通り、OSがこのセクタにアクセスした瞬間に応答が止まり、SSHを含めた全機能がハングアップします。

もうちょっと長い間動かしていたと思いましたが通電が2.6年ですか。5年くらいは持ちそうなものですが2.5インチのディスクだから耐久は弱いのでしょうか。
メーカーにも寄るかもしれませんが。

まとめ

突然ハング系のトラブルシュートはナレッジをかき集めてなぜか直ったというパターンが多かったのですが今回AIの力も借りてディスク原因まで漕ぎつけられたのはよかったです。

セクタ不良でOSが起動しないのは発生した経験もあるしわかるのですが、起動してからクラッシュというのは初めて出会いました。
寿命系は仕方ないとはいえ回避できないのが悔しいですね。

HDDの寿命なので無理に延命せず新しいディスクに再インストールしようと思います。
2年に一回UbuntuのLTSアップデートを記事にしていたのですが今回はできずに26.04のクリーンインストールの様子を書こうと思います。

麻雀の対局相手別ヒートマップを作る

飲み屋仲間で行っている麻雀定例会を月1でやっています。
運営がすごい頑張ってくれていて毎回少なくても3卓は立ちます。

私は初参加から半年強くらいですが対局相手も満遍なく回りました。
一応トータルスコアはプラスでいけているもののいまいちトップまで勝ちきれません。
ボロ負けしている相手はいないものの相性はあるよなと思って天敵をあぶり出すことにしました。

どうあぶり出すか

どういう図を出すのがいいかをGeminiに聞きつつ、スコア差分を比較するのが良いとの回答を得ました。
その差分の大きさが分かりやすいヒートマップで出すのが一番良いと考えました。

和了率や振込率とかで出したかったのですがさすがに仲間内の大会ではデータがありません。

ヒートマップと言ってもググりながら作るのは大変なのでこちらもGeminiに聞きながら作りました。

最近のAIは凄いですね。
無課金ですがそれでも数秒でそこそこのもの作ってくれるので。
ホントにプログラマいらなくなっちゃうかも。

コード

似たようなスコアリングツールは昔作ったのでそれをライブラリとして使いつつヒートマップ作成用のコードを作ります。

たった70行弱なので全部載っけます。

import pandas as pd
import plotly.graph_objects as go
import sys
import numpy as np

df = pd.read_csv(sys.argv[1])
start_date = df['gameid'].head(1).to_numpy()[0][:6]
end_date = df['gameid'].tail(1).to_numpy()[0][:6]

# 対戦相手とのスコア差分を計算するためのセルフマージ
merged = pd.merge(df, df, on='gameid', suffixes=('_me', '_opponent'))

# 自分自身との対戦を除外
merged = merged[merged['player_me'] != merged['player_opponent']]

# 自分のスコア - 相手のスコア」
merged['score_diff'] = merged['score_me'] - merged['score_opponent']

# 名前ごとの平均スコア差分を集計(ピボットテーブル作成)
all_players = sorted(df['player'].unique())

heatmap_df = merged.pivot_table(
    index='player_me',
    columns='player_opponent',
    values='score_diff',
    aggfunc='mean'
)

heatmap_df = heatmap_df.reindex(index=all_players, columns=all_players)

# 対局回数算出
count_df = merged.pivot_table(
    index='player_me',
    columns='player_opponent',
    values='score_diff',
    aggfunc='count'
)

z_text = np.where(
    heatmap_df.notna(),
    heatmap_df.round(1).astype(str) + "<br>(" + count_df.astype(str) + "回)",
    ""
)

# ヒートマップの作成
fig = go.Figure(data=go.Heatmap(
    z=heatmap_df.values,
    x=heatmap_df.columns,
    y=heatmap_df.index,
    colorscale='RdBu',
    zmid=0,
    text=z_text,
    texttemplate="%{text}",
    hoverinfo='z',
    colorbar=dict(title="平均スコア差")
))

fig.update_layout(
    title=f'対戦相手別 相性ヒートマップ (自分 - 相手の平均点差) & 対局回数, 期間: {start_date} - {end_date}',
    xaxis_title='対戦相手',
    yaxis_title='自分',
    width=1200,
    height=1000
)

fig.write_image("対局相性ヒートマップ.png")

Geminiがほとんど書いてくれたので中身をちゃんと把握出来てないですがdfで同じ表をマージしてデータをいじれるという知見を得ました。
しかしpandasは本当に便利です。データ処理何でもできてしまいますね。

毎回固定メンバーではなく参加者のブレがあるので対局回数も出すようにしました。 対局数回だと運の要素も強いので最低でも10回くらい対戦して差を固定させたいですね。

ちなみにリポジトリはこちら。

github.com

インプットデータ

別のリポジトリでも同じデータ使っているのですがそれを使いまわしてます。
以下のCSVを読ませます。プレイヤー名は一応マスクしてます。

2025/07~2025/12までの対局データです。サンプルで1回戦分。

$ head -13 totalscore.csv
gameid,score,rank,player
202507_1_A卓,-15.6,3,プレイヤー1
202507_1_A卓,-46.9,4,プレイヤー2
202507_1_A卓,55.2,1,プレイヤー3
202507_1_A卓,7.3,2,プレイヤー4
202507_1_B卓,-20.5,3,プレイヤー5
202507_1_B卓,-74.3,4,プレイヤー12
202507_1_B卓,16.6,2,プレイヤー6
202507_1_B卓,78.2,1,プレイヤー7
202507_1_C卓,-30.6,3,プレイヤー8
202507_1_C卓,-43.5,4,プレイヤー9
202507_1_C卓,21.8,2,プレイヤー10
202507_1_C卓,52.3,1,プレイヤー11

この日は私参加していないのですが3卓立ちました。毎回4回戦を行ってます。

環境

今回は画像出力したいので手元ですぐ確認できるメインのWindows機で動かします。

> Get-CimInstance Win32_OperatingSystem |
>>     Select-Object Caption, Version, BuildNumber

Caption                  Version    BuildNumber
-------                  -------    -----------
Microsoft Windows 10 Pro 10.0.19045 19045


> python --version
Python 3.13.13

マシンが古いのでWin10のままです…

出力

出力してみます。

> python .\compatibility_heatmap.py .\totalscore.csv

いい感じに出力できました。
プレーヤーのソートがちゃんと昇順になりませんでしたがマスク版データなのでいいでしょう。

青系が勝ち越し、赤系が負け越しで色が濃いほど差が大きいというわけです。
私はプレイヤー10に一番負け越していて、プレイヤー13に一番勝ち越しています。

一応負け越しの相手は判明しましたが打ち筋のデータがないので有効な対策は打てないですね。
注意して打つしかない、という馬鹿な対策になりそうです(笑)

まとめ

今回AIでサクッと作ってしまいましたが動くもの作るのが成果なので製造過程を頑張っても...という気でとりあえず完成を目指しました。
数年前はググって、ウンウン考えてコーディングしていたのですがマジでこんないい感じのものが数分で作れちゃいます。
小さいコードなのでバグもあまり出ようがなかったと思いますが、どの規模まで耐えられるのでしょうか。
ネットだとバグ取りのほうが大変だとよく聞きます。

しかし個人でもでかい規模のコードを書いてみたいものです。