目錄
系統壓力測試工具stress
1. cpu資源限制
1.2 限制CPU 核數
1.3 CPU 綁定
2. mem資源限制
3. 限制IO
二、端口轉發
三、容器卷
四、部署centos7容器應用
五、docker數據存儲位置
六、docker網絡
容器網絡分類
在使用 docker 運行容器時,一臺主機上可能會運行幾百個容器,這些容器雖然互相隔離,但是底層卻使用著相同的 CPU、內存和磁盤資源。如果不對容器使用的資源進行限制,那么容器之間會互相影響,小的來說會導致容器資源使用不公平;大的來說,可能會導致主機和集群資源耗盡,服務完全不可用。
CPU 和內存的資源限制已經是比較成熟和易用,能夠滿足大部分用戶的需求。磁盤限制也是不錯的,雖然現在無法動態地限制容量,但是限制磁盤讀寫速度也能應對很多場景。
至于網絡,docker 現在并沒有給出網絡限制的方案,也不會在可見的未來做這件事情,因為目前網絡是通過插件來實現的,和容器本身的功能相對獨立,不是很容易實現,擴展性也很差。
資源限制一方面可以讓我們為容器(應用)設置合理的 CPU、內存等資源,方便管理;另外一方面也能有效地預防惡意的攻擊和異常,對容器來說是非常重要的功能。
系統壓力測試工具stress
stress是一個linux下的壓力測試工具,專門為那些想要測試自己的系統,完全高負荷和監督這些設備運行的用戶。
1. cpu資源限制
1.1 限制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,符合之前的預期。
1.2 限制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(因為這根本是不可能的)。
1.3 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% 使用率。
2. 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
3. 限制IO
限制bps和iops bps是 byte per second ,每秒讀寫的數量
iops是 io per second ,每秒IO的次數
注:目前Block I0限額只對direct IO (不使用文件緩存)有效。
可以同過下面的參數控制容器的bps和iops; ? --device-read-bps:限制讀某個設備的bps. --devce-write-bps:限制寫某個設備的bps. --device-read-iops:限制讀某個設備的iops. --device-write-iops: 限制寫某個設備的iops. ? 限制情況下: [root@newrain ~]# docker run -it --device-write-bps /dev/sda:30MB ubuntu root@10845a98036e:/# time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct 結果如下圖1 ? 不限制情況下: [root@newrain ~]# docker run -it ubuntu root@10845a98036e:/# time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct 結果如下圖2
圖1
圖2
二、端口轉發
使用端口轉發解決容器端口訪問問題
-p:創建應用容器的時候,一般會做端口映射,這樣是為了讓外部能夠訪問這些容器里的應用。可以用多個-p指定多個端口映射關系。
mysql應用端口轉發:
查看本地地址:
[root@yixuan ~]# ip a
...
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000link/ether 00:0c:29:9c:bf:66 brd ff:ff:ff:ff:ff:ffinet 192.168.246.141/24 brd 192.168.246.255 scope global dynamic ens33valid_lft 5217593sec preferred_lft 5217593secinet6 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'wx@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
四、部署centos7容器應用
鏡像下載:
[root@yixuan ~]# docker pull daocloud.io/library/centos:7
systemd 整合:
因為 systemd 要求 CAPSYSADMIN 權限,從而得到了讀取到宿主機 cgroup 的能力,CentOS7 中已經用 fakesystemd 代替了 systemd 。 但是我們使用systemd,可用參考下面的 Dockerfile:
[root@yixuan ~]# mkdir test
[root@yixuan ~]# cd test/
[root@yixuan test]# vim Dockerfile
FROM daocloud.io/library/centos:7
MAINTAINER "soso" soso@qq.com
ENV container dockerRUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;VOLUME [ "/sys/fs/cgroup" ]CMD ["/usr/sbin/init"]
這個Dockerfile刪除fakesystemd 并安裝了 systemd。然后再構建基礎鏡像:
[root@yixuan test]# docker build -t local/c7-systemd .
執行沒有問題這就生成一個包含 systemd 的應用容器示例
[root@yixuan test]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
local/c7-systemd latest a153dcaa642e 6 minutes ago 391MB
為了使用像上面那樣包含 systemd 的容器,需要創建一個類似下面的Dockerfile:
[root@yixuan test]# mkdir http
[root@yixuan test]# cd http/
[root@yixuan http]# vim Dockerfile
FROM local/c7-systemd
RUN yum -y install httpd; yum clean all; systemctl enable httpd.service
EXPOSE 80
CMD ["/usr/sbin/init"]
構建鏡像:
[root@yixuan http]# docker build -t local/c7-systemd-httpd .
運行包含 systemd 的應用容器:
為了運行一個包含 systemd 的容器,需要使用--privileged選項, 并且掛載主機的 cgroups 文件夾。 下面是運行包含 systemd 的 httpd 容器的示例命令:
[root@yixuan http]# docker run --privileged -tid -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 local/c7-systemd-httpd
--privileged:授權提權。讓容器內的root用戶擁有正真root權限(有些權限是沒有的)
注意:如果不加會運行在前臺(沒有用-d),可以用ctrl+p+q放到后臺去
測試可用:
[root@yixuan http]# yum install -y elinks
[root@yixuan http]# elinks --dump http://192.168.246.141 #apache的默認頁面Testing 123..This page is used to test the proper operation of the [1]Apache HTTPserver after it has been installed. If you can read this page it meansthat this site is working properly. This server is powered by [2]CentOS.
再來個安裝openssh-server的例子:
[root@yixuan http]# cd ..
[root@yixuan test]# mkdir ssh
[root@yixuan test]# cd ssh/
[root@yixuan ssh]# vim Dockerfile
FROM local/c7-systemd
RUN yum -y install openssh-server; yum clean all; systemctl enable sshd.service
RUN echo 1 | passwd --stdin root
EXPOSE 22
CMD ["/usr/sbin/init"]
[root@yixuan ssh]# docker build --rm -t local/c7-systemd-sshd .
[root@yixuan ssh]# docker run --privileged -tid -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 2222:22 local/c7-systemd-sshd
[root@yixuan ssh]# ssh 192.168.246.141 -p 2222
[root@ce1af52a6f6c ~]#
五、docker數據存儲位置
查看存儲路徑
[root@yixuan ~]# docker info | grep RootDocker Root Dir: /var/lib/docker
修改默認存儲位置: 在dockerd的啟動命令后面追加--data-root參數指定新的位置
[root@yixuan ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --data-root=/data[root@yixuan ~]# systemctl daemon-reload
[root@yixuan ~]# systemctl restart docker
查看是否生效:
[root@yixuan ~]# docker info | grep RootDocker Root Dir: /data[root@yixuan ~]# cd /data/
[root@yixuan data]# ls
builder buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes
六、docker網絡
容器網絡分類
查看當前網絡:
[root@yixuan ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
9b902ee3eafb bridge bridge local
140a9ff4bb94 host host local
d1210426b3b0 none null local
docker安裝后,默認會創建三種網絡類型,bridge、host和none以及自定義網絡模式
1、bridge:網絡橋接
默認情況下啟動、創建容器都是用該模式,所以每次docker容器重啟時會按照順序獲取對應ip地址。
2、none:無指定網絡
啟動容器時,可以通過--network=none,docker容器不會分配局域網ip
3、host:主機網絡
docker容器和主機共用一個ip地址。使用host網絡創建容器:
[root@yixuan ~]# docker run -it --name testnginx2 --net host 98ebf73ab
[root@yixuan ~]# netstat -lntp | grep 80
tcp6 0 0 :::80 :::* LISTEN 3237/docker-proxy
瀏覽器訪問宿主ip地址
4、固定ip:
創建固定Ip的容器:
4.1、創建自定義網絡類型,并且指定網段
[root@yixuan ~]# docker network create --subnet=192.168.0.0/16 staticnet
4efd309244c6ad70eda2d047a818a3aec5b162f5ca29fb6024c09a5efbf15854
通過docker network ls可以查看到網絡類型中多了一個staticnet:
[root@yixuan ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
9b902ee3eafb bridge bridge local
140a9ff4bb94 host host local
d1210426b3b0 none null local
4efd309244c6 staticnet bridge local
4.2、使用新的網絡類型創建并啟動容器
[root@yixuan ~]# docker run -itd --name userserver --net staticnet --ip 192.168.0.2 daocloud.io/library/centos:7
通過docker inspect可以查看容器ip為192.168.0.2:
[root@yixuan ~]# docker inspect userserver | grep -i ipaddress"SecondaryIPAddresses": null,"IPAddress": "","IPAddress": "192.168.0.2",
關閉容器并重啟,發現容器ip并未發生改變
5、自定義網絡模式
自定義網絡模式:Docker 還允許創建自定義網絡來實現更靈活的網絡配置。在自定義網絡模式下,可以創建一個獨立的網絡,并將容器連接到該網絡中。這樣可以在自定義網絡中實現容器之間的通信,同時也可以通過網絡的連接方式將容器連接到宿主機網絡或其他網絡。