tjinjin's blog

自分用のメモです

CircleCI+ dockerでサーバCIの検証したときにiptablesでハマった

概要

CircleCIでサーバCIをしようと調査していたときに、ローカル環境とCircleCI環境で違う事象が起きたのでまとめた。

CircleCI上のコンテナではiptablesの停止するレシピを流した際に特にエラーにならないが、ローカル上のコンテナに対し同じレシピを流すとエラーが発生しテストが進まない

前提

  • ローカル環境:OSX10.10 boot2docker v1.4.1
  • リモート:CircleCI
  • 起動するdockerには同じDockerfileを使っている。
  • chef/serverspecでCIしている

切り分け

  • boot2docker上でcookした際にiptablesのレシピでエラー発生。/sbin/service iptables stopでエラーになっている。
[docker@8d3a8827187b ~]$ sudo /sbin/service iptables stop
iptables: Setting chains to policy ACCEPT: nat filter      [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:  iptable_nat                  [FAILED]
  • CircleCI上で同じレシピを実行しても問題は起きなかった。

調査ログ

カーネルの情報を確認

docker@boot2docker:~$ lsmod
Module                  Size  Used by    Tainted: G
veth                   12288  0
xt_nat                 12288  2
xt_addrtype            12288  2
xt_conntrack           12288  1
ipt_MASQUERADE         12288  1
iptable_nat            12288  1
nf_conntrack_ipv4      16384  2
nf_defrag_ipv4         12288  1 nf_conntrack_ipv4
nf_nat_ipv4            12288  1 iptable_nat
nf_nat                 16384  4 xt_nat,ipt_MASQUERADE,iptable_nat,nf_nat_ipv4
nf_conntrack           57344  6 xt_conntrack,ipt_MASQUERADE,iptable_nat,nf_conntrack_ipv4,nf_nat_ipv4,nf_nat
bridge                 61440  0
stp                    12288  1 bridge
llc                    12288  2 bridge,stp
ipv6                  229376 69 bridge,[permanent]
vboxsf                 28672  1
cpufreq_userspace      12288  0
cpufreq_stats          12288  0
cpufreq_powersave      12288  0
cpufreq_conservative    12288  0
squashfs               28672  0
loop                   20480  1
ppdev                  12288  0
parport_pc             24576  0
parport                28672  2 ppdev,parport_pc
intel_agp              12288  0
battery                12288  0
intel_gtt              16384  1 intel_agp
i2c_piix4              12288  0
agpgart                28672  2 intel_agp,intel_gtt
pcspkr                 12288  0
ac                     12288  0
vboxguest             139264  1 vboxsf
  • CircleCI上でも確認
ubuntu@box81:~$ lsmod
Module                  Size  Used by
xt_addrtype            12713  2
xt_nat                 12726  20
veth                   13331  0
xt_CHECKSUM            12549  11
iptable_mangle         12734  11
ipt_MASQUERADE         12880  12
iptable_nat            13151  11
nf_nat_ipv4            13316  1 iptable_nat
nf_nat                 26190  4 xt_nat,ipt_MASQUERADE,iptable_nat,nf_nat_ipv4
bridge                116375  0
stp                    12976  1 bridge
llc                    14441  2 bridge,stp
dm_crypt               23456  0
ip6table_filter        12815  0
ip6_tables             27503  1 ip6table_filter
xt_tcpudp              12924  97
nf_conntrack_ipv4      14857  16
nf_defrag_ipv4         12758  1 nf_conntrack_ipv4
xt_conntrack           12760  15
nf_conntrack           97139  6 ipt_MASQUERADE,iptable_nat,nf_nat_ipv4,nf_nat,nf_conntrack_ipv4,xt_conntrack
iptable_filter         12810  11
ip_tables              27717  3 iptable_mangle,iptable_nat,iptable_filter
x_tables               34194  11 xt_addrtype,xt_nat,xt_CHECKSUM,iptable_mangle,ipt_MASQUERADE,ip6table_filter,ip6_tables,xt_tcpudp,xt_conntrack,iptable_filter,ip_tables
ppdev                  17711  0
i2c_piix4              22299  0
serio_raw              13462  0
parport_pc             32866  0
xen_fbfront            17552  0
parport                42481  2 ppdev,parport_pc
fb_sys_fops            12703  1 xen_fbfront
mac_hid                13253  0
isofs                  40287  0
btrfs                 908260  1
raid10                 48666  0
raid456                92012  0
async_raid6_recov      17389  1 raid456
async_memcpy           12689  2 raid456,async_raid6_recov
async_pq               13456  2 raid456,async_raid6_recov
async_xor              13233  3 raid456,async_raid6_recov,async_pq
async_tx               13509  5 raid456,async_raid6_recov,async_memcpy,async_pq,async_xor
xor                    21411  2 btrfs,async_xor
raid6_pq               97812  3 btrfs,async_raid6_recov,async_pq
raid1                  40017  0
raid0                  17969  0
multipath              13145  0
linear                 12894  0
crct10dif_pclmul       14250  0
crc32_pclmul           13160  0
cirrus                 25194  1
ghash_clmulni_intel    13216  0
aesni_intel           152634  0
syscopyarea            12633  2 xen_fbfront,cirrus
sysfillrect            12901  2 xen_fbfront,cirrus
sysimgblt              12806  2 xen_fbfront,cirrus
aes_x86_64             17131  1 aesni_intel
lrw                    13323  1 aesni_intel
ttm                    90162  1 cirrus
gf128mul               14951  1 lrw
glue_helper            14095  1 aesni_intel
drm_kms_helper         53224  1 cirrus
ablk_helper            13597  1 aesni_intel
cryptd                 20531  3 ghash_clmulni_intel,aesni_intel,ablk_helper
drm                   308197  3 cirrus,ttm,drm_kms_helper
psmouse               113121  0
pata_acpi              13038  0
floppy                 74335  0
  • エラーメッセージからboot2docker側ではカーネルモジュールのアンインストールに失敗している。スクリプトを見ると確かにモジュールのアンインストールをしている。(※rmmod_rが関数になっており、その中でmodprobe -rしている。)しかし、docker側からカーネルの変更はできないのでエラーになっている。
$ cat /etc/init.d/iptables

stop(){
(中略)
if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then
        echo -n $"${IPTABLES}: Unloading modules: "
        ret=0
        for mod in ${NF_MODULES[*]}; do
            rmmod_r $mod
            let ret+=$?;
        done
(中略)
  • CircleCI側で/sbin/service iptables startを実行しても何も出力されず、返り値としては"7"が返却されている。そこでstatusコマンドを実行すると次のメッセージが出た。
$ sudo /sbin/service iptables status
iptables: Firewall is not running.
  • これはスクリプトを見ると、$VAR_SUBSYS_IPTABLESにファイルがないこと、かつ$NF_TABLESが空である場合は起動していないと見做しているようだった。
status() {
    if [ ! -f "$VAR_SUBSYS_IPTABLES" -a -z "$NF_TABLES" ]; then
        echo $"${IPTABLES}: Firewall is not running."
        return 3
    fi

このメッセージからCircleCI上コンテナのiptablesはそもそも起動していない可能性と予想。

  • start()のスクリプトを読むと、はじめに$IPTABLES_DATAの存在チェックをしていて、これは/etc/sysconfig/iptablesを指しており、どちらのコンテナ上にも存在しない。なので、コンテナ起動時のiptablesの状態に差異がありそう。
[docker@778027d23fff ~]$ sudo service iptables stop
iptables: Unloading modules:  iptable_mangle iptable_nat iptable_filter iptable_mangle iptable_nat iptable_filter ip_tables       [FAILED]
  • statusの実行ログを比較すると一部変数の設定が違うことがわかった。先ほどのstatus()の$NF_TABLESを確認するとboot2docker上のコンテナでは'nat filter'という変数があるが、CircleCI上では何も設定されていなかった。
# Get active tables
NF_TABLES=$(cat "$PROC_IPTABLES_NAMES" 2>/dev/null)
[ "$IPV" = "ip" ] && _IPV="ipv4" || _IPV="ipv6"
PROC_IPTABLES_NAMES=/proc/net/${IPV}_tables_names

boot2docker側を見ると、

[docker@cec732bdd885 ~]$ sudo cat /proc/net/ip_tables_names
nat
filter

となっており、値が存在している。CircleCI側のコンテナをで同じコマンドを実行してみたところPermissionエラーになった。sudoつけても確認できなかったので権限絞られてるのかなーという感じ。scriptないではパーミッションエラー時のエラー処理がされておらず結果的に値が入らず、たまたま成功しているようだった。

設定を変更してみる

  • boot2dockerのコンテナにログインし、/etc/sysconfig/iptables-configを確認
$ sudo vi /etc/sysconfig/iptables-config
~~
# Unload modules on restart and stop
#   Value: yes|no,  default: yes
# This option has to be 'yes' to get to a sane state for a firewall
# restart or stop. Only set to 'no' if there are problems unloading netfilter
# modules.
IPTABLES_MODULES_UNLOAD="yes"
[docker@ci-maker ~]$ sudo /sbin/service iptables stop
iptables: Setting chains to policy ACCEPT: nat filter      [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]

とりあえず、エラーは吐かなくなったがstatusを見ると設定が残っているようで、serverspecで失敗してしまう。

まとめ

dockerに使うコンテナのカーネルモジュールを頑張れば、CircleCI上と同等の状態にできるのかな。それは違う気もするので、dockerを使う際にはiptablesのテストは考えないという結論に落ち着きそう。

参考リンク