在使用docker過程中,我們經(jīng)常發(fā)現(xiàn)管理維護(hù)是一個(gè)很復(fù)雜過程,因?yàn)槲覀冊(cè)谑褂胐ocker commands的過程中,我們只會(huì)去使用我們認(rèn)為簡(jiǎn)單并且熟悉的命令,然而docker本身其實(shí)是提供給我們很多便捷且人性化的工具的,如果掌握這些使用技巧,也許你的維護(hù)管理工作將會(huì)事半功倍,并且給人看起來會(huì)很牛逼的樣子。
創(chuàng)建容器時(shí)傳入環(huán)境變量
在實(shí)際應(yīng)用場(chǎng)景中,不論是從安全還是可配置方面去考慮,很多參數(shù)是比較適合用環(huán)境變量加載進(jìn)去的,比如數(shù)據(jù)庫的連接信息,時(shí)區(qū),還有字體支持等等,在創(chuàng)建容器的時(shí)候其實(shí)都可以使用-e 指定key/value進(jìn)行傳遞環(huán)境變量進(jìn)去。
sh-4.2# docker run -itd --name test-env -e TZ='Asia/Shanghai' 172.25.46.9:5001/centos6.8-jdjr-test-app ee20b44301e27c16eae63dab243d293054178dd5f819c23d44bd9e534208bb42
sh-4.2# docker exec -it test-env date2017年 01月 17日 星期二 10:35:17 CST
sh-4.2# dateTue Jan 17 10:35:21 CST 2017
可以看到加了時(shí)區(qū)環(huán)境變量的容器已經(jīng)和宿主機(jī)在同一個(gè)時(shí)區(qū)(CST),并且時(shí)間和宿主機(jī)基本同步
sh-4.2# docker run -itd --name test 172.25.46.9:5001/centos6.8-jdjr-test-appd6a02874b999ff4eea79e3b302148b42043af01c89a5d31e5d858e0806f9077a
sh-4.2# docker exec -it test date2017年 01月 20日 星期五 01:43:48 Asia
默認(rèn)沒有加時(shí)區(qū)環(huán)境變量的容器還是Asia
調(diào)整宿主機(jī)和容器的時(shí)間差異
首先我們需要弄清幾個(gè)概念:在類unix系統(tǒng)中有硬件時(shí)鐘與系統(tǒng)時(shí)鐘,硬件時(shí)鐘是指主機(jī)板上的時(shí)鐘設(shè)備,也就是通??稍贐IOS畫面設(shè)定的時(shí)鐘,系統(tǒng)時(shí)鐘則是指kernel中的時(shí)鐘。unix以及l(fā)inux系統(tǒng)時(shí)間是從格林威治時(shí)間到當(dāng)前的秒數(shù),即1970年1月1日凌晨零點(diǎn)零分零秒到當(dāng)前的時(shí)間,全球都一樣,這是絕對(duì)值;而時(shí)區(qū)則是由于地理位置差異、行政區(qū)劃導(dǎo)致各地顯示時(shí)間的差異,為了克服時(shí)間上的混亂,規(guī)定將全球劃分為24個(gè)時(shí)區(qū),我們國家屬于東八區(qū)標(biāo)識(shí)為CST。
因此,對(duì)于 Docker 容器而言,根本不存在宿主和容器的時(shí)間差異問題,因?yàn)樗麄兪褂玫氖峭粋€(gè)內(nèi)核、同一個(gè)時(shí)鐘,二者完全一樣,所以根本不存在同步問題。一般來說這個(gè)問題是由時(shí)區(qū)導(dǎo)致的,可以使用date命令查看下容器當(dāng)前的時(shí)間時(shí)區(qū)是啥。UTC(通用協(xié)調(diào)時(shí))表示使用的是國際標(biāo)準(zhǔn)0時(shí)區(qū),UTC與格林尼治平均時(shí)(GMT, Greenwich Mean Time)一樣,都與英國倫敦的本地時(shí)相同。CST表示中國標(biāo)準(zhǔn)時(shí)間時(shí)區(qū)一般是中國上海"Aisa/Shanghai",也就是說UTC和CST相差了8個(gè)小時(shí)。
解決辦法:
創(chuàng)建容器的時(shí)候,使用-e 將時(shí)區(qū)信息傳入到容器內(nèi)部。
sh-4.2# docker run -itd --name test-env -e TZ='Asia/Shanghai' images
注意:其實(shí)使用單純的環(huán)境變量來改變?nèi)萜鲀?nèi)部的TIME ZONE,只會(huì)影響當(dāng)前容器用戶的時(shí)區(qū),一旦切換到真正的root用戶就會(huì)發(fā)現(xiàn)時(shí)區(qū)依然是不正確的,比如以下栗子:
$ docker run -itd --name test-env -e TZ='Asia/Shanghai' images
$ docker exec -it test-env bash
bash-4.1# date2017年 09月 20日 星期三 20:45:54 CST
bash-4.1# sudo su -c date2017年 09月 20日 星期三 08:46:02 EDT
bash-4.1#
那么如何真正解決時(shí)區(qū)這個(gè)問題呢?其實(shí)是/etc/localtime
在作怪,用戶只需要將容器內(nèi)部的localtime改成你想要的時(shí)區(qū)就行了。
bash-4.1# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime bash-4.1# date2017年 09月 20日 星期三 20:54:35 CST
bash-4.1# sudo su -c date2017年 09月 20日 星期三 20:54:39 CST
bash-4.1#
So,在使用Dockerfile構(gòu)建鏡像的時(shí)候?qū)?code style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13px; color: rgb(199, 37, 78); background-color: rgb(246, 246, 246); border-radius: 4px; padding: 2px 4px; border: none; white-space: pre-wrap; vertical-align: middle;">/usr/share/zoneinfo/Asia/Shanghai強(qiáng)制軟連接到/etc/localtime
就可以永久修復(fù)時(shí)區(qū)的問題了。
指定容器的rootfs的大小
在使用docker的過程中,會(huì)發(fā)現(xiàn)cpu和memory可以很隨意的動(dòng)態(tài)調(diào)整,但是默認(rèn)的rootfs卻是不能隨意調(diào)整的,默認(rèn)是10g大小,當(dāng)然如果對(duì)于數(shù)據(jù)有需求,可以通過掛載voulme進(jìn)行擴(kuò)展存儲(chǔ)。如果用戶執(zhí)意想要調(diào)整rootfs的大小,在docker1.12版本默認(rèn)提供了兩種方式:在啟動(dòng)docker 的時(shí)候加載參數(shù)--storage-opt dm.basesize=40G
用來調(diào)整默認(rèn)容器的rootfs大?。辉趧?chuàng)建容器的時(shí)候使用參數(shù)--storage-opt size=70G
來設(shè)置改容器的rootfs大小。
喜訊:在docker最近發(fā)布的1.13版本中,支持了磁盤的配額,不過還未測(cè)試
sh-4.2# docker run -itd --name volume-test --storage-opt size=70G 172.25.46.9:5001/centos6.8-jdjr-test-app18d47e69802aa84df00182885b256c50ebc56e15d8e6990fc1e187ffe254171e
sh-4.2# docker exec -it volume-test df -H | grep rootfsrootfs 76G 1.5G 74G 2% /
sh-4.2# docker exec -it test-env df -H | grep rootfsrootfs 11G 1.5G 9.3G 14% /
快速管理容器和鏡像
在docker中刪除容器需要指定容器名或者容器id,但是在容器比較多,并且狀態(tài)不一的情況下刪除容器還是需要走下心的。不過好處是docker ps默認(rèn)提供了很多好用的功能,可以很方便地管理容器(創(chuàng)建容器的時(shí)候如果加上label后更方便哦)。
原理:先用docker ps -a -q 輸出所有容器的container id(-f 表示過濾參數(shù)或者輸出格式),然后作為docker rm 的參數(shù)進(jìn)行批量刪除
輸出所有容器的name:
sh-4.2# docker ps --format='{{.Names}}'test-envtest-argstest-run
輸出所有容器名包含test的容器,并打印容器名
sh-4.2# docker ps -f name=test --format='{{.Names}}'test-envtest-argstest-run
查看退出狀態(tài)的容器,并打印容器名
sh-4.2# docker ps -f status=exited --format="{{.Names}}"thirsty_brahmagupta
clever_mestorf
hopeful_morse
stoic_morse
elated_williams
tender_jepsen
reverent_mirzakhani
刪除所有容器:
sh-4.2# docker rm -f -v $(docker ps -a -q)刪除/啟動(dòng)所有退出的容器:
sh-4.2# docker rm/start $(docker ps -qf status=exited)刪除所有鏡像:
sh-4.2# docker rmi $(docker images -q)查看懸掛鏡像:
sh-4.1# docker images -qf dangling=true只查看鏡像或者容器指定的信息(在docker1.10之后才支持的)
只列出鏡像的id以及倉庫名稱:
sh-4.2# docker images --format "{{.ID}}: {{.Repository}}"67591570dd29: centos
0a18f1c0ead2: rancher/server
只列出容器的相關(guān)id,image,status和name
sh-4.2# docker ps --format "{{.ID}}: {{.Image}} : {{.Status}} : {{.Names}}"66b60b72f00e: centos : Up 7 days : pensive_poincare
或者自己重新定義列,就和原生差不多:
sh-4.2# docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"CONTAINER ID IMAGE STATUS NAMES
66b60b72f00e centos Up 7 days pensive_poincare
注意:
其實(shí)上面的--format利用的就是go語言中的模版語法,所有容器的組織信息都在結(jié)構(gòu)體中:
*formatter.containerContext
容器label的使用
在實(shí)際運(yùn)維過程中,大量的容器可能會(huì)一些運(yùn)維上的挑戰(zhàn),通過使用label,可以很好的將容器分類。label貫穿于docker的整個(gè)過程。
這個(gè)label可以作為你區(qū)分業(yè)務(wù),區(qū)分模板各種區(qū)分容器的標(biāo)識(shí),通過標(biāo)識(shí),可以將容器更好的進(jìn)行分組
sh-4.2# docker run -itd --name volume-test --storage-opt size=70G --label zone=test 172.25.46.9:5001/centos6.8-jdjr-test-appc3772397e58e663095c2c0fd8d688b3d41b494097999ec2b6d6b7c509d23a138
創(chuàng)建容器的時(shí)候定義一個(gè)label,表示該容器在test這個(gè)區(qū)域
使用定義的label進(jìn)行快速檢索容器,并進(jìn)行下一步操作(比如刪除啦,更新啦)
sh-4.2# docker ps -qf label=zone=testc3772397e58e
sh-4.2# docker ps -f label=zone=test --format='{{.Names}}'volume-test
快速查看容器的相關(guān)配置信息
查看容器的devicemapper設(shè)備:
sh-4.2# docker inspect -f '{{.GraphDriver.Data.DeviceName}}' nginx docker-8:1-67411759-7c9d6d3327b02659c81bcb70bf6a4c7a45df6a589af2a2d42a387dc0e90d4913
查看容器的PID:
sh-4.2# docker inspect -f '{{.State.Pid}}' nginx 27521
查看容器name:
sh-4.2# docker inspect -f '{{.Name}}' nginx /nginx
獲取容器的ID:
sh-4.2# docker inspect --format {{.Id}} nginx53214bc9cd001f2c548edcce0c42fe51f1a118c08941406d43122a8348055843
使用alias來預(yù)定義常用的命令
docker管理命令經(jīng)常需要指定各種參數(shù),通過linux的alias命令將默認(rèn)的參數(shù)預(yù)定義起來,可以很方便的進(jìn)行管理容器。
sh-4.2# alias dockerrm='docker rm -f -v'sh-4.2# alias dockerexec='docker exec -it'sh-4.2# alias dockerrmimage='docker rmi'sh-4.2# dockerrm volume-testvolume-test
sh-4.2# dockerexec volume-test lsbin dev export lib media opt root selinux sys usr
boot etc home lib64 mnt proc sbin srv tmp var
sh-4.2# dockerexec volume-test bashbash-4.1#
使容器隨著docker daemon的啟動(dòng)一同啟動(dòng)
docker run 的時(shí)候加參數(shù)--restart=always
如何動(dòng)態(tài)修改容器的內(nèi)存和cpu限制docker1.10之后才支持的動(dòng)態(tài)調(diào)整
sh-4.2# dockerexec test-env cat /sys/fs/cgroup/memory/memory.limit_in_bytes9223372036854775807
sh-4.2# cat /sys/fs/cgroup/memory/memory.limit_in_bytes 9223372036854775807
可以看到,默認(rèn)沒有給容器限制內(nèi)存,它會(huì)共享宿主機(jī)的所有內(nèi)存
動(dòng)態(tài)調(diào)整內(nèi)存為2014M:
sh-4.2# docker update -m 2014M test-envtest-env
sh-4.2# dockerexec test-env cat /sys/fs/cgroup/memory/memory.limit_in_bytes2111832064
docker容器中真實(shí)用戶的隔離
注意:默認(rèn)docker容器內(nèi)部的用戶會(huì)繼承宿主機(jī)的用戶id,也就是說容器外部有一個(gè)uid為500的用戶test,容器內(nèi)部有一個(gè)uid為500的用戶admin,容器內(nèi)部運(yùn)行的程序如果在宿主機(jī)上查看的時(shí)候會(huì)發(fā)現(xiàn)程序的啟動(dòng)用戶會(huì)是外部宿主機(jī)的test用戶。
這是因?yàn)槟J(rèn)情況下容器的 user namespace 并未開啟,所以容器內(nèi)的用戶和宿主用戶共享 uid 空間。容器內(nèi)的 uid 為 0 的 root,就被系統(tǒng)視為 uid=0 的宿主 root,因此磁盤讀寫時(shí),具有宿主 root 同等讀寫權(quán)限。
開啟user namespace:
啟動(dòng)docker的時(shí)候加參數(shù)--userns-remap=defaulthttps://docs.docker.com/engine/reference/commandline/dockerd/#/daemon-user-namespace-options
在docker container和物理機(jī)中雙向拷貝文件
容器內(nèi)部文件拷貝到宿主機(jī):
sh-4.2# docker cp jupyter-70002111:/home/70002111/教程-研究功能介紹.ipynb .sh-4.2# lsDockerfile 教程-研究功能介紹.ipynb
宿主機(jī)文件拷貝到容器:
sh-4.2# docker cp Dockerfile jupyter-70002111:/home/70002111/sh-4.2# docker exec -it jupyter-70002188 ls Dockerfile
向容器內(nèi)部程序發(fā)送signal
注意:在給容器進(jìn)程發(fā)送SIGTERM信號(hào)時(shí)只會(huì)發(fā)給主進(jìn)程,也就是容器內(nèi) PID 為 1 的進(jìn)程。至于說主進(jìn)程啟動(dòng)的那些子進(jìn)程,完全看主進(jìn)程是否愿意轉(zhuǎn)發(fā)SIGTERM 給子進(jìn)程了。所以那些把 Docker當(dāng)做虛擬機(jī)用的,主進(jìn)程跑了個(gè)bash,然后exec 進(jìn)去啟動(dòng)程序的,或者來個(gè)&讓程序跑后臺(tái)的情況,應(yīng)用進(jìn)程必然無法收到SIGTERM。
</br>還有一種可能是在Dockerfile中的CMD那行用的是 shell 格式寫的命令,而不是 exec 格式。在鏡像中使用CMD啟動(dòng)的容器會(huì)加一個(gè) sh -c 來去執(zhí)行,因此使用 shell 格式寫 CMD 的時(shí)候,PID 為 1 的進(jìn)程是 sh,而它不轉(zhuǎn)發(fā)信號(hào),所以主程序收不到。
所以在寫CMD哪行命令的時(shí)候,最好按照exec格式去寫。
劃重點(diǎn): 由于在容器內(nèi)部是沒有init進(jìn)程的,所以容器的整個(gè)生命周期會(huì)和容器內(nèi)部PID為1的進(jìn)程緊密相連,用戶在使用過程中經(jīng)常會(huì)發(fā)現(xiàn)容器更新版本之后,業(yè)務(wù)調(diào)用方經(jīng)常會(huì)有一些請(qǐng)求異常,這其實(shí)也是因?yàn)槿萜鲀?nèi)部的1號(hào)進(jìn)程的設(shè)置有關(guān),導(dǎo)致容器在停止時(shí)可能直接發(fā)送SIGKILL信號(hào),導(dǎo)致容器當(dāng)前正在處理中的業(yè)務(wù)也會(huì)立即斷開連接,這樣可能會(huì)導(dǎo)致一些業(yè)務(wù)異常
總而言之,向容器內(nèi)部程序發(fā)送合適的信號(hào)是非常有必要的,這樣可以使你的容器很優(yōu)雅的退出。docker stop操作會(huì)讓容器在10s后進(jìn)行優(yōu)雅的退出
如何優(yōu)雅的關(guān)閉容器
容器的cache不釋放
$ echo 1 > /proc/sys/vm/drop_caches
橋接網(wǎng)絡(luò)連入下層網(wǎng)絡(luò)并使用IPAM (沒有NAT/端口映射)
注意:docker network是1.12版本加進(jìn)來的,支持了多種網(wǎng)絡(luò)插件
$ docker network create -d bridge --subnet=192.168.57.0/24 --ip-range=192.168.57.32/28 --gateway=192.168.57.11 --aux-address DefaultGatewayIPv4=192.168.57.1 -o com.docker.network.bridge.name=brnet brnet
$ brctl addif brnet eth2
$ docker run --net=brnet -it busybox ifconfig
注意其它主機(jī)的 --ip-range 和 --gateway 需要做對(duì)應(yīng)調(diào)整。
這種拓?fù)涫?,容器?nèi) eth0 連接 brnet 接口,該接口直接通過 eth2 訪問交換。
Docker查看某個(gè)容器綁定的cpu內(nèi)核
容器內(nèi)部第一個(gè)進(jìn)程編號(hào)一般為1
$ docker exec -it container-name taskset -c -p 1
pid 1's current affinity list:0-3
給docker配置hosts
$ docker run --add-host biaoge-ops:192.168.0.1 centos cat /etc/hosts127.0.0.1 localhost::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters192.168.0.1 biaoge-ops10.0.0.3 6ff3ea7114b4
查看容器大小
$ sudo docker ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE11b6cd007b26 finance/mysql5-1 "/usr/bin/mysqld_safe" 22 months ago Up 3