投稿者: manualmaton Page 3 of 280

IDEA SPHERE『坂道と、選ばれなかった物差し』

IDEA SPHEREとして、8年ほど前の記憶を。

発端

今でも乗っているブロンプトンで、(当時は1年も発っていない新車でした)奥秩父から祖父宅へと向かう途上です。

見通しはいいが延々と続く、なかなか骨の折れる上り坂の麓にさしかかろうかという中、

前方に二人のロードバイク乗りがいました。

  • 一人はフルカーボンに高級コンポーネント、引き締まった体つきの年配の方。
  • もう一人は、明らかにおろしたてのピカピカのロードに乗った30代くらいの男性

こちらを見るなり、二人がニヤニヤと笑ったのを覚えています。(特に年配の方)

やがて坂道にさしかかり、しばらくして、後ろにいた新しいロードの方が、勢いよく私を抜いていきました。

「おお、飛ばすなあ」

ぐらいの心境です。そもそも速度に差がある小径車とロードバイク。競うつもりは端からありません。

違和感

ところが、しばらくすると前方の自転車(というよりも自転車乗り)に違和感がありました。

  • 明らかに速度が落ちています。
  • ペダリングが不安定。
  • 脇腹をかばっているのが遠目にも分かります。

差は、少しずつ、しかし確実に縮まっていったわけです。

斜度が一番きつい区間を越え、ようやく平坦に近いところへ出た瞬間、私はそのロードを抜きました。

すると、後ろで見ていたであろう年配のローディが、すごい勢いでのぼってきて、新しいロードの前に立ち、何かを強い口調で言い始めました。

「こんなのに負けてちゃ、上達しないぞ」
「こうやってダンシングするんだ」

その瞬間、流石に気づきます。

私は当て馬にされたのかと。

  • 小径車。
  • 折り畳み。
  • ギアも少ない。
  • 前にバッグ。
  • 普段着
  • ビンディングも無し。

彼らから見れば、極上の“かませ犬”だったのだと思います。

物語の終わり

しかし、彼等には3つの誤算がありました。

地の利

祖父宅の近くとあるように、実質地元民です。どこで力を使い、キツいところと楽なところはどこか? 適切な力配分は? と体で知っていたこと。

「小径車が有利になる状況」

  • 慣性モーメントの小ささ
    • ロードの700Cホイールに比べ、16インチのブロンプトンのホイールは圧倒的に軽く、回転させ始める(あるいは加速を維持する)ためのエネルギーが少なくて済みます。
  • 低速域での粘り
    • 急勾配で速度が落ちた際、大きな車輪を回し続けるのは筋力的に大きな負担(高トルク)がかかりますが、小径車は軽い力でクルクルと回し続けることが可能です。

「戦力の過小評価」

これが最も致命的。

年配のローディーが犯した最大のミスは、「乗り手というエンジンの性能」と「経験値」を無視したことです。

こんな、素人に毛が生えた(ように見える)私を、小径車というだけで判断。

なにせカーボンとクロモリフレーム。軽さは歴然です。ギアもウェアの性能も明らかです。初心者に自信をつけさせるには十分な理があったのでしょう。

しかしながら、ロードの方々はブロンプトンのしなやかな剛性と「長距離を淡々と走ることができる『折りたたみ』自転車」という認識が欠けていて、私はその乗り方に合っていた。

ここで思うこと

この出来事を未だに昨日のことのように思い出すのは、私の普段のサーバ運用のスタイルと重なるからです。

「目的を見失ってはいけない」

サーバにしても、自転車にしても、その目的の本質は「安全性」です。特に、自転車はITと異なり「切り戻し」ができません。(できたらそれこそ魔法か何かです)

何かに勝つのは確かに重要ではありますが、「本質を見失っていないか?」「その勝負に適した獲物は?」「相手が有利、自分が不利な状況は?」を自答していく覚悟が問われました。

「相手に敬意を払う」

これは父が生前言っていた

「戦う相手には常に敬意を払え。その上で全力で叩き潰せ」

という言葉。これには続きがあり、父が夢枕に立ち

「これは、敬意を払わないと必ず慢心を生む。その慢心は油断になるという意味だからな」

とわざわざ但し書きをしたほどです。

まとめ:「道具に使われるか、道具が選ぶか」

またこれを引き合いに出しますが、『ハリー・ポッターと賢者の石』のオリバンダー翁の

The wand chooses the wizard, Mr. Potter. It's not always clear why
「杖が魔法使いを選ぶのです、Mr.ポッター。何故そうなるかは、はっきりとは分かりませんが」

に通じるものがあります。

高価な道具を使うことで自分が強くなったと錯覚してしまう。これは「自転車を楽しむ」ことではなく「他者と比較して優越感に浸る」ことが目的化している状態です。

抜かれた後に新人に説教を始めた年配者も、結局は「自分の見立てが外れた恥ずかしさ」を新人に転嫁しているに過ぎません。本来、自転車は自由な乗り物であり、他者を格付けするための道具ではないはずです。

結局の所、「道具と使い方、その覚悟」が問われる出来事だったので、未だに鮮明に覚えているんだろうなと思います。

『フルメタル・パニック ふもっふ』の『仁義なきファンシー』の

「貴様はひとつミスを犯した」
「敵の戦力は過小評価しないことだ。」

という真理を持って、本稿を締めくくりたいと思います。

サーバのネットワーク情報を一覧で見るためのワンライナー。(RHEL系/Ubuntu系)

設計書を書く際に面倒な「サーバの設定値の抜き出し」を楽にするためのコマンドです。

RHEL系

  • Red Hat Enterprise
  • Rocky
  • Alma

など、dnfで管理するタイプのコマンドです。

{ echo -e "| インタフェース | IPv4 アドレス | ゲートウェイ | DNS |"; echo -e "| --- | --- | --- | --- |"; nmcli -t -f GENERAL.DEVICE,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS device show | awk -F: '/^GENERAL.DEVICE/ {if (dev) printf "| %s | %s | %s | %s |\n", dev, addr, gw, dns; dev=$2; addr=gw=dns="-"; next} /^IP4.ADDRESS/ {addr=$2; next} /^IP4.GATEWAY/ {gw=$2; next} /^IP4.DNS/ {dns=(dns=="-" ? $2 : dns ", " $2); next} END {if (dev) printf "| %s | %s | %s | %s |\n", dev, addr, gw, dns}'; }

| インタフェース | IPv4 アドレス | ゲートウェイ | DNS |

実行と同時に、こういうマークダウンができあがります。(IPはダミーです)

インタフェースIPv4 アドレスゲートウェイDNS
ens192192.0.2.10/24192.0.2.18.8.8.8, 8.8.4.4
ens224198.51.100.50/24198.51.100.11.1.1.1
virbr0192.168.122.1/24--
docker0172.16.0.1/16--
lo127.0.0.1/8--

Ubuntu系

  • Debian
  • Ubuntu
  • LinuxMint

など、aptを用いるLinuxディストリビューションです。

Ubuntuはnmcliを用いないので、同じようにいきません。

{
  echo "| インタフェース | IPv4 アドレス | ゲートウェイ | DNS |"
  echo "| --- | --- | --- | --- |"
  nmcli -t -f GENERAL.DEVICE,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS device show | \
  awk -F: '/^GENERAL.DEVICE/ {if (dev) printf "| %s | %s | %s | %s |\n", dev, addr, gw, dns; dev=$2; addr=gw=dns="-"; next} 
           /^IP4.ADDRESS/ {addr=$2; next} 
           /^IP4.GATEWAY/ {gw=$2; next} 
           /^IP4.DNS/ {dns=(dns=="-" ? $2 : dns ", " $2); next} 
           END {if (dev) printf "| %s | %s | %s | %s |\n", dev, addr, gw, dns}'
}

これの実行結果は

インタフェースIPv4 アドレスゲートウェイDNS
br-dummy0110.0.0.1/16-(br-dummy01):
docker0172.16.0.1/16-(docker0):
eth0192.0.2.15/24192.0.2.1(eth0):, 8.8.8.8, 1.1.1.1
veth_abc123--(veth_abc123):
veth_def456--(veth_def456):
veth_ghi789--(veth_ghi789):

これをどっかに仕込んでおくだけでも管理は楽になります。

クリスマス防衛戦。(ipsetによるDDoS対策)

自分のサーバに組み込んでいるWebセキュリティシステム(と言ってもスクリプトと設定の組み合わせ) 『ONE OUTS』システム。こちらの弱点を見越した追加設定が効力を発揮しました。

何が起きたか?

「自分のvpsがDDoSを喰らったので、カーネルレベルで対処して沈静化」した時のメモです。

状況確認

Redmine サービスダウン

話は2025年12月25日7:40JST。筆者が管理しているサーバにて、サービスダウンを確認。

  1. SSH接続:OK
  2. Growi:OK
  3. BookStack:OK
  4. Redmine:アクセスしようとして「too much connections」

そこで、状況を調べます。

自作ツール「top-procs」にて

--- CPU Consumers (Top 10) ---
%CPU   %MEM   PID      USER         UNIT                           COMMAND
------------------------------------------------------------------------------------------
85.5   5.1    45114    www-data     apache2.service                 Passenger RubyApp: /home/www-data/app (production)

と、CPU利用率85%以上を確認。

確定:DDoS

netstat -tan を実行すると、以下のようなコネクションが大量に表示されました。

tcp6       0  34498 192.0.2.1:443       198.51.100.15:29862     LAST_ACK   
tcp6       0      0 192.0.2.1:443       203.0.113.84:47044      TIME_WAIT  
tcp6       0      0 192.0.2.1:443       192.0.2.55:38844        TIME_WAIT  
tcp6       0      0 192.0.2.1:443       198.51.100.200:57934    ESTABLISHED
...(中略)...
tcp6       0  34081 192.0.2.1:443       203.0.113.120:27327     LAST_ACK   

総計700行にも及ぶコネクション。これは確実に「DDoS」攻撃です。

  • Mod_Security
  • シェルスクリプト
  • Apache設定

で構成されたONE_OUTSシステムはアクセスログを主体としてL7層(アプリケーション層)での防御を行うもの。なので

  • SYNフラッド攻撃
  • アクセスログに残らないレベルの低レイヤー攻撃

と言った「そもそもログに残らない」「ページの閲覧など関係ない」相手には無意味です。(実際、上記をONE OUTSシステムに組み込んでもアクセスログ(という名の執拗なRedmineへのアクセス)が止まりません。

しかも、DDoSというものは実に厄介です。

  • 超・大量のIPから同時にアクセスしてくるためufw/iptablesで制御したとしても遅延が大きい
  • それでマシンパワーを喰う

という、「物理の力でごり押しする破壊行為」です。

漫画『ドリフターズ』にもある

「こりゃ堕とせんと思ったら
その時から目的は変わるのよ
占領からいやがらせに変わる」

この、いやがらせ目的のため、自分のサーバのリソースが奪われるという状況は見過ごせません。

防衛機構:piertotum locomotor

「こんなこともあろうかと」前もって用意していた「ipset」の設定をフルに使いました。

  • ルールを「集合(set)」として管理。
  • 「このIP群をブロック」というリストを一つのルールとしてまとめられる。
  • 内部的にはハッシュテーブル等を利用しており、検索がほぼ定数時間で完了するため非常に高速。

第2オクテット(/16)どころか、悪質なレンジに対しては「第1オクテット(/8)」すら一括でブロックする運用です。

上記リンクの通り

  1. ipsetコマンドをインストールします。
  2. ブロックリストの設定を行います。
  3. ipsetコマンドでSYNフラッド攻撃を行う攻撃者をレンジごとブロックします。

が事前準備です。

このipsetが有効であるかを確認

実はこれがハマった点でした。

 sudo iptables -L ufw-before-input -n --line-numbers | head -n 5

として、

Chain ufw-before-input (1 references)
num  target     prot opt source               destination         
1    DROP       0    --  0.0.0.0/0            0.0.0.0/0            match-set ufw-blocklist src
2    ACCEPT     0    --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     0    --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED

と、match-set ufw-blocklist srcが記されていることを確認します。(私はこれに記述違いがあり、後で修正する羽目になりました)

執拗に攻撃してくるIP/NWの正規表現化

これにはAIの力を借りました。netstat -tanの結果やアクセスログを元に

  • 執拗にアクセスを試みるIP群
  • それが属するASN

を第1オクテット/第2オクテットで抜き出してもらいます。

シェルスクリプトで一括登録

#!/bin/bash

# 1. ターゲットのipset名
SET_NAME="ufw-blocklist"

# 2. DDoS主犯格リスト (CIDR表記)
BAN_LIST=(
  "xx.0.0.0/8"
  "yy.0.0.0/8"
  "zzz.0.0.0/8"

  # 執拗な個体 (CIDRではなく単一IPも登録可能)
  "abc.def.0.0/16" 
)

echo "Hogwarts is threatened!: ${SET_NAME}..."

# 3. ループ処理で注入
for ip_range in "${BAN_LIST[@]}"; do
  # -exist オプションをつけることで、既に登録済みでもエラーにせずスキップさせる
  sudo ipset add ${SET_NAME} ${ip_range} -exist
  if [ $? -eq 0 ]; then
      echo "  Checking... ${ip_range} -> Loaded."
  else
      echo "  Error adding ${ip_range}"
  fi
done

echo "Man the boundaries, protest us, do your duty to our school!"
sudo ipset save ${SET_NAME} -f /etc/ufw/ipsets.save

echo "I've always wanted to use that spell!"

というシェルスクリプトで一気に登録しました。(処理中のechoは『ハリー・ポッターと死の秘宝 part2』屈指の名シーンです)

そもそも、私のvpsは

  • 広告を置いていません
  • アフィリエイトもありません

大嫌いだからです。 主目的は

「私が後で閲覧するときのメモ帳」です。なので、私がアクセスしてこないようなアクセス元のブロックは一切の躊躇を行いません。そのため、\/8で切ることに躊躇はしません。

確認

cat /etc/ufw/ipsets.save

で、

cat /etc/ufw/ipsets.save
create ufw-blocklist hash:net family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xcce80b68
add ufw-blocklist xxx.0.0.0/8

などと表示されればブロック成功です。

沈静化

効果は覿面でした。見られなかったRedmineサイトは無事に表示され、

--- CPU Consumers (Top 10) ---
%CPU   %MEM   PID      USER         UNIT                           COMMAND
------------------------------------------------------------------------------------------
19.3   5.1    45114    www-data     apache2.service                 Passenger RubyApp: /home/www-data/app (production)

CPU利用率も正常に用いました。

まとめ

今回、迅速に対処できたのは、以下の確信があったからです。

  • 「アプリケーション層の防御を突破できない攻撃者は、より原始的なレイヤーでの攻撃に切り替えてくる」
  • 「そして、運用側の注意が散漫になるタイミングを狙ってくる」

事前に「OSの負荷を抑えつつ高速にブロックできる仕組み」を構築していたことが功を奏しました。クリスマスというタイミングを狙ったのは、ホリデーシーズンによる対応の遅れを期待した計画的なものだったのでしょう。

連合艦隊解散の辞にある、

古人曰ク勝ツテ兜ノ緒ヲ締メヨト。

という言葉の重みを再認識する出来事でした。

Growi v7.1.x/v7.2.x→v7.4.0以降へのアップデート

概要

Growi 7.2.x → Growi 7.4.0にアップデートする 手順です。

Growi7.3.3より前のバージョンは脆弱性が存在します。Growiをインターネットで公開している方は、こちらの手順で上げましょう。

注意点

  • Growi 7.4.xはElasticSearchがv9でなければ動きません。
  • また、mongodbの最新版は、古いCPUでは動きません。
  • 上記理由のためGrowiをインターネット環境で動かしている場合は以下を十分検討ください。
    • WAFなどで防御する。
    • ハードウェア環境を最新のmongodb / Elasticsearchが動くものへとアップグレードする。
    • インターネット公開を諦める。

前提

  • 既にgrowi v7.1.x/v7.2.xをインストールしていること。
  • 管理画面トップやトップページ右下からバージョンが7.1.xまたは7.2.xであることを再確認します。
  • systemdによってサービス化されていること。
  • 具体的な手順はhttps://atelier.reisalin.com/projects/zettel/knowledgebase/articles/105
  • 最新版や安定版がリリースされていることを以下のサイトで確認していること。
  • https://github.com/growilabs/growi/releases
  • ※設定ファイルの変更やパッケージインストールの変更、nodeのバージョンアップの必要等があれば、それも事前に済ませます。

さっくりはならない手順

  1. Growiをメンテナンスモードにします。
  2. Growi・Elasticsearchのサービスを停止します。
  3. バックアップを取ります。
  4. gitコマンドで最新版をcheckoutします。
  5. アップグレードを行います。
  6. Elasticsearch・Growiのサービスを再開します。
  7. Growiのメンテナンスモードを解除します。
  8. アップグレードされたことを確認します。

メンテナンスモード有効化

  1. Growiに管理者権限でログインします。
  2. 管理トップ>アプリ設定に進み、「メンテナンスモードを開始する」をクリックします。
  3. トップページに戻り「メンテナンスモード」が表示されていることを確認します。

バックアップ

以下をバックアップします。

  • mongodbの格納データ
cat /etc/mongod.conf |grep dbPath

として、ここのディレクトリ一式を控えます。(筆者環境 /home/mongodb)

このディレクトリを任意の方法でバックアップします。

  • Growiの添付ファイル一式が納められているディレクトリ(ファイルアップロード先をlocalにしている場合のみ)
/growi/root/directory/apps/app/public

(筆者環境 /home/www-data/growi/apps/app/public)ここも念のためバックアップします。

※ 添付ファイルのアップロード先をAWSやAzureなどにしている場合は不要です

  • vpsや仮想ゲストの場合はシステム全体:推奨

スナップショット機能などでシステム全体をバックアップした方が確実で安心です。

ElasticsearchとGrowiの停止

  • Elasticsearchサービス停止
sudo systemctl stop elasticsearch.service
  • サービス停止確認
systemctl status elasticsearch.service

inactive(dead)を確認します。

  • Growiサービス停止
sudo systemctl stop growi.service
  • サービス停止確認
systemctl status growi.service

inactive(dead)を確認します。

作業前バックアップ

  • データディレクトリを丸ごとコピー (-aオプションでパーミッションを維持)
sudo cp -a /var/lib/elasticsearch/ /path/to/backup/dir/elastic_bk.$(date +%Y%m%d)

自分の環境に合わせます。

  • バックアップ確認
sudo ls -l /path/to/backup/dir/elastic_bk.$(date +%Y%m%d)

バックアップした内容があることを確認します。(※管理者権限でないとこのディレクトリを見ることはできません)

リポジトリ設定ファイル名をv9用に変更

Elasticsearchのバージョンを指定するリポジトリをv9に変更します。

  • 現行のリポジトリリストをバックアップ
sudo cp -pi /etc/apt/sources.list.d/elastic-8.x.list /path/to/backup/dir/elastic-8.x.list.$(date +%Y%m%d)
  • リポジトリリストのバックアップ確認
diff -u /path/to/backup/dir/elastic-8.x.list.$(date +%Y%m%d) /etc/apt/sources.list.d/elastic-8.x.list
  • リポジトリリストの名前変更
sudo mv /etc/apt/sources.list.d/elastic-8.x.list /etc/apt/sources.list.d/elastic-9.x.list
  • リポジトリリストの名前変更確認
ls -l /etc/apt/sources.list.d/elastic-9.x.list

ファイルがあることを確認します。

sedコマンドでファイル内の参照先を8.xから9.xに書き換え

sudo sed -i 's/8.x/9.x/g' /etc/apt/sources.list.d/elastic-9.x.list

Elasticsearchのアップグレード

  • パッケージ全体のバックアップ
sudo aptitude update

好みでaptitudeを用いています。必要に応じてaptを用いてください。

  • Elasticsearchのアップグレード
sudo aptitude upgrade elasticsearch

※ Growiインストール時、/etc/elasticsearch/jvm.optionsファイルなどの設定変更を行っているため、アップグレード時の設定ファイルを残すかどうかの確認では、必ずN(残す)を選択します。

  • プラグインのアンインストール

Growiに必要なElasticsearchのプラグインは自動更新されません。この処置を執らないとせっかくアップグレードしたのに起動しないという事態が発生します。

sudo /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-icu
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-kuromoji
  • プラグインの再インストール
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-icu
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-kuromoji

growiディレクトリに移動します

cd /home/www-data/growi && pwd

自分の環境に合わせます。(筆者環境/home/www-data/growi)

リリースタグを確認します。

  • リリースタグ取得
sudo git fetch --tags
  • リリースタグ確認
sudo git tag -l

スペースで確認していき、上記リリースサイトと同じバージョンがあることを確認します。

チェックアウトとインストールを行います。

  • 変更を一時的に退避
sudo git stash
  • チェックアウト
sudo git checkout 【バージョン】

リリースタグは再確認しましょう。今回は 2025/12/24 リリースされたv7.4.0を選択しました。

  • pnpm install
sudo pnpm i
  • ビルド
sudo npm run app:build

ElasticsearchとGrowiの再開

  • Elasticsearchサービス開始
sudo systemctl restart elasticsearch.service
  • サービス開始確認
systemctl status elasticsearch.service

active(running)を確認します。

  • バージョンアップ確認
curl -X GET "localhost:9200"

"number" : "9.1.3",など、9系にアップグレードされていることを確認します。

  • Growiサービス開始
sudo systemctl restart growi.service
  • サービス開始確認
systemctl status growi.service

active(running)を確認します。

メンテナンスモード無効化

  1. Growiに管理者権限でログインします。
  2. 管理トップ>アプリ設定に進み、「メンテナンスモードを終了する」をクリックします。
  3. トップページに戻り「メンテナンスモード」が表示されていないことを確認します。

バージョンアップを確認します。

  1. 画面下部にあるバージョンがチェックアウトしたバージョン(v7.4.x)であることを確認します。
  2. 各種機能(ページ閲覧や編集)などが正常に行えるかを確認します。

バージョンアップ後の作業

必要に応じてバックアップしたファイル一式やスナップショットを削除します。

Linuxサーバ、稼働中のサービスのバージョンを確認するワンライナー。

概要

Linuxサーバの構築時に非常に面倒で厄介な「動いているサービス(systemctl status hoge.service でenabledになっているもの)だけを取り出し、表に転記するという作業が

  • 限りなく単調で
  • とてもミスが多い

重箱の隅をつつくような作業を一発で解消するワンライナーの紹介です。

前提

RHEL系Linuxで動きます。(筆者環境Rocky Linux)

ワンライナー

※これは

sudo su 

としてから入力した方が無難です。(パッケージによっては一般的権限では見られないため)

 { echo -e "| ソフトウェア名 | バージョン |\n| --- | --- |"; systemctl list-unit-files --type=service --no-legend | awk '{print $1}' | grep -v '@\.service$' | xargs -r systemctl show -p FragmentPath | sed 's/^FragmentPath=//' | grep '^/' | sort -u | xargs -I {} sh -c 'if rpm -qf "{}" >/dev/null 2>&1; then rpm -qf "{}" --qf "| %{NAME} | %{VERSION}-%{RELEASE} |\n"; else echo "| $(basename "{}") | (not owned by any package) |"; fi' | sort -u; } 

スクリプトの解説

このコマンドは大きく分けて「ヘッダーの出力」「サービスファイルのパス特定」「パッケージ情報の取得と整形」の3つのフェーズで動いています。

1. 表のヘッダーを作成

  • { echo -e "| ソフトウェア名 | バージョン |\n| --- | --- |"; ... }
    • 最初にMarkdown形式の表の1行目(項目名)と2行目(区切り線)を出力します。
    • 全体を { } で囲むことで、ヘッダーと後の実行結果を一つの出力ストリームとしてまとめています。

2. サービス一覧からファイルパスを特定

ここからがメインのパイプラインです。

  • systemctl list-unit-files --type=service --no-legend
    • システム上の全サービスユニットを表示します。--no-legend でヘッダー行を省きます。
  • awk '{print $1}'
    • 出力結果から1列目(サービス名)だけを抜き出します(例: sshd.service)。
  • grep -v '@\.service$'
    • インスタンス化されたユニット(user@.service など)を除外します。これらは実ファイルが少し特殊なためです。
  • xargs -r systemctl show -p FragmentPath
    • 各サービスに対して、その定義ファイル(ユニットファイル)がディスク上のどこにあるか(FragmentPath)を取得します。
  • sed 's/^FragmentPath=//'
    • 出力に含まれる FragmentPath= という文字列を削除し、純粋なパス名だけにします。
  • grep '^/'
    • 空行や無効なパスを除外し、/ から始まる絶対パスのみを残します。
  • sort -u
    • 重複したパスを削除します。

3. RPMによるパッケージ照会と整形

特定されたファイルパスを一つずつ RPM データベースと照合します。

  • xargs -I {} sh -c '...'
    • 各パス({})に対して、シェルスクリプトを実行します。
  • if rpm -qf "{}" >/dev/null 2>&1; then ...
    • そのファイルが RPM パッケージによって管理されているかを判定します。
    • 管理されている場合: rpm -qf を使い、パッケージ名とバージョンを Markdown の行形式 | 名前 | バージョン | で出力します。
    • 管理されていない場合: (手動で作成したサービスなど)「(not owned by any package)」と出力します。
  • sort -u(最後)
    • 最終的なリストをソートし、重複を排除して綺麗に並べます。

実行結果のイメージ

コマンドを実行すると、以下のような結果が得られます。

ソフトウェア名バージョン
chrony4.1-3.el9
openssh-server8.7p1-10.el9
my-custom-script.service(not owned by any package)
systemd250-6.el9

このワンライナーの利点

  • 徹底した合理性。
    • なにせ「目視確認」という手段が排除されます。一切のヒューマンエラーがなくなります。
  • 一覧性と整合性。
    • 「最新の情報を全て表示せよ」という要求に対してもコマンドを叩くだけで済みます。
  • 再利用性
    • これが一番大きいです。2020年代ITで最も使いやすいMarkdownのテーブル形式。そのため、
      • Redmine
      • VS Code
      • Notion
        などにいくらでも反映可能。そして、一度テーブルにしてしまえばExcelへの転記も一発です。

まとめ

「ウサギとカメの寓話」は、コツコツやる者が強いパターンではありますが、「60分の道を頑張って55分に縮めるよりも、30分かけて『1分で終わる手段を考える』」方が、本当の「タイムパフォーマンス」だと思った次第です。

備考

Ubuntuサーバでも、このようにすれば動きます。

{
  echo -e "| ソフトウェア名 | バージョン |\n| --- | --- |"
  # 1. サービスファイルのパス一覧を取得
  systemctl list-unit-files --type=service --no-legend | \
  awk '{print $1}' | \
  grep -v '@\.service$' | \
  xargs -r systemctl show -p FragmentPath | \
  sed 's/^FragmentPath=//' | \
  grep '^/' | \
  sort -u | \
  # 2. 1行ずつ読み込んで処理(whileループでシェル起動を最小化)
  while read -r path; do
    # dpkg-query -S は dpkg -S より高速で安定しています
    res=$(dpkg-query -S "$path" 2>/dev/null)
    if [ $? -eq 0 ]; then
      pkg=$(echo "$res" | cut -d: -f1)
      # 複数のパッケージがヒットする場合があるため、最初の1つを取得して詳細表示
      dpkg-query -W -f="| \${Package} | \${Version} |\n" "${pkg%%,*}"
    else
      echo "| $(basename "$path") | (not owned by any package) |"
    fi
  done | sort -u
}

2025年12月の差しボド記録。

今回は軽めのボードゲーム。

ひらがじゃん

やはりこれは面白い。単語の使い道や語彙力が囚われます。

また、新たなゲームとしてロストシティタイルゲーム」を実施。

初戦、敗北。2ゲーム目は勝利。三戦目、マイナスが発生しての敗北。

二戦のゲームではありましたが、三戦目に自分がマイナス点を取っての敗北という。言いゲームである以上に、友人との近況報告で盛り上がりました。。

mermaid.jsによる料理の手順。

以前も作った天かす入りのポテトサラダ。

今回、新たにツナも足しました。いい出来だったのでメモを残します。

材料

  • ジャガイモ4個
  • ツナ缶1個
  • 長ネギ、白いところ1本分
  • カニカマ
  • 天かす
  • マヨネーズ
  • 塩こしょう

使った器具

  • レンジ対応の耐熱ガラスボウル
  • ジャガイモピーラー
  • しゃもじ
  • 包丁 / まな板
  • 木のへら / 芋潰し

手順

ジャガイモの下ごしらえをします

  1. ジャガイモは皮をむき、新芽を取ります
  2. 二つ切りにして薄いくし切りにします
  3. 切ったジャガイモは耐熱ガラスボウルに入れてラップをかけ、9分ほどレンジに入れます。

待っている間に準備をします

  1. ネギをみじん切りにしておきます
  2. 各種材料を用意しておきます

混ぜていきます

ジャガイモにじゅうぶん火が通ったら調理開始。

ここからは「粗熱を取る必要はありません」。混ぜるさなかで冷えていくからです。
また、耐熱ガラスボウルに入れることで、他の器に入れることなくダイレクトに混ぜあわせが可能です。

  1. みじん切りしたネギを入れ、潰していきます。
  2. あらかた潰し終えたら、ツナ缶(油ごと)とマヨネーズを入れます。
  3. マヨネーズとツナを更に潰しながら混ぜ合わせます。
  4. 色が馴染んできたらカニカマを入れて混ぜ合わせます。
  5. 天かすを入れて混ぜ合わせます。
  6. 最後に味を調えます。

mermaid.jsによる手順

今回の流れはこちら。

sequenceDiagram autonumber participant 料理人 participant ボウル participant レンジ rect rgb(240, 240, 240) Note over 料理人, ボウル: 下ごしらえ 料理人->>料理人: 芋の皮むき・芽取り・カット 料理人->>ボウル: 芋を入れラップをする end par 加熱と準備 ボウル->>レンジ: 加熱(9分) and 料理人->>料理人: ネギをみじん切りにする 料理人->>料理人: 他の材料を準備する end レンジ-->>ボウル: 加熱完了 rect rgb(255, 245, 230) Note over 料理人, ボウル: 仕上げ(ボウル内で直接調理) 料理人->>ボウル: ネギを加え、芋を潰す 料理人->>ボウル: ツナ(油ごと)・マヨを入れ混ぜる 料理人->>ボウル: カニカマを入れ混ぜる 料理人->>ボウル: 天かすを入れ混ぜる 料理人->>ボウル: 塩こしょうで味を調える end

こういうちょっとした手順確認でも使えるので便利です

一人鍋セット。

最近のお一人様需要の恩恵に与りました。

近所のスーパーで売られていて、思わず手に取った鍋セット。

  • 具材(豆腐含む)
  • 調味料

がアルミ鍋とセットになっていて、

  1. 豆腐と鍋のもとをパックから取り出す
  2. 水を規定量入れる
  3. 豆腐と鍋の素を入れる
  4. 火にかける

火が通ったらできあがり。

できあがりはこちらです。

そもそも鍋は

  • 見た目以上にカロリーが低い
  • 油を使わない(場合が大概)
  • 野菜とタンパク質が豊富。
  • 何よりからだが暖まる

と、冬にうってつけの料理です。これを美味しく戴き

締めの雑炊もしっかり戴きました。

これの注意点は:アルミ鍋は耐久性がないため、二度目の加熱には向いていません。この時は別の鍋を使います

朝は朝で、

別に買っていたかき鍋を味噌汁代わりにした次第です。

空中散歩、再び

「魔法の箒を思わせるような都内の超低空飛行」を体験するため、羽田空港まで東京モノレール羽田線を利用。

滑るように、建物を縫うかのようにモノレールは羽田空港に到着。

ここでの目的は

「チップス」です。疲れにダイレクトに効く揚げ物、それも英国スタイルとなれば味は格別。

この、

  • ジェットの爆音が響き
  • 時折、燃料のにおいがあり
  • 展望デッキからの飛行機が見られる

という、「東京都内で最も空に近い場所」はかなり乙なものです。

こういう場所ではフィッシュアイもしっかり活きました。

CPU/メモリの利用状況を確認するスクリプト.part2

Webサーバのみならず、サーバ運用において「どのプロセスがCPU/メモリを喰っているか」というボトルネックの把握は重要です。

それを把握するためのスクリプトのご紹介です。

なぜボトルネックの把握が重要なのか

以下の3点が主な理由です:

  1. リソースの最適化と安定運用
     高負荷プロセスを特定することで、不要な消費を抑え、他のサービスへの影響を防げます。
  2. 障害予防と早期対応
     異常なリソース使用は障害の前兆であることが多く、早期発見によりダウンタイムを回避できます。
  3. 攻撃予兆への対応
     DDoS/執拗な攻撃などはリソース寮にダイレクトに現れます。

把握するためのシェルスクリプト

といっても、topwコマンドなどでは煩雑な情報が多いため、シンプルに

  1. CPUを多く使っているプロセス
  2. メモリを多く使っているプロセス

に絞り込みを行います。というのも、プロセスの暴走は先に示したとおり、CPU/メモリを多く使うからです。

それをより分かりやすく視覚化するスクリプト例が以下の通り。

top-procs.sh等の名前で、任意の場所に作成します。

#!/bin/bash

#================================================================
# System Resource Monitor (Refined by Riza)
#================================================================

# --- Colors ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# --- Defaults ---
TOP_N=5
MODE="all" # all, cpu, mem

# --- Usage ---
usage() {
    echo -e "${CYAN}Usage: $(basename "$0") [-c] [-m] [-n NUM] [-h]${NC}"
    echo "  -c      : CPU使用率順で表示"
    echo "  -m      : メモリ使用率順で表示"
    echo "  -n NUM  : 表示する行数を指定 (Default: ${TOP_N})"
    echo "  -h      : ヘルプ表示"
    exit 0
}

# --- Argument Parsing (getopts is standard) ---
while getopts "cmn:h" opt; do
  case $opt in
    c) MODE="cpu" ;;
    m) MODE="mem" ;;
    n) TOP_N="$OPTARG" ;;
    h) usage ;;
    \?) usage ;;
  esac
done

# --- Core Function ---
# Arguments: 
# 1: Sort Key (e.g., -pcpu or -pmem)
# 2: Title
# 3: Format String for ps
show_top() {
    local sort_key="$1"
    local title="$2"

    echo -e "\n${BOLD}${CYAN}--- ${title} (Top ${TOP_N}) ---${NC}"

    # Header format specifically tailored for clarity
    printf "${YELLOW}%-6s %-6s %-8s %-20s %s${NC}\n" "%CPU" "%MEM" "PID" "USER" "COMMAND"
    echo "---------------------------------------------------------"

    # ps command explanation:
    # -e : Select all processes
    # -o : User-defined format
    # --sort : Internal sorting (descending with -)
    # head : Limit output

    ps -e -o pcpu,pmem,pid,user,args --sort="${sort_key}" | \
    head -n "$((TOP_N + 1))" | tail -n "$TOP_N" | \
    awk '{
        # Highlighting logic
        cpu=$1; mem=$2;

        # Colorize CPU if > 50% or MEM > 50% (Arbitrary threshold for visual aid)
        color="'"${NC}"'";
        if (cpu > 50.0 || mem > 50.0) color="'"${RED}"'";
        else if (cpu > 10.0 || mem > 10.0) color="'"${GREEN}"'";

        # Reconstruct the line with printf for alignment
        # $1=CPU, $2=MEM, $3=PID, $4=USER, $5...=COMMAND

        # Capture the full command (args) which starts from $5
        cmd=""; for(i=5;i<=NF;i++) cmd=cmd" "$i;

        printf "%s%-6s %-6s %-8s %-20s %s%s\n", color, $1, $2, $3, $4, cmd, "'"${NC}"'"
    }'
}

# --- Main Logic ---

case $MODE in
    cpu)
        show_top "-pcpu" "CPU Consumers"
        ;;
    mem)
        show_top "-pmem" "Memory Consumers"
        ;;
    all)
        show_top "-pcpu" "CPU Consumers"
        show_top "-pmem" "Memory Consumers"
        ;;
esac

echo ""

以前の改良点

  1. ps --sort の活用: これが最大の改良点よです。ps aux | sort -k ... はデータをテキストとして処理するから遅いのですが、ps --sort=-pcpuはカーネルから情報を取得する段階でソートします。
  2. -n オプションの追加: ./top-procs.sh -n 10 と打てばトップ10が見られるようにしています。
  3. しきい値による色付け: awk の中で、CPUやメモリを激しく消費しているプロセス(例えば50%以上)を赤色で表示するようにしています。
./top-procs.sh

を実行することで、

--- CPU使用率 (上位 5 プロセス) ---
 %CPU    PID  COMMAND
-----------------------------------------
 52.10%  12345  ruby_app_server: /var/www/webapp1 (production)
  9.40%   1086  /usr/sbin/database_server [...]
  3.80%  42162  /usr/sbin/web_server -k start
  1.50%  42161  /usr/sbin/web_server -k start
  0.90%   7978  nodejs_process /path/to/nodejs_app/server.js

--- メモリ使用率 (上位 5 プロセス) ---
 %MEM    PID  COMMAND
-----------------------------------------
 13.10%   1984  /usr/bin/java -Xms256m -Xmx256m [...] search_engine -Des.path.home=/usr/share/search_engine [...]
 10.00%   1086  /usr/sbin/database_server [...]
  7.50%  12345  ruby_app_server: /var/www/webapp1 (production)
  3.90%  78630  ruby_app_server: /var/www/webapp2 (production)
  3.80%  76583  ruby_app_server: /var/www/webapp3 (production)

が出てきます。

この例では、rubyアプリが圧倒的にCPUを消費し、ElasticSearchがメモリを食っているというのが分かります。

そして、

  • -a / 引数無し : CPUとメモリの両方を表示
  • -c : CPU情報のみを表示
  • -m : メモリ情報のみを表示
  • -h : これら引数やスクリプトの内容を表示

と、目的に合わせた柔軟な表示も可能にしています。

ついでにコマンド化

こういった障害発生時のボトルネック判定時、いちいちスクリプトの場所を探すという悠長なことはできません。

なので、余裕がある(つまりこのスクリプトを作成した直後です)状況で、

sudo ln -sf /path/to/script/top-procs.sh /usr/local/bin/top-procs

として、どこからでもコマンドを呼び出せるようにします。(スクリプトの場所は自分がこれを保存した絶対パスを指定してください)

which top-procs

/usr/local/bin/top-procs

と表示されればコマンド化は完了。こうすることにより、どのユーザーでもこのコマンド一発で上記のボトルネック判定が可能になります。

Page 3 of 280

Powered by WordPress & Theme by Anders Norén