20200315のLinuxに関する記事は16件です。

Apache Zeppelin のインストール

やってみたのでメモ

前提

CentOS 7
Java インストール済み

手順

  1. tar ファイルをダウンロードする

    # curl -LkvOf https://ftp.jaist.ac.jp/pub/apache/zeppelin/zeppelin-0.8.2/zeppelin-0.8.2-bin-all.tgz
    
  2. ダウンロードした tar ファイルを展開する

    # tar zxf zeppelin-0.8.2-bin-all.tgz
    
  3. 展開してできたディレクトリを/opt配下に移動する

    # mv zeppelin-0.8.2-bin-all /opt/zeppelin
    
  4. zeppelin 実行用ユーザを作成する

    # useradd zeppelin
    
  5. 必要に応じて環境変数を設定する(ここではIPアドレス)

    # cp /opt/zeppelin/conf/zeppelin-env.sh.template /opt/zeppelin/conf/zeppelin-env.sh
    # vi /opt/zeppelin/conf/zeppelin-env.sh
    
    export ZEPPELIN_ADDR=<ブラウザでアクセスするIPアドレス>                          # Bind address (default 127.0.0.1)
    
  6. ディレクトリの所有者を zeppelin 実行用ユーザに変更する

    # chown -R zeppelin:zeppelin /opt/zeppelin
    
  7. zeppelin サービスファイルを作成する

    # vi /etc/systemd/system/zeppelin.service
    
    [Unit]
    Description=Apache Zeppelin Service
    
    [Service]
    Type=forking
    User=zeppelin
    ExecStart=/opt/zeppelin/bin/zeppelin-daemon.sh start
    ExecStop=/opt/zeppelin/bin/zeppelin-daemon.sh stop
    
    [Install]
    WantedBy=multi-user.target
    
  8. サービスファイルを再読み込みする

    # systemctl daemon-reload
    
  9. 起動する

    # systemctl start zeppelin
    
  10. ブラウザからアクセスする

    Zeppelin.アクセス直後png.png

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく使うLinuxコマンド

Linuxサーバーの操作するときに、ちょっとした操作の備忘録です
追記増えるため度に更新していきます

cdコマンド [移動]

指定のパスに移動するためのコマンド

[vagrant@vagrant ~]$ cd /var/www
[vagrant@vagrant www]$

pwdコマンド [現在位置を把握する]

現在の場所を確認するためのコマンド

[vagrant@vagrant ~]$ pwd
/home/vagrant

cpコマンド [複製]

コピーコマンド。
ファイルやフォルダなど複製するためのコマンド

cp コピー元ファイル コピー先ファイル
cp fileA.txt fileA_copy.txt

よく使うオプション

オプション 内容
-a 元ファイルの属性とディレクトリの構成を保持してコピーする。可能な限り。
-r ディレクトリごとコピーする(再帰的)

フォルダごとコピー
cp -r コピー元ディレクトリ コピー先ディレクトリ

その他オプション参考URL
https://eng-entrance.com/linux_command_cp

mvコマンド [移動/名前変更]

ファイルなど場所移動や名前変更を行う

ファイル名変更

mv 対象ファイル 変更したいファイル名
mv file1 file2

ファイル名を変更したいファイル名に変更したいときに使用します
※また、フォルダ名の変更でも動きます

移動

mv ファイル1 ファイル2 ファイル3 ディレクトリA
mv file1 file2 file3 ./dir
ファイルを対象のフォルダに移動します

rsyncコマンド [同期する]

リモート環境とファイルやディレクトリを「同期(sync)する」というコマンド

rsync -[オプション] 同期元/ 同期先/

よく使うオプション

オプション 内容
-a アーカイブモード
-v 動作内容を表示する
-z 転送中のデータを圧縮する

パーミッション関係のオプション

オプション 内容
-e 実行可能属性を保持する
-p パーミッションを保持する
-g 所有グループをそのまま保持する
-o 所有者をそのまま保持する(自分以外の所有者を保持するにはroot権限が必要)

シンボリックリンク関係のオプション

オプション 内容
-l シンボリックリンクをシンボリックリンクのままコピーする

その他詳しい詳細はこちらを参照ください
https://www.atmarkit.co.jp/ait/articles/1702/02/news031.html

sshを使用し自身のPCに同期する例
rsync -avz -e 'ssh -i ~/Documents/login/[キー名].pem'  --rsync-path='sudo rsync'  [sshユーザー]@[ドメイン]:/var/www/html/[サーバー対象ディレクトリ] ./[ローカル対象ディレクトリ名]

※サーバー上フォルダのファイルを自身のPC、または別サーバーにバックアップとるときに使います。

dfコマンド[ディスクの空き領域表示]

ディスクの空き領域が表示することができます

※ディクスの容量不足が起きるとファイルがアップロードできないなど起こるため容量を確認するなど定期的に行ったりします

よく使うオプション

オプション 内容
-h サイズに応じて読みやすい単位で表示する
--total 空き領域の合計を表示

duコマンド

ディスクの使用量を表示

du [オプション] [ファイルまたはディレクトリ]

※ディクスの容量不足の際の原因探しなどに使用する事が多いです

よく使うオプション

オプション 内容
-s 指定したディレクトリの合計のみを表示 (サブディレクトリは含めない)
-d深さ 集計するディレクトリの深さを指定
-h サイズに応じて読みやすい単位で表示
ディレクトリ直下のフォルダのみ容量表示

du -sh ./* | sort -nr

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Linux 2のsshd_configとauthorized_keysを学ぶ

内容

初期Amazon Linux 2の/etc/ssh/sshd_config設定を色々と変更しつつsshd_configについて学んでいく。
ついでにauthorized_keysも。

ちょっとだけsshd_configを学ぶ

取り敢えず使いそうなところの設定

意味 該当箇所
パスワード認証 PasswordAuthentication
チャレンジレスポンス認証 ChallengeResponseAuthentication
公開鍵認証 PubkeyAuthentication
rootログイン PermitRootLogin
接続ポート Port XX(基本22)
SSH接続バージョン Protocol

Amazon Linux 2の初期設定

設定
#Port 22

#PubkeyAuthentication yes
# the setting of "PermitRootLogin without-password".

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
PasswordAuthentication no

# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication no

#PermitRootLogin yes

どういう設定が良いのか

を調べてみた所共通して多く出てきたのが以下設定。

・rootログインの禁止
・パスワード認証の禁止
・公開鍵認証の
・SSH接続Verison 2のみ許可
・チャレンジレスポンス認証を無効化

推奨値 設定値 推奨値との比較
rootログインの禁止 #PermitRootLogin yes ×
チャレンジレスポンス認証 ChallengeResponseAuthentication no
公開鍵認証の許可 #PubkeyAuthentication yes
SSH接続Verison 2のみ許可 設定見つけられず
パスワード認証の禁止 PasswordAuthentication no

ここで、×と?だったものに関して接続を試してみるとどうなるのかを検証。

rootログインの禁止

/etc/ssh/sshd_configのrootログインに関する設定値は以下であった

#PermitRootLogin yes

ただし、公式サイトでは以下のように記載されている。
Amazon Linux 2

デフォルトで安全
Amazon Linux 2 では、SSH キーペアの使用およびリモートルートログインの無効化により、リモートアクセスが制限されています。また、Amazon Linux 2 では、必須ではないにもかかわらずインスタンスにインストールされるパッケージの数が削減されるため、セキュリティの脆弱性のリスクを抑えることができます。深刻度が "緊急" または "重要" であるセキュリティアップデートは、初回起動時に自動的に適用されます。

試してみないと良く分からないので試してみます。

ss_002.JPG
ss_003.JPG

Tera Termを使用して、ユーザーをrootとし接続してみます。
......
.......
........
.........

Please login as the user "ec2-user" rather than the user "root".

_人人人人人人_
> ナニコレ <
 ̄Y^Y^Y^Y^Y^Y^ ̄

結果、公式通りrootでのログインはできなかったが、rootログイン時に出力される上記の文は
どこに設定されているのかは知っておきたかったので色々と調べてみた所以下に設定されていることが分かりました。

/root/.ssh/authorized_keys
no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"ec2-user\" rather than the user \"root\".';echo;sleep 10" ssh-rsa <文字列> <キーペア名>

上記が何かというとauthorized_keysのオプションで、色々設定できるらしいです。初めて知りました。
今回の場合だとssh-rsaの前に記載されています。

オプション 意味
no-port-forwarding ポート転送禁止の設定
no-agent-forwarding 認証エージェント転送禁止の設定
no-X11-forwarding X11(画面)転送禁止の設定
command="command" 実行可能なコマンドの設定

オプション消したらrootでログインできるのでは...?
という事で、バックアップを取得してからssh-rsaより前を削除してみます。

# cp -p /root/.ssh/authorized_keys /root/.ssh/authorized_keys_yyyymmdd
# ls -a /root/.ssh/
# vi /root/.ssh/authorized_keys
# cat /root/.ssh/authorized_keys
# systemctl restart sshd.service

authorized_keysの設定が反映されたので、新しくSSH接続を試してみます。
※無いとは思いますが何かあって入れなくなると困るので現在の接続は保ったままとします。

ss_005.JPG
できました。
念のためにユーザ確認をしてみます。

# whoami
root

ログインすることができました。
authorized_keysのオプションを無くした状態でsshd_configの#PermitRootLogin yesを以下のように変更するとどうなるのかを試してみます。
(変更前)
#PermitRootLogin yes

(変更後)
PermitRootLogin no

sshd_configのバックアップを作成して、バックアップがあるかを確認します。
その後設定を変更して、設定反映をします。

# cp -p sshd_config sshd_config_yyyymmdd
# ls -l
# vi /etc/ssh/sshd_config
# systemctl restart sshd.service

それでは新しい接続でrootのログインを試してみます。
ss_003.JPG

ss_006.JPG

認証に失敗しました。再試行してくださいと表示されてrootにログインすることができなくなりました。
なので、デフォルトでauthorized_keysのオプションによりログインが出来なくなっていて、
そのオプションを削除するとsshd_configにてPermitRootLogin noとなっていないため
rootでログインできることが分かりました。

結果

sshd_config authorized_keys 接続可否
#PermitRootLogin yes オプション有 ×
#PermitRootLogin yes オプション無
PermitRootLogin no オプション有 ×
PermitRootLogin no オプション無 ×

sshd_configでPermitRootLogin noと設定した方が良いことが分かる。

SSH接続Verison 2のみ許可

Tera TermでSSHバージョン(V)を[SSH1]を選択して[OK]を押下します。
ss_000.JPG

ss_001.JPG

...
....
.....

_人人人人人人_
> できない <
 ̄Y^Y^Y^Y^Y^Y^ ̄

/etc/ssh/sshd_configにはProtocolの設定がなかったが何故だろうと思い調べた所
OpenSSH 7.4でSSH v1は廃止されたとのこと。つまり何もせずともSSH v2となる。
OpenSSH 7.4/7.4p1 (2016-12-19)

  • This release removes server support for the SSH v.1 protocol.

Amazon Linux 2はどのバージョンなのかを確認してみる。

# ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

OpenSSH 7.4p1でした。

結果

OpenSSH_7.4よりSSH v1が廃止されたため、特に何もせずともSSH v2となっていた。

終わり

最後にもう一度比較してみたいと思います。

推奨値 設定値 推奨値との比較
rootログインの禁止 #PermitRootLogin yes
チャレンジレスポンス認証 ChallengeResponseAuthentication no
公開鍵認証の許可 #PubkeyAuthentication yes
SSH接続Verison 2のみ許可 設定見つけられず
パスワード認証の禁止 PasswordAuthentication no

初期Amazon Linux 2の設定は何も設定しなくても上記の推奨値に関しては問題ないことが分かった。
知らないことだらけだったためとても勉強になった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(Amazon Linux 2の)sshd_configとauthorized_keysを学ぶ

内容

初期Amazon Linux 2の/etc/ssh/sshd_config設定を色々と変更しつつsshd_configについて学んでいく。
ついでにauthorized_keysも。

ちょっとだけsshd_configを学ぶ

取り敢えず使いそうなところの設定

意味 該当箇所
パスワード認証 PasswordAuthentication
チャレンジレスポンス認証 ChallengeResponseAuthentication
公開鍵認証 PubkeyAuthentication
rootログイン PermitRootLogin
接続ポート Port XX(基本22)
SSH接続バージョン Protocol

Amazon Linux 2の初期設定

設定
#Port 22

#PubkeyAuthentication yes
# the setting of "PermitRootLogin without-password".

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
PasswordAuthentication no

# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication no

#PermitRootLogin yes

どういう設定が良いのか

を調べてみた所共通して多く出てきたのが以下設定。

・rootログインの禁止
・パスワード認証の禁止
・公開鍵認証の
・SSH接続Verison 2のみ許可
・チャレンジレスポンス認証を無効化

推奨値 設定値 推奨値との比較
rootログインの禁止 #PermitRootLogin yes ×
チャレンジレスポンス認証 ChallengeResponseAuthentication no
公開鍵認証の許可 #PubkeyAuthentication yes
SSH接続Verison 2のみ許可 設定見つけられず
パスワード認証の禁止 PasswordAuthentication no

ここで、×と?だったものに関して接続を試してみるとどうなるのかを検証。

rootログインの禁止

/etc/ssh/sshd_configのrootログインに関する設定値は以下であった

#PermitRootLogin yes

ただし、公式サイトでは以下のように記載されている。
Amazon Linux 2

デフォルトで安全
Amazon Linux 2 では、SSH キーペアの使用およびリモートルートログインの無効化により、リモートアクセスが制限されています。また、Amazon Linux 2 では、必須ではないにもかかわらずインスタンスにインストールされるパッケージの数が削減されるため、セキュリティの脆弱性のリスクを抑えることができます。深刻度が "緊急" または "重要" であるセキュリティアップデートは、初回起動時に自動的に適用されます。

試してみないと良く分からないので試してみます。

ss_002.JPG
ss_003.JPG

Tera Termを使用して、ユーザーをrootとし接続してみます。
......
.......
........
.........

Please login as the user "ec2-user" rather than the user "root".

_人人人人人人_
> ナニコレ <
 ̄Y^Y^Y^Y^Y^Y^ ̄

結果、公式通りrootでのログインはできなかったが、rootログイン時に出力される上記の文は
どこに設定されているのかは知っておきたかったので色々と調べてみた所以下に設定されていることが分かりました。

/root/.ssh/authorized_keys
no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"ec2-user\" rather than the user \"root\".';echo;sleep 10" ssh-rsa <文字列> <キーペア名>

上記が何かというとauthorized_keysのオプションで、色々設定できるらしいです。初めて知りました。
今回の場合だとssh-rsaの前に記載されています。

オプション 意味
no-port-forwarding ポート転送禁止の設定
no-agent-forwarding 認証エージェント転送禁止の設定
no-X11-forwarding X11(画面)転送禁止の設定
command="command" 実行可能なコマンドの設定

オプション消したらrootでログインできるのでは...?
という事で、バックアップを取得してからssh-rsaより前を削除してみます。

# cp -p /root/.ssh/authorized_keys /root/.ssh/authorized_keys_yyyymmdd
# ls -a /root/.ssh/
# vi /root/.ssh/authorized_keys
# cat /root/.ssh/authorized_keys
# systemctl restart sshd.service

authorized_keysの設定が反映されたので、新しくSSH接続を試してみます。
※無いとは思いますが何かあって入れなくなると困るので現在の接続は保ったままとします。

ss_005.JPG
できました。
念のためにユーザ確認をしてみます。

# whoami
root

ログインすることができました。
authorized_keysのオプションを無くした状態でsshd_configの#PermitRootLogin yesを以下のように変更するとどうなるのかを試してみます。
(変更前)
#PermitRootLogin yes

(変更後)
PermitRootLogin no

sshd_configのバックアップを作成して、バックアップがあるかを確認します。
その後設定を変更して、設定反映をします。

# cp -p sshd_config sshd_config_yyyymmdd
# ls -l
# vi /etc/ssh/sshd_config
# systemctl restart sshd.service

それでは新しい接続でrootのログインを試してみます。
ss_003.JPG

ss_006.JPG

認証に失敗しました。再試行してくださいと表示されてrootにログインすることができなくなりました。
なので、デフォルトでauthorized_keysのオプションによりログインが出来なくなっていて、
そのオプションを削除するとsshd_configにてPermitRootLogin noとなっていないため
rootでログインできることが分かりました。

結果

sshd_config authorized_keys 接続可否
#PermitRootLogin yes オプション有 ×
#PermitRootLogin yes オプション無
PermitRootLogin no オプション有 ×
PermitRootLogin no オプション無 ×

sshd_configでPermitRootLogin noと設定した方が良いことが分かる。

SSH接続Verison 2のみ許可

Tera TermでSSHバージョン(V)を[SSH1]を選択して[OK]を押下します。
ss_000.JPG

ss_001.JPG

...
....
.....

_人人人人人人_
> できない <
 ̄Y^Y^Y^Y^Y^Y^ ̄

/etc/ssh/sshd_configにはProtocolの設定がなかったが何故だろうと思い調べた所
OpenSSH 7.4でSSH v1は廃止されたとのこと。つまり何もせずともSSH v2となる。
OpenSSH 7.4/7.4p1 (2016-12-19)

  • This release removes server support for the SSH v.1 protocol.

Amazon Linux 2はどのバージョンなのかを確認してみる。

# ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

OpenSSH 7.4p1でした。

結果

OpenSSH_7.4よりSSH v1が廃止されたため、特に何もせずともSSH v2となっていた。

終わり

最後にもう一度比較してみたいと思います。

推奨値 設定値 推奨値との比較
rootログインの禁止 #PermitRootLogin yes
チャレンジレスポンス認証 ChallengeResponseAuthentication no
公開鍵認証の許可 #PubkeyAuthentication yes
SSH接続Verison 2のみ許可 設定見つけられず
パスワード認証の禁止 PasswordAuthentication no

初期Amazon Linux 2の設定は何も設定しなくても上記の推奨値に関しては問題ないことが分かった。
知らないことだらけだったためとても勉強になった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxログイン時にターミナルでの操作ログを自動で取得する

久しぶりの投稿です。
最近はgpd-micropcにubuntu mateをインストールして遊んでいます。

経緯

ターミナルでの操作ログを自動で取得する方法がないか、探しいていました。
はじめは.bashrcにscriptコマンド書けばよいだろと思っていましたが、どうもうまくいかないようです。。
色々探した挙げ句、参考になる記事を発見しました。
How do I log all input and output in a terminal session?

環境

gpd micropcにubuntu-mateインストールしたものです

OS: Ubuntu 19.10 eoan↲                                                                                                                      
Kernel: x86_64 Linux 5.3.0-40-generic↲
Shell: bash↲
DE: MATE 1.22.2↲
CPU: Intel Celeron N4100 @ 4x 2.4GHz↲
GPU: Mesa DRI Intel(R) UHD Graphics 600 (Geminilake 2x6)-↲

やったこと

1. rsyslog設定ファイル作成

loggerコマンドでログを出力させる形になるので、以下に設定ファイルを作成

vim /etc/rsyslog.d/bash.conf
/etc/rsyslog.d/bash.conf
local6.*    /var/log/commands.log↲

2. .bashrc 追記

操作ログ取得対象のユーザのホームディレクトリ配下の.bashrc に以下を追記

vim /home/hoge(対象ユーザ)/.bashrc↲
/home/hoge(対象ユーザ)/.bashrc
whoami="$(whoami)@$(echo $SSH_CONNECTION | awk '{print $1}')"export PROMPT_COMMAND='RETRN_VAL=$?;logger -p local6.debug "$whoami [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL]"'

3. rsyslogサービス再起動

rsyslogサービスを再起動する

systemctl status rsyslog.service 

4. ログローテート用設定ファイル作成

logrotate.d で日次でローテートするように、設定ファイルを作成する

vim /etc/logrotate.d/commands
/etc/logrotate.d/commands↲
/var/log/commands.log*↲
{↲
»-rotate 7↲
»-daily↲
»-dateext↲
»-missingok↲
»-notifempty↲
»-compress↲
»-postrotate↲
»-»-/usr/lib/rsyslog/rsyslog-rotate↲
»-endscript↲
»-su root root↲                                                                                                                             
}↲

確認

.bashrc を読み込んで

source .bashrc 

以下のようにファイルが /var/log 配下にできていることを確認

ls -la /var/log/commands.log*
-rw-r----- 1 syslog adm 4753  3月 15 19:34 /var/log/commands.log
/var/log/commands.log
Mar 15 19:34:29 hoge hoge: hoge@export [3457]: 2020-03-15 19:34:29 cd [0]
Mar 15 19:34:39 hoge hoge: hoge@export [3457]: 2020-03-15 19:34:37 source .bashrc  [0]
Mar 15 19:36:56 hoge hoge: hoge@export [3457]: 2020-03-15 19:36:56 ls -la /var/log/commands.log* [0]

所感

logrotateできてるかあやしい。。
後、scriptコマンドみたいに出力もほしい。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

事故を出さない虎の巻

「機能は追加(修正)する」「事故も起こさない」。 「両方」やらなくっちゃあならないってのが「エンジニア」のつらいところだな。 覚悟はいいか?オレはできてる。

事故の定義

ここで述べる事故とは、ユーザのサービス利用を妨げる事象、サービスの信頼が失われる事象を指す。
事故足りうる事象に次のものが挙げられる。

  • インフラ障害(ネットワーク障害、サーバ障害)
  • DB障害(データ不整合、デッドロック)
  • 脆弱性(情報漏えい、インジェクション、改ざん、不正アクセス)
  • メール・SNSの誤送信
  • 実装バグ(二重課金、サーバエラー、操作不能、意図しない動作)

事故足り得る前提として1つ目に規模の問題がある。10人が使ってるサービスの障害と100万人が使ってるサービスの障害を比較するのであれば、明らかに後者のほうが重大な事故である。次にサービス全体へ影響を与えた障害なのか、ごく一部のユーザにのみ影響を与えた障害なのか事故の規模を考慮する必要がある。
(あまりにも規模が小さいものであれば、事故が起こった原因は置いておいて対応としては示談で済むかもしれない)
2つ目にサービスが取り扱っている情報の重要度に依存する。例えば、個人情報を扱っていないサービスであれば盗まれるものが無いのでセキュリティに関してあまり考えなくてもよいし(今どきそんなサービスほぼ無いけど)、銀行のシステムであればネットワークアクセスログはもちろん全て取る必要があるし、改ざんも不正アクセスも防がないといけない。

人間はミスをする生き物なので、手動でやる限りいつか同じミスをしたり、別の人だったら同じミスを犯す。
いずれもチェックが手動だけでなく自動化できればそれに越したことがない。
事故が起こる要因として実施者の想定の範囲外で起こるケースが多々にしてあるので規模が大きいシステムを運用している場合はメンバー間で情報を共有することや修正によってどこに影響を与えるかキーワード検索したり想像力を働かせることが大事。
(例えば、修正変更がメインシステムだけでなく担当範囲外を超えたサブシステムに影響を与える場合など)
監視や通知の仕組みを作り、エラーメッセージを読むことが大事。
AWSやGCPなどのクラウドサービスでシステム構築してる際は、Datadogを使うのがおすすめ。

なお、特に言語や実行プラットフォームは何でも良いのだがPaaSクラウドを用いて構築された一般的なウェブサービスを想定して書いている。

インフラ障害

主にネットワークや認証・認可周りの設定間違いサーバリソース不足に関して事故が起こりやすい。
一度設定すればほぼいじらないものと、機能改修によって都度チェックが必要なものと、常に監視が必要なものと分類される。

プライベートネットワークの設定間違い

AWSなどでVPC(Virtual Private Cloud)を使ってサーバ間のアクセスに仮想プライベートネットワークを構築する際、
以前まで疎通できていたサーバ間の通信が繋がらなくなるような事故。
対策としては設定したネットワークの各サーバ間、サーバからインターネットへの疎通確認をすること。
一度確認したらネットワークを再構築する際以外は起こりえない。

DNSレコードの設定間違い

AWSのRoute53のようなサービスでDNSレコードを設定している場合にCNAMEレコードやAレコード、TXTレコードなどの設定を間違えてしまい起こる事故。
ドメインを変更した際などに設定忘れしたり、疎通確認を忘れた場合に事故が起きる。
一度確認したらドメインを変更する際やレコードを変更する以外は起こりえない。

SSL証明書の期限切れ

SSL証明書の期限が切れて数年後にいきなりhttps通信できなくなり、ブラウザ警告が出る事故。
SSL証明書の自動更新設定をするか、期限が切れる前に通知するなどの対策が必要。

認証・認可の設定間違い

クラウド関連の機能追加・変更する際にクラウドサービスへのアクセス権限を間違えて変更した場合などに起こる事故。
例えば、AWSのIAMを使っている場合などにS3アクセス権限をReadの権限のみでWriteの権限を忘れたりなどに起こる。
対策は実際にプログラムや設定したユーザ権限で実行してアクセスできているか書き込みできているか確認する。

キャッシュの設定間違い(CDNの設定)

CloudFrontなどのCDNの設定で特定のURLパスに対してキャッシュを持つことができる。
キャッシュを持つことで2回目以降は高速にレスポンスを返すことができるが、
whitelistパラメータの設定漏れがある場合、パラメータがALBやサーバに渡されていない事故が発生する。
APIキャッシュを作成する際は実装パラメータを追加した際、whitelistにも追加する必要があるので注意したい。

サーバ障害

ここで指すサーバはAPIサーバ・DBサーバなどを含む。いずれも常時監視が必要。
原則、単一障害点を作らない
(例えば、サーバが一台しかなくてサーバを落としたらシステム全体が止まるなど)
以下のサーバのリソースが問題となる

  • サーバのCPU性能不足による遅延
  • サーバのメモリ不足
  • サーバのディスク不足

具体的には次のような対策がある

CPUスペックを上げる

無限ループなど負荷が高い処理でCPUが100%に張り付いた状態が続くと他の処理ができなくなり、パフォーマンスが落ちる。CPU稼働率とAPIレスポンス時間などを監視し、CPUリソースが足りない場合は負荷がかかっている処理を修正したり、CPUスペックを引き上げる必要がある。
また、AWSのt2インスタンスやHerokuのdynoなどはCPUリソースの上限があり、上限を超える稼働はサーバが止まるので注意が必要。CPUブーストを使ったり、課金したりする必要がある。

スワップ領域を作成する

スワップ領域を作成しておくことでメモリ不足になったときにディスク領域を一時的に使うことができる。
スワップ領域はディスク領域のため、IO処理が発生しパフォーマンスは落ちるがメモリ領域がパンクして最悪サーバが止まることはない。
(スワップ領域を使いだす前にはメモリを増やしたいが・・・)
メモリがパンクしてるとvimなどのエディタでファイルを開くこともできなくなったりするので、サーバを再起動したり、lessでファイルを開いて編集したりする。(過去の経験上lessが一番軽かった)

ログローテーションを設定する、アップロードファイルはAPIサーバ内に保存しない

ログファイルを何もしないとサーバのディスクを専有していき、いずれディスクをパンクさせる。
logrotateコマンドを使うことで古いログファイルを定期的に消すことができる。(これによりログファイルでのパンクを防げる)
ファイルのアップロードなどをサービスで扱う場合はサーバに直接アップロードするのではなく、S3などのファイルストレージサービスにアップロードする。

バックアップを残す

AWS EC2を使っているのであれば、AWSの設定画面にてEC2インスタンスまるごとバックアップを取ることができる。主にすぐに復旧できない不具合があった場合にDBのバックアップと合わせて、ロールバックする。

監視設定をする

CPU、メモリ、ディスクやヘルスチェックに対して一定時間レスポンスがないなどに関して、
Datadogで閾値を超えたら通知(メール、Slack)を飛ばすことが可能。

冗長化、オートスケールする

サーバを複数台構成にして、ロードバランサー経由でのアクセスにすれば、1台落ちても別のサーバでカバーできるため、システムを止めなくてすむ。(ロードバランサーのヘルスチェックでサーバを死活監視している)
負荷が増えてきた場合はサーバの台数を増やしてオートスケールする。
AWS LambdaやFirebase Functionsなどのサーバレスなマネージドサービスで稼働するのであれば、オートスケーリングも自動でやってくれてメモリの増設などは設定のみで済む。
また、Dockerコンテナでサーバを作成している場合はAWS Fargateを使ってDockerコンテナをオートスケーリング稼働する方法もある。

クラウド自体の障害を確認する

まれにだがクラウドサービス自体が障害を起こしている場合がある。
AWS:AWS Service Health Dashboard
GitHub:GitHub Status
長期的に復旧されない場合はビジネスインパクトが非常に大きい、対策としては一時的にリージョンを分散させて冗長化させるなどがある。

実装バグ対策

事故の原因としてはこれが一番多い。
実装者がシステム仕様や言語仕様やライブラリに関する理解が乏しくて発生する場合が多い。
また技術的負債が溜まっており、設計ミスやコードの可読性や統一性、検索容易性が失われている場合、より事故が起きやすい。
コードレビューで見るべきものに関してはコードレビュー虎の巻にまとめた。

セキュリティの事故

ユーザのシステムの信頼を失墜させる事故。個人情報の流失や最悪金銭的な損害にも発展する。
当然だが、httpだと盗聴される(というかもはやブラウザで警告出る)のでhttps通信する。
ユーザが比較的自由に入力できるフォーム入力周りはセキュリティホールになりがちなので特に注意する。

  • XSS:悪意のあるユーザがブラウザで実行できる悪意のあるJSスクリプトなどをフォーム送信してDB保存する。DB保存されたデータを別のユーザがブラウザで参照した場合に悪意のあるJSスクリプトが実行され、ブラウザに保存されているログイントークンなどの情報を悪意のあるユーザに送信してしまう。対策としては入力時にエンコードしてしまう、フロントエンド側で実行コードとして表示しないなどがある。Reactなどのフレームワークを使っている場合は自動的に無害化してくれる。(dangerouslysetinnerhtmlは除く)
  • SQLインジェクション:悪意のあるユーザがSQL文をフォーム送信し、DBの情報を盗み取ったり改ざんしたりする行為。直接ユーザ入力内容をクエリに埋め込むのでなく、プレイスホルダ機能を使うなどでクエリに問題がある際は実行させない方法などがある。
  • セッションハイジャック:他のユーザのログイントークンを盗み出し、あるいは推測し、他のユーザとしてログインできてしまうこと。これができると他のユーザになりすましや情報取得できてしまう。ログイントークンを改ざんや流出させない、ログイントークンをユーザ別に発行せずにid=1など推測しやすい情報でセッション切り替えできてしまうなどの実装をしない。ログイントークンには改ざん不能なJsonWebTokenなどを使う。
  • CSRF:APIサーバがCORFを許可している場合、外部のサイトからフォーム送信できてしまう。このためフィッシングサイトで本物サーバ側へリクエストさせ、ユーザの情報を盗む手段に使える。特にログイン情報など重要な情報を送信するフォームにはフォームを表示するたびにワンタイムトークンを埋め込み正規のフォーム送信か確認する。
  • DoS攻撃:無意味な大量アクセスでサーバをダウンさせようとする攻撃。ファイアウォール機能でIPを一時的にbanするなどの対策がある。AWSだとWAFを使うなど

他にも色々あるが、パスワードは生でDB保存せずにハッシュ化する。認証必須のAPIと認証不要のAPIを切り分けるは必須。

ルーティング周りの事故

パス追加時に他のパスを上書きしてしまったりキャッシュ起因で発生する

ルーティング追加時に他のルーティングを上書きしてしまう

例えば、APIパスを追加した際に既存のルーティングを上書きしてしまい、対象のAPIにアクセスできなくなる事故。
(SPAであればReact Routerなどの疑似ルーティングのRouteを上書きして、対象のページが表示できなくなる事故もある。)

POST /api/hoge
POST /api/hoge/:id // ←追加
POST /api/hoge/:key // ←URLパラメータ的には異なるがパス的には上のAPIが優先され、到達できなくなる 

追加する際は他のパスのアクセスを上書きしていないか確認し、順番を変えたり、別のパスにするのが大事。
具体的にはAPIの呼び出しテストを作成することでCIで再帰テストをすることが可能。
指定のパスに対しての呼び出しテストなので呼び出される想定の関数はモック化して良い。

キャッシュがあるのに古いAPIやページのパスを消してしまう

古いAPIやページのURLを削除してしまうとブラウザキャッシュが残っていて古いAPIやページのURLにアクセスが来てしまい問題となる。
特にSPAの場合、古いbundle.jsはCDNキャッシュ&ブラウザキャッシュが消えるまで&ブラウザリロードするまで残り続けるため、CDNキャッシュクリアと古いAPIは新しいAPIにリダイレクトする必要がある。
他にも、Google Botなどは古いパスのキャッシュを持っているため、古い方にアクセスが来る。
キャッシュを使ってる場合は古いAPIやページにアクセスが来るので新しいページに301リダイレクトする必要がある。

外部API起因の事故

外部サービスのAPIを呼び出している場合、外部APIの仕様を確認する必要がある

エラー時の処理

考慮漏れしがち。API仕様を見てもどんなエラーが返ってくるかわからない場合や通信エラーの場合でも制御する必要がある。
エラー時に後の処理を行うかの判断もしないといけない。

APIリクエスト上限

これもAPI仕様を見ていないと見落としがち、タチが悪いのはlocalだとリクエスト数少なくて問題ないが、本番環境に上げたら、大量にリクエストしてしまってエラーになる場合がある。課金などで上限を上げれる場合が多いが、代替手段がある場合はそもそも使わないか元が取れる場合に限る。上限を上げれない場合は、APIリクエスト数の上限を超えないようにキューイング(バッチ化)するか、リアルタイム性を求められる場合はエラーとして返す(ユーザに待ってもらう)方法がある。

API呼び出しアカウントのセッション切れ、セッション上限

ステートレスでない(Rest API)でないステートフルなAPIの場合、ユーザアカウントでのセッションを保つ場合がある。セッション切れした場合は再度ログインする必要があるので再ログイン処理の対応も必要となる。
外部サービスのセッションを持つAPIなどはセッション上限などを超えないようにする必要がある

メモリリーク

GC(ガベージコレクション)が無い言語(C、C++など)はもちろん動的メモリ確保した際に利用後、明示的にメモリ解放しないとメモリ領域を食いつぶす。
GCがある言語(JavaScriptなど)でもnewでメモリアロケーションしたインスタンスが循環参照してる場合はGCでもメモリ解放されずメモリリークとなる。
メモリリーク箇所の検出を行い、循環参照している場合は弱参照(WeakRef)やスマートポインタを使う方法がある。
そもそもnewを極力使わない、循環参照させないのも手である。

ちなみにJavaScriptの場合はプロポーサル段階だがWeakRefが存在しているのと
NodeJSのメモリリーク検出方法が参考になる。

パフォーマンス低下による事故

レコード数が多いテーブルはパフォーマンスに注意する。
バックエンドの処理はパフォーマンスが低下するとレスポンス時間が遅くなりシステムがハングする。
readの方はテーブルのよく検索に使われるフィールドにindexを貼る、マイグレーションスクリプトやバッチ処理での大量Writeはbulk処理をして短時間で書き込む対策が必要。
あとはマルチプロセス(cluster)でCPUコア数分サーバ起動することで暫定的な負荷分散はできる。
システム全体のパフォーマンスのボトルネックを見つけるにはframegraphを出力する。

例えば、NodeJSは0から始めるNode.jsパフォーマンスチューニングに調査方法がよくまとまっている。

データ不整合の事故

データ挿入などのデータマイグレーションスクリプトの実装間違えや途中エラーはデータの不整合を起こすので、トランザクションで実装し、問題がある場合はロールバックする。
さらには実行後に問題があった場合に備えて、実行前にはデータのバックアップも取っておく。
また、課金周りなど複数テーブルへの書き込みが必要な重要な処理はトランザクションして不整合を防ぐ。

APIマイグレーションの事故

サブシステムのAPIをメインのシステムから参照している場合にサブシステムのAPIをアップグレードして別のデータを返す必要がある場合、
基本的にサブシステムとメインシステムを完全同時にリリースすることはできないので、ステップ踏む必要がある。

  1. サブシステムの新APIを実装、旧APIはまだ消さない。サブシステムをリリースする。
  2. メインシステムにサブシステムの新API呼び出しの処理を実装、旧APIを消す。メインシステムをリリース。
  3. サブシステムの旧APIを消す。サブシステムをリリースする。

変更がサブシステムに影響を与えてしまう事故

変更がメインシステムだけでなくサブシステムにも影響を与えないか考慮する。
この辺はシステムの全体像がわかっていないと厳しい、有識者が実装、レビューするしかない。
普段から情報をシェアし合う体制が必要。
特に起こりやすいのはDBのテーブルフィールドを変更・削除した場合に
redashなどのBIツールのクエリやsalesforceなどの別システムへのデータ同期を自動で行っている場合などに影響がでて事故る。

排他制御の事故

DBトランザクション、マルチスレッドの排他制御などは、処理をブロッキングしてしまうため、解除し忘れるとシステムをハングさせる。(デットロック)
例えばトランザクション開始時にtry構文でwrapしてやり、finally文で必ずロック解除するようにするなどの対策がある。

ライブラリ(OSS)のバージョンアップに伴う事故

これはnpmやgemなどのパッケージマネージャーツールで3rdパーティライブラリを管理している場合に起こる
ライブラリを使って良いのは保守を上回るメリットがある場合だけで、オーバスペックなライブラリは容量を食うし(特にユーザがアプリやJSファイルをDL際に影響する)、ライブラリのバージョンアップが義務付けられるのでそもそも不要なライブラリは入れずに言語仕様や標準のAPIで実装する。
ライブラリのバージョンは動作確認が取れるまで無闇に上げずに固定しておく(メジャーバージョン、マイナーバージョン、パッチバージョン)。
また、package-json.lockやyarn.lockなど詳細な依存関係を管理しているファイルは3rdパーティライブラリが依存しているライブラリのバージョンが記載されているため、無闇に消してはいけない。
これらのファイルを消してしまうと再インストールした際に3rdパーティライブラリが依存しているライブラリのバージョンが引き上がって事故ることがあるからだ(1回あった)。

技術的負債

事故直接の要因ではないかもしれないが、怠ると事故を引き起こす要因となりえるもの。

型付きの言語で実装する

特にバックエンドは型付きの言語で実装したほうが良い(NodeJS+TypeScript、go、Java)。
理由としては、静的コンパイルによってケアレスミスが防げるからだ。

  • 型チェックで意図しない型のパラメータが引数に渡ってしまうのを防げる
  • 型チェックでパラメータの引数への渡し忘れを防げる
  • 型があることでprimitiveなデータなのかクラスやオブジェクトの型なのかすぐに判別がつく
  • 型チェックがあることでoptionalな引数かそうでないかが型定義でわかる(TypeScriptの場合)
  • 戻り値の型がわかる

TypeScriptでの実装の場合、TypeScriptの為のクリーンコードが参考になる。

設計

KISS(シンプルな設計・実装にする)を心がける。
クラスを使う場合、SOLID原則デメテルの法則も意識すると仕様変更にも強く、テストしやすい。(依存と関心の分離)

  • デフォルト引数を与えてフェールセーフにする、ただし空関数をデフォルト引数に指定するなどの場合は実装漏れなどはエラーは握りつぶさずにエラーログを送信してすぐ発見できるように通知する
  • DBテーブルフィールドの直接の変更、削除は事故になるので別フィールドを追加して処理とデータも移行してから元のフィールドを削除する
  • DRYに則って同じような処理は関数に共通化する。変更が少ないユーティリティは共通化してもよいが、過度な共通化は無駄に影響範囲を広げてしまう・・・あなたはDRY原則を誤認している?
  • 呼び出し箇所が多い関数やテーブルフィールドの修正時には影響範囲に注意する。依存グラフをツールで出力するなどで把握する
  • ビジネスロジックはインタフェース、abstractで抽象化する方が仕様変更に対応しやすいので望ましい。実装は委ねられるが、引数と戻り値の型が保証される。

可読性、検索のしやすさ

命名規則、コーディングルールを統一する。プロジェクトが小さいうちは良いのだが、プロジェクトが大きくなるとファイルの即時検索ができないと明らかに作業効率が落ちる。
キャメルケース、スネークケースなどはどれか一つに統一する(混ぜない!)。
意外に大事なのがtypoを防ぐ、別の意味で使っているのに同じ変数名や関数名にしない、表記揺れをなくす。
これは、修正漏れを防いだり、誤解を招くことに起因する。(ドメインモデルを統一する)
プロジェクト内の既存のtypoの検索にはあいまい検索でfzfなどが使える。
コード量に比例してバグの量も増えるので、YAGNI(無駄な実装をしない、残さない)を常に心がける。

  • lintを入れてコーディングルールを統一する
  • 適切なコメントを入れる(主に機能やビジネスロジックの仕様の説明)、できる限り簡潔に書く
  • ファイル名、変数名、関数名は命名規則を統一する、中身がわかりやすい名前をつける(typoしない)
  • コードが追いにくくなるので関数の呼び出し(コールスタック)を深くしない
  • ネストは深くしない(条件分岐の早期リターンする、非同期コールバック処理はawaitする)
  • 変数、DBテーブルフィールドのダブルミーニングはしない
  • 継承より合成を優先して使う(継承だと不要な変数、メソッドまで継承するリスクや可読性・メンテナンス性が落ちる)、継承自体を禁止する必要はないが、子継承までが限界だと思う

PRの掟(おきて)

事故を起こさないようにするためのPRのルール、PRの役割を複数持たせない(単一責任)
コード量に比例してエラー数も増えるため、修正量は少なくする。
特にソースファイルをまたいでいる数が多い修正や依存が強い箇所の修正は危険なので、極力修正を混ぜない。
リリース後作業が必要なもの、重要な修正に関してはチェックリストをつける。

テスト

資産になるテストを書き、CIで再帰テストを行う。

  • 正常系、境界値、異常系の単体テストを書く
  • 重複したテストを書かない
  • できる限り並列テストにする
  • 条件が複雑なものほど再現が困難なため単体テストで網羅する、単体テストできるような構造にする
  • 単体テストとAPIテストなど異なる種別のテストは同じファイルに書かない(フォルダ分けする)
  • E2Eテストは壊れやすいが網羅性が高いので、サービスのコア機能などにピンポイントで使う
  • 表示の差分テストはビジュアルリグレッションテストが良い(UIライブラリのバージョンアップにも追従できる)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

eGPUでハイスペックLinuxデスクトップをDeep Learning Workstation化計画(eGPUセットアップ編)

以前投稿した記事「Intel NUC + SSD 2TB + DRAM 32GBでハイスペックLinuxデスクトップを作ってみた」で、自作したハイスペックLinuxデスクトップを紹介した時に今後の課題としてThunderbolt 3に対応していることを生かしてeGPUを用いたGPUマシーン化に挑戦したいと書きました。様々な幸運から予算が編成出来たため、必要なパーツが揃いました。本記事ではeGPUを用いたハイスペックLinuxデスクトップのDeep Learning Workstation化計画の第一弾としてeGPUのセットアップを実施したいと思います。

そもそもeGPUとは?

External GPUつまり外部GPUという意味です。本来はGPUがCPU内蔵GPUのみのラップトップ向けに開発された技術で、グラフィック性能が不足しているマシーンに高速通信規格のインターフェースを用いてより性能の高いGPUを接続してグラフィック性能を向上させることが目的です。最近ではApple StoreがMacBook向けにeGPUのユニット(AMDのRadeonを内蔵)を販売しており、対応しているMacBookに接続するとグラフィック性能を向上させることが出来ます。今回はデスクトップ、しかもLinuxのマシーンでeGPUを接続し、Deep Learning用ワークステーション化したいと思います。接続に使用するインターフェースはThunderbolt 3です。

eGPUのメリット・デメリット

eGPUのメリット・デメリットを記載します。

  • メリット

    1. 必要な時のみGPUを起動出来る(常にGPUが必要では無い用途に最適)
    2. PCが小型化でき、スペースを取らない(狭い関東のマンション住まいには有り難い)
    3. 低消費電力化が実現出来る(メリット1より)
    4. 低予算で自作可能(今回のPCのスペックでPC本体と合わせて22万は結構低予算の筈)
  • デメリット

    1. Thunderbolt 3はPCI Express x16より通信速度が劣る為、GPUとの通信速度は低下する
    2. セットアップが煩雑

今回使用するeGPU

今回使用するeGPUは、本来ビデオカードとして販売されているGPUをビデオカードをeGPU化するキットに組み込んだ物です。eGPUの費用は以下の通りです。

パーツの種類 型番 メーカー 価格 備考
ビデオカード NVIDIA Geforce RTX 2080 SUPER
GALAKURO GAMING
玄人志向 ¥74,800. NTT-X Storeで購入
eGPU化キット Razer Core X
RC21-01310100-R3J1
Razer ¥36,080. パソコン工房の通販で購入

合計: ¥110,880.
PC本体との合計: ¥218,065. (PayPay考慮時: ¥173,604.)

使用するGPU: NVIDA Geforce RTX 2080 SUPER

Deep Learning用ワークステーションとして使用することが構築の最重要目標であるため、GPUはNVIDIA社のGeforce RTX 2080 SUPER(玄人志向製)を購入しました。もともと2ランク下の2070 SUPERを購入する予定でしたがNTT-X Storeでセールをしていた為、当該GPUを衝動買いしてしまいました。

RTX2080SP.png
(2020/2/18時点でのNTT-X Store販売ページ)

NVIDIAが一般用GPUとして販売しているGeforceシリーズでは上から3番目(TITAN RTX, RTX 2080 Tiの次)のランクのGPUです。正直Thunderbolt 3接続でフル性能が出ない環境での80シリーズの利用はコストパフォーマンス的に最悪かもしれません。。。

使用するeGPU化キット: Razer Core X

ビデオカードをeGPU化するキットです。筆者がPC自作に使用したベアボーンIntel NUCに接続するeGPUの構築にどのメーカーのキットを購入しようかと迷っていた時に見つけた当該サイトで紹介されていました。値段、性能、発売元の信頼性という3つの点でバランスが取れた製品なのではないでしょうか?(あくまで筆者の個人的見解)但し、国内の通販サイトでは軒並み完売しており、1ランク上のUSBポートやLANポートを搭載して2万円高いモデルのみ販売しているサイトが増えています。筆者はパソコン工房の通販でやっと見つけて購入しました。中華製の格安キットも有りますが、失敗して2080 SUPERを破壊してしまうと洒落にならないため、少々高額でもグラフィック系メーカーが販売している製品を購入しました。

image.png

送られて来た現物を見て予想以上の大きさに正直唖然としました。PC本体よりeGPUの方がはるかに大きいという結末を迎えました。:scream:

eGPUのセットアップ

実際に実施したセットアップ手順を以下に記載します。あっさりと記載していますが、この構築方法が分かるまでは苦闘の歴史:scream_cat:でした。(ドライバーインストール行程など)

eGPUの組み立て

Geforce RTX 2080 SUPER(以下RTX2080SP)をRazer Core X(以下Core X)にドッキングし、eGPU化します。取付はCore Xの蓋を開けて内部のPCIスロットににRTX2080SPを取り付けて電源コード(8pin, 6pin)を繋ぐだけなので難しくありませんが、何分本体サイズが大きい為、作業性が劣悪だったり、スロットに入っているか確認するのが大変だったりはします。組み立てが完了したら、この時点ではPC本体に接続しません。以降CUDA Toolkitをインストールし、NVIDIAドライバーでeGPUを動作させる所までのセットアップ方法を記載します。但し、資料があまりにも少ない希少な構成でのセットアップは一筋縄では行かず、何度もリカバリーやドライバーの再インストールを実施し、Trial & Errorで分かった方法です。:dancer::dancer_tone1::dancer_tone2::dancer_tone3::dancer_tone4::dancer_tone5:

:innocent: 2020年3月現在のeGPUをセットアップする方法です。
:warning: あくまでLinux専用マシンとしてセットアップすることを想定しています。

BIOSアップデート

セットアップの為に色々調査していて気が付いたのですが、今回PC自作に用いたIntel NUC 8i7BEHのBIOSのバージョンが古く(0064)、最新版は0077でした。0064はどうやらRTXシリーズが販売される前のバージョンの様なのでアップデートすることにしました。

セキュアブートの無効化

今回構築に使用したIntel NUCはUEFI対応モデルであり、セキュアブートが有効になっている場合が有ります。その場合は、まずセキュアブートを無効化してからセットアップを行って下さい。セキュアブートを有効化したままでのセットアップは鍵登録を実施する必要が有ります。現段階ではeGPUが正常に動作する保証が無い為、無効化していますが、今後セキュアブートやUEFIについて理解が深まったら、セキュアブート有効化時のセットアップも試みたいと思います。

:warning: 上記の操作を行うとWindowsとのデュアルブートは実現できなくなります。:scream:

secure boot

  • Intel NUCのBIOSへの入り方は起動時にF2キーを連打します。
  • BIOS画面でBoot/Secure Boot/Secure Bootを無効にします。

UEFIがサードパーティードライバーをロードすることを許可

RTX2080SPを動作させる為にNVIDIAドライバーというNVIDIA製ドライバーをインストールします。BIOSでUEFIがサードパーティードライバーをロードすることを許可する設定をしておかないとUEFIでNVIDIAドライバーがロードされずRTX2080SPが使えません。

third party

  • BIOS画面でSeurity/Security Features/Allow UEFI Third Party Driver loadedにチェック

OpenSSH Serverのセットアップ(PCが2台以上有る場合)

2台以上PCが有る場合、セットアップ対象のPCにSSHでログイン出来ると便利です。OpenSSH Serverをインストールして、SSHでログイン出来る様にしておきます。(OSが最新版では無い可能性が有るのでapt upgradeも合わせて実施しておきます)

sudo apt install -y openssh-server

鍵ファイルを用いてパスワード無しログインを行う方法、パスワードログインによる方法など、好み・環境により選択する環境は異なってくると思います。各々の設定方法はネット上の記事を参考にして下さい。

eGPUの接続とThnderbolt 3の認証

eGPUをPC本体に接続します。PCを起動すると、ファンが爆音で回転します。これは故障では無く、Thuderbolt 3機器の認証が完了していない為、ファン制御などの主要機能が働いていないために発生する動作です。GUIが有効な場合(Desktop版の場合)パスワード認証を求める画面が表示されるので、パスワードを入力すれば爆音は止まります。この方法はCLIを利用している場合には使えないので、CLI環境でも認証が出来る方法を記載します。

Thunerbolt 3機器のuuidの確認

boltctlコマンドでThunderbolt 3機器(本記事ではCoreX)のuuidを調べます。以下の様な表示が出ます。uuidを控えておきます。

$ boltctl 
 ● Razer Core X
   ├─ type:          peripheral
   ├─ name:          Core X
   ├─ vendor:        Razer
   ├─ uuid:          <固有のuuid>
   ├─ status:        connected
   │  ├─ domain:     domain0
   │  └─ authflags:  none
   ├─ connected:     <接続した日時>
   └─ stored:        no

認証を実行

以下のコマンドで認証を実行します。パスワードの入力が求めれますので、ユーザーパスワードを入力すれば完了です。

sudo boltctl authorize <控えておいたuuid>

認証が完了すると爆音が止まります。

認証後にboltctlを実行すると以下の様に表示されます。

$ boltctl 
 ● Razer Core X
   ├─ type:          peripheral
   ├─ name:          Core X
   ├─ vendor:        Razer
   ├─ uuid:          <固有のuuid>
   ├─ status:        authorized
   │  ├─ domain:     domain0
   │  └─ authflags:  none
   ├─ authorized:    <認証が完了した日時>
   ├─ connected:     <接続した日時>
   └─ stored:        no

このままでは認証情報が保存されず、シャットダウンすると認証情報が消えてしまいます(爆音の再来)。故に自動的に認証する様に設定を行います。

boltcl enroll --policy auto <控えておいたuuid>

実行結果
● Razer Core X                                                                                                                                                                                                    
   ├─ type:          peripheral
   ├─ name:          Core X
   ├─ vendor:        Razer
   ├─ uuid:          <固有のuuid>
   ├─ dbus path:     /org/freedesktop/bolt/devices/<固有のuuid>
   ├─ status:        authorized
   │  ├─ domain:     domain0
   │  ├─ parent:     c7010000-0090-8d18-a3c2-b11ccc631023
   │  ├─ syspath:    /sys/devices/pci0000:00/0000:00:1c.4/0000:02:00.0/0000:03:00.0/0000:04:00.0/domain0/0-0/0-1
   │  └─ authflags:  none
   ├─ authorized:    <認証を実施した日時>
   ├─ connected:     <接続した日時>
   └─ stored:        <認証情報を保存した日時>
      ├─ policy:     auto
      └─ key:        no

Nouveauドライバーの停止

NVIDIAのビデオカードをLinuxに接続するとNVIDIAドライバーをリバースエンジニアリングして実装されているOSS版ドライバーNouveauドライバーが有効になります。確認方法は以下のコマンドを実行します。

lsmod | grep nouveau

有効になっている場合、nouveau項目が表示されます。無効にする為、以下のコマンドを実行します。

sudo sh -c "echo 'blacklist nouveau' > /etc/modprobe.d/blacklist-nouveau.conf"
sudo sh -c "echo 'options nouveau modeset=0' >> /etc/modprobe.d/blacklist-nouveau.conf"
sudo update-initramfs -u
sudo reboot

NVIDAドライバーのセットアップ

Ubuntuのaptにリポジトリを追加し、ドライバーをインストールします。NVIDA公式のスクリプトからインストールする方法は本記事で試した構成(eGPU構成)では上手く実行出来ませんでした。

sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
ubuntu-drivers devices

ubuntu-drivers devicesを実行すると以下の様な表示が出ます。(実行環境によって表示は異なります)
vendor   : NVIDIA Corporation
driver   : nvidia-driver-440 - third-party free recommended
driver   : nvidia-driver-435 - distro non-free
driver   : xserver-xorg-video-nouveau - distro free builtin

インストール時は、recommendedが付いているバージョンをインストールします。

sudo apt install nvidia-driver-xxx
sudo reboot

再起動後、nvidia-smiコマンドでGPUの状態を確認します。
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64       Driver Version: 440.64       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 208...  Off  | 00000000:07:00.0 Off |                  N/A |
|  0%   38C    P8    16W / 250W |      0MiB /  7982MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

eGPU Switcherのセットアップ(eGPU上で動作するX-Window System)

eGPUをセットアップすると、X-Window Systemが動作不能(つまりGUIが起動しない)に陥り、散々手を尽くしましたが全く改善しませんでした。万事休す。今までの投資は全て水泡に帰する:scream:のかと思っていたところ、eGPUの普及を推進するコミュニティーなるものが発足しており、そのコミュニティーのページで解決の糸口が掲載されていました。海外はこういうコミュニティーの発足が本当に早いですね。英語力を磨いて積極的に参加出来る様になりたいと今回ばかりは本気で思いました。また、eGPU Switcherをセットアップすることでマザーボード上の映像出力端子も使える様になるので、必要に応じてeGPUをON/OFF可能となります。

sudo add-apt-repository ppa:hertg/egpu-switcher
sudo apt update
sudo apt install -y egpu-switcher
sudo egpu-switcher setup
sudo reboot

後は表示されるオプション(GPUの選択等)を行い、再起動すればeGPU上でX-Window Systemが起動します。再起動後、ビデオカード側の出力ポートにケーブルを接続すればGUIがディスプレイに表示されます。(起動前に接続を推奨)

egpu-switcherのセットアップ行程例
Found 2 possible GPUs...

  1: NVIDIA Corporation Device 1e81 (rev a1) (7:0:0)
  2: Intel Corporation Device 3ea5 (rev 01) (0:2:0)

Would you like to define a specific INTERNAL GPU? [y/N]: y
Choose your preferred INTERNAL GPU [1-2]: 2
Choose your preferred EXTERNAL GPU [1-2]: 1

[info] Saved new configuration to /etc/egpu-switcher/egpu-switcher.conf
[info] Automatically detecting if egpu is connected... 
[info] EGPU is connected.
[info] Create symlink /etc/X11/xorg.conf -> /etc/X11/xorg.conf.egpu
Created symlink /etc/systemd/system/multi-user.target.wants/egpu.service → /etc/systemd/system/egpu.service.
[success] Done... Setup finished

再起動後のnvidia-smiの表示例
$ nvidia-smi 
Sun Mar 15 17:37:20 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64       Driver Version: 440.64       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 208...  Off  | 00000000:07:00.0  On |                  N/A |
|  0%   30C    P8    17W / 250W |    240MiB /  7973MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1324      G   /usr/lib/xorg/Xorg                            18MiB |
|    0      1446      G   /usr/bin/gnome-shell                          49MiB |
|    0      1627      G   /usr/lib/xorg/Xorg                            96MiB |
|    0      1772      G   /usr/bin/gnome-shell                          72MiB |
+-----------------------------------------------------------------------------+


CUDA Toolkitのインストール

以下のコマンドでインストール出来ます。

sudo apt install -y nvidia-cuda-*
sudo reboot

この方法でインストールを実施するとCUDA 9.1のToolkitがインストールされてしまうことが分かりました。ここは最新のCUDA Toolkitをインストールしたいのでここで漸くNVIDIA公式サイトで掲載されている方法でインストールを実施します。

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda-repo-ubuntu1804-10-2-local-10.2.89-440.33.01_1.0-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu1804-10-2-local-10.2.89-440.33.01_1.0-1_amd64.deb
sudo apt-key add /var/cuda-repo-10-2-local-10.2.89-440.33.01/7fa2af80.pub
sudo apt update
sudo apt -y install cuda

インストール後、.bashrcに以下のPATHを登録します。

export cuda_version=$(/usr/local/cuda/bin/nvcc -V | grep "Cuda compilation" | awk '{print $5}' | awk '{sub(",.*","");print $1;}')
export PATH=/usr/local/cuda-${cuda_version}/bin:$PATH
export CPATH=/usr/local/cuda-${cuda_version}/include:$CPATH
export LIBRARY_PATH=/usr/local/cuda-${cuda_version}/lib64:$LIBRARY_PATH
  • 2020/3/16追記: CUDAのバージョンが変わってもPATHが通る様に変更

CUDAの動作確認

再起動後、以下のテストソースをnvccでビルド、実行します。

hello.cu
#include <stdio.h>

__global__ void helloFromGPU()
{
    printf("Hello World from GPU\n");
}

int main(void)
{
    printf("Hello World from CPU\n");

    helloFromGPU <<<1, 10>>>();
    cudaDeviceReset();
    return 0;
}

以下の様に表示されればセットアップ成功です。
ビルドと実行
$ nvcc hello.cu
$ ./a.out 
Hello World from CPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
最新アーキテクチャ対応版(2020/3現在)
$ nvcc -arc sm_75 hello.cu
$ ./a.out 
Hello World from CPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU
Hello World from GPU

参考: https://developer.nvidia.com/cuda-gpus

追記: lspciによる確認

セットアップ完了後、lspciコマンドで状態を確認してみます。この時GPUの名前が正しく反映されていない場合が有ります。その場合は、update-pciidsコマンドを実行すると名前が正しく反映されます。

参考: https://www.nemotos.net/?p=3176

$ lspci | grep -i nvidia                                                                                                                   
07:00.0 VGA compatible controller: NVIDIA Corporation Device 1e81 (rev a1) 
07:00.1 Audio device: NVIDIA Corporation Device 10f8 (rev a1) 
07:00.2 USB controller: NVIDIA Corporation Device 1ad8 (rev a1) 
07:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad9 (rev a1) 
$ sudo update-pciids
Downloaded daily snapshot dated 2020-03-07 03:15:01
$ lspci | grep -i nvidia
07:00.0 VGA compatible controller: NVIDIA Corporation TU104 [GeForce RTX 2080 SUPER] (rev a1) 
07:00.1 Audio device: NVIDIA Corporation TU104 HD Audio Controller (rev a1) 
07:00.2 USB controller: NVIDIA Corporation TU104 USB 3.1 Host Controller (rev a1) 
07:00.3 Serial bus controller [0c80]: NVIDIA Corporation TU104 USB Type-C UCSI Controller (rev a1)

まとめ

Razer Core XとNVIDIA Geforce RTX 2080 SUPERを用いてeGPUを構築し、Linuxマシーンへの接続、各種セットアップを実施しました。今回はOSSの強さ・弱さ双方を身をもって体験した構築作業となりました。今後、Jupyter LabサーバーをDockerで構築したり、K8Sの環境構築などを行い、このWorkstationをフル活用して行きたいと思います。

Reference

俺的備忘録 〜なんかいろいろ〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

すぐ使えるsyslogサーバーをPlay with Dockerで作ってみた

検証のためのサーバーをDockerで作りたい

ということでPlay with Dockerで遊んでみました!

●必要なもの
dockerアカウント
ブラウザ(Chrome推奨)

●作ったもの
https://hub.docker.com/r/tt15/centos-syslog

Play with Docker操作

https://labs.play-with-docker.com

Play with DockerにDockerアカウントでログイン(Chrome推奨のよう)
+ ADD NEW INSTANCE を押すとDocker環境が立ち上がります
(IPアドレスは自動で振られ、インスタンス同士、インターネットへのpingは可能)
右側の画面でDockerコマンドを打ち、環境を構築していきます。

docker.test
$ docker pull centos:centos7

centos7: Pulling from library/centos
ab5ef0e58194: Pull complete 
Digest: sha256:4a701376d03f6b39b8c2a8f4a8e499441b0d567f9ab9d58e4991de4472fb813c
Status: Downloaded newer image for centos:centos7
docker.io/library/centos:centos7

docker pullでDocker Hub上にあるイメージをダウンロードできます。

*メモ
”公開しているユーザー”:”タグと呼ばれるバージョンのようなもの”という感じです。

▼▼▼▼▼▼▼▼▼

docker.test
$ docker run -it -d --privileged --name centos -p 514:514/udp centos:centos7 /sbin/init

484bc681a2d879c94ecabc8401756fee3828f5d16c9ed8dfffb3937d91506100

docker runでpullしたイメージを立ち上げます。ちなみに、docker psで今起動しているイメージが確認できます。

*メモ
-itはttyのため
-dを入れないと次のプロンプトが出てこない
--privilegedはsudo的なもの
--nameでわかりやすい名前をつけておかないとランダムでつけられる
-pでsyslogに使うポートを開けておく
pullしたイメージを引数として
/sbin/initを実行する、これをしないと後で使うsystemctlコマンドが使えなかった

▼▼▼▼▼▼▼▼▼

docker.test
$ docker exec -it centos /bin/bash

[root@484bc681a2d8 /]# 

docker execでrunしているイメージ上のコマンドを実行できる。

*メモ
-itでttyを使って対話的な処理が可能、ということだと思う。。
/bin/bashでlsとかcatとか打てますよね。
ちなみにCtrl+Cでdocker側に戻れます。

CentOS操作

bash.rx
[root@484bc681a2d8 /]# yum -y install rsyslog
[root@484bc681a2d8 /]# yum -y install net-tools

それぞれ実行後にドバドバとダウンロードの様子が出ます。
syslogdに必要なパッケージをインストール。
CentOSのネットワーク情報を表示するコマンドのパッケージもインストール。後の動作確認で利用。
あとはrsyslogの設定を最小限して完成です。

*メモ
net-toolsは廃止予定らしく非推奨とのこと。代替コマンドのssが使えなかったしnetstatが慣れているので利用しました。

rsyslog.conf操作

bash.test
[root@484bc681a2d8 /]# vi /etc/rsyslog.conf
rsyslog.conf
# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

# Provides TCP syslog reception
$ModLoad imtcp
$InputTCPServerRun 514

rsyslogをインストールしたのでrsyslog.confがあるはず。
全ての設定が#でコメントアウトされているので上の4箇所の部分の#を削除。
vi的にはxでカーソル右隣の文字を削除できます。
保存はexcキーの後、:を押して、wq!を入力。

▼▼▼▼▼▼▼▼▼

bash.rx
[root@484bc681a2d8 /]# systemctl restart rsyslog
[root@484bc681a2d8 /]# netstat -antup
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address   Foreign Address State PID/Program name    
tcp     0      0 0.0.0.0:514        0.0.0.0:*      LISTEN   879/rsyslogd        
tcp6    0      0 :::514             :::*           LISTEN   879/rsyslogd        
udp     0      0 0.0.0.0:514        0.0.0.0:*               879/rsyslogd        
udp6    0      0 :::514             :::*                    879/rsyslogd    

rsyslogを再起動すると、変更したrsyslog.confが読み込まれる。
netstat -antupでTCP、UDPの接続状況が表示できる。ポート514でrsyslogが起動していることがわかる。

*メモ
-antupは-a,-n,,,それぞれのオプションをくっつけて書いたもの。
-aは全て
-nは数字での表示
-tはTCP
-uはUDP
-pはプロセスIDの表示

これでrsyslogの受信側は準備完了!

検証

ここまでと同じ流れでもう1つ+ ADD NEW INSTANCEして、rsyslogを立ち上げる。
送信側を立ち上げる時に違うのはrsyslog.confの設定部分のみ!

bash.tx
[root@484bc681a2d8 /]# vi /etc/rsyslog.conf
*.info;mail.none;authpriv.none;cron.none* @192.168.0.1:514
*.info;mail.none;authpriv.none;cron.none* @@192.168.0.1:514

rsyslog.confの一番最後にこの二行を追加。
IPアドレスは実環境のものに変更するのを忘れずに。
あとは、設定後のrestartも!

*メモ
@がUDP、@@がTCPって雑だなぁ。。

▼▼▼▼▼▼▼▼▼

bash.tx
[root@484bc681a2d8 /]# logger
test1
test2
test3

送信側にて、loggerコマンドでlogを送りつけられるようです。
受信側の/var/log/messagesに送られるので見てみましょう。

bash.rx
[root@484bc681a2d8 /]# tail /var/log/messages
Mar 15 07:05:43 484bc681a2d8 root: test1
Mar 15 07:05:45 484bc681a2d8 root: test2
Mar 15 07:05:46 484bc681a2d8 root: test3

tailはテキストファイルの最終行から表示するコマンド。
ちゃんと送れてる!!

感想

Play with Dockerを使うと送信側、受信側がサクッと作れるので楽でした!
ブラウザだけで完結するのもありがたい。
4時間が過ぎてもDocker Hub上に作ったイメージをpushしておけばどこからでもdocker pullできます。(docker login, docker tag, docker pushでいけます)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linux勉強会 第5回: ディレクトリ操作とリンクと i ノード

*この記事は、株式会社Ancarで行っている勉強会のおさらい & 外部発信記事として公開されています。

まえがき

おはこんばんにちは。
社内勉強会第5回目となります。
コロナの影響でリモートでの勉強会となりましたが、負けずに今回もやっていきましょう!!

おしながき

  • ディレクトリ操作
    • 作業ディレクトリの移動と表示 ( cd , pwd )
    • ディレクトリを行き来する ( pushd , popd )
    • ディスク使用容量を調べる ( du )
    • ディスク容量情報を表示する ( df )
  • リンクとiノード
    • リンクの設定 ( ln )

※macOSの標準ターミナル使用

ディレクトリ操作

作業ディレクトリの移動と表示 ( cd , pwd )

第2回で触れた内容ですが、おさらいです。
cdコマンド (Change working Directory): カレントディレクトリの位置を変更するコマンド
pwdコマンド (Print Working Directory): カレントディレクトリの名称を表示するコマンド

# 絶対指定で移動する
$ cd /home/una/hoge/fuga

# 現在のディレクトリ表示
$ pwd
/home/una/hoge/fuga

# piyoディレクトリに移動する
$ cd piyo

$ pwd
/home/una/hoge/fuga/piyo

パス指定を容易にするために、特殊なディレクトリ記号が存在します。
~-は便利なので、学んでからよく利用しています。
もう、cd ../../をしなくていいですね...!!

# 直前のディレクトリに戻る
cd ~-

# ひとつ上(親)のディレクトリに移る
cd ..

# ホームディレクトリに移る
cd ~

# 同上
cd

ディレクトリを行き来する ( pushd , popd )

pushedコマンド: ワーキングディレクトリの履歴をスタック(積み上げ)式に保存し、指定されたディレクトリ移動するコマンド
popdコマンド: 記録された履歴に従い、フォルダを移動する

$ pwd
/home/una/hoge

# スタック式でディレクトリを積んでいくと....
$ pushd fuga/
~/hoge/fuga ~

# hoge配下に積まれましたね
$ pwd
/home/una/hoge/fuga

# ポップすると....
$ popd
~/hoge ~

# fugaが取り出されたスタック内容になりました
$ pwd
/home/una/hoge


恥ずかしながら、今回初めて知ったコマンドでした。
古代コマンドと思いきや、下記の通りコンピュータの根幹を成す大切な働きをしていました。

スタックは、コンピュータで用いられる基本的なデータ構造の1つで、データを後入れ先出し(LIFO: Last In First Out; FILO: First In Last Out)の構造で保持するものである。
https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF

ディスク容量情報を表示する ( df )

dfコマンド(Disk Free): ディスクの空き容量を表示するコマンド
後に紹介するduコマンドとセットで利用する機会が多いと思います。

例えば、サーバーがfullに近い状態になったとします。
原因調査をする際に、dfでディスク使用状況を確認し、duでどのファイルがディスクを圧迫しているか調べるという使用方法ができます。

$ df
ファイルシス         1K-ブロック     使用   使用可 使用% マウント位置
devtmpfs            485712       0  485712    0% /dev
tmpfs               503664       0  503664    0% /dev/shm
tmpfs               503664     624  503040    1% /run
tmpfs               503664       0  503664    0% /sys/fs/cgroup
/dev/xvda1         8376300 1724520 6651780   21% /
tmpfs               100736       0  100736    0% /run/user/1000

# hオプションでは単位を見やすい形にしてくれます
$ df -h
ファイルシス        サイズ  使用  残り 使用% マウント位置
devtmpfs         475M     0  475M    0% /dev
tmpfs            492M     0  492M    0% /dev/shm
tmpfs            492M  656K  492M    1% /run
tmpfs            492M     0  492M    0% /sys/fs/cgroup
/dev/xvda1       8.0G  1.7G  6.4G   21% /
tmpfs             99M     0   99M    0% /run/user/1000

ディスク使用容量を調べる ( du )

duコマンド(Disk Usage) : ディスクの使用領域のサイズを表示するコマンド

オプション カスタマイズ内容
-a ディレクトリ内の各ファイルの使用量も表示
-b バイト単位で表示する
-k キロバイト単位で表示する ( デフォルト )
-s 総計サイズのみ表示する
$ du
304 ./dir1  #dir1 の総使用サイズ ( 単位キロ )
656 ./dir2  #dir2 の総使用サイズ
960 .       #総計サイズ   

#キロバイト単位
$ du -k
152     ./dir1  
328     ./dir2 
480     .

#ディレクトリ全て
$ du -a  
304     ./dir1
656     ./dir2
   ~省略~
960     .  

#総計サイズ
$ du -s
960     .

リンクとiノード

リンクの設定 ( ln )

lnコマンド(LiNk): リンクを設定するコマンド

リンクにはハードリンクとシンボリックリンクという2種類の方法があります。

シンボリックリンク: sample_s は sample1 を指す。 sample1 はファイル実体を指す
ハードリンク:    sample_h と sample2 は同じファイル実体を指す

表面的な機能は同じですが、現在は柔軟性のあるシンボリックリンクが主流となっています。
理由は下記の通りです。

  • シンボリックリンクはディレクトリのリンクもできる
  • シンボリックリンクは別のファイルシステム上のファイルともリンクできる

今回はシンボリックリンクについて触れます。

オプション カスタマイズ内容
-s シンボリックリンクにする ( -s がないとハードリンク )
-i 「別名となるパス名」がすでに存在するとき確認実行する
-f 「別名となるパス名」がすでに存在するとき強制実行する

リンクの設定

# sampleファイル作成
$ echo This is sample1 > sample1

# シンボリックリンクを設定
$ ln -s sample1 sample_s

# シンボリックリンクを使ってファイル表示
$ cat sample_s
This is sample1

リンク方法の確認

# リストを見る
$ ls -F
sample1  sample_s@  #@がシンボリックリンクのマーク

# リスト詳細を見る
$ ls -l
total 8
-rw-r--r--  1 una  staff  16  3 10 06:45 sample1
lrwxr-xr-x  1 una  staff   7  3 10 06:45 sample_s -> sample1  #シンボリックリンクはポインタ表示される

ディレクトリ名のリンク

$ cat hoge/fuga
This is sample3

# hogeのリンクであるpiyoを作る
$ ln -s hoge piyo

# piyoを使ってfugaを表示する
$ cat piyo/fuga
This is sample3

ディレクトリ間のリンク

リンク名を現在のディレクトリに作る

# 同一名のリンクを作る
$ ln -s hoge/sample1 .

# sample2 というリンクを作る
$ ln -s hoge/sample1 sample2

# リンク内容
$ ls -l
lrwxrwxrwx 1 una  staff 12  3月 10 06:55 sample1 -> hoge/sample1
lrwxrwxrwx 1 una  staff 12  3月 10 06:56 sample2 -> hoge/sample1

# 同一名のリンクで表示指定
$ cat sample1   
This is sample1. # hoge/sample1を表示している

# sample2というリンク名で表示指定
$ cat dfile2    
This is sample1. # こちらも同様

リンク名を異なるディレクトリに作る

$ cat sample3
This is sample3

# piyo を作っておく
$ mkdir piyo

# 相対パスで指定(絶対パスでも問題なし)
$ ln -sf ../sample3 piyo/sample4

# リンク先
$ ls -l piyo
合計 0
lrwxrwxrwx 1 una staff 9  3月 10 07:01 sample4 -> ../sample3

# 表示ができている..!!
$ cat piyo/sample4
This is sample3

リンク設定はlinuxチョットデキルになるためにはマストなコマンドですね。
なんとなく使っていましたが、改めて学習すると新たな気付きが多い....

まとめ

今回取り上げたコマンドも、基礎コマンドといえると思います。
ただ、日頃利用するコマンドでも知らないオプションや背景などがあり、非常に興味深いものでした。
古代コマンドも知らないだけで、便利な使い方もあるやもしれません。
忘れないためにも、積極的に利用して身体に染み込ませたいと思います!!

過去の勉強会のリンクはこちら

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【GNOME】日本語変換時に変換候補文字が消えずに残ってしまう不具合の回避方法

LinuxのウィンドウマネージャであるGNOMEで日本語変換する際に、変換候補の最後の文字が消えずにカーソル位置に表示され続ける問題の回避方法です。

いくら検索しても出てこないので特殊な環境下でしか発生しないのかもしれませんが、同じ症状に遭遇している人と将来の自分のために記事にしました。

環境

ローカルインストールのUbuntuにGnomeを入れ、標準で入っていたibus-mozc(「Google日本語入力」)を使用して日本語入力した際に発生しました。

Ubuntu 18.04.4 LTS
GNOME 3.28.2
ibus 1.5.17-3ubuntu5.2
ibus-mozc 2.20.2673.102+dfsg-2ubuntu0.18.04.1

症状

ターミナル上などで日本語変換しようとして、例えば「にほんご」と入力し、思い直してバックスペースで全ての文字を削除すると、なぜか最後の「ご」だけ消えずにカーソル位置に表示されたままになってしまいます。

対処方法

IMEをmozc登場前に主流だったAnthyに変更します。
(私は長いことCannaを使っていましたが)

Anthyのインストール

sudo apt install ibus-anthy

IMEを変更
次の順にメニューを選択して変更します

設定 ー> 地域と言語 ー> 入力ソース 「+」 ー> 日本語 ー> 日本語(Anthy)

私の環境では、上記で解消しました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sarコマンドを使わないでCPUの数を調べる方法

0. はじめに

CPUの数を調べることになったのですが、普段使っているsarコマンドが使えない環境下で調べることになり、ググったのでまとめます。

なお、sysstatがインストールされ、sarの設定も完了している環境下で、sarを実行した結果はこのようになります。
本記事では下記の(4 CPU)をsarコマンドを使わずに確認する方法を取り上げていきます。

[root@$hostname ~]# sar
Linux (略) (4 CPU)

11:03:41     LINUX RESTART  (4 CPU)

11:10:01 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle
11:20:01 AM     all      0.00      0.00      0.01      0.00      0.00     99.99
Average:        all      0.00      0.00      0.01      0.00      0.00     99.99
[root@$hostname ~]# 

1.目次

  • 2.検証環境/バージョン情報
  • 3.【方法①】lscpuコマンドを使ってCPUの数を調べる
  • 4.【方法②】sarがやっていることを再現することでCPUの数を調べる
  • 5. sarはどうやってCPU[0-9]+を数えているのか
  • 6. sarで確認できるCPUとはなんなのか
  • 7. 論理プロセッサーと物理コア数、ソケット、スレッドとの関係性
  • 8.【余談】sar -rコマンドとfreeコマンドの比較
  • 9. 参考

2.検証環境/バージョン情報

  • AWS (EC2, VPC, EIP, etc)
    • Red Hat Enterprise Linux release 8.1 (Ootpa)
    • t2.xlarge
    • ストレージはGeneral Purpose SSD (gp2)、16size (t2.xlargeを選択した場合の初期値)
    • sysstat version 11.7.3
/etc/cron.d/sysstat
# Run system activity accounting tool every 10 minutes
*/10 * * * * root /usr/lib64/sa/sa1 1 1
# 0 * * * * root /usr/lib64/sa/sa1 600 6 &
# Generate a daily summary of process accounting at 23:53
53 23 * * * root /usr/lib64/sa/sa2 -A

3.【方法①】lscpuコマンドを使ってCPUの数を調べる

こちらの方法が一番簡単です。

[root@$hostname ~]# lscpu | grep -E '^Thread|^Core|^Socket|^CPU\('
CPU(s):              4
Thread(s) per core:  1
Core(s) per socket:  4
Socket(s):           1
[root@$hostname ~]# 

出所:sarインストール&設定手順

4.【方法②】sarがやっていることを再現することでCPUの数を調べる

理由は次章で取り扱いますが、sarは /sys/devices/system/cpu/cpu[0-9]+のCPUの数を数えていることが分かりました。

[root@$hostname ~]# ls /sys/devices/system/cpu/ | grep -E "cpu[0-9]+"
cpu0
cpu1
cpu2
cpu3
[root@$hostname ~]# ls /sys/devices/system/cpu/ | grep -E "cpu[0-9]+" | wc -l
4
[root@$hostname ~]# 

5. sarはどうやってCPU[0-9]+を数えているのか

さて、sarはどうやってCPU[0-9]+を数えているのでしょうか。
ここではsarのソースコードをたよりに調査しました。
ソースコードはgithubで公開されています。
下記のとおりcpu[0-9]+を数えている箇所と、cpu[0-9]+を数えている対象のパスが定義されている箇所を抜粋します。

  • cpu[0-9]+を数えている箇所
/*
 ***************************************************************************
 * Count number of processors in /sys.
 *
 * IN:
 * @highest If set to TRUE, then look for the highest processor number.
 *      This is used when eg. the machine has 4 CPU numbered 0, 1, 4
 *      and 5. In this case, this procedure will return 6.
 *
 * RETURNS:
 * Number of processors (online and offline).
 * A value of 0 means that /sys was not mounted.
 * A value of N (!=0) means N processor(s) (cpu0 .. cpu(N-1)).
 ***************************************************************************
 */
int get_sys_cpu_nr(int highest)
{
    DIR *dir;
    struct dirent *drd;
    struct stat buf;
    char line[MAX_PF_NAME];
    int num_proc, proc_nr = -1;

    /* Open relevant /sys directory */
    if ((dir = opendir(SYSFS_DEVCPU)) == NULL)
        return 0;

    /* Get current file entry */
    while ((drd = readdir(dir)) != NULL) {

        if (!strncmp(drd->d_name, "cpu", 3) && isdigit(drd->d_name[3])) {
            snprintf(line, MAX_PF_NAME, "%s/%s", SYSFS_DEVCPU, drd->d_name);
            line[MAX_PF_NAME - 1] = '\0';
            if (stat(line, &buf) < 0)
                continue;
            if (S_ISDIR(buf.st_mode)) {
                if (highest) {
                    sscanf(drd->d_name + 3, "%d", &num_proc);
                    if (num_proc > proc_nr) {
                        proc_nr = num_proc;
                    }
                }
                else {
                    proc_nr++;
                }
            }
        }
    }

    /* Close directory */
    closedir(dir);

    return (proc_nr + 1);
}

出所:https://github.com/sysstat/sysstat/blob/master/count.c

Cのコード自体はあまり詳しくないので、間違っているかもしれませんが、おそらくSYSFS_DEVCPU配下のcpu[0-9]+を数えているのではないかと思います。

  • cpu[0-9]+を数えている対象のパス
| /* Files */ |
|:--|:--|:--|:--|
| #define STAT |  |  | PRE "/proc/stat" |
(略)
| #define SYSFS_DEVCPU |  | PRE "/sys/devices/system/cpu" |
(略)
| #define SLASH_DEV |  | PRE "/dev/" |

出所:github.com/sysstat/sysstat/blob/master/common.h

6. sarで確認できるCPUとはなんなのか

kernelのドキュメントを参考にするかぎり、sarで確認できるCPUは論理プロセッサーと考えてよいでしょう。

What: /sys/devices/system/cpu/
(略)
Description:
A collection of both global and individual CPU attributes
Individual CPU attributes are contained in subdirectories
named by the kernel's logical CPU number, e.g.:
/sys/devices/system/cpu/cpu#/

出所:kernel docs sysfs-devices-system-cpu

7. 論理プロセッサーと物理コア数、ソケット、スレッドとの関係性

ここで、lscpuを実行して表示された論理プロセッサーと物理コア数、ソケット、スレッドとの関係性が気になったので調べました。

下記のブログではlscpuコマンドの実行結果を例に論理プロセッサーと物理コア数、ソケット、スレッドとの関係性についてこのように紹介しています。

論理プロセッサー = cpuソケット数 × 物理コア数/ソケット × スレッド数/物理コア数

The total number of logical cores = CPU sockets × physical cores per socket × threads per physical core. Therefore, the computer has 2 × 8 × 2 = 32 logical cores in total.
CPU(s): 32

出所: How many physical and logical CPU cores in your computer | Wei Bai 白巍

[root@$hostname ~]# lscpu | grep -E '^Thread|^Core|^Socket|^CPU\('
CPU(s):              4    # 論理プロセッサー
Thread(s) per core:  1    # スレッド数/物理コア数
Core(s) per socket:  4    # 物理コア数/ソケット
Socket(s):           1    # cpuソケット数
[root@$hostname ~]# 

ところで、先日読んでいた[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識にて、sar -rコマンドとfreeコマンドの相違点の解説がわかりやすかったので、紹介させてください。

8.【余談】sar -rコマンドとfreeコマンドの比較

freeコマンドのフィールド sar -rコマンドのフィールド 内容
total 該当なし システムに搭載されている全メモリの量。
free kbmemfree 見かけ上の空きメモリ。
buff/cache kbbuffers + kbcached バッファキャッシュ、及びページキャッシュが利用するメモリ。システムの空きメモリが減少してきたら、カーネルによって解放される。
available 該当なし 実質的な空きメモリ。フィールドの値に、空きメモリが足りなくなってきたら解放できるカーネル内メモリ領域のサイズを足したもの。解放できるメモリにはバッファキャッシュやページキャッシュの大部分、及びその他カーネルメモリの一部が含まれる。

参考:[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識(一部筆者改変)

freeコマンドの実行結果

[root@$hostname ~]# free
              total        used        free      shared  buff/cache   available
Mem:       16256588      239692    14588360       24948     1428536    15692440
Swap:             0           0           0

sar -rコマンドの実行結果

[root@$hostname ~]# sar -r | head
Linux (略) (4 CPU)

11:03:41     LINUX RESTART  (4 CPU)

11:10:01 AM kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
11:20:01 AM  14581020  15690700   1675568     10.31      2716   1333316    311968      1.92    399160    993580         0
11:30:01 AM  14580936  15690652   1675652     10.31      2716   1333324    296508      1.82    399204    993332         0
11:40:01 AM  14580064  15689796   1676524     10.31      2716   1333328    315288      1.94    400016    993336         0
11:50:01 AM  14581008  15690804   1675580     10.31      2716   1333336    315288      1.94    399880    992556        20
12:00:01 PM  14580612  15690412   1675976     10.31      2716   1333344    323312      1.99    401148    991320         0
[root@$hostname ~]# 

P.S. Twitterもやってるのでフォローしていただけると泣いて喜びます:)

@gkzvoice

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Apache ActiveMQ のインストール

メモ

手順

  1. ActiveMQ の実行用ユーザを作成する

    # useradd activemq
    
  2. 必要なディレクトリを作成する

    # mkdir -p /var/log/activemq \
    && mkdir -p /var/lib/activemq
    
  3. ActiveMQ のバイナリをあらかじめダウンロードしておくか、次のコマンドでダウンロードする

    # curl -LkvOf https://archive.apache.org/dist/activemq/5.15.11/apache-activemq-5.15.11-bin.tar.gz
    
  4. 展開する

    # tar zxvf apache-activemq-5.15.11-bin.tar.gz -C /opt/
    
  5. シンボリックリンクをはっておく

    # ln -s /opt/apache-activemq-5.15.11 /opt/activemq
    
  6. ログファイルの出力先を変更します。

    # vi /opt/activemq/conf/log4j.properties
    
    実行vimコマンド
    :%s/${activemq.data}/\/var\/log\/activemq/g
    
    実行後のファイル
    # File appender
    log4j.appender.logfile=org.apache.log4j.RollingFileAppender
    log4j.appender.logfile.file=/var/log/activemq/activemq.log
    log4j.appender.logfile.maxFileSize=1024KB
    log4j.appender.logfile.maxBackupIndex=5
    log4j.appender.logfile.append=true
    log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.logfile.layout.ConversionPattern=%d | %-5p | %m | %c | %t%n
    
    ###########
    # Audit log
    ###########
    
    log4j.additivity.org.apache.activemq.audit=false
    log4j.logger.org.apache.activemq.audit=INFO, audit
    
    log4j.appender.audit=org.apache.log4j.RollingFileAppender
    log4j.appender.audit.file=/var/log/activemq/audit.log
    log4j.appender.audit.maxFileSize=1024KB
    log4j.appender.audit.maxBackupIndex=5
    log4j.appender.audit.append=true
    log4j.appender.audit.layout=org.apache.log4j.PatternLayout
    log4j.appender.audit.layout.ConversionPattern=%-5p | %m | %t%n
    
  7. 設定ファイルを編集

    # vi /opt/activemq/bin/linux-x86-64/wrapper.conf
    
    #********************************************************************
    # Wrapper Properties
    #********************************************************************
    
    #wrapper.debug=TRUE
    set.default.ACTIVEMQ_HOME=../..
    set.default.ACTIVEMQ_BASE=../..
    set.default.ACTIVEMQ_CONF=%ACTIVEMQ_BASE%/conf
    set.default.ACTIVEMQ_DATA=/var/lib/activemq #作成したディレクトリに変更
    wrapper.working.dir=.
    
    ...
    
    #********************************************************************
    # Wrapper Logging Properties
    #********************************************************************
    # Format of output for the console.  (See docs for formats)
    wrapper.console.format=PM
    
    # Log Level for console output.  (See docs for log levels)
    wrapper.console.loglevel=INFO
    
    # Log file to use for wrapper output logging.
    wrapper.logfile=/var/log/activemq/wrapper.log  #作成したディレクトリ内に配置するように変更
    
    ...
    
  8. 環境変数を一部変更

    # vi /opt/activemq/bin/env
    
    # Configure a user with non root privileges, if no user is specified do not change user
    # (the entire activemq installation should be owned by this user)
    ACTIVEMQ_USER="activemq" #作成したユーザに変更
    
    # location of the pidfile
    ACTIVEMQ_PIDFILE="/opt/activemq/data/activemq.pid" #作成したディレクトリ内に配置するように変更
    
  9. 各種ファイルなどの所有者を実行用ユーザに変更する

    # chown -R activemq:activemq /opt/activemq/ /var/log/activemq/ /var/lib/activemq/
    
  10. ActiveMQ 用のサービスファイルを作成する

    # vi /etc/systemd/system/activemq.service
    
    [Unit]
    Description=ActiveMQ message queue service
    After=network.target
    
    [Service]
    User=activemq
    Group=activemq
    PIDFile=/opt/activemq/data/activemq.pid
    ExecStart=/opt/activemq/bin/activemq start
    ExecStop=/opt/activemq/bin/activemq stop
    
    [Install]
    WantedBy=multi-user.target
    
  11. サービスファイルを読み込んで起動する

    # systemctl daemon-reload \
    && systemctl start activemq
    

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

マルチブートUSBの作り方(Windows10対応)

概要

USBメモリへ複数のOSインストールISOファイルを配置し,ブートローダをインストールすることで,マルチブートなUSBメモリを作成する.
手順として,まずWindows10のインストーラを作成したのち,そこへGRUBをインストールする.GRUBから各種LinuxOSのインストーラISOファイルを読み込ませたり,Windowsのインストーラへチェインロードすることで,マルチブートを実現する.

必要なもの

  • Windows10 マシン
  • Arch Linux マシン(他のディストリビューションでも可能)
  • USBメモリ(16GB以上推奨)

手順

Windowsでの作業

インストーラーにしたいUSBメモリをあらかじめWindowsマシンに挿入しておく.
最低8GB必要な上,他のLinuxディストリビューションのISOを配置するので,なるべく大容量のものを選択する.

Microsoftのメディア作成ツールをダウンロードする.
上記ページより「MediaCreationToolXXXX.exe」をダウンロードして起動する.「XXXX」のところにはインストールするバージョンが入る.

起動したツールの説明に従い,USBメモリにOSインストーラを作成する.
インストーラが作成できた旨が表示されれば成功.一先ずWindowsでの作業は終わり.

インストーラはFAT32で作成されるため,大容量のUSBメモリを利用しても32GBのパーティションに区切られる.Windowsのディスク管理から残った領域を別パーティションとして利用することもできる.

Linuxでの作業

作成したインストーラUSBメモリをLinuxマシンへ挿入する.また必要なLinux ISOファイルを用意しておくこと.

以下,Arch Linuxを前提に進める.他のディストリビューションではソフトウェア名称やオプション等が異なる場合がある.本稿で説明するGRUBのバージョンはGRUB2である.

まずGRUBをインストールする.

sudo pacman -S grub

USBメモリをマウントする.アクセス可能になったら,/mnt/usb/efi/boot/bootx64.efiをリネームし,bootx64.efi.windowsとする.

mkdir /mnt/usb
mount /dev/sdb1 /mnt/usb

mv /mnt/usb/efi/boot/bootx64.efi /mnt/usb/efi/boot/bootx64.efi.windows

USBメモリにGRUBをインストールする.

grub-install --target x86_64-efi --efi-directory /mnt/usb --boot-directory=/mnt/usb/boot --removable

次にGRUBの設定ファイルを作成する.パーティションのUUIDを記述する必要があるため,先に調べておく.XXXX-XXXXのようなハイフン区切りの8桁の文字列を取得する.

lsblk -f | grep /mnt/usb

コンフィグファイルの書き方はArch Wikiを参考にする.Ubuntu18.04に関してはWikiのUbuntuの情報が古いため以下を参照したほうがよい.Arch Linux,CentOS7,Ubuntu 18.04 Serverの該当するバージョンのISOファイルを用意するか,ISOファイルのバージョンに合わせて該当箇所を修正する.ISOファイルは/mnt/usb/boot/isoディレクトリを作成し,そこにファイルを配置する.Windows10は最初にインストールしたものをチェインロードして起動する.作成したコンフィグファイルは/mnt/usb/boot/grub/grub.cfgとして保存する.grub.cfgの2行目のUUIDは先程取得したものを指定する.

mkdir /mnt/usb/boot/iso
vim /mnt/usb/boot/grub/grub.cfg
grub.cfg
# path to the partition holding ISO images (using UUID)
set imgdevpath='/dev/disk/by-uuid/XXXX-XXXX'

insmod all_video

menuentry '[loopback]archlinux-2020.03.01-x86_64' {
    set isofile='/boot/iso/archlinux-2020.03.01-x86_64.iso'
    loopback loop $isofile
    linux (loop)/arch/boot/x86_64/vmlinuz archisodevice=/dev/loop0 img_dev=$imgdevpath img_loop=$isofile
    initrd (loop)/arch/boot/x86_64/archiso.img
}

menuentry "[loopback]CentOS-7-x86_64-Minimal-1908" {
    set isofile='/boot/iso/CentOS-7-x86_64-Minimal-1908.iso'
    loopback loop $isofile
    linux (loop)/isolinux/vmlinuz noeject inst.stage2=hd:/dev/sdb1:/$isofile
    initrd (loop)/isolinux/initrd.img
}

menuentry '[loopback]ubuntu-18.04.4-live-server-amd64' {
    set isofile='/boot/iso/ubuntu-18.04.4-live-server-amd64.iso'
    loopback loop $isofile
    linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile locale=en_US.UTF-8
    initrd (loop)/casper/initrd
}

menuentry '[chain]Win10_1909_Japanese_x64' {
  insmod chain
  chainloader /efi/boot/bootx64.efi.windows
}

各種ファイルの配置をしたら作業は終了.
OSインストールしたいマシンに挿入し,起動時にBIOSのブートメニューからUSBのパーティション1を選択する.GRUBのメニューが表示されれば成功.

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

findのmtimeオプションとその仲間たち ([cma]{time|min}オプション

最近会社で聞かれて答えたことをまとめてみました。

Linuxで、一定期間以上過去のファイルをgzipするcron処理があって、その日数を変更したい、という話がありました。以下のようなコマンドを使っていました。

find ~~~ -mtime +2 -exec gzip {} \;

mtimeの+2のところを変えることになりました。"+2"の意味を聞かれて答えたこと、間違って答えてしまったこと、答えていない周辺部分をまとめてみました。

動作確認環境

以下の中で、実際に動かして確認している部分は"Lubuntu 18.04.4"を使用しています。

findの基本機能のおさらい

findの基本機能は、指定したディレクトリ以下を走査してファイル一覧を標準出力に出力するコマンドです。

もともとの話に出ていたコマンドでは、"-exec"オプションを";"付きで使用しています。これは、"ファイル一覧を出力する"代わりに、"個々のファイルを引数にして、-execに後続するコマンドを実行する"という意味です。"{}”の位置に個々のファイルがセットされた状態でコマンド実行されます。なお、個人的には"-exec"を使う場面では、代わりに"xargs"を使うことが多いです。

以降では、ファイルに付随する日時を取り扱うオプションに焦点をあてて説明していきます。その他の機能の詳細はJMのfindで確認するのがよいでしょう。

日時ベースでの絞り込み

mtimeオプションは、出力するファイルを、ファイルの更新日時条件で絞り込むためのものです。

このように、ファイルに付随する日時を絞り込み条件にするオプションには、以下があります。

  • ?time ( ctime / mtime / atime )
  • ?min ( cmin / mmin / amin )

また、それに関連するオプションには以下があります。

  • daystart

以降では、これらを細かく説明していきます。

なお、日時条件でファイルを絞り込むオプションには、"指定したファイルよりも新しい"という条件を指定するためのオプションもあります。しかし、この記事では説明を省略します。

?time と ?min

これらのオプションは、出力するファイルをファイルの"最終ステータス変更日時"、"最終更新日時"、"最終アクセス日時"の条件で絞り込むためのものです。

オプションの一文字目が日時の種類を表します。

  • c : 最終ステータス変更日時
  • m : 最終更新日時
  • a : 最終アクセス日時

オプションの二文字目以降が日時の単位を表します。

  • time : 日数単位
  • min : 分数単位

そして、数値もしくは符号付き数値をオプションに後続させます。符号の有無は、以下のような意味になります。

  • 符号なし : ぴったりその数値
  • プラス : 指定された数値より大きい
  • マイナス : 指定された数値ほど小さい

例えば"-mtime +2"は、おおざっぱに言えば"最終更新日時が2日以上過去のもの"となります。

では、これらの要素をより詳しく掘り下げていくこととしましょう。

数値指定(?time)

この節では、最終更新日時"mtime"を使って説明します。

きっかけとなった話では"一定以上過去"のファイルが対象でした。前述のように"-mtime +2"といった表記で指定することになります。さて、このような指定をすると、どのようなファイルが対象となるのでしょうか?少し実験してみます。

タイムスタンプを6時間ごとに変えたファイルを数日分作成して、どのファイルがfindの対象となるかを確認してみます。

まずはテスト環境の準備です。

linuser$ rm -f *.txt
linuser$ for day in $( seq -w $( date +%d --date '5 days ago' ) $( date +%d ) ); do for hour in $( seq -w 0 6 23 ); do touch -m -t 202003${day}${hour}00 $day-$hour.txt; done; done
linuser$ date; ls -l
2020年  3月 14日 土曜日 10:23:45 JST
合計 0
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 11-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 11-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 00:00 12-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 12-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-18.txt
linuser$

末尾の2行の時刻が表示されていないのは、ファイルの更新日時が未来を指しているからです。
さて、findを実行してみます。

linuser$ find . -type f -mtime +2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 ./09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 ./09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 ./09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 ./09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 ./10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 ./10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 ./10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 ./10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 ./11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 ./11-06.txt
linuser$ find . -type f -mtime 2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 ./11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 ./11-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 00:00 ./12-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 ./12-06.txt
linuser$ find . -type f -mtime -2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 ./12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 ./12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 ./13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 ./13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 ./13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 ./14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 ./14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-18.txt
linuser$

このような結果となりました。

"-mtime 2"を指定すると、"48時間前~72時間前"が出力されています。つまり、"過去にさかのぼる日数が2日、但し24時間未満切り捨て"のような意味合いとなります。

以下のような図で表すと、より分かりやすくなるかもしれません。

findのmtimeの範囲_daystartなし.png

計算単位が日数なのに、基点が0時ではないという部分が分かりにくいところです。

実は、基点を0時にするオプションがあります。それが"-daystart"です。このオプションをつけてもう一度試してみましょう。"-mtime"よりも前に"-daystart"を指定する必要があります。

linuser$ find . -type f -daystart -mtime +2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 ./09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 ./09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 ./09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 ./09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 ./10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 ./10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 ./10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 ./10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 ./11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 ./11-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 ./11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 ./11-18.txt
linuser$ find . -type f -daystart -mtime 2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 ./12-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 ./12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 ./12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
linuser$ find . -type f -daystart -mtime -2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 ./13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 ./13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 ./13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 ./14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 ./14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-18.txt
linuser$

わかりやすい結果になったのではないでしょうか。暦上の日数がそのまま、数値指定となっています。こちらも同じく図で表してみます。

findのmtimeの範囲_daystartあり.png

さて、分かりやすくなったものの、こちらにも分かりにくい動作部分があります。どこか分かるでしょうか?

"13日00時"は、"-mtime 2"の結果にも"-mtime -2"の結果にも含まれています。一方、"12日00時"は、どこにも含まれていません。

直感的には、境界線を含めてすべての日時が"-mtime +2"か"-mtime 2"か"-mtime -2"のいずれか一つのみに含まれると思ってしまうのではないでしょうか。そして、マニュアルにも24時間で割って端数切捨てとありますので、境界線はどちらか一方に寄るのが妥当な気がします。

しかし実際はそうではないようです。

数値指定(?min)

"mmin"は、日単位ではなく分単位だということが違うだけで、"mtime"と同じです。

こちらは"-daystart"付きのみで確認してみます。現在日時で行うと、すぐにファイルの状況が変わってしまうので。

linuser$ rm *.txt
linuser$ for day in $( date +%d ); do for min in $( seq -w 53 59 ); do touch -m -t 202003${day}23${min} $day-23${min}.txt; done; done
linuser$ ls -l
合計 0
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2353.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2354.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2355.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2356.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2357.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2358.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2359.txt
linuser$ find . -type f -daystart -mmin +5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2353.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2354.txt
linuser$ find . -type f -daystart -mmin 5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2356.txt
linuser$ find . -type f -daystart -mmin -5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2356.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2357.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2358.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2359.txt
linuser$

こちらも"-mtime"と同様の結果になりました。56分は"-mmin 5"と"-mmin -5"の両方に含まれる、55分はどこにも含まれないという結果になりました。

日時の種類

"?time"や"?min"に使用できる日時の種類には、以下の3つがあると書きました。

  • c : 最終ステータス変更日時
  • m : 最終更新日時
  • a : 最終アクセス日時

会社では"c"は"ファイル作成日時"と答えてしまいました。これは誤りです。上に書いた"JMのfind"の表記では"最終ステータス変更日時"となっています。

大まかには、"最終アクセス日時"はファイルの読み込み、"最終更新日時"はファイルの書き込み、"最終ステータス変更日時"はファイルの書き込みやi-nodeデータの変更で変わります。i-nodeデータの変更は、ファイル名、所有者やグループやパーミッション、リンク数などの変更が該当します。

では、確認してみることにしましょう。00時にファイル作成、01時にファイル読み込み、02時にファイル名変更、03時にファイル書き込みを行い、それぞれの時点での状況を確認してみます。

linuser# rm -f a; date 03140000; touch a; stat a; date 03140100; cat a > /dev/null; stat a; date 03140200; mv a b; mv
b a; stat a; date 03140300; echo OK >> a; stat a
2020年  3月 14日 土曜日 00:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 00:00:00.007999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 00:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 01:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 00:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 02:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 02:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 03:00:00 JST
  File: a
  Size: 3               Blocks: 8          IO Block: 4096   通常ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 03:00:00.003999999 +0900
Change: 2020-03-14 03:00:00.003999999 +0900
 Birth: -
linuser#

statコマンドの出力結果のうち、"Access/Modify/Change"が"atime/mtime/ctime"です。"a/m/c"は、どうやら"Access/Modify/Change"の頭文字っぽいですね。

予想していたように、以下のように状態が変わっていることが分かります。

  • 00時:ファイル作成した日時が、3つの日時すべてにセットされている。
  • 01時:ファイルからの読み込みのタイミングで、Accessが01時にセットされている。
  • 02時:ファイル名変更のタイミングで、Changeが02時にセットされている。
  • 03時:ファイルへの書き込みのタイミングで、ModifyとChangeが03時にセットされている。

さらについでに言うと、lsやstatコマンドを実行するだけでは"atime"は変わらないことも分かりました。最終アクセス日時が指す"アクセス"は、"ファイル読み込み"のみです。

最後に

まとめることで、ctimeについての理解を正すことができました。また、?timeや?minの判定方法、対象となる日時の範囲も、間違って覚えていたことが分かりました。

まとめることが理解を深めることにつながることを、実感しました。

なお、atimeはファイル読み込みで変わります。"open()だけでは変わらず、read()しないと変わらない"というところまで説明したいと思っていました。しかし、お手軽にシェルスクリプトだけで確認する方法が思い浮かばず、断念しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

findのmtimeオプションとその仲間たち ([cma](time|min)オプション

最近会社で聞かれて答えたことをまとめてみました。

Linuxで、一定期間以上過去のファイルをgzipするcron処理があって、その日数を変更したい、という話がありました。以下のようなコマンドを使っていました。

find ~~~ -mtime +2 -exec gzip {} \;

mtimeの+2のところを変えることになりました。"+2"の意味を聞かれて答えたこと、間違って答えてしまったこと、答えていない周辺部分をまとめてみました。

動作確認環境

以下の中で、実際に動かして確認している部分は"Lubuntu 18.04.4"を使用しています。

findの基本機能のおさらい

findの基本機能は、指定したディレクトリ以下を走査してファイル一覧を標準出力に出力するコマンドです。

もともとの話に出ていたコマンドでは、"-exec"オプションを";"付きで使用しています。これは、"ファイル一覧を出力する"代わりに、"個々のファイルを引数にして、-execに後続するコマンドを実行する"という意味です。"{}”の位置に個々のファイルがセットされた状態でコマンド実行されます。なお、個人的には"-exec"を使う場面では、代わりに"xargs"を使うことが多いです。

以降では、ファイルに付随する日時を取り扱うオプションに焦点をあてて説明していきます。その他の機能の詳細はJMのfindで確認するのがよいでしょう。

日時ベースでの絞り込み

mtimeオプションは、出力するファイルを、ファイルの更新日時条件で絞り込むためのものです。

このように、ファイルに付随する日時を絞り込み条件にするオプションには、以下があります。

  • ?time ( ctime / mtime / atime )
  • ?min ( cmin / mmin / amin )

また、それに関連するオプションには以下があります。

  • daystart

以降では、これらを細かく説明していきます。

なお、日時条件でファイルを絞り込むオプションには、"指定したファイルよりも新しい"という条件を指定するためのオプションもあります。しかし、この記事では説明を省略します。

?time と ?min

これらのオプションは、出力するファイルをファイルの"最終ステータス変更日時"、"最終更新日時"、"最終アクセス日時"の条件で絞り込むためのものです。

オプションの一文字目が日時の種類を表します。

  • c : 最終ステータス変更日時
  • m : 最終更新日時
  • a : 最終アクセス日時

オプションの二文字目以降が日時の単位を表します。

  • time : 日数単位
  • min : 分数単位

そして、数値もしくは符号付き数値をオプションに後続させます。符号の有無は、以下のような意味になります。

  • 符号なし : ぴったりその数値
  • プラス : 指定された数値より大きい
  • マイナス : 指定された数値ほど小さい

例えば"-mtime +2"は、おおざっぱに言えば"最終更新日時が2日以上過去のもの"となります。

では、これらの要素をより詳しく掘り下げていくこととしましょう。

数値指定(?time)

この節では、最終更新日時"mtime"を使って説明します。

きっかけとなった話では"一定以上過去"のファイルが対象でした。前述のように"-mtime +2"といった表記で指定することになります。さて、このような指定をすると、どのようなファイルが対象となるのでしょうか?少し実験してみます。

タイムスタンプを6時間ごとに変えたファイルを数日分作成して、どのファイルがfindの対象となるかを確認してみます。

まずはテスト環境の準備です。

linuser$ rm -f *.txt
linuser$ for day in $( seq -w $( date +%d --date '5 days ago' ) $( date +%d ) ); do for hour in $( seq -w 0 6 23 ); do touch -m -t 202003${day}${hour}00 $day-$hour.txt; done; done
linuser$ date; ls -l
2020年  3月 14日 土曜日 10:23:45 JST
合計 0
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 11-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 11-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 00:00 12-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 12-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-18.txt
linuser$

末尾の2行の時刻が表示されていないのは、ファイルの更新日時が未来を指しているからです。
さて、findを実行してみます。

linuser$ find . -type f -mtime +2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 ./09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 ./09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 ./09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 ./09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 ./10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 ./10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 ./10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 ./10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 ./11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 ./11-06.txt
linuser$ find . -type f -mtime 2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 ./11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 ./11-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 00:00 ./12-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 ./12-06.txt
linuser$ find . -type f -mtime -2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 ./12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 ./12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 ./13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 ./13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 ./13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 ./14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 ./14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-18.txt
linuser$

このような結果となりました。

"-mtime 2"を指定すると、"48時間前~72時間前"が出力されています。つまり、"過去にさかのぼる日数が2日、但し24時間未満切り捨て"のような意味合いとなります。

以下のような図で表すと、より分かりやすくなるかもしれません。

findのmtimeの範囲_daystartなし.png

計算単位が日数なのに、基点が0時ではないという部分が分かりにくいところです。

実は、基点を0時にするオプションがあります。それが"-daystart"です。このオプションをつけてもう一度試してみましょう。"-mtime"よりも前に"-daystart"を指定する必要があります。

linuser$ find . -type f -daystart -mtime +2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月  9 00:00 ./09-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 06:00 ./09-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 12:00 ./09-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月  9 18:00 ./09-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 00:00 ./10-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 06:00 ./10-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 12:00 ./10-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 10 18:00 ./10-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 00:00 ./11-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 06:00 ./11-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 12:00 ./11-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 11 18:00 ./11-18.txt
linuser$ find . -type f -daystart -mtime 2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 12 06:00 ./12-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 12:00 ./12-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 12 18:00 ./12-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
linuser$ find . -type f -daystart -mtime -2 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 13 00:00 ./13-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 06:00 ./13-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 12:00 ./13-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 13 18:00 ./13-18.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 00:00 ./14-00.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14 06:00 ./14-06.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-12.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-18.txt
linuser$

わかりやすい結果になったのではないでしょうか。暦上の日数がそのまま、数値指定となっています。こちらも同じく図で表してみます。

findのmtimeの範囲_daystartあり.png

さて、分かりやすくなったものの、こちらにも分かりにくい動作部分があります。どこか分かるでしょうか?

"13日00時"は、"-mtime 2"の結果にも"-mtime -2"の結果にも含まれています。一方、"12日00時"は、どこにも含まれていません。

直感的には、境界線を含めてすべての日時が"-mtime +2"か"-mtime 2"か"-mtime -2"のいずれか一つのみに含まれると思ってしまうのではないでしょうか。そして、マニュアルにも24時間で割って端数切捨てとありますので、境界線はどちらか一方に寄るのが妥当な気がします。

しかし実際はそうではないようです。

数値指定(?min)

"mmin"は、日単位ではなく分単位だということが違うだけで、"mtime"と同じです。

こちらは"-daystart"付きのみで確認してみます。現在日時で行うと、すぐにファイルの状況が変わってしまうので。

linuser$ rm *.txt
linuser$ for day in $( date +%d ); do for min in $( seq -w 53 59 ); do touch -m -t 202003${day}23${min} $day-23${min}.txt; done; done
linuser$ ls -l
合計 0
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2353.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2354.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2355.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2356.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2357.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2358.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 14-2359.txt
linuser$ find . -type f -daystart -mmin +5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2353.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2354.txt
linuser$ find . -type f -daystart -mmin 5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2356.txt
linuser$ find . -type f -daystart -mmin -5 | xargs -r ls -l
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2356.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2357.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2358.txt
-rw-rw-r-- 1 linuser linuser 0  3月 14  2020 ./14-2359.txt
linuser$

こちらも"-mtime"と同様の結果になりました。56分は"-mmin 5"と"-mmin -5"の両方に含まれる、55分はどこにも含まれないという結果になりました。

日時の種類

"?time"や"?min"に使用できる日時の種類には、以下の3つがあると書きました。

  • c : 最終ステータス変更日時
  • m : 最終更新日時
  • a : 最終アクセス日時

会社では"c"は"ファイル作成日時"と答えてしまいました。これは誤りです。上に書いた"JMのfind"の表記では"最終ステータス変更日時"となっています。

大まかには、"最終アクセス日時"はファイルの読み込み、"最終更新日時"はファイルの書き込み、"最終ステータス変更日時"はファイルの書き込みやi-nodeデータの変更で変わります。i-nodeデータの変更は、ファイル名、所有者やグループやパーミッション、リンク数などの変更が該当します。

では、確認してみることにしましょう。00時にファイル作成、01時にファイル読み込み、02時にファイル名変更、03時にファイル書き込みを行い、それぞれの時点での状況を確認してみます。

linuser# rm -f a; date 03140000; touch a; stat a; date 03140100; cat a > /dev/null; stat a; date 03140200; mv a b; mv b a; stat a; date 03140300; echo OK >> a; stat a
2020年  3月 14日 土曜日 00:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 00:00:00.007999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 00:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 01:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 00:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 02:00:00 JST
  File: a
  Size: 0               Blocks: 0          IO Block: 4096   通常の空ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 00:00:00.007999999 +0900
Change: 2020-03-14 02:00:00.007999999 +0900
 Birth: -
2020年  3月 14日 土曜日 03:00:00 JST
  File: a
  Size: 3               Blocks: 8          IO Block: 4096   通常ファイル
Device: 801h/2049d      Inode: 18054       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-03-14 01:00:00.003999999 +0900
Modify: 2020-03-14 03:00:00.003999999 +0900
Change: 2020-03-14 03:00:00.003999999 +0900
 Birth: -
linuser#

statコマンドの出力結果のうち、"Access/Modify/Change"が"atime/mtime/ctime"です。"a/m/c"は、どうやら"Access/Modify/Change"の頭文字っぽいですね。

予想していたように、以下のように状態が変わっていることが分かります。

  • 00時:ファイル作成した日時が、3つの日時すべてにセットされている。
  • 01時:ファイルからの読み込みのタイミングで、Accessが01時にセットされている。
  • 02時:ファイル名変更のタイミングで、Changeが02時にセットされている。
  • 03時:ファイルへの書き込みのタイミングで、ModifyとChangeが03時にセットされている。

さらについでに言うと、lsやstatコマンドを実行するだけでは"atime"は変わらないことも分かりました。最終アクセス日時が指す"アクセス"は、"ファイル読み込み"のみです。

最後に

まとめることで、ctimeについての理解を正すことができました。また、?timeや?minの判定方法、対象となる日時の範囲も、間違って覚えていたことが分かりました。

まとめることが理解を深めることにつながることを、実感しました。

なお、atimeはファイル読み込みで変わります。"open()だけでは変わらず、read()しないと変わらない"というところまで説明したいと思っていました。しかし、お手軽にシェルスクリプトだけで確認する方法が思い浮かばず、断念しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OS と Linux ディストリビューション

インフラを学ぶにあたり重要となってくるOSや
Linux ディストリビューションについて投稿しようと思います。

OSとは

オペレーティングシステム(Operating System, OS)のこと
直訳すると「動作システム」となります。

キーボードの操作、マウスの挙動、イヤホンの音声などなどハードについてや
入出力、タスク管理、ファイル管理などのソフトについてなど
基本機能のシステムにあたります。

OSの種類

代表的なOSは下記のようなものがあり

具体的な派生としては
バージョン違いのwindows10やvista
後述する。機能性別のlinuxディストリビューションなどあります。

  • Windows
  • Mac OS
  • iOS
  • Linux
  • Android

Windows

Microsoft社が開発したOSです。パソコンで使用されているOSの中では
世界で最もユーザーが多いOSです。

Mac OS

Apple社が作ったOSです。銀色ボディが特徴的なパソコンにインストールされています。
なめらか操作のタッチパッドや、シンプルさで、人気があります。

iOS

Apple社がMac OSを携帯端末用に改良して作ったOSです。
iPhoneやiPadなどにインストールされています。

Linux

WindowsやMacが有料なのに対して、Linuxは無料で使えます。
Linuxには、誰でも自由に改良できるという特徴もあります。

Android

Google社がLinuxを携帯端末用に改良して作ったOSです。
Xperia、Galaxyなどのスマートフォンやタブレットに入っています。

Linux

Linuxは、主にサーバー用として使うOSです。
以下のようなIT技術者向けです。

  • OS開発
  • 大規模基幹システムの開発,運用
  • Iotなどのロボット、家電の組み込み系エンジニア
  • ネットワーク機器やデータベースに関わるインフラ従事者

原本となるのがカーネルで、企業などが改造したのがディストリビューションになります。

Linuxカーネル

Linuxの原本となるのがLinuxカーネル
リーナス・トーバルズ氏が開発したLinux。

Linuxディストリビューション

Linuxカーネルを企業などが改造したもの
Linuxカーネルと区別するための呼称。

ディストリビューションとは分布とか流通とかいう意味で
Linux関連で使うときには「種類」にあたります。

Linuxはオープンソースなので進化(改造)の過程で、様々な分布元(企業など)から
様々な種類のLinuxができています。

ちなみに、これを提供する人を「ディストリビューター」と呼びます。

Linuxディストリビューション代表3系列

RedHat系

商業ディストリビューションの代表格

管理を楽にするツール提供に率先的であったり、メーカーによる独自のドライバなども
rpm形式で配布していることが多い。

入門者はまずRedHat系をたしなむ方が仕事につながりやすい。

Debian系

Debianはパッケージ管理が非常に優秀なディストリビューションである。
パッケージはdeb形式を採用し管理の為のフロントエンドapt系のコマンドを使用する。
aptによりパッケージ依存関係を迅速かつ的確に解決してくれる。

Slackware系

なによりも「シンプル」というのが特長だ。
上記の2種のディストリビューションと違いパッケージ管理において
パッケージ間の依存関係の解決をおこなわない。

Linuxディストリビューション簡易一覧

RedHatEnterpriseLinux(Redhat系)

業界のデファクトスタンダードであり、最も成功した商業ディストリビューション
非常に高価。

CentOS(Redhat系)

RedHatEnterpriseLinuxの商標などを徹底的に除去し、リビルドした
クローン代表的な存在としてCentOSがある。

基本的に商用でも無料で使える。

Fedora(Redhat系)

RedHat社が支援するコミュニティーベースのディストリビューション
RedHat社の実験場とも言える。

リリースが非常に早く、ライフサイクルが短い、RedHat社が関与する重要技術は
まずFedoraに導入され、実証と完成を得たのがRedHatEnterpriseLinuxへと反映される。

Debian(Debian系)

Linuxの世界でも最も組織的でありフリーであることを尊重しているディストリビューション
組織規模も最大級であり、パッケージ管理ツールが非常に優秀

Ubuntu(Debian系)

最もユーザに優しいディストリビューション
通常版とロングサポート版、デスクトップ用途とサーバ用途をある程度わけて開発しているのも特徴

Slackware(Slackware系)

老舗ディストリビューション
「シンプルで見通しがよい」のが特徴

GentooLinux(その他ソースベース)

最も難易度が高いディストリビューション
ローリングリリースという形式をとっており随時新しいパッケージが導入されるようになっている。

公式ハンドブックを片手に一からベースシステムを手動でインストールすることになる。
パッケージ管理はソースコードから半自動でコンパイルするようになっている。

ArchLinux(その他ソースベース)

「シンプル」を念頭において開発されているディストリビューションである。
開発者側からみたシンプルさであり、余計なGUIフロントエンドや過度な独自化を避けることを意味している。

GentooLinuxと決定的に違うのは「バイナリパッケージが提供される」という点である。
これによりユーザは迅速に自分好みの環境を構築することが可能となっている。

LinuxFromScratch(LFS) (その他ソースベース)

「自分で全てを管理したい!!」というユーザのいきつく果ては「LinuxFromScratch」である。
その名のとおりベースシステムからなにから全てを自分でコンパイルし環境を構築するのである。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む