Skip to content

Instantly share code, notes, and snippets.

@hayajo
Last active October 29, 2020 10:02
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hayajo/1afb475d06c9b0aeb340 to your computer and use it in GitHub Desktop.
Save hayajo/1afb475d06c9b0aeb340 to your computer and use it in GitHub Desktop.
TCP/IPとか運用とかsystemdとか #オンシャヘイシャ

第1回 TCP/IPとiptables

TCP/IP

TCP/IPモデル

TCP/IPは下記の4層で構成されています。

TCP/IPモデル 役割 OSI参照モデル
アプリケーション層 ソフトウェア同士のやりとり HTTP, SSH, DNS 7, 6, 5
トランスポート層 データ転送の制御 TCP, UDP 4
インターネット層 IPアドレスの割り当て、ルーティング IP, ICMP, ARP 3
インターフェース層 機器同士の通信 Ethernet, PPP 2

カプセル化

データを転送する際には各層でヘッダを付けて、下の層へデータを渡していきます。

カプセル化

ref. 詳説 TCP/IPプロトコル:第6回 イーサネット(その1) - イーサネットの規格とCSMA/CDアクセス制御方式 (1/5) - @IT

Ethernetフレームではフレームの最大サイズは1518バイトとなっています。 「ジャンボフレーム」という規格に対応したハブやEthernetカードを使うと最大サイズが拡張されるので、やりとりするパケット数を減らすことができます。

Ethernet

EthernetではMACアドレスを宛先としてデータを送信します。

Ethernetのデータ通信では、Ethernetフレームで付与される情報だけを扱うのでデータの中身のIPの情報は参照しません。

そのため「どのMACアドレスのホストがどのIPアドレスに対応するのか」という対応表が必要となるのですが、これを解決するのがARPです。 ARPはIPアドレスからMACアドレスを調べます。

「192.168.0.10」のIPアドレスにデータを送信する場合、最初に送信するときはこのIPアドレスがどのMACアドレスを持っているか分からないので、APR要求を行い、192.168.0.10のMACアドレスを取得して、ARPキャッシュに記録します。 移行の通信ではこのARPキャッシュを使ってMACアドレスを解決します。

ARP要求はルータまでしか届きません。 ルータを超えてデータを転送するときは、ルータがEthernetヘッダを付け替えてもう一方のネットワークへ転送します。

インターネットに向けて通信する場合、デフォルトゲートウェイが最寄りのルータとなります。

UDP/TCP

TCP/IPでの通信方法には"UDP"と"TCP"の2種類があります。

UDP TCP
コネクションレス型 コネクション型
高速 低速
パケットの順序は保証なし、再送なし 順序制御、再送制御、ウィンドウ制御、フロー制御
DNS, NTP, DHCP, etc. HTTP, FTP, SMTP, etc.

3ウェイハンドシェーク

TCPでは通信を開始する際に下図のやりとりを行います。 これを3ウェイハンドシェークといいます。

![Alt text](http://g.gravizo.com/g? @startuml; participant "Client" as C; participant "Server" as S; group 3-Way Hand Shake; C -> S: SYN; S -> C: ACK/SYN; C -> S: ACK; end; C -> S: data[1]; S -> C: ACK[2]; C -> S: data[3]; note right; Incorrect order; end note; C -> S: data[2]; group Resend; S -> C: ACK[2]; C -> S: data[2]; end; S -> C: ACK[3]; @enduml )

シーケンス番号

TCPでは送受信するデータにはシーケンス番号が付与されます。 ACKでは次に欲しいシーケンス番号を「応答番号」として返します。

受信側では順序が正しくなかったり欠落した場合には「再送してほしいシーケンス番号」を応答番号として返すことで、データを再送してもらえます。 送信側では応答番号を確認することでデータが相手に届いたかを判断できます。

データの再送はTCPが自動的に行うので、上位層では何も気にする必要はありません。

tcpdump

パケットを覗いてみよう。

local $ vagrant up
local $ vagrant ssh

ターミナル(1)で下記を実行。 後述のターミナル(3)からの接続が来たら"hello world"など適当に返す(レスポンスデータの終了はCtrl-D。プロセスの終了はCtrl-C)。

vagrant $ sudo /bin/sh -c 'while [ 1 ]; do nc -l 127.0.0.1 80; done'

別のターミナル(2)を開いて実行。次のいずれかのコマンドを実行(3ウェイハンドシェイクを確認する場合は最初のコマンドがおすすめ)。

vagrant $ sudo tcpdump -i lo port 80
vagrant $ sudo tcpdump -s0 -A -i lo port 80   # ASCIIで表示
vagrant $ sudo tcpdump -s0 -X -i lo port 80   # 16進とASCIIで表示

tcpdumpは内部でBerkeley Packet Filter(BPF)を利用する。BPFは下記リンクを参照。

別のターミナル(3)を開いて実行。

vagrant $ curl 127.0.0.1

iptables

パケットフィルタリングとNAT機能を提供するソフトウェアです。

大まかには テーブル x チェイン x (マッチ条件) x ターゲット を組み合わせて設定を行います。

テーブル

iptablesには役割の違う4つのテーブルが存在します。 それぞれのテーブルの役割は下記の通りです。

テーブル 役割
filter パケットの通過や遮断
nat NAT(ネットワークアドレス変換機能)
mangle パケットの改変
raw コネクション追跡を除外するマーク(NOTRACK)を付与する

チェイン

チェインはパケットの取り扱いルールのリストです。

図のようなタイミングで各チェインに登録されたルールが適用されます。 チェインはユーザー定義もできます。

![Alt text](http://g.gravizo.com/g? digraph G { incoming [shape=plaintext]; outgoing [shape=plaintext]; local_process [shape=box;style=filled]; incoming -> PREROUTING; PREROUTING -> FORWARD [label="for other host"]; PREROUTING -> INPUT [label="for this host"]; FORWARD -> POSTROUTING; INPUT -> local_process; local_process -> OUTPUT; OUTPUT -> POSTROUTING; POSTROUTING -> outgoing; { rank=same; incoming PREROUTING FORWARD POSTROUTING outgoing }; { rank=same; INPUT local_process OUTPUT }; } ) Alt test

各テーブルで利用できるチェインは次のようになっています。

チェイン 説明 filter nat mangle raw
PREROUTING ルーティング前に受信したパケットに対するチェイン(宛先を変換)           ✔   ✔    
INPUT 受信したパケットに対するチェイン
FORWARD 受信したマシンを経由するパケットに対するチェイン
OUTPUT     ローカルプロセスで生成されたパケットに対するチェイン(送信を制御) ✔     ✔   ✔     ✔  
POSTROUTING 送信するパケットに対するチェイン(送信元を変換)                           ✔   ✔        

ターゲット

ターゲットは条件にマッチしたパケットの処理方法になります。 主要なターゲット次のとおりです。

ターゲット 説明
ACCEPT パケットを通す
DROP パケットを捨てる
REJECT パケットを捨てる(ICMPエラーを返す)
REDIRECT 特定ポートにリダイレクトする
SNAT 送信元IPアドレスを変更する
DNAT 送信先IPアドレスを変更する
MASQUERADE SNATと同様。外部I/Fが固定IPではない場合などに利用
LOG ログを出力する

各ターゲットについての詳細はこのへんを参照。

iptablesの使い方

おおまかな書式は次のとおりです。

iptables [-t テーブル] コマンド [チェイン] [パラメーター(オプション)...] [-j ターゲット]
  • ルールを確認する(-tによるテーブル指定を省略した場合はfilterテーブルが対象となります)

      $ sudo iptables --list
    
  • チェインのルールをすべて削除し、各チェインのポリシー(デフォルトのターゲット)を設定する

      $ sudo iptables --flush
      $ sudo iptables --policy INPUT DROP # !!!リモートから操作している場合はやっちゃダメ!!!
      $ sudo iptables --policy FORWARD ACCEPT
      $ sudo iptables --policy OUTPUT ACCEPT
    
  • ポート22への接続を許可する

      $ sudo iptables --append INPUT --protocol tcp --dport 22 --jump ACCEPT
    
  • ホストからのSMTP接続を192.168.1.1のみに制限する

      $ sudo iptables --append OUTPUT --protocol tcp --dport 25 --destination ! 192.168.1.1 --jump DROP
    
  • すべてのパケットを拒否する

      $ sudo iptables --append INPUT --jump DROP
    

試してみよう

iptablesを設定します。

$ vagrant up
$ vagrant ssh

下記のポリシーを設定してみましょう。対象はすべてfilterテーブルとなります。

  • stateがRELATED, ESTABLISHEDのパケットの受信を許可する(stateモジュールを利用する)
  • icmpパケット(pingなど)の受信を許可する
  • ループバックインターフェース(lo)への接続を許可する
  • ポート22への新しい接続(stete NEW)を許可する
  • それ以外の受信パケットは拒否する(REJECT)
  • 転送パケットはすべて拒否する(REJECT)
  • 送信パケットはすべて許可する

答え

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
-A INPUT -j REJECT
-A FORWARD -j REJECT

第2回 トラブルシューティング

構成の把握

トラブルシューティングを円滑に行うためには、ネットワーク全体のトポロジやパブリック、プライベートIPアドレス、ルーティング、それぞれのサーバの役割などの管理を適切に行なっていなければなりません。

これらの管理を怠っていた場合、障害への対応の遅れや余計な手間、トラブルを招いてしまいます。

PowerPointやVisio、Excel、専用管理ツール、または自作するなどして、これらのリソースをしっかりと管理しましょう。

ログを読む

兎にも角にも、まずはエラーメッセージを確認します。

/var/log配下のログファイルやアプリケーションのログを確認し、エラーメッセージが出力されている場合はよく読んで次の行動に移りましょう。

ネットワークのトラブルシューティング

ネットワークの状態を確認するにはpingtraceroutedigなどのコマンドを使用します。

pingで疎通確認

対象のサーバと通信可能か確認します。

$ ping 172.16.33.10

PING 172.16.33.10 (172.16.33.10): 56 data bytes
64 bytes from 172.16.33.10: icmp_seq=0 ttl=64 time=1.241 ms
64 bytes from 172.16.33.10: icmp_seq=1 ttl=64 time=0.312 ms
64 bytes from 172.16.33.10: icmp_seq=2 ttl=64 time=0.312 ms
^C
--- 172.16.33.10 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.312/0.565/1.241/0.392 ms

各行にあるmsは応答時間です。

pingコマンドはICMPで通信しています。 対象のホストでICMPがフィルタされている場合はpingに失敗するので注意してください。

tracerouteで経路確認

宛先のホストへのネットワーク上の経路を確認します。

$ traceroute yahoo.co.jp

traceroute: Warning: yahoo.co.jp has multiple addresses; using 183.79.135.206
traceroute to yahoo.co.jp (183.79.135.206), 64 hops max, 52 byte packets
 1  192.168.0.1 (192.168.0.1)  2.059 ms  3.576 ms  1.074 ms
 2  khp059134001129.ppp-bb.dion.ne.jp (59.134.1.129)  6.209 ms  3.523 ms  5.000 ms
 3  oymbbar001-2.bb.kddi.ne.jp (106.162.212.30)  11.691 ms  12.472 ms  12.525 ms
 4  tm4bbac02.bb.kddi.ne.jp (27.90.191.218)  15.936 ms  20.042 ms  14.786 ms
 5  otejbb206.int-gw.kddi.ne.jp (118.152.254.65)  18.298 ms  18.146 ms  15.251 ms
 6  jc-ote302.int-gw.kddi.ne.jp (118.155.197.179)  14.947 ms  54.521 ms  18.458 ms
 7  106.187.28.202 (106.187.28.202)  18.407 ms  18.659 ms  19.823 ms
 8  * * *
 9  61.206.157.74 (61.206.157.74)  23.759 ms  22.868 ms  24.816 ms
10  124.83.252.242 (124.83.252.242)  29.696 ms  29.789 ms  30.318 ms
11  * * *
12  * * *
...

tracerouteはパケットが想定外のルーティングをされていないかを確認するときなどに使います。

tracerouteもICMPで通信します。 結果の一部で*が表示されることがあります。これはICMPをフィルタしているネットワークになります。

digで名前解決を確認

DNSサーバに対して名前解決を行います。

$ dig yahoo.co.jp

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.30.rc1.el6_6.3 <<>> yahoo.co.jp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35922
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;yahoo.co.jp.                   IN      A

;; ANSWER SECTION:
yahoo.co.jp.            52      IN      A       182.22.59.229
yahoo.co.jp.            52      IN      A       183.79.135.206

;; Query time: 20 msec
;; SERVER: 10.0.2.3#53(10.0.2.3)
;; WHEN: Tue Jul 21 05:00:33 2015
;; MSG SIZE  rcvd: 61
  • statusNOERRORなので正しく情報が取得できています。ドメインが存在しない場合はNXDOMAINと表示されます。
  • flagsqr rd raQR(Query/Response), RD(Recursion Desired), RA(Recursion Available)フラグが有効になっていることを表します。他にもAA(Authority Answer), TC(TrunCation)があります。
  • QUESTION SECTIONは問い合わせ内容です。yahoo.co.jpのAレコードを調べたことが分かります。
  • ANSWER SECTIONは答えです。2件のアドレスが答えとして返ってきています。
  • この他にも権威サーバーを表すAUTHORITY SECTIONや付随情報を表すADDITIONAL SECTIONがあります。
  • 問い合わせタイプにはA(Address Mapping: default), AAAA(IPv6 Address), CNAME(Canonical Name), NS(Name Server), MX(Mail Exchanger), SOA(Start of Authority), HINFO(Host Information), ISDN(Integrated Services Digital Network), PRT(Reverse-lookup Pointer), TXT(Text), ANYがあります。

DNSサーバを指定する場合は@SERVERとします。

$ dig @SERVER yahoo.co.jp

また、逆引き(IPアドレスをドメイン名に変換)する場合は-xを使用します。

$ dig -x 182.22.59.229

TCPでの接続確認

指定したポートで通信可能か確認します。 ファイアウォールなどで通信が妨げられていないか確認できます。

$ nc www.example.com 80

ちなみにncは-lオプションで指定したポートをLISTENできます。 ネットワーククライアントの接続テストなどに利用できます。

$ sudo nc -l 80

プロセスまわりのトラブルシューティング

システム全体やプロセスの状況を確認するにはtopdstatpsなどのコマンドを使用します。

top

システムの状況を確認します。

$ top -b -n 1 -a | head -n 20
top - 04:31:46 up  1:51,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 103 total,   1 running, 102 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.1%us,  0.1%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1020000k total,   241936k used,   778064k free,     9596k buffers
Swap:   950268k total,        0k used,   950268k free,   134136k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
2681 root      20   0 94252 4268 3320 S  0.0  0.4   0:00.22 sshd
1210 postfix   20   0 81592 3868 2948 S  0.0  0.4   0:00.03 qmgr
1203 root      20   0 81328 3840 2900 S  0.0  0.4   0:00.09 master
2705 postfix   20   0 81408 3828 2912 S  0.0  0.4   0:00.03 pickup
1246 root      18  -2 12416 2624  556 S  0.0  0.3   0:00.01 udevd
1247 root      18  -2 12416 2612  544 S  0.0  0.3   0:00.00 udevd
2684 vagrant   20   0 94252 2228 1260 S  0.0  0.2   0:00.04 sshd
2685 vagrant   20   0  109m 1944 1560 S  0.0  0.2   0:00.04 bash
  931 root      20   0  243m 1588 1040 S  0.0  0.2   0:00.07 rsyslogd
1105 root      20   0  328m 1504  880 S  0.0  0.1   0:02.39 VBoxService
    1 root      20   0 19232 1496 1220 S  0.0  0.1   0:01.73 init
1215 root      20   0  114m 1400  768 S  0.0  0.1   0:02.13 crond
  971 rpcuser   20   0 23348 1332  888 S  0.0  0.1   0:00.01 rpc.statd

注目するところはload average, Tasks, Cpu(s), Mem, Swapです。

load average

ロードアベレージです。左から1分、5分、15分の平均になります。

CPUのコア数で割って1以下であればおよそ問題ありません。

Tasks

プロセスの数が表示されます。

zombieが大量にないか確認してください。

Cpu(s)

CPU利用率が表示されます。

項目 解説
%us ユーザープロセス
%sys システムプロセス
%ni niceされたプロセス
%id アイドル
%wa I/O待ち
%hi ハードウェア割り込み
%si ソフトウェア割り込み
%st 仮想プロセス

%id以外の値が大きい場合はなにか対処が必要かもしれません。

Mem

メモリの利用率が表示されます。

項目 解説
total メモリの合計
used 利用中メモリ
free 空きメモリ
buffers 確保メモリ

freeが少ない場合はメモリ不足の恐れがあります。

Swap

スワップの利用率が表示されます。

メモリのfreeが多いのにスワップのusedが高い場合はswappinessの値を調整します。

値が大きいほどスワップしやすくなります。

$ cat /proc/sys/vm/swappiness
60

vmstat

CPU、メモリの使用状況を確認します。

topで確認した時にCPUもメモリもスワップも大した数値ではないけどload averageが高いという場合はI/Oに問題があります。

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
1  0      0 778064   9612 134156    0    0     5     3    9    7  0  0 100  0  0
0  0      0 778064   9612 134156    0    0     0     0   42   19  0  0 100  0  0
0  0      0 778056   9620 134152    0    0     0    12   23   32  0  0 100  0  0
0  0      0 778056   9620 134152    0    0     0     0   22   19  0  0 100  0  0
0  0      0 778056   9620 134156    0    0     0     0   16   23  0  0 100  0  0

注目するところはprocsです。 rが実待ちプロセス、bがブロック中プロセスの数になります。

rの値がCPU数の倍以上の場合は注意が必要です。 bの値が高い場合は更に分析が必要ですが、SSDを採用したりディスクを分散する対策が必要になる場合があります。

dstat

システムの状況をより詳しく確認します。

$ dstat -tclmdn --top-io --top-bio
----system---- ----total-cpu-usage---- ---load-avg--- ------memory-usage----- -dsk/total- -net/total- ----most-expensive---- ----most-expensive----
  date/time   |usr sys idl wai hiq siq| 1m   5m  15m | used  buff  cach  free| read  writ| recv  send|     i/o process      |  block i/o process
22-07 08:29:11|  0   0 100   0   0   0|   0    0    0| 110M 12.3M  137M  737M|4738B 4178B|   0     0 |bash        334B    1B|
22-07 08:29:12|  0   0 100   0   0   0|   0    0    0| 110M 12.3M  137M  737M|   0     0 |  60B  166B|                      |
22-07 08:29:13|  0   0 100   0   0   0|   0    0    0| 110M 12.3M  137M  737M|   0     0 |  60B  118B|                      |
22-07 08:29:14|  0   0  99   0   0   0|   0    0    0| 110M 12.3M  137M  737M|   0     0 |  60B  102B|                      |
22-07 08:29:15|  0   0 100   0   0   0|   0    0    0| 110M 12.3M  137M  737M|   0     0 |  60B  102B|                      |                      

--listでプラグインの一覧を確認できます。

ps

システムで動作してるプロセスを確認します。

$ ps axlfww
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
1     0     2     0  20   0      0     0 kthrea S    ?          0:00 [kthreadd]
1     0     3     2 -100  -      0     0 migrat S    ?          0:00  \_ [migration/0]
1     0     4     2  20   0      0     0 ksofti S    ?          0:00  \_ [ksoftirqd/0]
1     0     5     2 -100  -      0     0 cpu_st S    ?          0:00  \_ [stopper/0]
5     0     6     2 -100  -      0     0 watchd S    ?          0:00  \_ [watchdog/0]
1     0     7     2 -100  -      0     0 migrat S    ?          0:00  \_ [migration/1]
...
1     0  2776     1  20   0 175748  3776 poll_s Ss   ?          0:00 /usr/sbin/httpd
5    48  2778  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2779  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2780  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2781  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2782  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2783  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2784  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
5    48  2785  2776  20   0 175748  2420 inet_c S    ?          0:00  \_ /usr/sbin/httpd
  • -なしでBSDオプションで指定
  • a, xは互いに指定するとすべてのプロセスをリストする
  • lは長いフォーマットで出力する
  • fはプロセスツリーを表示する
  • wは出力幅を広げる。2つ指定すると幅の制限がなくなる
項目 解説
UID 実行ユーザーID
PID プロセスID
PPID 親プロセスのプロセスID
NI nice値
VSZ 仮想メモリサイズ
RSS スワップされていない物理メモリ(kB)
STAT プロセス状態
COMMAND 実行コマンド

strace

プロセスのシステムコールを確認します。

$ sudo strace -p 2778
Process 2778 attached - interrupt to quit
accept4(4, {sa_family=AF_INET6, sin6_port=htons(54263), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10
getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
fcntl(10, F_GETFL)                      = 0x2 (flags O_RDWR)
fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
read(10, "GET / HTTP/1.1\r\nUser-Agent: curl"..., 8000) = 162
stat("/var/www/html/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/var/www/html/index.html", 0x7ffffbdbbd50) = -1 ENOENT (No such file or directory)
...

lsof

プロセスが開いているファイルを確認します。

$ sudo lsof -p 2778
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
httpd   2778 apache  cwd    DIR  253,0     4096       2 /
httpd   2778 apache  rtd    DIR  253,0     4096       2 /
httpd   2778 apache  txt    REG  253,0   355232 2362037 /usr/sbin/httpd
httpd   2778 apache  mem    REG  253,0     9488 2361963 /usr/lib64/apr-util-1/apr_ldap-1.so
httpd   2778 apache  mem    REG  253,0    65928 2228254 /lib64/libnss_files-2.12.so
httpd   2778 apache  mem    REG  253,0    10416 2362033 /usr/lib64/httpd/modules/mod_version.so
httpd   2778 apache  mem    REG  253,0    27312 2361992 /usr/lib64/httpd/modules/mod_cgi.so
httpd   2778 apache  mem    REG  253,0    22992 2361999 /usr/lib64/httpd/modules/mod_disk_cache.so
...

データベース

MySQL, PostgreSQLで実行中のプロセスを確認します。

mysql

SHOW full processlist

postgres

SELECT * FROM pg_stat_activity

第3回 systemdとjournald

systemd

systemdはSysV initやUpstartに代わってLinuxの起動プロセスを管理する仕組みです。

主要なLinuxディストリビューションではすでに採用/段階的な導入が行われてきています。

SysV initとの違い

systemdのSysV initとの違いは以下が挙げられます。

  • 起動処理の並列化(起動時間短縮)
  • サービス状態、ログ、PIDを監視(サービスプロセス管理)
  • cgroupsを利用したプロセスのグルーピング(サービス実行環境管理)
  • journaldによるログ管理(ログ管理)

ユニット

ユニットとはsystemdが管理する処理の単位です。 SysV initでは"ランレベル"や"サービス"と呼ばれていたものに相当します。

主なユニットタイプは次のものがあります。

名前 概要
service サービスを起動
target 複数のユニットをグループ化する(ランレベルに相当)
mount ファイルシステムのマウント
swap スワップ領域を有効化
device ディスクデバイス
socket ソケット
timer タイマー。cron的なことができます

ユニット状況の確認

ユニットの状況を確認するにはsystemctl (list-units)を実行します。

$ systemctl

表示するユニットタイプを指定するには-t(--type)オプションを指定します。

$ systemctl -t service
UNIT                               LOAD   ACTIVE SUB     DESCRIPTION
auditd.service                     loaded active running Security Auditing Service
crond.service                      loaded active running Command Scheduler
dbus.service                       loaded active running D-Bus System Message Bus
getty@tty1.service                 loaded active running Getty on tty1
kdump.service                      loaded failed failed  Crash recovery kernel arming
kmod-static-nodes.service          loaded active exited  Create list of required static device nodes for the current kernel
lvm2-lvmetad.service               loaded active running LVM2 metadata daemon
lvm2-monitor.service               loaded active exited  Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
lvm2-pvscan@8:2.service            loaded active exited  LVM2 PV scan on device 8:2
network.service                    loaded active exited  LSB: Bring up/down networking
NetworkManager.service             loaded active running Network Manager
polkit.service                     loaded active running Authorization Manager
postfix.service                    loaded active running Postfix Mail Transport Agent
...

各項目の意味は次の通りです。

項目 意味
UNIT ユニット名
LOAD 設定の読み込み状況
ACTIVE 実行状態の概要
SUB 実行状態の詳細
DESCRIPTION ユニット説明

ユニット一覧の取得

$ systemctl list-unit-files

chkconfig --listに相当するコマンドはsystemctl list-unitfiles -t serviceになります。

$ systemctl list-unit-files -t service
UNIT FILE                                   STATE
arp-ethers.service                          disabled
auditd.service                              enabled
auth-rpcgss-module.service                  static
autovt@.service                             disabled
blk-availability.service                    disabled
brandbot.service                            static
console-getty.service                       disabled
console-shell.service                       disabled
cpupower.service                            disabled
crond.service                               enabled
dbus-org.freedesktop.hostname1.service      static
...

STATEの意味は次の通りです。

STATE 意味
enabled システム起動時に実行されます
disabled システム起動時に実行されません
static システム起動時の実行の有無は設定できません
デバイス一覧の取得

デバイス一覧は-t deviceです。

$ systemctl -t device
UNIT                                                                          LOAD   ACTIVE SUB     DESCRIPTION
sys-devices-pci0000:00-0000:00:03.0-net-enp0s3.device                         loaded active plugged PRO/1000 MT Desktop Adapter
sys-devices-pci0000:00-00...1-host0-target0:0:0-0:0:0:0-block-sda-sda1.device loaded active plugged VBOX_HARDDISK
sys-devices-pci0000:00-00...1-host0-target0:0:0-0:0:0:0-block-sda-sda2.device loaded active plugged LVM PV t2Hvum-FrN8-FITs-xw9B-1aXt-3We2-mEexIg on /dev/sda2
sys-devices-pci0000:00-00...0-ata1-host0-target0:0:0-0:0:0:0-block-sda.device loaded active plugged VBOX_HARDDISK
sys-devices-platform-serial8250-tty-ttyS0.device                              loaded active plugged /sys/devices/platform/serial8250/tty/ttyS0
...
マウント状況の確認

マウント状況一覧は-t mountです。

$ systemctl -t mount
UNIT                    LOAD   ACTIVE SUB     DESCRIPTION
-.mount                 loaded active mounted /
boot.mount              loaded active mounted /boot
dev-hugepages.mount     loaded active mounted Huge Pages File System
dev-mqueue.mount        loaded active mounted POSIX Message Queue File System
home.mount              loaded active mounted /home
sys-kernel-config.mount loaded active mounted Configuration File System
...
スワップ状況の確認

スワップ状況一覧は-t swapです。

$ systemctl -t swap
UNIT             LOAD   ACTIVE SUB    DESCRIPTION
dev-dm\x2d1.swap loaded active active /dev/dm-1
...

サービスの操作

サービスの起動や停止を行うのにもsystemctlを使用します(serivceコマンドに相当)。

  • サービスの起動

      $ sudo systemctl start httpd
    
  • サービスのステータス確認

      $ sudo systemctl status httpd
      httpd.service - The Apache HTTP Server
         Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled)
         Active: active (running) since 火 2015-07-28 13:45:41 UTC; 4s ago
       Main PID: 12683 (httpd)
         Status: "Processing requests..."
         CGroup: /system.slice/httpd.service
                 ├─12683 /usr/sbin/httpd -DFOREGROUND
                 ├─12684 /usr/sbin/httpd -DFOREGROUND
                 ├─12685 /usr/sbin/httpd -DFOREGROUND
                 ├─12686 /usr/sbin/httpd -DFOREGROUND
                 ├─12687 /usr/sbin/httpd -DFOREGROUND
                 └─12688 /usr/sbin/httpd -DFOREGROUND
      
       7月 28 13:45:41 localhost.localdomain httpd[12683]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using localhost.localdom...is message
       7月 28 13:45:41 localhost.localdomain systemd[1]: Started The Apache HTTP Server.
      Hint: Some lines were ellipsized, use -l to show in full.
    
  • サービスの再起動

      $ sudo systemctl restart httpd
    
  • サービスの停止

      $ sudo systemctl stop httpd
    

サービスの自動起動設定

システム起動時にサービスを自動起動させるにはsystemctl enebleを実行します(chkconfig相当)。

$ sudo systemctl enable httpd
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'

実行すると/usr/lib/systemd/system/配下にある、該当のユニットファイルのシンボリックリンクが/etc/systemd/system/multi-user.target.wantsに作成されます。

これはSysV initにおける/etc/init.dのサービス起動スクリプトと/etc/rc.dのランレベルごとに作成されるシンボリックリンクに相当します。

自動起動をやめるにはsystemctl disableを実行します。

$ sudo systemctl disable httpd
rm '/etc/systemd/system/multi-user.target.wants/httpd.service'

サービスの自動起動設定を確認するにはsystemctl is-enabledを実行します。

$ systemctl is-enabled httpd
disabled

また、systemctl maskを実行するとサービスがsystemdの管理から除外されます。

$ sudo systemctl mask httpd
ln -s '/dev/null' '/etc/systemd/system/httpd.service'

除外されたサービスは手動でも起動できません。

$ sudo systemctl start httpd
Failed to issue method call: Unit httpd.service is masked.

systemctl unmaskを実行するとサービスをsystemdの管理に戻すことができます。

$ sudo systemctl unmask httpd
rm '/etc/systemd/system/httpd.service'

サービスユニットの作成

/etc/systemd/system/myapp.serviceを下記の内容で準備します。

$ cat /etc/systemd/system/myapp.service
[Unit]
Description=Mojolicious App
After=local-fs.target network.target

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
User=vagrant
Group=vagrant
ExecStart=/usr/local/bin/hypnotoad -f /home/vagrant/myapp.pl
ExecReload=/usr/local/bin/hypnotoad -f /home/vagrant/myapp.pl
ExecStop=/usr/local/bin/hypnotoad -s /home/vagrant/myapp.pl
Restart=on-failure

myapp.serviceを有効化し、起動します。

$ sudo systemctl enable myapp
$ sudo systemctl start myapp
$ systemctl status myapp
myapp.service - Mojolicious App
  Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
  Active: active (running) since 水 2015-07-29 02:33:06 UTC; 6s ago
  Process: 19074 ExecStop=/usr/local/bin/hypnotoad -s /home/vagrant/myapp.pl (code=exited, status=0/SUCCESS)
Main PID: 19077 (/home/vagrant/m)
  CGroup: /system.slice/myapp.service
          ├─19077 /home/vagrant/myapp.pl
          ├─19078 /home/vagrant/myapp.pl
          ├─19079 /home/vagrant/myapp.pl
          ├─19080 /home/vagrant/myapp.pl
          └─19081 /home/vagrant/myapp.pl

8080ポートにアクセスして動作を確認しましょう。

$ curl localhost:8080

ユニットファイルを編集した時はreenableを実行してください。

$ sudo systemctl reenable myapp

ユニットファイル

ユニットファイルは[Unit], [Service], [Install]などのセクションに分けて記述します。

Unitセクション

ユニットに依存しないオプションの設定を行うセクションです。

主なオプションは次の通りです。

オプション 説明
Description ユニットの説明
Documentation ユニットのドキュメントのURI
Requires/Wants ユニットと同時に有効化が必要なユニット
Before このユニットより後に起動するべきユニット(ここで挙げるユニットより先に起動します)
After このユニットより先に起動するべきユニット(ここで挙げるユニットより後に起動します)

After=local-fs.target network.targetでは、local-fsターゲットとnetworkターゲットが起動した後に、このユニットを起動します。

詳しくは systemd.unit(5) を参照してください。

Installセクション

ユニットの自動起動に関するオプションの設定を行うセクションです。

主なオプションは次の通りです。

オプション 説明
WantedBy/RequiredBy このユニットのシンボリックリンクを指定したユニットの.wants/.requiresに作成

WantedBy=multi-user.targetでは/etc/systemd/system/multi-user.target.wantsにこのユニットのシンボリックリンクが作成されます。

詳しくは systemd.unit(5) を参照してください。

Serviceセクション

serviceタイプのユニット固有のオプションの設定を行うセクションです。

主なオプションは次の通りです。

オプション 説明
Type サービスプロセスの起動完了の判定方法
User サービスプロセスの実行ユーザー
Group サービスプロセスの実行グループ
ExecStart サービス起動コマンド
ExecReload サービスリロードコマンド
ExecStop サービス停止コマンド
Restart サービスプロセス停止時の再起動条件

Typeのデフォルト値はsimpleです。他にもoneshot, forking, notify, dbusなどがあります。 simpleはフォアグラウンドで実行するプロセス、oneshotは一度だけ実行するプロセス、forkingはデーモン型のプロセス、notify, dbusはsystemdもしくはD-BuxのAPIを通じて起動完了を通知するプロセスに対応します。

Restart=on-failureではプロセスが異常終了(exitステータスが0以外)、特定のシグナル(SIGHUP, SIGTERM, SIGINT, SIGPIPE)以外を受け取った、もしくはSuccessExitStatus=で指定したステータスコードやシグナル以外の場合に自動的に再起動する設定です。 デフォルト値はnoとなっており、自動的に再起動を行いません。 この他にもalways, on-success, on-abnormal, on-abort, on-watchdogがサポートされています。

詳しくは systemd.service(5) を参照してください。

起動の仕組みとユニットのディレクトリ

systemdは/etc/systemd/system以下のサブディレクトリに作成された、ユニットファイルへのシンボリックリンクを順次実行して、サービスを起動します。 サービスの起動は並列に行われ、起動順はユニットファイルに記載された依存関係を元に、systemd側で自動的に制御してくれます。

ユニットファイルのシンボリックリンク元ファイルは/usr/lib/systemd/systemです。シンボリックリンク先ディレクトリとの関係を下記に示します。

ディレクトリ 説明
/usr/lib/systemd/system サービス起動のためのユニットファイルが格納されています。/etc/rc.d/init.d相当。
/etc/systemd/system サービス起動のためのユニットファイルに対するシンボリックリンクが配置されます。/etc/rc.d/相当。

サブディレクトリはターゲットユニットごとに作成されており、主なディレクトリの役割は次の通りです。

ディレクトリ 役割
sysinit.target.wants 起動初期に実行されるターゲットです。rc.sysinit相当。
basic.target.wants システム共通に実行されターゲットです。
multi-user.target.wants ランレベル3に相当するターゲットです。
graphical.target.wants ランレベル5に相当するターゲットです。

デフォルトターゲット

現在のデフォルトターゲットを確認するにはsystemctl get-defaultを実行します。

$ systemctl get-default
multi-user.target

デフォルトターゲットを変更するにはsystemctl set-defaultを実行します。

$ sudo systemctl set-default graphical.target
$ sudo reboot

一時的なターゲットの変更

一時的にターゲットを変更するにはsystemctl isolateを実行します(telinit相当)。

$ sudo systemctl isolate graphical.target

systemdの注意点

systemctlではサービスを制御するコマンドとして下記のものが用意されています。

  • start
  • stop
  • restart
  • reload

従来のSysV initの起動スクリプトでは自由にコマンドを定義できましたが、systemctlでは上記のコマンド以外の制御コマンドはありません。

例えばSysV initのhttpd起動スクリプトでは上記の他に

  • condrestart
  • try-restart
  • force-reload
  • graceful
  • help
  • configtest

なども定義されています。

これらのコマンドはsytemctlでは対応できないので、serviceコマンドで実行してきたこれらのコマンドの実行方法を事前に検証しておく必要があります。

$ sudo apachectl configtest
$ sudo apachectl graceful

journald

systemdでは各ユニットのログ出力を収集して独自のバイナリファイルに保存しています。

収集対象のログは

  • プロセスのSTDOUT/STDERR
  • syslogへの出力
  • journald APIで渡されたデータ

となります。

ログの収集、保管はsystemd-journald.serviceが行っています。

$ systemctl status systemd-journald
systemd-journald.service - Journal Service
  Loaded: loaded (/usr/lib/systemd/system/systemd-journald.service; static)
  Active: active (running) since 水 2015-07-29 01:05:54 UTC; 4h 5min ago
    Docs: man:systemd-journald.service(8)
          man:journald.conf(5)
Main PID: 468 (systemd-journal)
  Status: "Processing requests..."
  CGroup: /system.slice/systemd-journald.service
          └─468 /usr/lib/systemd/systemd-journald

ログの確認

journaldで収集されたログはjournalctlで確認します。

$ sudo journalctl
-- Logs begin at 水 2015-07-29 01:05:52 UTC, end at 水 2015-07-29 05:15:00 UTC. --
7月 29 01:05:52 localhost.localdomain systemd-journal[97]: Runtime journal is using 6.2M (max 49.6M, leaving 74.5M of free 490.4M, current limit 49.6M).
7月 29 01:05:52 localhost.localdomain systemd-journal[97]: Runtime journal is using 6.2M (max 49.6M, leaving 74.5M of free 490.4M, current limit 49.6M).
7月 29 01:05:52 localhost.localdomain kernel: Initializing cgroup subsys cpuset
7月 29 01:05:52 localhost.localdomain kernel: Initializing cgroup subsys cpu
7月 29 01:05:52 localhost.localdomain kernel: Initializing cgroup subsys cpuacct
7月 29 01:05:52 localhost.localdomain kernel: Linux version 3.10.0-229.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.2 20140120 (Red Hat 4.8.2-16) (GCC) ) #1 SMP
...

-kオプションを付与するとdmesg相当の内容を確認できます。

$ sudo journalctl -k

特定のユニットのログを確認するには-u UNITを付与します。

$ sudo journalctl -u myapp

また-o OUTPUTで出力フォーマットを指定したり、-ftail -fと同様に出力をリアルタイムで確認できます。

$ sudo journalctl -u myapp -o json-pretty -f

ログの保存

ログの出力先について、/etc/systemd/journald.confのデフォルト設定(Storage=auto)では、次のような動作をします。

  1. /var/log/journaldディレクトリが存在すればそこに書き込む
  2. /var/log/journaldディレクトリが存在しないか書き込み不可な場合は、/run/log/journalディレクトリに書き込む

/run/log/journalディレクトリはtmpfsで作成された一時領域なので再起動時にログは消えてしまいます。

ログを残す場合は/var/log/journalディレクトリを作成してシステムを再起動します。

$ sudo mkdir /var/log/journal
$ sudo chmod 700 /var/log/journal
$ sudo reboot
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "boxcutter/centos71"
config.vm.network "private_network", ip: "172.16.33.10"
config.vm.provider "virtualbox" do |vb|
vb.cpus = 3
vb.memory = 1024
end
config.vm.provision "shell", inline: <<-SHELL
sudo yum install -y tcpdump nc iptables-services
sudo systemctl enable iptables && sudo systemctl start iptables
sudo yum install -y bind-utils lsof strace dstat
sudo yum install -y httpd perl perl-core
sudo wget -O - https://cpanmin.us | perl - --sudo App::cpanminus
sudo /usr/local/bin/cpanm --notest Mojolicious
if [ ! -e /home/vagrant/myapp.pl ]; then (cd /home/vagrant && sudo su -l vagrant -c 'mojo generate lite_app'); fi
SHELL
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment