探討Docker容器中修改系統變量的方法
探討完Docker對共享內存狀態持久化的支持狀況后,我將遺留產品build到一個pre-production image中,測試啟動是否OK。很顯然,我過於樂觀了,Docker之路並不平坦。我收到了shmget報出的EINVAL錯誤碼,提示參數非法。 shmget的manual對EINVAL錯誤碼的說明如下: EINVAL: A new segment was to be created and size < SHMMIN or size > SHMMAX, or no new segment was to be created, a segment with given key existed, but size is greater than the size of that segment. 顯然我們要創建的shared memory的size很可能大於SHMMAX這個系統變量了。那么一個從base image創建出的容器中的系統變量到底是什么值呢?我們來查看一下,我們基於"centos:centos6"啟動一個Docker容器,並檢查其中的 系統變量值設置: $ sudo docker run -it "centos:centos6" /bin/bash bash-4.1# cat /proc/sys/kernel/shmmax 33554432 bash-4.1# sysctl -a|grep shmmax kernel.shmmax = 33554432 可以看出默認情況下,當前容器中root賬號看到的shmmax值我33554432, 我的程序要創建的shm size的確要大於這個值,報出EINVAL錯誤也就無可厚非了。我嘗試按照物理機上的方法臨時修改一下該值: bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax bash: /proc/sys/kernel/shmmax: Read-only file system /proc/sys/kernel/shmmax居然是只讀的,無法修改。 我又嘗試修改/etc/sysctl.conf這個持久化系統變量的地方,但打開/etc/sysctl.conf文件,我發現我又錯了,這 個文件中shmmax的值如下: # Controls the maximum shared segment size, in bytes kernel.shmmax = 68719476736 /etc/sysctl.conf文件 中的系統變量shmmax的值是68719476736,而系統當前的實際值則是33554432,難道是/etc /sysctl.conf中的值沒有生效,於是我手工重新加載一次該文件: -bash-4.1# sysctl -p error: "Read-only file system" setting key "net.ipv4.ip_forward" error: "Read-only file system" setting key "net.ipv4.conf.default.rp_filter" error: "Read-only file system" setting key "net.ipv4.conf.default.accept_source_route" error: "Read-only file system" setting key "kernel.sysrq" error: "Read-only file system" setting key "kernel.core_uses_pid" error: "net.ipv4.tcp_syncookies" is an unknown key error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key error: "net.bridge.bridge-nf-call-iptables" is an unknown key error: "net.bridge.bridge-nf-call-arptables" is an unknown key error: "Read-only file system" setting key "kernel.msgmnb" error: "Read-only file system" setting key "kernel.msgmax" error: "Read-only file system" setting key "kernel.shmmax" error: "Read-only file system" setting key "kernel.shmall" 我得到了和之前類似的錯誤結果:只讀文件系統,無法修改。於是乎兩個問題縈繞在我的面前: 1、為什么容器內當前系統變量值與sysctl.conf中的不一致? 2、為什么無法修改當前系統變量值? 在翻閱了Stackoverflow, github docker issues后,我得到了的答案如下: 1、Docker的base image做的很精簡,甚至都沒有init進程,原本在OS啟動時執行生效系統變量的過程(sysctl -p)也給省略了,導致這些系統變量依舊保留着kernel默認值。以CentOs為例,在linux kernel boot后,init都會執行/etc/rc.d/rc.sysinit,后者會加載/etc/sysctl.conf中的系統變量值。下面是 CentOs5.6中的rc.sysinit代碼摘錄: … … # Configure kernel parameters update_boot_stage RCkernelparam sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1 … … 2、Docker容器中的系統變量在non-priviledged模式下目前(我使用的時docker 1.2.0版本)就無法修改,這 和resolv.conf、hosts等文件映射到宿主機對應的文件有不同。 $ mount -l …. …. /dev/mapper/ubuntu–Server–14–vg-root on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/mapper/ubuntu–Server–14–vg-root on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/mapper/ubuntu–Server–14–vg-root on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered) … … 那么我們該如何修改系統變量值來滿足遺留產品的需求呢? 一、使用–privileged選項 我們使用–privileged這個特權選項來啟動一個基於centos:centos6的新容器,看看是否能對shmmax這樣的系統變量值 進行修改: $ sudo docker run -it –privileged "centos:centos6" /bin/bash bash-4.1# cat /proc/sys/kernel/shmmax 33554432 bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax bash-4.1# cat /proc/sys/kernel/shmmax 68719476736 bash-4.1# sysctl -p net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 … … kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 可以看出,通過–privileged選項,容器獲得了額外的特權,並且可以對系統變量的值進行修改了。不過這樣的修改是不能保存在容器里的, 我們stop 容器,再重啟該容器就能看出來: $ sudo docker start 3e22d65a7845 $ sudo docker attach 3e22d65a7845 bash-4.1# cat /proc/sys/kernel/shmmax 33554432 shmmax的值在容器重啟后又變回了原先的那個默認值。不過重啟后的容器依舊具有privileged的特權,我們還可以重新手工執行命令對系 統變量進行修改: bash-4.1# echo 68719476736 > /proc/sys/kernel/shmmax bash-4.1# cat /proc/sys/kernel/shmmax 68719476736 但即便這樣,也無法滿足我們的需求,我們總不能每次都在容器中手工執行系統變量值修改的操作吧。privileged選項的能力能否帶到 image中呢?答案是目前還不能,我們無法在build image時通過privileged選項修改系統變量值。 這樣一來,我們能做的只有把產品啟動與系統變量值修改放在一個腳本中了,並將該腳本作為docker 容器的cmd命令來執行,比如我們構建一個Dockerfile: FROM centos:centos6 MAINTAINER Tony Bai<bigwhite.cn@gmail.com> RUN yum install python-setuptools -y RUN easy_install supervisor RUN mkdir -p /var/log/supervisor COPY ./supervisord.conf /etc/supervisord.conf COPY ./start.sh /bin/start.sh RUN chmod +x /bin/start.sh CMD ["/bin/start.sh] //start.sh sysctl -p /usr/bin/supervisord 這樣,start.sh在supervisord啟動前將系統變量值重新加載,而supervisord后續啟動的程序就可以看到這些新系統變量 的值了。不過別忘了利用這個image啟動容器時要加上–priviledged選項,否則容器啟動就會失敗。 二、使用phusion/baseimage 前面說過/etc/sysctl.conf中的值沒有生效是因為docker官方提供的centos:centos6把init進程的初始化過程給精 簡掉了。phusion/baseimage是目前docker registery上僅次於ubuntu和centos兩個之后的base image,其提供了/sbin/my_init這個init進程,用於在container充當init進程的角色。那么my_init是否可以用於執行sysctl -p呢?我們試驗一下: 我們先pull這個base image下來:sudo docker pull phusion/baseimage。pull成功后,我們先基於“phusion/baseimage”啟動一個容器做一些explore工作: $ sudo docker run -i -t "phusion/baseimage" *** Running /etc/my_init.d/00_regen_ssh_host_keys.sh… No SSH host key available. Generating one… Creating SSH2 RSA key; this may take some time … Creating SSH2 DSA key; this may take some time … Creating SSH2 ECDSA key; this may take some time … Creating SSH2 ED25519 key; this may take some time … invoke-rc.d: policy-rc.d denied execution of restart. *** Running /etc/rc.local… *** Booting runit daemon… *** Runit started as PID 100 通過nsenter進去,查看一下/sbin/my_init的源碼,我們發現這是一個python腳本,不過從頭到尾瀏覽一遍,沒有發現sysctl加載/etc/sysctl.conf系統變量的操作。 不過,phusion文檔中說my_init可以在初始化過程中執行/etc/my_init.d下的腳本。那是不是我們將一個執行sysctl -p的腳本放入/etc/my_init.d下就可以實現我們的目的了呢?試試。 我們編寫一個腳本:load_sys_varibles.sh #!/bin/sh sysctl -p > init.txt 下面是制作image的Dockerfile: FROM phusion/baseimage:latest MAINTAINER Tony Bai <bigwhite.cn@gmail.com> RUN echo "kernel.shmmax = 68719476736" >> /etc/sysctl.conf RUN mkdir -p /etc/my_init.d ADD load_sys_varibles.sh /etc/my_init.d/load_sys_varibles.sh RUN chmod +x /etc/my_init.d/load_sys_varibles.sh CMD ["/sbin/my_init"] phusion/baseimage是基於ubuntu的OS,其sysctl.conf默認情況下沒啥內容,所以我們在Dockerfile中向這個文件寫入我們需要的系統變量值。構建image並啟動容器: $ sudo docker build -t "myphusion:v1" ./ Sending build context to Docker daemon 13.12 MB Sending build context to Docker daemon Step 0 : FROM phusion/baseimage:latest —> cf39b476aeec Step 1 : MAINTAINER Tony Bai <bigwhite.cn@gmail.com> —> Using cache —> d0e9b51a3e4f Step 2 : RUN echo "kernel.shmmax = 68719476736" >> /etc/sysctl.conf —> Using cache —> 2c800687cc83 Step 3 : RUN mkdir -p /etc/my_init.d —> Using cache —> fe366eea5eb4 Step 4 : ADD load_sys_varibles.sh /etc/my_init.d/load_sys_varibles.sh —> a641bb595fb9 Removing intermediate container c381b9f001c2 Step 5 : RUN chmod +x /etc/my_init.d/load_sys_varibles.sh —> Running in 764866552f25 —> eae3d7f1eac5 Removing intermediate container 764866552f25 Step 6 : CMD ["/sbin/my_init"] —> Running in 9ab8d0b717a7 —> 8be4e7b6b174 Removing intermediate container 9ab8d0b717a7 Successfully built 8be4e7b6b174 $ sudo docker run -it "myphusion:v1" *** Running /etc/my_init.d/00_regen_ssh_host_keys.sh… No SSH host key available. Generating one… Creating SSH2 RSA key; this may take some time … Creating SSH2 DSA key; this may take some time … Creating SSH2 ECDSA key; this may take some time … Creating SSH2 ED25519 key; this may take some time … invoke-rc.d: policy-rc.d denied execution of restart. *** Running /etc/my_init.d/load_sys_varibles.sh… sysctl: setting key "kernel.shmmax": Read-only file system *** /etc/my_init.d/load_sys_varibles.sh failed with status 255 *** Killing all processes… 唉,還是老問題!即便是在my_init中執行,依舊無法逾越Read-only file system,查看Phusion/baseimage的Dockerfile才知道,它也是From ubuntu:14.04的,根不變,上層再怎么折騰也沒用。 換一種容器run方法吧,加上–privileged: $ sudo docker run -it –privileged "myphusion:v1" *** Running /etc/my_init.d/00_regen_ssh_host_keys.sh… No SSH host key available. Generating one… Creating SSH2 RSA key; this may take some time … Creating SSH2 DSA key; this may take some time … Creating SSH2 ECDSA key; this may take some time … Creating SSH2 ED25519 key; this may take some time … invoke-rc.d: policy-rc.d denied execution of restart. *** Running /etc/my_init.d/load_sys_varibles.sh… *** Running /etc/rc.local… *** Booting runit daemon… *** Runit started as PID 102 這回靈光了。enter到容器里看看設置的值是否生效了: root@9e399f46372a:~#cat /proc/sys/kernel/shmmax 68719476736 結果如預期。這樣來看phusion/baseimage算是為sysctl -p加載系統變量值提供了一個便利,但依舊無法脫離–privileged,且依舊無法在image中持久化這個設置。 在Docker github的issue中有人提出建議在Dockerfile中加入類似RUNP這樣的帶有特權的指令語法,但不知何時才能在Docker中加入這一功能。 總而言之,基於目前docker官網提供的base image,我們很難找到特別理想的修改系統變量值的方法,除非自己制作base image,這個還沒嘗試過,待后續繼續研究。
參考資料:
http://www.kuqin.com/shuoit/20141020/342750.html
Docker 容器修改內核參數問題:https://discuss.csphere.cn/t/docker/1964
http://bbs.chinaunix.net/thread-1950450-1-1.html
http://bbs.chinaunix.net/archiver/tid-1950450.html
http://blog.csdn.net/linquidx/article/details/5914640
http://blog.csdn.net/zjl410091917/article/details/7444478
http://ubuntuforums.org/showthread.php?t=1198281
http://www.tuicool.com/articles/nIZJFn
Invalid argument“ setting key ”net.core.somaxconn":http://stackoverflow.com/questions/23862410/invalid-argument-setting-key-net-core-somaxconn
k8s特權模式使用注意: