CentOS7でinterfaceの起動時に任意のスクリプトを実行する
CentOSは5,6しか使ったことなかったのですが、何も考えずにサーバにCentOS7をインストールしたら何もかもが変わっていてびっくりしました。
とりあえずネットワーク周りの設定でハマったのと、日本語で情報がなかったのでメモ。
(結局はNetwork Managerが面倒くさいだけな気はするのですが。。)
例えば、あるinterfaceを起動したときに、同時にそれに関連するネットワーク設定ができると便利ですよね。
Debian等でれば、/etc/network/interfacesにこんな感じで書けば、interface起動時に好きなコマンドを実行できたりします。
| 1 2 3 4 5 6 7 | 
auto veth-sw1ns1
 
iface veth-sw1ns1 inet manual
 
    pre-up ip link add veth-sw1ns1 type veth peer name veth-ns1sw1
 
    pre-up ip netns add ns1
 
    pre-up ip link set veth-ns1sw1 netns ns1
 
    pre-up ip netns exec ns1 ip link set veth-ns1sw1 up
 
    post-down ip link del veth-sw1ns1
 | 
しかし、CentOS7はNetwork Managerがすべてを握っているため、果たしてどこに書いてやれば良いのか。

具体的にやりたかったのは起動時にこの環境が立ち上がることです。
(enp4s0の起動と同時に作りたい)
- netnsを作成
- vethのペアを作成
- vethの片方をnetnsに紐付け
- vethの有効化
OpenvSwitchの設定関しては、一度設定すればOpenvSwitch側のデータベースに入るのでrebootしても同じ設定で起きてきます。
また、OpenvSwitchの設定的にはinterfaceの名前をもとにポートを紐付けているようなので、
毎回起動時に同じ名前でinterfaceを作ってあげればifindex等は変わっても良さそうです。
まずは準備、とは言ってもOpenvSwitchの設定だけ先に入れておくだけです。
OpenvSwitchの設定はこんな感じで。
| 1 2 3 | 
ovs-vsctl add-br sw1
 
ovs-vsctl add-port sw1 enp4s0
 
ovs-vsctl add-port sw1 veth-sw1ns1 tag=32
 | 
では、本題、interfaceの起動時に環境設定スクリプトを走らせる方法です。
まず初めに、スクリプトの起点となるinterfaceだけNetwork Managerの呪縛を解いてあげます。
(これも色々試してみたのですが、上手く行ったのはこの方法だけでした。)
/etc/sysconfig/network-script/ifcfg-enp4s0にこんな感じで記述。
(最終行のNM_CONTROLLED=noといのが大事です)
| 1 2 3 4 5 6 7 8 9 10 11 | 
HWADDR=00:30:18:FF:FF:FF
 
TYPE=Ethernet
 
PROXY_METHOD=none
 
BROWSER_ONLY=no
 
IPV6INIT=no
 
NAME=enp4s0
 
DEVICE=enp4s0
 
UUID=2396d3d1-40ff-3ea5-a4ff-************ 
ONBOOT=yes
 
AUTOCONNECT_PRIORITY=-999
 
NM_CONTROLLED=no
 | 
で、nmcliを叩くとあら不思議、enp4s0がunmanagedになってNetwork Managerの呪いから解き放たれました。
| 1 2 3 4 5 6 7 8 9 10 | 
[root@localhost ~]# nmcli dev
 
DEVICE      TYPE         STATE        CONNECTION
 
docker0     bridge       connected    docker0
 
enp5s0      ethernet     connected    enp5s0
 
enp2s0      ethernet     unavailable  -- 
enp3s0      ethernet     unavailable  -- 
enp4s0      ethernet     unmanaged    -- 
lo          loopback     unmanaged    -- 
ovs-system  openvswitch  unmanaged    -- 
sw1         openvswitch  unmanaged    -- | 
この時点で、ifup(/sbin/ifup)によりInterfaceの起動が制御されるようになりました。
ifupはシェルスクリプトで書かれており、中身を覗くとコードの最後のほうで/etc/sysconfig/network-scripts/ifup-ethを読んでいることがわかります。
| 1 2 3 4 5 6 7 8 9 10 11 12 | #前省略 
OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${DEVICETYPE}"
 
if [ ! -x ${OTHERSCRIPT} ]; then
 
    OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${TYPE}"
 fi 
if [ ! -x ${OTHERSCRIPT} ]; then
 
    OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth"
 fi 
exec ${OTHERSCRIPT} ${CONFIG} $2
 | 
さらに/etc/sysconfig/network-scripts/ifup-ethを読んでみると、今度は最終行で/etc/sysconfig/network-scripts/ifup-postが呼ばれています。
| 1 | 
exec /etc/sysconfig/network-scripts/ifup-post ${CONFIG} ${2}
 | 
さらにさらに、/etc/sysconfig/network-scripts/ifup-postの中で/sbin/ifup-localが第一引数にデバイス名を渡して呼ばれています
| 1 2 3 | 
if [ -x /sbin/ifup-local ]; then
 
    /sbin/ifup-local ${DEVICE}
 fi | 
デフォルトでは/sbin/ifup-localは存在していないので、実行されませんが作ってあげると実行してくれます。
こんな感じで書いてあげます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/bin/sh 
if [[ "$1" == "enp4s0" ]]
 then 
    echo "interface[$1]"
 
    #ovs
 
    sudo ip link set sw1 up
 
    #ns1
 
    ip netns add ns1
 
    ip link add veth-sw1ns1 type veth peer name veth-ns1sw1
 
    ip link set veth-ns1sw1 netns ns1
 
    ip netns exec ns1 ip link set veth-ns1sw1 up
 
    ip link set veth-sw1ns1 up
 
    echo "created ns1"
 else 
    echo "interface[$1](DO_NOTHING)"
 fi | 
権限付与。
| 1 | 
[root@localhost ~]# chmod a+x /sbin/ifup-local
 | 
これで、rebootしてみると、interfaceの起動時(ifupが呼ばれたとき)にこのスクリプトが実行されてくれます。
| 1 2 3 4 5 6 7 8 9 | 
[root@localhost ~]# journalctl -u network
 -- Logs begin at Thu 2017-09-21 02:08:03 EDT, end at Thu 2017-09-21 08:03:16 EDT. -- 
Sep 21 02:08:20 localhost systemd[1]: Starting LSB: Bring up/down networking...
 
Sep 21 02:08:21 localhost network[1150]: Bringing up loopback interface:  /sbin/ifup-local:interface[lo](DO_NOTHING)
 
Sep 21 02:08:21 localhost network[1150]: [  OK  ]
 
Sep 21 02:08:27 localhost network[1150]: Bringing up interface enp4s0:  /sbin/ifup-local:interface[enp4s0]
 
Sep 21 02:08:27 localhost network[1150]: created ns1
 
Sep 21 02:08:28 localhost network[1150]: [  OK  ]
 
Sep 21 02:08:28 localhost network[1150]: Bringing up interface enp5s0:  [  OK  ]
 | 
手順と動作の仕組みさえ分かってしまえば簡単でしたね。
ここまで来るのに結構苦労してるので、以下はそのへんの余談。
Network Managerからinterfaceを解き放つのは、上記の方法以外はうまく行きませんでした。
[方法1]
Network Managerの設定ファイルにinterfaceを無視するように書いてみる。
 → Interfaceの設定ファイル(ifcfg-hoge)自体が消失します。
   ifupもこのifcfgの設定ファイルを見ているため、ファイルがないとifupで「そんなデバイス知らねぇ」と怒られてしまいます。
[方法2]
続いて、nmcliからmanagedを外す。
コマンドは至ってシンプルでこれだけ。
| 1 | 
nmcli dev set enp4s0 managed no
 | 
→ しかし一瞬外れたように見えますが、rebootすると結局managedに戻ります。
[方法3]
そもそもNetwork Managerから外す必要があるのか、
 → Network Managerで管理させていると起動処理にifupが呼ばれていない。
   さきほどのjournalctlのログでもNetwork Managerで管理しているenp5s0は先程のifup-localを通ってないことがわかります。
  (Network Managerが直接システムコールか何かを叩いているのか中身はよくわかりませんが)
[結論]
Network Managerの管理から外してあげると、ifupのスクリプトによってinterfaceが起動される。
(Network Managerで管理させている場合でも、OS起動後に手動でifup叩けば一応設定は入るはするのですがrebootのたびに手動オペレーションはめんどくさい)
Network Managerの管理から外すために、ここに書いたような手順でやると、rebootでもとに戻ったり、ifupが参照する設定ファイルが消えたりで他のところで影響がでる。
nmcliの出力とnetworkのログを読んでいて気づいたのですが、実はlo interfaceは最初からunmanagedです。
つまり、loは特になにも設定しなくてもifupによって起動され、さきほどの/sbin/ifup-localも呼ばれます。
これを利用して、lo interfaceの起動時に他のinterfaceの設定をやってしまう方法もありそうです。
が、ログを見る限り順番的にlo interfaceが最初に上がって、それ以外のinterfaceはそのあとNetwork Managerによって起動されそうな雰囲気。
今回はinterfaceそのものの設定を変更するスクリプトは書いてないので問題ないですが、interface設定をしたとしても上書きされて意味なしです。
(nmcli dev enp4s0 managed noを叩けばNetwork Managerが設定する頃にはunmanagedなので、無視してくれるかもしれませんが、あまりいい方法ではないと思ったので試してないです)
以上余談でした。
色々ハマったあげく、今はこの方法でうまく行ってます。
Network Managerのクセが強すぎて、たったこれだけのことでかなり苦戦しました。
何か他にいい方法を知ってる方がいれば教えてください。。