あるファイルがn日以上更新されてないか調べる方法
find -mtime を使う
# 最終更新から 24 時間以上経過しているか if [[ "$(find /path/to/file -mtime +0)" != "" ]]; then echo 'OLD!' else echo 'NEW!' fi
-mtime +0
がなぜ「24時間以上前」になるのか?について:
-mtime n ファイルの最終内容更新日時が、基点となる時刻から計算して n 日前に当たれば、真を返す (訳注: 基点となる時刻は、デフォルトでは find を実行している今現在である)。 (snip) なお、デフォルトの動作のように、現在時刻から数えて 24 時間前から 48 時間前までを 1 日前とする
つまり、
- 1日前 = 24時間前から48時間前
なので、
- n日前 = n*24時間前から(n+1)*24時間前
つまり、
-mtime 0 = 最終更新が0日前 = 0時間前から24時間前 -mtime 1 = 最終更新が1日前 = 24時間前から48時間前 -mtime 2 = 最終更新が2日前 = 48時間前から72時間前
次に n の記法について。
数値の引き数は、以下の形で指定することができる。 +n n より大きい。 -n n より小さい。 n ちょうど n。
なので、
-mtime +0
- =
-mtime 1
または-mtime 2
または...
- = 最終更新が0日前より過去
- = 24時間前より過去
となる。
ほかにイケてる方法があったら教えてください!!><
これだけ覚えればOK、rsyncのディレクトリ、ファイルパスの指定方法
ディレクトリどうしをまるごとコピーしたい場合
コピー元・先両方に末尾に / をつけて、コピー先のディレクトリまで指定する。
rsync -av /path/to/dir/ remote:/path/to/dir/
rsync -av /path/to/dir remote:/path/to
とか別の書き方もあるけどパット見わかりづらいので ダメ 。
ディレクトリの末尾に /
をつけるのが肝要。
ファイルをコピーしたい場合
コピー元・先両方をファイルのパスで指定する、 もしくは、 コピー元をファイルのパスで、コピー先を末尾に / をつけたディレクトリのパスで指定する。
rsync -av /path/to/file remote:/path/to/file もしくは rsync -av /path/to/file remote:/path/to/
おまけ
コピー元・先でパスが同じ場合
コピー元・先でパスが同じなら -R
を使うのもよい。コピー先のパス指定ミスが防げるので。
rsync -avR /path/to/dir/ remote:/ rsync -avR /path/to/file remote:/
コピー元がリモートで、複数指定する場合
rsync -av remote:/path/to/file1 remote:/path/to/file2 /path/to/dir/
と書いてもよいが、リモートホスト名は省略できる。
rsync -av remote:/path/to/file1 :/path/to/file2 /path/to/dir/
Ubuntu 18.04 で Postfix の multi instances の service unit を有効にする方法
Managing multiple Postfix instances on a single host に従い、multi instances の設定をした後、その service unit を用意する方法のメモ。
/lib/systemd/system-generators/*
などにあるファイルはOS起動時や systemd のリロード (systemctl daemon-reload
) 時に実行される- postfix パッケージに
/lib/systemd/system-generators/postfix-instance-generator
が含まれている postfix-instance-generator
は、postconf -h multi_instance_directories
の結果に応じて/run/systemd/generator/postfix.service.wants/postfix@postfix-XXX.service
な symlink を作るpostmulti -I postfix-XXX -G mta -e create
すると、/etc/postfix/main.cf
のmulti_instance_directories
に/etc/postfix-XXX
が追加される
/run/systemd/generator/postfix.service.wants/*
は、systemctl start postfix
したときに一緒に start される- なので、
systemctl start postfix
で子インスタンスも起動される。stopとかrestartも同様
結論
子インスタンスの設定したあとに systemctl daemon-reload
すればおk
Apache が AH00144 で落ちる件
事象
- Ubuntu 18.04
- apache2 (2.4.29-1ubuntu4.12)
で、apache2 プロセスが次のエラーメッセージを吐いて落ちるという連絡を受けて調べました。これはその原因と対処法のメモです。
[mpm_prefork:emerg] [pid 18633] (43)Identifier removed: AH00144: couldn't grab the accept mutex [mpm_prefork:emerg] [pid 18632] (43)Identifier removed: AH00144: couldn't grab the accept mutex [core:alert] [pid 18624] AH00050: Child 18632 returned a Fatal error... Apache is exiting! [:emerg] [pid 18624] AH02818: MPM run failed, exiting
原因
簡単にいうと、Apache が Mutex に使っているセマフォを systemd-logind が消してしまうのが原因です。
Apache の Mutex
Mutex ディレクティブ のデフォルトは Mutex default
で、どの機構が採用されるかは APR に委ねられています。
どの機構が採用されるかはこのようにして確認できます。
$ cat mutex-default.c #include <apr_portable.h> int main(int argc, char** argv) { printf("%s\n", apr_proc_mutex_defname()); return 0; } $ gcc $(apr-config --includes) mutex-default.c $(apr-config --link-ld) $ ./a.out sysvsem
存在する IPC セマフォなどの確認は ipcs
コマンドでできます。
Apache が起動していれば、セマフォがいくつか表示されるはずです。
$ ipcs -a ------ Message Queues -------- key msqid owner perms used-bytes messages ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status ------ Semaphore Arrays -------- key semid owner perms nsems 0x00000000 5111808 gyoza 600 1 0x00000000 5144577 gyoza 600 1 0x00000000 5046274 gyoza 600 1 0x00000000 5177347 gyoza 600 1 0x00000000 5210116 gyoza 600 1
systemd-logind の RemoveIPC
systemd-logind はデフォルトで RemoveIPC=yes
になっています。
RemoveIPC
とは logind.conf(5) によれば次のように説明されています。
RemoveIPC= Controls whether System V and POSIX IPC objects belonging to the user shall be removed when the user fully logs out. Takes a boolean argument. If enabled, the user may not consume IPC resources after the last of the user's sessions terminated. This covers System V semaphores, shared memory and message queues, as well as POSIX shared memory and message queues. Note that IPC objects of the root user and other system users are excluded from the effect of this setting. Defaults to "yes".
つまり、
- logind 管理下のセッションが全てなくなるときに、そのユーザーの IPC セマフォなどを全て削除する
- ただし、root とシステムユーザーは除外される
ということです。
システムユーザーとは、 uid が 999 以下のユーザーのことです。
再現方法
- Apache を非システムユーザー (uid が 1000 以上) で起動する → セマフォが作られる)
- その非システムユーザーで SSH ログインする
- ログアウトする → セマフォが消される
- Apache にリクエストが来るとプロセスが死ぬ
これで再現します。
通常は Apache はシステムユーザである www-data の権限で起動されるのでこの問題は発生しませんが、この環境では深淵な理由で非システムユーザーで起動していました。
対処法
内製のデーモンで IPC を使っていると同じようにハマりそうなので、後者の対処法を採用しました。
Apache でセマフォではない Mutex を使う
Mutex file:/var/lock/apache2 default
systemd-logind で IPC を消さないようにする
# install -d -o root -g root -m 755 /etc/systemd/logind.conf.d/ # cat << EOF > /etc/systemd/logind.conf.d/ipc.conf [Login] RemoveIPC=no EOF # systemctl restart systemd-logind.service
kernel panic 時の oops メッセージを netconsole でインターネット越しに送信する
自宅サーバー (!) がちょいちょい落ちるんです。たぶんコンソールには kernal panic 時の oops メッセージが表示されてると思うんですが、モニタは繋いでないし宅内には他にサーバーもいないのでシリアル経由で送信することもできないので確認する術がなく。
で、そういえば netconsole ってのがあったなーと思い出したんで、設定してみた次第です。
ちなみに送信先は GCE の Always Free のインスタンス (ちゃんと中国とオーストラリアからのアクセスはフィルタしています) です。
疎通の確認
netconsole は UDP を使います。受信側のポート番号を決めて (6666 とします) 、まずはそれが通るようにフィルタリングの設定をします。
疎通確認はこんな感じで。
# 受信側 sudo tcpdump -i any -nlxX port 6666
# 送信側 echo konyanya-chiwa > /dev/udp/DEST_IP_ADDRESS/6666
送信側の設定
echo 'options netconsole netconsole=6665@SRC_IP_ADDRESS/eth0,6666@DEST_IP_ADDRESS/GW_MAC_ADDRESS' | sudo tee /etc/modprobe.d/netconsole.conf echo netconsole | sudo tee -a /etc/modules sudo modprobe netconsole
SRC_IP_ADDRESS
- 送信元の eth0 についてるプライベート IP アドレス
DEST_IP_ADDRESS
- 送信先のグローバル IP アドレス
GW_MAC_ADDRESS
- 送信元のデフォルトゲートウェイの MAC アドレス
- 送信先が同じサブネット内の場合は送信先の MAC アドレスを指定しますが、サブネット外の場合はデフォルトゲートウェイの MAC アドレスを指定します
00:00:5e:00:53:31
な感じで指定
試行錯誤するときはこんな感じでおk
sudo modprobe netconsole 'netconsole=6665@SRC_IP_ADDRESS/eth0,6666@DEST_IP_ADDRESS/GW_MAC_ADDRESS' sudo rmmod netconsole
受信側の設定
syslog は使わずに udplogger を使いました。
curl -L -o udplogger.c 'https://osdn.net/projects/akari/scm/svn/blobs/head/branches/udplogger/udplogger.c?export=raw' gcc -Wall -o udplogger udplogger.c mkdir /var/log/netconsole udplogger dir=/var/log/netconsole port=6666
動作確認
送信側で
echo s | sudo tee /proc/sysrq-trigger
すると、受信側で
# /var/log/netconsole/YYYY-MM-DD.log 2020-02-21 13:25:36 192.0.2.100:6665 [271267.178610] sysrq: Emergency Sync
と出力されるはずです。
参考
Ubuntu 18.04 で OS 起動時の apt update と unattended-upgrade を抑制する方法
時間のない人向けのまとめ
sudo systemctl edit apt-daily.timer sudo systemctl edit apt-daily-upgrade.timer
どちらも次の内容で保存します。
[Timer] Persistent=false
もしくは、直接ファイルを編集して反映してもよいです。
sudo install -d -o root -g root -m 755 /etc/systemd/system/apt-daily.timer.d cat <<EOF | sudo tee /etc/systemd/system/apt-daily.timer.d/override.conf [Timer] Persistent=false EOF cp -pR /etc/systemd/system/apt-daily.timer.d/ /etc/systemd/system/apt-daily-upgrade.timer.d/ sudo systemctl daemon-reload
何が問題なのか?
Ubuntu 18.04、少なくとも EC2 用の Ubuntu cloud image の 20200131 版 (18.04.4) では、毎日 6:00 頃に apt update
と unattended-upgrade
(セキュリティ関連の更新パッケージのインストール) が行われる (詳しくは後述) のですが、その時間帯に電源がオフで稼働していなかった場合は、次の OS 起動時に実行される設定になっています。
「その時間帯に電源がオフで稼働していなかった」は、AMI からインスタンスを起動したときも同じ状況になるので、AMI が古ければ古いほど更新パッケージが多くなり、起動直後に負荷が高まったり、しばらく apt install
できない (unattended-upgrade
がロックを獲得しているので) 時間が続いたりします。
その結果、次のような問題が発生します。
- インスタンス起動後に自動的にプロビジョニングを行うようにしている運用の場合、プロビジョニング (の過程のパッケージのインストール) が完遂するまでの時間が安定しなかったり、場合によってはタイムアウトしてエラー終了してしまう
特にオートスケーリングの場合は、一刻も早くインスタンスを投入したいので深刻な問題になります。
またもし、AMI 採取用のインスタンスから日次で AMI を作る運用の場合は、パッケージはほぼ最新であることが期待できます。
というわけで、 OS 起動時の更新処理を実行されないようにした、というお話でした。
時間のある人向けの詳細
まず定期的に更新処理が実施される仕組みの説明から。
次の 2 つの systemd の timer ユニットがトリガーとなります。
apt-daily.timer
apt-daily-upgrade.timer
内容を確認すると、
$ systemctl cat apt-daily.timer # /lib/systemd/system/apt-daily.timer [Unit] Description=Daily apt download activities [Timer] OnCalendar=*-*-* 6,18:00 RandomizedDelaySec=12h Persistent=true [Install] WantedBy=timers.target $ systemctl cat apt-daily-upgrade.timer # /lib/systemd/system/apt-daily-upgrade.timer [Unit] Description=Daily apt upgrade and clean activities After=apt-daily.timer [Timer] OnCalendar=*-*-* 6:00 RandomizedDelaySec=60m Persistent=true [Install] WantedBy=timers.target
となっており、 apt-daily.timer
は毎日 6:00 と 18:00 頃、 apt-daily-upgrade.timer
は毎日 6:00 頃に発火するのがわかります。
発火すると、対応する systemd の service ユニットが実行されます。
systemctl cat
で確認すると、実行されるコマンドライン (ExecStart
) が確認できます。
apt-daily.service
ExecStart=/usr/lib/apt/apt.systemd.daily update
apt-daily-upgrade.service
ExecStart=/usr/lib/apt/apt.systemd.daily install
ざっくり言うと、
/usr/lib/apt/apt.systemd.daily update
は
- apt-get update
- apt-get --download-only dist-upgrade
- unattended-upgrade --download-only
/usr/lib/apt/apt.systemd.daily install
は
- unattended-upgrade
を行うためのもので、APT の設定で個別に実施する/しないを制御することができます。
デフォルトでの次のような設定になっていて、
$ apt-config dump | grep Periodic APT::Periodic ""; APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Download-Upgradeable-Packages "0"; APT::Periodic::AutocleanInterval "0"; APT::Periodic::Unattended-Upgrade "1";
apt-get update
実施する--download-only
系は実施しないunattended-upgrade
実施する
という挙動になります。
ここまでが定期的に実行される仕組みの説明で、次からは主題の OS 起動時の更新処理の仕組みについてです。
timer ユニットの定義をみると Persistent=true
というのがあり、これがキモです。
systemd.timer(5) から引用すると
Persistent=
Takes a boolean argument. If true, the time when the service unit was last triggered is stored on disk. When the timer is activated, the service unit is triggered immediately if it would have been triggered at least once during the time when the timer was inactive. This is useful to catch up on missed runs of the service when the system was powered down. Note that this setting only has an effect on timers configured with OnCalendar=. Defaults to false.
とのことなので、冒頭の方法で Persistent=false
と上書き設定したわけです。
ちなみに
定期的なのも含め一切の自動的な更新処理をオフにしたい場合は、 /usr/lib/apt/apt.systemd.daily
に
# check if the user really wants to do something AutoAptEnable=1 # default is yes eval $(apt-config shell AutoAptEnable APT::Periodic::Enable) if [ $AutoAptEnable -eq 0 ]; then exit 0 fi
という処理があるので
echo 'APT::Periodic::Enable "0";' | sudo tee /etc/apt/apt.conf.d/99disable-periodic
とかするといいんじゃないかと思います。
あと /etc/cron.daily/apt-compat
という crontab があるんですが、
if [ -d /run/systemd/system ]; then exit 0 fi
となってるので systemd な環境では無いのと同じです。
MySQL 5.7でクライアントプログラムがCPUを食いつぶす件
同根のバグレポートが散見されますが、ここ数ヶ月、状況をみるに修正される見込みがなさそうなので記録しておきます。
事象
MySQL 5.7 の libmysqlclient (libmysqlclient.so.20) を使用しているプロセスが、無限ループに突入して CPU (user%) を食いつぶし続ける。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 28150 hirose31 20 0 32488 6080 5492 R 93.8 0.3 0:16.70 mysql
発現条件
MySQLが提供しているパッケージのMySQL 5.7.25, 5.7.26, 5.7.27 で確認。
- Ubuntu 18.10, mysql-community-client 5.7.25-1ubuntu18.10
- Ubuntu 18.04, mysql-community-client 5.7.25-1ubuntu18.04
- Ubuntu 18.04, mysql-community-client 5.7.26-1ubuntu18.04
- Ubuntu 18.04, mysql-community-client 5.7.27-1ubuntu18.04
- Ubuntu 16.04, mysql-community-client 5.7.25-1ubuntu16.04
- CentOS 7.6, mysql-community-client-5.7.25-1.el7.x86_64
- Fedora 29, mysql-community-client-5.7.25-1.fc29.x86_64
- Oracle Linux 7.6, mysql-community-client-5.7.25-1.el7.x86_64
再現手順
- mysql_real_connect() でサーバーに接続する
- なんらかのシグナルを受信する
- mysqld サーバー側からコネクションを切断する
- 例えば以下のようにして切断する
- wait-timeout を超過する
- mysqld を停止する
- mysql_real_query() もしくは mysql_ping() が呼ばれる
- 無限ループに突入して CPU (user%) を食いつぶす
なお、MySQL 5.6 (libmysqlclient 18) や MySQL 8.0 (libmysqlclient 21) では発生しない。原因は yaSSL にあるのだけど、MySQL 8.0 は yaSSL ではなく OpenSSL を使っているので。
再現コード
回避方法
MySQL サーバーの my.cnf の [mysqld]
グループに skip-ssl
と書く。
# /etc/my.cnf [mysqld] ... skip-ssl ...
追記 2019-11-22
SSL の実装はソースツリー内にある yaSSL と OpenSSL とで選択できたのですが、MySQL 5.6.46, 5.7.28 から yaSSL が削除され OpenSSL のみとなりました。
当然、バイナリで配布されているパッケージも OpenSSL を使うようにビルドされています。そこで 5.7.28 で試したところ、問題が再現しないことが確認できました。