Docker安全及日志管理

资源列表

操作系统配置主机名IP
CentOS 7.92C4Gmaster192.168.93.165
CentOS 7.92C4Gclient192.168.93.166

基础环境

  • 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
  • 关闭安全内核机制
setenforce 0
sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config
  • 修改主机名
hostnamectl set-hostname master
hostnamectl set-hostname client
  • 绑定映射关系
cat >> /etc/hosts << EOF
192.168.93.165 master
192.168.93.166 client
EOF

一、Docker安全相关介绍

1.1、Docker容器与虚拟机的区别

1.1.1、隔离与共享
  • 虚拟机通过添加Hypervisor层,虚拟出网卡、内存、CPU等虚拟硬件,再在其上建立虚拟机,每个虚拟机都有自己的系统内核。而Docker容器则是通过隔离的方式,将文件系统、进程、设备、网络等资源进行隔离,再对权限、CPU资源等进行控制,最终让容器之间互不影响,容器无法影响宿主机。容器与宿主机共享内核、文件系统、硬件等资源
1.1.2、性能与损耗
  • 与虚拟机相比,容器资源损耗要少。同样的宿主机下,能够建立容器的数量要比虚拟机多。但是,虚拟机的安全性要比容器稍好,要从虚拟机攻破宿主机或其他虚拟机,需要先攻破Hypervisor层,这是极其困难的。而docker容器与宿主机共享内核、文件系统等资源,更有可能对其他容器、宿主机产生影响

1.2、Docker存在的安全问题

1.2.1、Docker自身漏洞
  • 作为一款应用Docker本身实现上会有代码缺陷。CVE官方记录Docker历史版本公有超过20项漏洞,可参见Docker官方网站。黑客常用的攻击手段主要有代码执行、权限提升、信息泄露、权限绕过等。目前Docker版本更迭非常快,Docker用户最好将Docker升级为最新的版本
1.2.2、Docker源码问题

Docker提供了Docker hub,可以让用户上传创建的镜像,以便其他用户下载,快速搭建环境。但同时也带来了一些安全问题。例如下面三种方式:

(1)、客户上传恶意镜像

  • 如果有客户在制作的镜像中植入木马】后门等恶意软件,那么环境从一开始就已经不安全了,后续更没有什么安全可言

(2)、镜像使用有漏洞的软件

  • Docker hub上能下载的镜像里面,75%的镜像都安装了有漏洞的软件。所以下载镜像后,需要检查里面软件的版本信息,对应的版本是否存在漏洞,并及时更新打上补丁

(3)中间人攻击篡改镜像

  • 镜像在传输的过程中可能被篡改,目前新版本的Docker已经提供了相应的校验机制来预防这个问题

1.3、Docker架构缺陷与安全机制

Docker本身的架构与机制就可能产生问题,例如这样一种攻击场景,黑客已经控制了宿主机上的一些容器,或者获得了通过在公有云上建立容器的方式,然后对宿主机或其他容器发起攻击

1.3.1、容器之间的局域网攻击
  • 主机上的容器之间可以构成局域网,因此针对局域网的ARP欺骗】嗅探、广播风暴等攻击方式更可以用上。所以,在一个主机上部署多个容器需要合理的配置网络,设置iptables规则
1.3.2、DDos攻击耗尽资源
  • Cgroup安全机制就是要防止此类攻击的,不要为单一的容器分配过多的资源即可避免类似问题
1.3.3、有漏洞的系统调用
  • Docker与虚拟机的一个重要的区别就是Docker与宿主机公有一个操作系统内核。一旦宿主机内核存在可以越权或者提权漏洞,尽管Docker使用普通用户执行,在容器被入侵时,攻击者还可以利用内核漏洞跳到宿主机做更多的事情
1.3.4、共享root用户权限
  • 如果以root用户权限运行容器,容器内的root用户也就拥有了宿主机的root权限

1.4、Docker安全基线标准

  • 根据Docker官方文档整理,下面从内核、主机、网络、镜像、容器以及其他等6个方面总结Docker安全基线标准
1.4.1、内核级别
  • 及时更新内核
  • UserNameSpace(容器内的root权限在容器之外处于非高权限状态)
  • Cgroups(对资源的配额和度量)
  • SElinux/AppArmor/GRSEC(控制文件访问权限)
  • Capability(权限划分)
  • Seccomp(限定系统调用)
  • 禁止将容器的命令空间与宿主机进程命令空间共享
1.4.2、主机级别
  • 为容器创建独立分区

  • 仅运行必要的服务

  • 禁止将宿主机上敏感目录映射到容器

  • 对Docker守护进程、相关文件和目录进行审计

  • 设置适当的默认文件描述符数

  • 用户权限为root的Docker相关文件的访问权限应该为644或者更低权限

  • 周期性检查每个主机的容器清单,并清理不必必要的容器

1.4.3、网络级别
  • 通过iptables设定规则实现禁止或允许容器之间网络流量
  • 允许Docker修改iptables
  • 禁止将Docker绑定到其他IP/Port或者UnixSocket
  • 禁止在容器上映射特权端口
  • 容器上只开放所需要的端口
  • 禁止在容器上使用主机网络模式(host)
1.4.4、镜像级别
  • 创建本地镜像仓库服务器
  • 镜像中软件都为最新版本
  • 使用可信镜像文件,并通过安全通道下载
  • 重新构建镜像而非对容器和镜像打补丁
  • 合理管理镜像标签,及时移除不再使用的镜像
  • 使用镜像扫描
  • 使用镜像签名
1.4.5、容器级别
  • 容器最小化,操作系统镜像最小集
  • 容器以单一主进程的方式运行
  • 禁止privileged标记使用特权容器
  • 禁止在容器上运行ssh服务
  • 以只读的方式挂载容器的根目录系统
  • 明确定义属于容器的数据盘符
  • 通过设置no-failure限制容器尝试重启的次数
  • 限制容器中可用的进程数,以方式fork bomb
1.4.6、其他设置
  • 定期对宿主机系统及容器进行安全审计
  • 使用最少资源和最低权限运行容器
  • 避免在同一宿主机上部署大量容器,维持一个能够管理的数量
  • 监控Docker容器的使用,性能以及其他各项指标
  • 增加实时 威胁检测和事件响应功能
  • 使用中心和远程日志收集服务

实例:容器相关的常用安全配置方法

一、Docker TLS通信安全

  • 按照Docker官方的说法,为了防止链劫持、会话劫持等问题导致Docker通信时被中间人攻击,c/s两端应该通过加密的方式通讯

1.1、创建密钥

hostnamectl set-hostname master
[root@master ~]# mkdir /tls
[root@master ~]# cd /tls/
[root@master tls]# echo "127.0.0.1 master" >> /etc/hosts
# 使用OpenSSL创建CA、服务器的端口段密钥
[root@master tls]# openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
...................++
...........................................................................................................................++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:	 # 密码123123
Verifying - Enter pass phrase for ca-key.pem:   # 确定密码

[root@master tls]# openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:	# 输入刚刚的密码123123
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:HN
Locality Name (eg, city) [Default City]:ZhengZhou
Organization Name (eg, company) [Default Company Ltd]:kgc
Organizational Unit Name (eg, section) []:kgc
Common Name (eg, your name or your server's hostname) []:master
Email Address []:

[root@master tls]# openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
......................................................................................................++
..................................................................................................................++
e is 65537 (0x10001)

[root@master tls]# openssl req -subj "/CN=master" -sha256 -new -key server-key.pem -out server.csr

# 由于可以通过IP地址贺DNS名称进行TLS连接,因此IP地址需要在创建证书时指定,例如,允许连接的IP为127.0.0.1、192.168.93.166
[root@master tls]# echo subjectAltName = DNS:master,IP:192.168.93.165,IP:127.0.0.1 >> extfile.cnf
# 将Docker守护程序密钥的扩展用法属性设置为仅用于服务器身份验证
[root@master tls]# echo extendedKeyUsage = serverAuth >> extfile.cnf

# 生成签名证书
[root@master tls]# openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=master
Getting CA Private Key
Enter pass phrase for ca-key.pem:

[root@master tls]# openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................................................................................................++
...........................................................................................................................................................................................................................................................................................++
e is 65537 (0x10001)

[root@master tls]# openssl req -subj '/CN=client' -new -key key.pem -out client.csr
[root@master tls]# echo extendedKeyUsage = clientAuth > extfile-client.cnf

# 生成签名证书
[root@master tls]# openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

# 生成后,可以安全地删除两个证书签名请求贺扩展配置文件
[root@master tls]# rm -rf client.csr server.csr extfile.cnf extfile-client.cnf

# 为了数据的安全,进行对密钥的权限更改
[root@master tls]# chmod -v 0400 ca-key.pem key.pem server-key.pem
[root@master tls]# chmod -v 0444 ca.pem server-cert.pem cert.pem

# 把密钥上传到client
[root@master tls]# scp ca.pem root@192.168.93.166:/etc/docker/
[root@master tls]# scp cert.pem root@192.168.93.166:/etc/docker/
[root@master tls]# scp key.pem root@192.168.93.166:/etc/docker/

# 重新启动docker开启TLS认证加密
## 方法1:
[root@master tls]# vim /lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/tls/ca.pem --tlscert=/tls
/server-cert.pem --tlskey=/tls/server-key.pem -H tcp://0.0.0.0:2376 -H unix:/
//var/run/docker.sock
#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
[root@master tls]# systemctl daemon-reload 
[root@master tls]# systemctl restart docker

## 方法2:
[root@master tls]# systemctl stop docker
[root@master tls]# dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376

1.2、client连接

hostnamectl set-hostname client
[root@client ~]# vim /etc/hosts
192.168.93.165 master
[root@client ~]# cd /etc/docker/
# 使用密钥连接master并查看master的docker版本
[root@client docker]# docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=master:2376 version
Client: Docker Engine - Community
 Version:           26.1.2
 API version:       1.45
 Go version:        go1.21.10
 Git commit:        211e74b
 Built:             Wed May  8 14:01:02 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          26.1.2
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.10
  Git commit:       ef1912d
  Built:            Wed May  8 13:59:55 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.31
  GitCommit:        e377cd56a71523140ca6ae87e30244719194a521
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

二、容器最小化

  • 如果仅在容器中运行必要的服务,像SSH等服务是不能轻易开启去连接容器的。通常使用以下方式来进入容器
[root@master ~]# docker exec -it 容器名称 bash

三、Docker RemoteAPI访问控制

  • Docker的远程调用API接口存在未授权访问漏洞,至少应显示外网访问。建议使用Socket方式访问
# docker.scok是daemon监听的套接字,容器中的进程可以通过它与docker daemon通信
[root@master ~]# dockerd -H unix:///var/run/docker.sock[root@master ~]# vim /usr/lib/systemd/system/docker.service
# 进行修改即可
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://192.168.93
.165:2375
[root@master ~]# systemctl daemon-reload 
[root@master ~]# systemctl restart docker
[root@master ~]# netstat -anpt | grep docker
tcp        0      0 192.168.93.165:2375     0.0.0.0:*               LISTEN      13281/dockerd
  • 然后在宿主机的firewalld上做IP访问控制即可。(source address 是客户端地址)
[root@master ~]# systemctl start firewalld

# 添加防火墙富规则
[root@master ~]# firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.93.166" port protocol="tcp" port="2375" accept"
success

# 刷新防火墙
[root@master ~]# firewall-cmd --reload
success
  • 然后在客户端192.168.93.166上即授权访问控制
# 在客户端控制服务端拉去一个nginx镜像
[root@client ~]# docker -H=tcp://192.168.93.165:2375 pull nginx

# 在服务端查看是否有nginx镜像
[root@master ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
nginx        latest    4f67c83422ec   3 days ago   188MB

四、限制流量转向

  • 使用防火墙过滤器限制Docker容器的源IP地址范围与外界通信
# 拒绝10.0网段的主机与服务器通信
[root@master ~]# firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="192.168.10.0/24" reject"

五、DockerBenchforSecurity介绍

  • Docker Bench for Security是一个脚本,它是在生产环境中常见的检测检查部署Docker容器的最佳安全实现,测试都是自动化的,受CIS Docker 1.13基准的启发而来
  • 要使用docker-slim,从Github下载其二进制文件。二进制文件可用于Linux和Mac。下载二进制文件后,将其添加到环境变量PATH中
[root@master ~]# yum -y install git
[root@master ~]# git clone https://github.com/docker/docker-bench-security.git
Cloning into 'docker-bench-security'...
remote: Enumerating objects: 2726, done.
remote: Counting objects: 100% (804/804), done.
remote: Compressing objects: 100% (242/242), done.
remote: Total 2726 (delta 611), reused 634 (delta 554), pack-reused 1922
Receiving objects: 100% (2726/2726), 4.46 MiB | 1.50 MiB/s, done.
Resolving deltas: 100% (1889/1889), done.
[root@master ~]# cd docker-bench-security/
[root@master docker-bench-security]# sh docker-bench-security.sh
##################################################################
# --------------------------------------------------------------------------------------------
# Docker Bench for Security v1.6.0
#
# Docker, Inc. (c) 2015-2024
#
# Checks for dozens of common best-practices around deploying Docker containers in production.
# Based on the CIS Docker Benchmark 1.6.0.
# --------------------------------------------------------------------------------------------

Initializing 2024-06-02T06:50:17-04:00


Section A - Check results

[INFO] 1 - Host Configuration
[INFO] 1.1 - Linux Hosts Specific Configuration
[DEPRECATION NOTICE]: API is accessible on http://192.168.93.165:2375 without encryption.
         Access to the remote API is equivalent to root access on the host. Refer
         to the 'Docker daemon attack surface' section in the documentation for
         more information: https://docs.docker.com/go/attack-surface/
In future versions this will be a hard failure preventing the daemon from starting! Learn more at: https://docs.docker.com/go/api-security/
[WARN] 1.1.1 - Ensure a separate partition for containers has been created (Automated)
[INFO] 1.1.2 - Ensure only trusted users are allowed to control Docker daemon (Automated)
[INFO]       * Users: 
[WARN] 1.1.3 - Ensure auditing is configured for the Docker daemon (Automated)

Section C - Score

[INFO] Checks: 86
[INFO] Score: -2
# 部分内容省略
##################################################################

# 输出结果中,带有不同的级别,说明问题的严重程度,最后会给出整体检查结果和评分
1、标红[WARN]是需要调整
2、标绿[PASS]表示通过检测
3[INFO]可根据实际需要确定是否进行调整
# 一般要尽量避免出现WARN或以上的问题

六、Cgroup资源配置方法

  • Docker通过Cgroup来控制容器使用的资源配额,包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制
  • Cgroup是ControlGroups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如CPU、内存、磁盘IO等等)的机制,被LXC、docker等很多项目用于实现进程资源控制。Cgroup本身是提供将进程分组话管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理是通过该功能来实现的。这些具体的资源管理功能称为Cgroup子系统

6.1、子系统实现

  • blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等
  • CPU:使用调度程序为cgroup任务提供CPU的访问
  • cpuacct:产生cgroup任务的CPU资源报告
  • cpuset:如果是多核心的CPU,这个子系统会为cgroup任务分配单独的CPU和内存
  • devices:允许或拒绝cgroup任务对设备的访问
  • freezer:暂停和恢复cgroup任务
  • memory:设置每个cgroup的内存限制以及生产内存资源报告
  • net_cls:标记每个网络包以供cgroup方便使用
  • ns:命令空间子系统
  • perf_event:增加了对每个group的监测跟踪的能力,可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程

6.2、使用Stress工具测试CPU和内存

  • 使用Dockerfile来创建一个基于CentOS的stress工具镜像
root@master ~]# docker pull centos:7
[root@master ~]# mkdir /root/stress
[root@master ~]# cat /root/stress/Dockerfile
FROM centos:7
MAINTAINER Wzh
RUN yum -y install wget
RUN wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install stress
[root@master ~]# cd /root/stress/
[root@master stress]# docker build -t centos:stress .
  • 使用如下命令创建容器,命令中的–cpu-shares参数值不能保证可以获得1个vcpu或者多少GHz的CPU资源,它仅是一个弹性的加权值,值越大或者分配的资源就越多
[root@master stress]# docker run -itd --cpu-shares 100 centos:stress
  • Cgroups只在容器分配的资源紧缺时,即在需要对容器的资源进行限制时,才会生效。因此,无法单纯根据某个容器的CPU份额来确定有多少CPU资源分配给它,资源分配结果取决于同时运行的其他容器的CPU分配和容器中进程运行情况
  • 可以通过cpushare可以设置容器使用CPU的优先级,比如启动了两个容器及运行查看CPU百分比
 # stress -c 10向容器中开启10个进程用于测试
[root@master stress]# docker run -itd --name cpu512 --cpu-shares 512 centos:stress stress -c 10

# 进入容器使用top查看资源咱占用情况
[root@master ~]# docker exec -it cpu512 bash
[root@5a30bc38e2d9 /]# top
top - 11:26:33 up 10 min,  0 users,  load average: 7.89, 2.69, 0.98
Tasks:  13 total,  11 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s): 98.8 us,  1.2 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0
KiB Mem :  3861260 total,  2380572 free,   312972 used,  1167716 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  3302036 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     9 root      20   0    7312     96      0 R  20.3  0.0   0:17.46 stress 
    12 root      20   0    7312     96      0 R  20.3  0.0   0:17.62 stress 
    13 root      20   0    7312     96      0 R  20.3  0.0   0:18.17 stress 
     6 root      20   0    7312     96      0 R  19.9  0.0   0:17.96 stress 
     7 root      20   0    7312     96      0 R  19.9  0.0   0:17.58 stress 
     8 root      20   0    7312     96      0 R  19.9  0.0   0:17.66 stress 
    15 root      20   0    7312     96      0 R  19.9  0.0   0:17.59 stress 
    10 root      20   0    7312     96      0 R  19.6  0.0   0:17.47 stress 
    11 root      20   0    7312     96      0 R  19.6  0.0   0:17.58 stress 
    14 root      20   0    7312     96      0 R  19.6  0.0   0:18.16 stress 
     1 root      20   0    7312    624    528 S   0.0  0.0   0:00.01 stress 
    16 root      20   0   11828   1896   1492 S   0.0  0.0   0:00.01 bash   
    30 root      20   0   56192   2012   1444 R   0.0  0.1   0:00.00 top    

  • 下面设置CPU资源份额翻倍
[root@master ~]# docker run -itd --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10
[root@master ~]# docker exec -it cpu1024 bash
[root@dc64e7513c1f /]# top
top - 11:28:19 up 12 min,  0 users,  load average: 13.22, 5.74, 2.24
Tasks:  13 total,  11 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0
KiB Mem :  3861260 total,  2369580 free,   323532 used,  1168148 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  3291300 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     7 root      20   0    7312     92      0 R  14.2  0.0   0:03.71 stress 
    13 root      20   0    7312     92      0 R  14.2  0.0   0:03.68 stress 
    15 root      20   0    7312     92      0 R  14.2  0.0   0:03.50 stress 
    14 root      20   0    7312     92      0 R  13.9  0.0   0:03.51 stress 
     9 root      20   0    7312     92      0 R  12.9  0.0   0:03.32 stress 
    10 root      20   0    7312     92      0 R  12.6  0.0   0:03.32 stress 
    12 root      20   0    7312     92      0 R  12.6  0.0   0:03.29 stress 
    11 root      20   0    7312     92      0 R  12.3  0.0   0:03.31 stress 
    16 root      20   0    7312     92      0 R  12.3  0.0   0:03.33 stress 
     8 root      20   0    7312     92      0 R  11.9  0.0   0:03.31 stress 
     1 root      20   0    7312    620    528 S   0.0  0.0   0:00.01 stress 
    17 root      20   0   11828   1904   1496 S   0.0  0.0   0:00.01 bash   
    31 root      20   0   56192   2012   1444 R   0.0  0.1   0:00.00 top    

# 可以看容器512CPU占用率要比1024高

七、CPU周期限制

Docker提供了–cpu-period、–cpu-quota两个参数控制容器可以分配到CPU时钟周期

  • –cpu-period:是用来指定容器对CPU的使用要在多长事件内做一次重新分配

  • –cpu-quota:是用来指定在这个周期内,最多可以有多少时间跑这个容器。与–cpu-shares不同的是,这种配置是指定一个绝对值,容器对CPU资源的使用绝对不会超过配置的值

  • cpu-period和cpu-quota的单位为微秒(us)。cpu-period的最小值为1000微妙,最大值为1秒。默认值为0.1秒。cpu-quota的值默认为-1,表示不做控制。cpu-period和cpu-quota参数一般联合使用

# 例如:容器进程需要每1秒使用单个CPU的0.2秒时间,可以将cpu-period设置为1000000(即1秒),cpu-quota设置为200000(0.2秒)
[root@master ~]# docker run -itd --cpu-period 1000000 --cpu-quota 200000 centos:stress 
[root@master ~]# docker exec -it condescending_spence bash
[root@3d0f9e29cc61 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 
1000000
[root@3d0f9e29cc61 /]# cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 
200000

八、CPUCore控制

  • 对于多核CPU的服务器,Docker还可以控制容器运行使用哪些CPU内核,即使用–cpuset-cpus参数,这对具有多CPU的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置
# 只允许这个容器使用0、1两个内核
[root@master ~]# docker run -itd --name cpu1 --cpuset-cpus 0-1 centos:stress
[root@master ~]# docker exec -it cpu1 bash
[root@ea4cd6c80f5e /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-1

九、CPU配额控制参数的混合使用

  • 通过cpuset-cpus参数指定容器A使用CPU内核0,容器B使用CPU内核1
[root@master ~]# docker run -itd --name cpu3 --cpuset-cpus 1 --cpu-shares 512 centos:stress stress -c 1
[root@master ~]# docker exec -it cpu3 bash
[root@3a197567bf58 /]# top
top - 11:52:58 up 37 min,  0 users,  load average: 20.67, 20.14, 16.51
Tasks:   4 total,   2 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0
KiB Mem :  3861260 total,  2333408 free,   357196 used,  1170656 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  3256448 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     7 root      20   0    7312     96      0 R  54.8  0.0   0:32.37 stress 
     1 root      20   0    7312    424    344 S   0.0  0.0   0:00.00 stress 
     8 root      20   0   11828   1900   1496 S   0.0  0.0   0:00.01 bash   
    22 root      20   0   56192   2000   1444 R   0.0  0.1   0:00.00 top  
    
    
    
[root@master ~]# docker run -itd --name cpu4 --cpuset-cpus 1 --cpu-shares 1024 centos:stress stress -c 1
[root@master ~]# docker exec -it cpu4 bash
[root@080ee5432922 /]# top
top - 11:56:33 up 40 min,  0 users,  load average: 21.80, 20.81, 17.52
Tasks:   4 total,   2 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0
KiB Mem :  3861260 total,  2325228 free,   364704 used,  1171328 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  3248516 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     7 root      20   0    7312     96      0 R  66.7  0.0   0:16.50 stress 
     1 root      20   0    7312    424    344 S   0.0  0.0   0:00.01 stress 
     8 root      20   0   11828   1896   1496 S   0.0  0.0   0:00.01 bash   
    22 root      20   0   56192   1964   1428 R   0.0  0.1   0:00.00 top   
# 上面的centos:stress镜像安装了stress工具,用来测试CPU和内存的负载。通过在两个容器上分别执行stress -c 1命令,将会给系统一个随机负载,产生1个进程,这个进程都反复不停的计算有rand()产生随机数的平方根,知道资源耗尽

十、内存限额

与操作系统类似,容器可使用的内存包括两部分:物理内存和Swap。Docker通过下面两组参数来控制容器内存的使用量

  • -m或–memory:设置内存的使用限额,例如100M、1024M
  • –memory-swap:设置内存+swap的使用限额
# 执行如下命令允许该容器最多使用200M的内存和不能超过300的swap(swap100内存)
[root@master ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
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 [7] forked
stress: dbug: [7] allocating 293601280 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 293601280 bytes
stress: dbug: [7] allocating 293601280 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 293601280 bytes
stress: dbug: [7] allocating 293601280 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: dbug: [7] freed 293601280 bytes
stress: dbug: [7] allocating 293601280 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
--vm 1:启动1个内存工作线程
--vm-bytes 280M:每个线程分配280内存

# 默认情况下,容器可以使用主机上的所有空闲内存。与CPU的cgroups配置类似,Docker会自动为容器在目录/sys/fs/cgroup/memory/docker<容器的完整长度 ID>中创建相应cgroup配置文件

# 因为280M在可分配的范围(300M)内,所以工作线程能够正常工作,其工作过程是
分配280M内存
释放280M内存
再分配280M内存
再释放280M内存
一直循环

# 如果让工作线程分配的内存超过300M,分配的内存超过限制,stress线程报错,容器退出
[root@master ~]# docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M
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 [7] forked
stress: dbug: [7] allocating 325058560 bytes ...
stress: dbug: [7] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 7 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 0s

十一、Blocklo的限制

  • 默认情况下,所有容器能平等地读写磁盘,可以通过设置–blkio-weight参数来改变容器的vlock IO的优先级
  • –blkio-weigit与–cpu-shares类似,设置的是相对权重值,默认为500。在下面的例子中,容器A读写磁盘的宽带是容器B的两倍
[root@master ~]# docker run -it --name IO_A --blkio-weight 600 centos:stress [root@c3d24510ba18 /]# cat /sys/fs/cgroup/blkio/blkio.weight
600

[root@master ~]# docker run -it --name IO_B --blkio-weight 300 centos:stress
[root@5f98b4ad5a2a /]# cat /sys/fs/cgroup/blkio/blkio.weight
300

十二、bps和iops的限制

  • bps是byte per second,每秒读写的数据量。iops是io per second,每秒的IO的次数
  • 可以通过以下参数控制容器的bps和iops
--device-read-bps:限制读某个设备的bps
--device-write-bsp:限制写某个设备的bps
--device-read-iops:限制某个设备的iops
--device-write-iops:限制写某个设备的iops

# 下面的示例是显示容器写/dev/sda的速率为5MB/s
[root@master ~]# docker run -it --device-write-bps /dev/sda:5MB centos:stress
# 输出完时间为20秒左右
[root@4775b55a6f0b /]# dd if=/dev/zero of=test bs=1M count=100 oflag=direct 
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 20.0017 s, 5.2 MB/s


# 不限速,不限速时间还没1秒就输出完毕了
[root@master ~]# docker run -it centos:stress
[root@ee280332da35 /]# dd if=/dev/zero of=test bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.0775017 s, 1.4 GB/s


# 通过dd命令测试在容器中写磁盘的速度。因为容器的文件系统是在host/dev/sda上的,在容器中写文件相当于对host/dev/sda进行写操作。另外,oflag=direct指定用direct IO方式写文件,这样--device-weite-bps才能生效
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐