docker資源限制
在使用 docker 運行容器時,一臺主機上可能會運行幾百個容器,這些容器雖然互相隔離,但是底層卻使用着相同的 CPU、內存和磁盤資源。如果不對容器使用的資源進行限制,那麼容器之間會互相影響,小的來說會導致容器資源使用不公平;大的來說,可能會導致主機和集羣資源耗盡,服務完全不可用。
CPU 和內存的資源限制已經是比較成熟和易用,能夠滿足大部分用戶的需求。磁盤限制也是不錯的,雖然現在無法動態地限制容量,但是限制磁盤讀寫速度也能應對很多場景。
至於網絡,docker 現在並沒有給出網絡限制的方案,也不會在可見的未來做這件事情,因爲目前網絡是通過插件來實現的,和容器本身的功能相對獨立,不是很容易實現,擴展性也很差。
資源限制一方面可以讓我們爲容器(應用)設置合理的 CPU、內存等資源,方便管理;另外一方面也能有效地預防惡意的攻擊和異常,對容器來說是非常重要的功能。
系統壓力測試工具stress
stress是一個linux下的壓力測試工具,專門爲那些想要測試自己的系統,完全高負荷和監督這些設備運行的用戶。
cpu資源限制
限制CPU Share
什麼是cpu share:
docker 允許用戶爲每個容器設置一個數字,代表容器的 CPU share,默認情況下每個容器的 share 是 1024。這個 share 是相對的,本身並不能代表任何確定的意義。當主機上有多個容器運行時,每個容器佔用的 CPU 時間比例爲它的 share 在總額中的比例。docker 會根據主機上運行的容器和進程動態調整每個容器使用 CPU 的時間比例。
例子:
如果主機上有兩個一直使用 CPU 的容器(爲了簡化理解,不考慮主機上其他進程),其 CPU share 都是 1024,那麼兩個容器 CPU 使用率都是 50%;如果把其中一個容器的 share 設置爲 512,那麼兩者 CPU 的使用率分別爲 67% 和 33%;如果刪除 share 爲 1024 的容器,剩下來容器的 CPU 使用率將會是 100%。
好處:
能保證 CPU 儘可能處於運行狀態,充分利用 CPU 資源,而且保證所有容器的相對公平;
缺點:
無法指定容器使用 CPU 的確定值。
設置 CPU share 的參數:
-c --cpu-shares,它的值是一個整數
我的機器是 4 核 CPU,因此運行一個stress容器,使用 stress 啓動 4 個進程來產生計算壓力:(無CPU限制)
[root@yixuan ~]# docker pull progrium/stress
[root@yixuan ~]# yum install -y htop
[root@yixuan ~]# docker run --rm -it progrium/stress --cpu 4
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [6] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked
在另外一個 terminal 使用 htop 查看資源的使用情況:
上圖中看到,CPU 四個覈資源都達到了 100%。
爲了比較,另外啓動一個 share 爲 512 的容器:
1.先將沒有做限制的命令運行起來
[root@yixuan ~]# docker run --rm -it progrium/stress --cpu 4
2.在開啓一個終端,運行做了CPU限制的命令
[root@yixuan ~]# docker run --rm -it -c 512 progrium/stress --cpu 4
stress: info: [1] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 12000us
stress: dbug: [1] --> hogcpu worker 4 [6] forked
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [7] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [8] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [9] forked
3.在開啓一個終端執行htop命令
[root@yixuan ~]# htop
因爲默認情況下,容器的 CPU share 爲 1024,所以這兩個容器的 CPU 使用率應該大致爲 2:1,下面是啓動第二個容器之後的監控截圖:
兩個容器分別啓動了四個 stress 進程,第一個容器 stress 進程 CPU 使用率都在 60% 左右,第二個容器 stress 進程 CPU 使用率在 30% 左右,比例關係大致爲 2:1,符合之前的預期。
限制CPU 核數
限制容器能使用的 CPU 核數
-c --cpu-shares 參數只能限制容器使用 CPU 的比例,或者說優先級,無法確定地限制容器使用 CPU 的具體核數;從 1.13 版本之後,docker 提供了 --cpus 參數可以限定容器能使用的 CPU 核數。這個功能可以讓我們更精確地設置容器 CPU 使用量,是一種更容易理解也因此更常用的手段.
--cpus 後面跟着一個浮點數,代表容器最多使用的核數,可以精確到小數點二位,也就是說容器最小可以使用 0.01 核 CPU。
限制容器只能使用 1.5 核數 CPU:
[root@yixuan ~]# docker run --rm -it --cpus 1.5 progrium/stress --cpu 3
stress: info: [1] dispatching hogs: 3 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 9000us
stress: dbug: [1] --> hogcpu worker 3 [6] forked
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [7] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [8] forked
在容器裏啓動三個 stress 來跑 CPU 壓力,如果不加限制,這個容器會導致 CPU 的使用率爲 300% 左右(也就是說會佔用三個核的計算能力)。實際的監控如下圖:
可以看到,每個 stress 進程 CPU 使用率大約在 50%,總共的使用率爲 150%,符合 1.5 核的設置。
如果設置的 --cpus 值大於主機的 CPU 核數,docker 會直接報錯:
[root@yixuan ~]# docker run --rm -it --cpus 8 progrium/stress --cpu 3 #啓用三個進程做測試
docker: Error response from daemon: Range of CPUs is from 0.01 to 4.00, as there are only 4 CPUs available.
See 'docker run --help'.
如果多個容器都設置了 --cpus ,並且它們之和超過主機的 CPU 核數,並不會導致容器失敗或者退出,這些容器之間會競爭使用 CPU,具體分配的 CPU 數量取決於主機運行情況和容器的 CPU share 值。也就是說 --cpus 只能保證在 CPU 資源充足的情況下容器最多能使用的 CPU 數,docker 並不能保證在任何情況下容器都能使用這麼多的 CPU(因爲這根本是不可能的)。
CPU 綁定
限制容器運行在某些 CPU 核
注:
一般並不推薦在生產中這樣使用
docker 允許調度的時候限定容器運行在哪個 CPU 上。
案例:
假如主機上有 4 個核,可以通過 --cpuset 參數讓容器只運行在前兩個核上:
[root@yixuan ~]# docker run --rm -it --cpuset-cpus=0,1 progrium/stress --cpu 2
stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 6000us
stress: dbug: [1] --> hogcpu worker 2 [6] forked
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [7] forked
這樣,監控中可以看到只有前面兩個核 CPU 達到了 100% 使用率。
mem資源限制
docker 默認沒有對容器內存進行限制,容器可以使用主機提供的所有內存。
不限制內存帶來的問題:
這是非常危險的事情,如果某個容器運行了惡意的內存消耗軟件,或者代碼有內存泄露,很可能會導致主機內存耗盡,因此導致服務不可用。可以爲每個容器設置內存使用的上限,一旦超過這個上限,容器會被殺死,而不是耗盡主機的內存。
限制內存帶來的問題:
限制內存上限雖然能保護主機,但是也可能會傷害到容器裏的服務。如果爲服務設置的內存上限太小,會導致服務還在正常工作的時候就被 OOM 殺死;如果設置的過大,會因爲調度器算法浪費內存。
合理做法:
1. 爲應用做內存壓力測試,理解正常業務需求下使用的內存情況,然後才能進入生產環境使用
2. 一定要限制容器的內存使用上限,儘量保證主機的資源充足,一旦通過監控發現資源不足,就進行擴容或者對容器進行遷移如果可以(內存資源充足的情況)
3. 儘量不要使用 swap,swap 的使用會導致內存計算複雜,對調度器非常不友好
docker 限制容器內存使用量:
docker 啓動參數中,和內存限制有關的包括(參數的值一般是內存大小,也就是一個正數,後面跟着內存單位 b、k、m、g,分別對應 bytes、KB、MB、和 GB):
-m --memory:容器能使用的最大內存大小,最小值爲 4m
如果限制容器的內存使用爲 64M,在申請 64M 資源的情況下,容器運行正常(如果主機上內存非常緊張,並不一定能保證這一點):
[root@yixuan ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 64M --vm-hang 0
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [6] forked
stress: dbug: [6] allocating 67108864 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] sleeping forever with allocated memory
容器可以正常運行。
-m 64m:限制你這個容器只能使用64M
--vm-bytes 64M:將內存撐到64兆是不會報錯,因爲我有64兆內存可用。
hang:就是卡在這裏。
--vm:生成幾個佔用內存的進程
而如果申請 150M 內存,會發現容器裏的進程被 kill 掉了(worker 6 got signal 9,signal 9 就是 kill 信號)
[root@yixuan ~]# docker run --rm -it -m 64m progrium/stress --vm 1 --vm-bytes 150M --vm-hang 0
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [6] forked
stress: dbug: [6] allocating 157286400 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 6 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 1s
io 資源限制(瞭解)
對於磁盤來說,考量的參數是容量和讀寫速度,因此對容器的磁盤限制也應該從這兩個維度出發。目前 docker 支持對磁盤的讀寫速度進行限制,但是並沒有方法能限制容器能使用的磁盤容量(一旦磁盤 mount 到容器裏,容器就能夠使用磁盤的所有容量)。
第一種是:磁盤的讀寫速率的限制
第二種是:磁盤的讀寫頻率的限制
限制磁盤的讀寫速率
docker 允許你直接限制磁盤的讀寫速率,對應的參數有:
--device-read-bps:磁盤每秒最多可以讀多少比特(bytes)
--device-write-bps:磁盤每秒最多可以寫多少比特(bytes)
上面兩個參數的值都是磁盤以及對應的速率,單位可以是 kb、mb 和 gb。
另外兩個參數可以限制磁盤讀寫頻率(每秒能執行多少次讀寫操作):
--device-read-iops:磁盤每秒最多可以執行多少 IO 讀操作
--device-write-iops:磁盤每秒最多可以執行多少 IO 寫操作
端口轉發
使用端口轉發解決容器端口訪問問題
-p:創建應用容器的時候,一般會做端口映射,這樣是爲了讓外部能夠訪問這些容器裏的應用。可以用多個-p指定多個端口映射關係。
mysql應用端口轉發:
查看本地地址:
[root@yixuan ~]# ip a
...
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:9c:bf:66 brd ff:ff:ff:ff:ff:ff
inet 192.168.246.141/24 brd 192.168.246.255 scope global dynamic ens33
valid_lft 5217593sec preferred_lft 5217593sec
inet6 fe80::a541:d470:4d9a:bc29/64 scope link
valid_lft forever preferred_lft forever
運行容器:使用-p作端口轉發,把本地3307轉發到容器的3306,其他參數需要查看發佈容器的頁面提示
[root@yixuan ~]# docker pull daocloud.io/library/mysql:5.7
[root@yixuan ~]# docker run -d --name mysql1 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=Qf@123! daocloud.io/library/mysql:5.7
a4327dbddf665b4302c549320bff869b8a027c2e1eead363d84ce5d06acf2698
-e MYSQL_ROOT_PASSWORD= 設置環境變量,這裏是設置mysql的root用戶的密碼
通過本地IP:192.168.246.141的3307端口訪問容器mysql1內的數據庫,出現如下提示恭喜你
1.安裝一個mysql客戶端
[root@yixuan ~]# yum install -y mysql
2.登錄
[root@yixuan ~]# mysql -uroot -p'Qf@123!' -h 192.168.246.141 -P3307
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.26 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
-P(大P):當使用-P標記時,Docker 會隨機映射一個 32768~49900 的端口到內部容器開放的網絡端口。如下:
[root@yixuan ~]# docker pull daocloud.io/library/redis
[root@yixuan ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
daocloud.io/library/redis latest 598a6f110d01 2months ago 118MB
[root@yixuan ~]# docker run --name myredis -P -d daocloud.io/library/redis
ca06a026d84a0605d9a9ce6975389a79f4ab9a9a043a03f088cd909c1fe52e29
[root@yixuan ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ca06a026d84a daocloud.io/library/redis "docker-entrypoint.s…" 22 seconds ago Up 21 seconds 0.0.0.0:32768->6379/tcp myredis
從上面的結果中可以看出,本地主機的32768端口被映射到了redis容器的6379端口上,也就是說訪問本機的32768端口即可訪問容器內redis端口。
在別的機器上通過上面映射的端口32768連接這個容器的redis
[root@docker-server2 ~]# yum install -y redis
[root@docker-server2 ~]# redis-cli -h 192.168.246.141 -p 32768
192.168.246.141:32768> ping
PONG
192.168.246.141:32768>
容器卷
把本地宿主機上面的某一個目錄掛載到容器裏面的目錄去。這兩個目錄都不用提前存在,會自動創建
新卷只能在容器創建過程當中掛載
[root@yixuan ~]# docker run -it --name testnginx -v /test:/test2 daocloud.io/library/nginx /bin/bash
root@86320e734cd1:/# ls
root@86320e734cd1:/# ctrl+p+q #退出
測試:
[root@yixuan ~]# cd /test/
[root@yixuan test]# ls
[root@yixuan test]# touch a.txt
[root@yixuan test]# cd
[root@yixuan ~]# docker exec -it testnginx /bin/bash
root@86320e734cd1:/# cd test2/
root@86320e734cd1:/test2# ls
a.txt
共享文件:
[root@yixuan ~]# mkdir /dir
[root@yixuan ~]# vim /dir/a.txt
123
[root@yixuan ~]# docker run -it --name testnginx2 -v /dir/a.txt:/dir1/a.txt daocloud.io/library/nginx /bin/bash
root@f899be627552:/# cat dir1/a.txt
123
root@f899be627552:/#
注意:如果是共享文件,修改宿主機上面的文件內容,容器裏面的文件不會同步更新,如果在容器裏面進行修改文件,本地會同步。
共享其他容器的卷(其他容器用同一個卷):
[root@yixuan ~]# docker run -it --name testnginx1 --volumes-from testnginx daocloud.io/library/nginx /bin/bash
root@50e6f726335c:/# ls
bin dev home lib64 mnt proc run srv test2 usr
boot etc lib media opt root sbin sys tmp var
root@50e6f726335c:/# cd test2/
root@50e6f726335c:/test2# ls
a.txt
實際應用中可以利用多個-v選項把宿主機上的多個目錄同時共享給新建容器:
比如:
# docker run -it -v /abc:/abc -v /def:/def 1ae9