docker資源限制(3)

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章