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を使えと回答がきたので後者を試してみることにしました。
ブラウザベースで描画してくれるパッケージです。
インストール
仮想環境作ってから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を見つけてこちらのほうが色々触れそうだったので遊んでみることにしました。
クライアントーサーバ構成でレスポンスが帰ってくるものが良いと思い、またマネージドサービスも触ろうということでサーバレスで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
- スティング (1973年)
詐欺師の主人公が仲間の仇のためマフィアのボスを嵌めるという話です。
大がかりな詐欺セット作ったりと入念な計画が成功へ導くという感覚が得られる感じがします。
古い映画ながら古臭さを感じることはなくストーリーも面白い映画でした。
章立てになっているのもおしゃれでよかった。 ジュラシックパーク (1993年)
超有名な恐竜映画です。友人に見たほうがいいと何度も進められたので視聴しました。
パニックホラーの要素もありつつグロい感じはそんなになかったので楽しめました。
有名なのであまり語ることもないですがシンプルに面白いです。メリッサ・マッカーシー 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回くらい対戦して差を固定させたいですね。
ちなみにリポジトリはこちら。
インプットデータ
別のリポジトリでも同じデータ使っているのですがそれを使いまわしてます。
以下の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でサクッと作ってしまいましたが動くもの作るのが成果なので製造過程を頑張っても...という気でとりあえず完成を目指しました。
数年前はググって、ウンウン考えてコーディングしていたのですがマジでこんないい感じのものが数分で作れちゃいます。
小さいコードなのでバグもあまり出ようがなかったと思いますが、どの規模まで耐えられるのでしょうか。
ネットだとバグ取りのほうが大変だとよく聞きます。
しかし個人でもでかい規模のコードを書いてみたいものです。