测试问题总结(二)----------Docker

1、Docker是什么?

Docker是一个虚拟环境容器,可以将你的环境、代码、配置文件等一并打包到这个容器,并发布和应用到任意平台中。

Docker的三个概念:

镜像(Image):类似于虚拟机中的镜像,是一个包含有文件系统的面向Docker引擎的只读模板。任何应用程序运行都需要环境,而镜像就是用来提供这种运行环境的,例如一个Ubuntu镜像就是一个包含Ubuntu操作系统环境的模板,同理在该镜像上装上Apache软件,就可以称为Apache镜像;

容器(Container):类似于一个轻量级的沙盒,可以将其看做一个极简的Linux系统环境,包括root权限、进程空间、用户空间和网络空间等。以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是相互隔离的、互补影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变;

仓库(Repository):类似于代码仓库,这里是镜像仓库,是Docker用来集中存放镜像文件的地方。注意与注册服务器(Registry)的区别:注册服务器是存放仓库的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分。比如Ubuntu仓库存放有多个版本的Ubuntu镜像;

Ubuntu Docker安装:

Docker支持以下的Ubantu版本:Ubuntu Precise 12.04 (LTS)、Ubuntu Trusty 14.04 (LTS)、Ubuntu Wily 15.10;

前提条件:

Docker要求Ubuntu系统的内核版本高于3.10,查看本页面的前提条件来验证你的Ubuntu版本是否支持Docker;通过uname -r命令查看你当前的内核版本;

安装Docker:

1:curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

2:centos安装  yum -y install docker.io

3:ubuntu安装 apt-get install docker.io

启动Docker服务:sudo service docker start,而后配置镜像加速器;亲测加速之后下载速度提升20倍左右,这里配置的是阿里云的镜像加速器;注册阿里云,打开容器镜像服务,获取镜像加速器地址:阿里云镜像仓库;

执行如下命令安装,安装完成后执行命令:docker --version查看版本;

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'

{

  "registry-mirrors": ["https://1vtrf9ho.mirror.aliyuncs.com"]

}

EOF

sudo systemctl daemon-reload

sudo systemctl restart docker

Docker中关于镜像的基本操作:

获取镜像:可以使用docker pull命令来从仓库获取所需要的镜像。执行:sudo docker pull ubuntu:12.04

下载过程中,会输出获取镜像的每一层信息,该命令实际相当于$ sudo docker pull registry.hub.docker.com/ubuntu:12.04 命令,即从注册服务器 registry.hub.docker.com 中的 ubuntu 仓库来下载标记为 12.04 的镜像。完成后,即可随时使用该镜像了,例如创建一个容器,让其中运行 bash 应用:sudo docker run -t -i ubuntu:12.04 /bin/bash。执行ctrl+D撤出该容器。

列出本地所有镜像sudo docker images,列出信息中,可以看到如下几个字段的信息:来自于那个仓库、镜像的标记、ID号、创建时间、镜像大小;

列出本地所有容器docker ps -a;

启动容器:sudo docker run ubuntu /bin/bash 标红的为容器名称;

基本命令总结:

sudo service docker start 启动docker

sudo service docker stop 停止docker  

sudo docker images 查看所有镜像

docker rmi <image id>删除images

docker rmi -f {TAG}  根据tag删除images

docker rmi $(docker images -q)删除全部image

docker rmi $(docker images | grep "^<none>" | awk "{print $3}")  删除untagged images,也就是那些id为<None>的image

sudo docker ps -a 查看所有容器

docker stop CONTAINER ID 停用容器

docker rm CONTAINER ID 删除容器

docker stop $(docker ps -a -q) 停用所有容器

docker rm $(docker ps -a -q) 删除所有容器

docker stop $(docker ps -q) & docker rm $(docker ps -aq) 停用并删除所有容器

docker pull {镜像名称} 拖镜像

docker run -d -p {映射端口}:{本地端口} --name{自定义名称} {镜像名称:tag标签}     运行镜像

docker exec -it {container names} bash  进入容器

sudo dockerlogin --username={阿里云用户名} {远程仓库名称} 登录阿里云远程仓库

sudo docker tag [ImageId] {远程仓库名称}:[TAG]   修改镜像名称  

sudo docker push registry.cn-hangzhou.aliyuncs.com/msj:[TAG] 上传镜像到阿里云远程仓库

 

2、Docker容器化Jenkins

首先,pull一个Jenkins镜像docker pull jenkins。

查看已安装的Jenkins镜像:docker images;

创建一个Jenkins目录:mkdir /home/jenkins_home;

启动一个jenkins容器    docker run -d --name jenkins -p 8081:8080 -v /home/jenkins_home:/home/jenkins_home jenkins  其中8081:8080,表示jenkins内部使用8080端口,服务器使用8081端口,然后将二者映射起来,之后在浏览器访问的时候实际上还是访问服务器的8081端口;

查看Jenkins服务:docker ps|grep jenkins;

启动服务器:locahost:8081/jenkins;

去容器内部找密码:

进入容器:docker exec -it jenkins bash ,执行cat /var/jenkins_home/secrets/initialAdminPassword  可得结果即为jenkins初始密码:

输入密码之后,重启docker镜像  docker restart   {CONTAINER ID}

再次启动服务器,Jenkins服务已经起来了,后面就是配置插件的问题了。

 

3、Docker部署Jenkins(dockerfile实现)

docker+jenkins开始合体!

我用的是ubuntu14.04的基础镜像,并且在基础镜像中已经把ant,tomcat,jdk的安装包配置好了。具体的这里不做赘述。

在/tmp/目录下建一个Dockerfile文件:

touch Dockerfile

vi Dockerfile

下面是我写的Dockerfile文件

第一行FROM是基于哪个镜像;

第二行是联系方式;

RUN是Dockerfile内部运行的命令;

ENV用来来配置环境变量;

CMD是构建之后,执行的命令,这里启动tomcat;

EXPOSE 8080 这里是tomcat需要的端口;

这些写完之后保存,然后执行如下命令构建

docker build -t {name}:{tag} .其中-t  给镜像命名,tag是标签,后面有个 .  ,意思是从当前目录查找Dockerfile

 

上图可以看到dockerfile在按照命令步骤执行,我们再来运行一下docker images,查看构建的镜像。

 

现在我们启动一下这个镜像

docker run -d --name jenkins -p 80:8080 -v /home/jenkins_home1:/home/jenkins_home1 jenkins:u3   -p是将端口从8080映射到80

查看一下运行状况,发现这个jenkins服务已经起来了,运行在80端口

页面启动jenkins

 

4、容器化的Apache服务并监控

1:基础容器编译部署apache

1.1:pull一个ubuntu 16.04的镜像

sudo docker pull ubuntu:16.04

1.2:运行容器  

sudo docker run -d --name ubuntu -p 80:80 ubuntu:16.04

1.3:安装ubuntu依赖项

apt update

apt-get install vim

apt-get install net-tools

apt-get install iputils-ping

apt-get install openssh-server

apt-get install openssh-client

apt-get install lrzsz

apt-get install gcc

apt-get install libpcre3 libpcre3-dev

apt-get install make

apt-get install openssl libssl-dev

apt-get install libxml2 libxml2-dev

apt-get install zip unzip

apt-get install libexpat1-dev

apt-get install libnghttp2-dev

1.4:创建安装目录

mkdir /etc/apache2

mkdir /etc/apache2/src

cd /etc/apache2/src

1.5:下载源码并解压

如果路径失效,需要去官网下载相应的安装包

wget http://archive.apache.org/dist/apr/apr-1.5.2.tar.gz

wget http://archive.apache.org/dist/apr/apr-util-1.5.4.tar.gz

wget http://apache.mirrors.lucidnetworks.net//httpd/httpd-2.4.27.tar.gz

wget http://cn2.php.net/distributions/php-7.1.8.tar.gz

tar -xvf apr-1.5.2.tar.gz

tar -xvf apr-util-1.5.4.tar.gz

tar -xvf httpd-2.4.27.tar.gz

tar -xvf php-7.1.8.tar.gz

mv -f apr-1.5.2 httpd-2.4.27/srclib/apr

mv -f apr-util-1.5.4 httpd-2.4.27/srclib/apr-util

1.6:安装依赖包zlib1g,zlib1g.dev

直接安装

sudo apt-get install libpcre3 libpcre3-dev  

编译安装

在 http://jaist.dl.sourceforge.net/project/pcre/pcre/ 下载

得到文件: pcre-8.32.tar.gz

解压:tar -zxvf pcre-8.32.tar.gz

编译:

cd /home/tnuser/hunter/installers/pcre-8.32    

/configure --prefix=/home/tnuser/pcre/  

 

1.7:安装依赖包openssl和openssl-devel

直接安装

apt-get install openssl openssl-devel

 

下载源码编译安装

cd /etc/apache2/src

wget https://www.openssl.org/source/openssl-1.0.2h.tar.gz

tar zxf openssl-1.0.2h.tar.gz

cd openssl-1.0.2h

./config shared zlib

如果提示 because of configuration changes, you MUST do the following before*** building:

则需要在build之前做make depend

make depend

make

make install

mv /usr/bin/openssl /usr/bin/openssl.bak

mv /usr/include/openssl /usr/include/openssl.bak

ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl

ln -s /usr/local/ssl/include/openssl /usr/include/openssl

echo “/usr/local/ssl/lib” >> /etc/ld.so.conf

ldconfig –v

 

检测安装是否成功

openssl version –a

1.8:编译apache

openssl version -a

mkdir server_root

cd src/httpd-2.4.27 ./configure --prefix=/etc/apache2/server_root --with-included-apr --with-mpm=worker --enable-so --enable-nonportable-atomics=yes --enable-ssl --enable-include --enable-cgi --enable-expires --enable-status --enable-info --enable-rewrite --enable-speling

make

make install

mkdir /etc/apache2/php7

cd /etc/apache2/src/php-7.1.8

./configure --with-apxs2=/etc/apache2/server_root/bin/apxs --prefix=/etc/apache2/php7

make

make test

make install

 

修改httpd.conf文件

vim /etc/apache2/server_root/conf/httpd.conf

在httpd.conf文件中添加:

AddType application/x-httpd-php .php 

AddType application/x-httpd-php-source .phps

去掉httpd.conf里ServerName前面的注释: 

 

重启apache服务

cd /etc/apache2/server_root/bin

./apachectl start

1.9:容器打包

 

2:配置server-status 监控apache

2.1:server-status简介

server-status是Apache查看状态的一个功能模块,在Apache 1.3.2及以后的版本提供

2.2:打开server-status

vi /etc/apache2/server_root/conf/httpd.conf

去掉LoadModule status_module modules/mod_status.so的注释

修改Include 路径 为 /etc/apache2/server_root/conf/extra/httpd-info.conf

修改配置如下

vi /etc/apache2/server_root/conf/extra/httpd-info.conf

<Location /server-status>

    SetHandler server-status

    Order deny,allow

Allow from all

Allow from 10.20.7.15

</Location>

去掉ExtendedStatus On 注释

vi /etc/apache2/src/httpd-2.4.33

./configure --enable-module=so --enable-info

重启apache

cd /etc/apache2/server_root/bin

./apachectl restart

3:镜像包部署apache

3.1:拉取镜像

Docker pull registry.cn-hangzhou.aliyuncs.com/zhufc/apache-status:zhu

3.2:映射80端口

docker run -d --name apache -p 80:80 registry.cn-hangzhou.aliyuncs.com/zhufc/apache-status:zhu

3.3:进入容器启动apache

docker exec -it da6a434c99e6 bash

cd /etc/apache2/server_root/bin

./apachectl start

3.4:容器后台运行

Ctrl+alt+q+p 

3.4:容器后台运行

Ctrl+alt+q+p 

4:页面验证apache监控

http://10.20.7.15/server-status

http://10.20.7.15/server-status/phpmyadmin

 

 

5、Docker容器CPU、memory资源限制

在使用 docker 运行容器时,默认的情况下,docker没有对容器进行硬件资源的限制,当一台主机上运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。

docker 作为容器的管理者,自然提供了控制容器资源的功能。正如使用内核的 namespace 来做容器之间的隔离,docker 也是通过内核的 cgroups 来做容器的资源限制;包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。

Docker内存控制OOME在linxu系统上,如果内核探测到当前宿主机已经没有可用内存使用,那么会抛出一个OOME(Out Of Memory Exception:内存异常 ),并且会开启killing去杀掉一些进程。

一旦发生OOME,任何进程都有可能被杀死,包括docker daemon在内,为此,docker特地调整了docker daemon的OOM_Odj优先级,以免他被杀掉,但容器的优先级并未被调整。经过系统内部复制的计算后,每个系统进程都会有一个OOM_Score得分,OOM_Odj越高,得分越高,(在docker run的时候可以调整OOM_Odj)得分最高的优先被kill掉,当然,也可以指定一些特定的重要的容器禁止被OMM杀掉,在启动容器时使用 –oom-kill-disable=true指定。

参考:Docker监控容器资源的占用情况

cgroup简介

cgroup是Control Groups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被LXC、docker等很多项目用于实现进程资源控制。cgroup将任意进程进行分组化管理的 Linux 内核功能。cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现:

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

目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。

可以使用stress工具来测试CPU和内存。使用下面的Dockerfile来创建一个基于Ubuntu的stress工具镜像。

FROM ubuntu:14.04
RUN apt-get update &&apt-get install stress

 

资源监控的关键目录:cat读出 
已使用内存: 
/sys/fs/cgroup/memory/docker/应用ID/memory.usage_in_bytes

分配的总内存: 
/sys/fs/cgroup/memory/docker/应用ID/memory.limit_in_bytes

已使用的cpu:单位纳秒 
/sys/fs/cgroup/cpuacct/docker/应用ID/cpuacct.usage

系统当前cpu:

$ cat /proc/stat | grep 'cpu '(周期/时间片/jiffies)
#得到的数字相加/HZ(cat /boot/config-`uname -r` | grep '^CONFIG_HZ='
ubuntu 14.04为250)就是系统时间(秒)
#再乘以10*9就是系统时间(纳秒)

例子

[~]$ cat /proc/stat
cpu 432661 13295 86656 422145968 171474 233 5346
cpu0 123075 2462 23494 105543694 16586 0 4615
cpu1 111917 4124 23858 105503820 69697 123 371
cpu2 103164 3554 21530 105521167 64032 106 334
cpu3 94504 3153 17772 105577285 21158 4 24
intr 1065711094 1057275779 92 0 6 6 0 4 0 3527 0 0 0 70 0 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
ctxt 19067887
btime 1139187531
processes 270014
procs_running 1
procs_blocked 0


输出解释
CPU 以及CPU0、CPU1、CPU2、CPU3每行的每个参数意思(以第一行为例)为:
参数 解释
user (432661) 从系统启动开始累计到当前时刻,用户态的CPU时间(单位:jiffies) ,不包含 nice值为负进程。
nice (13295) 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间(单位:jiffies) 
system (86656) 从系统启动开始累计到当前时刻,核心时间(单位:jiffies) 
idle (422145968) 从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它等待时间(单位:jiffies) 
iowait (171474) 从系统启动开始累计到当前时刻,硬盘IO等待时间(单位:jiffies) ,
irq (233) 从系统启动开始累计到当前时刻,硬中断时间(单位:jiffies) 
softirq (5346) 从系统启动开始累计到当前时刻,软中断时间(单位:jiffies) 

cpu使用率: 
(已使用2-已使用1)/(系统当前2-系统当前1)*100%

 

内存限制

Docker 提供的内存限制功能有以下几点:

  • 容器能使用的内存和交换分区大小。
  • 容器的核心内存大小。
  • 容器虚拟内存的交换行为。
  • 容器内存的软性限制。
  • 是否杀死占用过多内存的容器。
  • 容器被杀死的优先级

一般情况下,达到内存限制的容器过段时间后就会被系统杀死。

内存限制相关的参数

执行docker run命令时能使用的和内存限制相关的所有选项如下。

选项描述
-m,--memory内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
--memory-swap内存+交换分区大小总限制。格式同上。必须必-m设置的大
--memory-reservation内存的软性限制。格式同上
--oom-kill-disable是否阻止 OOM killer 杀死容器,默认没设置
--oom-score-adj容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
--memory-swappiness用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
--kernel-memory核心内存限制。格式同上,最小为 4M

用户内存限制

用户内存限制就是对容器能使用的内存和交换分区的大小作出限制。使用时要遵循两条直观的规则:-m,--memory选项的参数最小为 4 M。--memory-swap不是交换分区,而是内存加交换分区的总大小,所以--memory-swap必须比-m,--memory大。在这两条规则下,一般有四种设置方式。

你可能在进行内存限制的实验时发现docker run命令报错:WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.

这是因为宿主机内核的相关功能没有打开。按照下面的设置就行。

step 1:编辑/etc/default/grub文件,将GRUB_CMDLINE_LINUX一行改为GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

step 2:更新 GRUB,即执行$ sudo update-grub

step 3: 重启系统。

1. 不设置

如果不设置-m,--memory--memory-swap,容器默认可以用完宿舍机的所有内存和 swap 分区。不过注意,如果容器占用宿主机的所有内存和 swap 分区超过一段时间后,会被宿主机系统杀死(如果没有设置--00m-kill-disable=true的话)。

2. 设置-m,--memory,不设置--memory-swap

-m--memory设置一个不小于 4M 的值,假设为 a,不设置--memory-swap,或将--memory-swap设置为 0。这种情况下,容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因为 Docker 默认容器交换分区的大小和内存相同。

如果在容器中运行一个一直不停申请内存的程序,你会观察到该程序最终能占用的内存大小为 2a。

比如$ docker run -m 1G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小也为 1G。容器内的进程能申请到的总内存大小为 2G。

3. 设置-m,--memory=a--memory-swap=b,且b > a

-m设置一个参数 a,给--memory-swap设置一个参数 b。a 时容器能使用的内存大小,b是容器能使用的 内存大小 + swap 分区大小。所以 b 必须大于 a。b -a 即为容器能使用的 swap 分区大小。

比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小为 2G。容器内的进程能申请到的总内存大小为 3G。

4. 设置-m,--memory=a--memory-swap=-1

-m参数设置一个正常值,而给--memory-swap设置成 -1。这种情况表示限制容器能使用的内存大小为 a,而不限制容器能使用的 swap 分区大小。

这时候,容器内进程能申请到的内存大小为 a + 宿主机的 swap 大小。

Memory reservation

这种 memory reservation 机制不知道怎么翻译比较形象。Memory reservation 是一种软性限制,用于节制容器内存使用。给--memory-reservation设置一个比-m小的值后,虽然容器最多可以使用-m使用的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到--memory-reservation设置的值大小。

没有设置时(默认情况下)--memory-reservation的值和-m的限定的值相同。将它设置为 0 会设置的比-m的参数大 等同于没有设置。

Memory reservation 是一种软性机制,它不保证任何时刻容器使用的内存不会超过--memory-reservation限定的值,它只是确保容器不会长时间占用超过--memory-reservation限制的内存大小。

例如:

$ docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash

如果容器使用了大于 200M 但小于 500M 内存时,下次系统的内存回收会尝试将容器的内存锁紧到 200M 以下。

例如:

$ docker run -it --memory-reservation 1G ubuntu:16.04 /bin/bash

容器可以使用尽可能多的内存。--memory-reservation确保容器不会长时间占用太多内存。

OOM killer

默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。这个杀死进程来节省内存的进程,我们姑且叫它 OOM killer。我们可以通过设置--oom-kill-disable选项来禁止 OOM killer 杀死容器内进程。但请确保只有在使用了-m/--memory选项时才使用--oom-kill-disable禁用 OOM killer。如果没有设置-m选项,却禁用了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。

下面的例子限制了容器的内存为 100M 并禁止了 OOM killer:

$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash

是正确的使用方法。

而下面这个容器没设置内存限制,却禁用了 OOM killer 是非常危险的:

$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash

容器没用内存限制,可能或导致系统无内存可用,并尝试时杀死系统进程来获取更多可用内存。

一般一个容器只有一个进程,这个唯一进程被杀死,容器也就被杀死了。我们可以通过--oom-score-adj选项来设置在系统内存不够时,容器被杀死的优先级。负值更教不可能被杀死,而正值更有可能被杀死。

核心内存

核心内存和用户内存不同的地方在于核心内存不能被交换出。不能交换出去的特性使得容器可以通过消耗太多内存来堵塞一些系统服务。核心内存包括:

  • stack pages(栈页面)
  • slab pages
  • socket memory pressure
  • tcp memory pressure

可以通过设置核心内存限制来约束这些内存。例如,每个进程都要消耗一些栈页面,通过限制核心内存,可以在核心内存使用过多时阻止新进程被创建。

核心内存和用户内存并不是独立的,必须在用户内存限制的上下文中限制核心内存。

假设用户内存的限制值为 U,核心内存的限制值为 K。有三种可能地限制核心内存的方式:

  1. U != 0,不限制核心内存。这是默认的标准设置方式
  2. K < U,核心内存时用户内存的子集。这种设置在部署时,每个 cgroup 的内存总量被过度使用。过度使用核心内存限制是绝不推荐的,因为系统还是会用完不能回收的内存。在这种情况下,你可以设置 K,这样 groups 的总数就不会超过总内存了。然后,根据系统服务的质量自有地设置 U。
  3. K > U,因为核心内存的变化也会导致用户计数器的变化,容器核心内存和用户内存都会触发回收行为。这种配置可以让管理员以一种统一的视图看待内存。对想跟踪核心内存使用情况的用户也是有用的。

例如:

$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash

容器中的进程最多能使用 500M 内存,在这 500M 中,最多只有 50M 核心内存。

$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash

没用设置用户内存限制,所以容器中的进程可以使用尽可能多的内存,但是最多能使用 50M 核心内存。

Swappiness

默认情况下,容器的内核可以交换出一定比例的匿名页。--memory-swappiness就是用来设置这个比例的。--memory-swappiness可以设置为从 0 到 100。0 表示关闭匿名页面交换。100 表示所有的匿名页都可以交换。默认情况下,如果不适用--memory-swappiness,则该值从父进程继承而来。

例如:

$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash

--memory-swappiness设置为 0 可以保持容器的工作集,避免交换代理的性能损失。

$ docker run -tid —name mem1 —memory 128m ubuntu:16.04 /bin/bash
$ cat /sys/fs/cgroup/memory/docker/<容器的完整ID>/memory.limit_in_bytes
$ cat /sys/fs/cgroup/memory/docker/<容器的完整ID>/memory.memsw.limit_in_bytes

 

CPU 限制

概述

Docker 的资源限制和隔离完全基于 Linux cgroups。对 CPU 资源的限制方式也和 cgroups 相同。Docker 提供的 CPU 资源限制选项可以在多核系统上限制容器能利用哪些 vCPU。而对容器最多能使用的 CPU 时间有两种限制方式:一是有多个 CPU 密集型的容器竞争 CPU 时,设置各个容器能使用的 CPU 时间相对比例。二是以绝对的方式设置容器在每个调度周期内最多能使用的 CPU 时间。

CPU 限制相关参数

docker run命令和 CPU 限制相关的所有选项如下:

选项描述
--cpuset-cpus=""允许使用的 CPU 集,值可以为 0-3,0,1
-c,--cpu-shares=0CPU 共享权值(相对权重)
cpu-period=0限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000]
--cpu-quota=0限制 CPU CFS 配额,必须不小于1ms,即 >= 1000
--cpuset-mems=""允许在上执行的内存节点(MEMs),只对 NUMA 系统有效

其中--cpuset-cpus用于设置容器可以使用的 vCPU 核。-c,--cpu-shares用于设置多个容器竞争 CPU 时,各个容器相对能分配到的 CPU 时间比例。--cpu-period--cpu-quata用于绝对设置容器能使用 CPU 时间。

--cpuset-mems暂用不上,这里不谈。

CPU 集

我们可以设置容器可以在哪些 CPU 核上运行。

例如:

$ docker run -it --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash

表示容器中的进程可以在 cpu 1 和 cpu 3 上执行。

$ docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash
$ cat /sys/fs/cgroup/cpuset/docker/<容器的完整长ID>/cpuset.cpus

表示容器中的进程可以在 cpu 0、cpu 1 及 cpu 3 上执行。

在 NUMA 系统上,我们可以设置容器可以使用的内存节点。

例如:

$ docker run -it --cpuset-mems="1,3" ubuntu:14.04 /bin/bash

表示容器中的进程只能使用内存节点 1 和 3 上的内存。

$ docker run -it --cpuset-mems="0-2" ubuntu:14.04 /bin/bash

表示容器中的进程只能使用内存节点 0、1、2 上的内存。

CPU 资源的相对限制

默认情况下,所有的容器得到同等比例的 CPU 周期。在有多个容器竞争 CPU 时我们可以设置每个容器能使用的 CPU 时间比例。这个比例叫作共享权值,通过-c--cpu-shares设置。Docker 默认每个容器的权值为 1024。不设置或将其设置为 0,都将使用这个默认值。系统会根据每个容器的共享权值和所有容器共享权值和比例来给容器分配 CPU 时间。

假设有三个正在运行的容器,这三个容器中的任务都是 CPU 密集型的。第一个容器的 cpu 共享权值是 1024,其它两个容器的 cpu 共享权值是 512。第一个容器将得到 50% 的 CPU 时间,而其它两个容器就只能各得到 25% 的 CPU 时间了。如果再添加第四个 cpu 共享值为 1024 的容器,每个容器得到的 CPU 时间将重新计算。第一个容器的CPU 时间变为 33%,其它容器分得的 CPU 时间分别为 16.5%、16.5%、33%。

必须注意的是,这个比例只有在 CPU 密集型的任务执行时才有用。在四核的系统上,假设有四个单进程的容器,它们都能各自使用一个核的 100% CPU 时间,不管它们的 cpu 共享权值是多少。

在多核系统上,CPU 时间权值是在所有 CPU 核上计算的。即使某个容器的 CPU 时间限制少于 100%,它也能使用各个 CPU 核的 100% 时间。

例如,假设有一个不止三核的系统。用-c=512的选项启动容器{C0},并且该容器只有一个进程,用-c=1024的启动选项为启动容器C2,并且该容器有两个进程。CPU 权值的分布可能是这样的:

PID    container    CPU CPU share
100    {C0}     0   100% of CPU0
101    {C1}     1   100% of CPU1
102    {C1}     2   100% of CPU2

$ docker run -it --cpu-shares=100 ubuntu:14.04 /bin/bash
$ cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.shares

表示容器中的进程CPU份额值为100。

 
 

CPU 资源的绝对限制

Linux 通过 CFS(Completely Fair Scheduler,完全公平调度器)来调度各个进程对 CPU 的使用。CFS 默认的调度周期是 100ms。

关于 CFS 的更多信息,参考CFS documentation on bandwidth limiting

我们可以设置每个容器进程的调度周期,以及在这个周期内各个容器最多能使用多少 CPU 时间。使用--cpu-period即可设置调度周期,使用--cpu-quota即可设置在每个周期内容器能使用的 CPU 时间。两者一般配合使用。

例如:

$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000,表示该容器每 50ms 可以得到 50% 的 CPU 运行时间。

$ docker run -it --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash
$ cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.cfs_period_us
$ cat /sys/fs/cgroup/cpu/docker/<容器的完整长ID>/cpu.cfs_quota_us

将容器的 CPU 配额设置为 CFS 周期的两倍,CPU 使用时间怎么会比周期大呢?其实很好解释,给容器分配两个 vCPU 就可以了。该配置表示容器可以在每个周期内使用两个 vCPU 的 100% 时间。

CFS 周期的有效范围是 1ms~1s,对应的--cpu-period的数值范围是 1000~1000000。而容器的 CPU 配额必须不小于 1ms,即--cpu-quota的值必须 >= 1000。可以看出这两个选项的单位都是 us。

 

正确的理解“绝对”

注意前面我们用--cpu-quota设置容器在一个调度周期内能使用的 CPU 时间时实际上设置的是一个上限。并不是说容器一定会使用这么长的 CPU 时间。比如,我们先启动一个容器,将其绑定到 cpu 1 上执行。给其--cpu-quota--cpu-period都设置为 50000。

$ docker run --rm --name test01 --cpu-cpus 1 --cpu-quota=50000 --cpu-period=50000 deadloop:busybox-1.25.1-glibc

调度周期为 50000,容器在每个周期内最多能使用 50000 cpu 时间。

再用docker stats test01可以观察到该容器对 CPU 的使用率在100%左右。然后,我们再以同样的参数启动另一个容器。

$ docker run --rm --name test02 --cpu-cpus 1 --cpu-quota=50000 --cpu-period=50000 deadloop:busybox-1.25.1-glibc

再用docker stats test01 test02可以观察到这两个容器,每个容器对 cpu 的使用率在 50% 左右。说明容器并没有在每个周期内使用 50000 的 cpu 时间。

使用docker stop test02命令结束第二个容器,再加一个参数-c 2048启动它:

$ docker run --rm --name test02 --cpu-cpus 1 --cpu-quota=50000 --cpu-period=50000 -c 2048 deadloop:busybox-1.25.1-glibc

再用docker stats test01命令可以观察到第一个容器的 CPU 使用率在 33% 左右,第二个容器的 CPU 使用率在 66% 左右。因为第二个容器的共享值是 2048,第一个容器的默认共享值是 1024,所以第二个容器在每个周期内能使用的 CPU 时间是第一个容器的两倍。

 

磁盘IO配额控制

相对于CPU和内存的配额控制,docker对磁盘IO的控制相对不成熟,大多数都必须在有宿主机设备的情况下使用。主要包括以下参数:

  • –device-read-bps:限制此设备上的读速度(bytes per second),单位可以是kb、mb或者gb。
  • –device-read-iops:通过每秒读IO次数来限制指定设备的读速度。
  • –device-write-bps :限制此设备上的写速度(bytes per second),单位可以是kb、mb或者gb。
  • –device-write-iops:通过每秒写IO次数来限制指定设备的写速度。
  • –blkio-weight:容器默认磁盘IO的加权值,有效值范围为10-100。
  • –blkio-weight-device: 针对特定设备的IO加权控制。其格式为DEVICE_NAME:WEIGHT

存储配额控制的相关参数,可以参考Red Hat文档中blkio这一章,了解它们的详细作用。

磁盘IO配额控制示例

blkio-weight

要使–blkio-weight生效,需要保证IO的调度算法为CFQ。可以使用下面的方式查看:

root@ubuntu:~# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

使用下面的命令创建两个–blkio-weight值不同的容器:

docker run -ti –rm –blkio-weight 100 ubuntu:stress
docker run -ti –rm –blkio-weight 1000 ubuntu:stress

在容器中同时执行下面的dd命令,进行测试:

time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct

最终输出如下图所示:

quota08

在我的测试环境上没有达到理想的测试效果,通过docker官方的blkio-weight doesn’t take effect in docker Docker version 1.8.1 #16173,可以发现这个问题在一些环境上存在,但docker官方也没有给出解决办法。

device-write-bps

使用下面的命令创建容器,并执行命令验证写速度的限制。

docker run -tid –name disk1 –device-write-bps /dev/sda:1mb ubuntu:stress

通过dd来验证写速度,输出如下图示:

quota09

可以看到容器的写磁盘速度被成功地限制到了1MB/s。device-read-bps等其他磁盘IO限制参数可以使用类似的方式进行验证。

容器空间大小限制

在docker使用devicemapper作为存储驱动时,默认每个容器和镜像的最大大小为10G。如果需要调整,可以在daemon启动参数中,使用dm.basesize来指定,但需要注意的是,修改这个值,不仅仅需要重启docker daemon服务,还会导致宿主机上的所有本地镜像和容器都被清理掉。

使用aufs或者overlay等其他存储驱动时,没有这个限制。

 

6、修改Docker容器启动配置参数

有时候,我们创建容器时忘了添加参数 --restart=always ,当 Docker 重启时,容器未能自动启动,

现在要添加该参数怎么办呢,方法有二:

1、Docker 命令修改

docker container update --restart=always 容器名字

2、直接改配置文件

首先停止容器,不然无法修改配置文件

配置文件路径为:/var/lib/docker/containers/容器ID

在该目录下找到一个文件 hostconfig.json ,找到该文件中关键字 RestartPolicy

修改前配置:"RestartPolicy":{"Name":"no","MaximumRetryCount":0}

修改后配置:"RestartPolicy":{"Name":"always","MaximumRetryCount":0}

最后启动容器。

 

修改docker容器的挂载路径


 

  • 停止所有docker容器
    sudo docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)
  • 停止docker服务
    sudo service docker stop
  • 修改mysql路径
    cd ~
    sudo cp -r mysql/ /home/server/
  • 备份容器配置文件
    cd /var/lib/docker/containers/de9c6501cdd3
    cp hostconfig.json hostconfig.json.bak
    cp config.v2.json config.v2.json.bak
  • 修改hostconfig的冒号前的配置路径
    vi hostconfig.json
    
    "Binds": ["/home/server/mysql/conf/my.cnf:/etc/mysql/my.cnf", "/home/server/mysql/logs:/logs", "/home/server/mysql/data:/mysql_data"],
  • 修改config的Source的配置路径
    vi config.v2.json
    
           "MountPoints": {
    
                  "/etc/mysql/my.cnf": {
    
                         "Source": "/home/server/mysql/conf/my.cnf",
    
                         "Destination": "/etc/mysql/my.cnf",
    
                         "RW": true,
    
                         "Name": "",
    
                         "Driver": "",
    
                         "Relabel": "",
    
                         "Propagation": "rprivate",
    
                         "Named": false,
    
                         "ID": ""
    
                  },
    
                  "/logs": {
    
                         "Source": "/home/server/mysql/logs",
    
                         "Destination": "/logs",
    
                         "RW": true,
    
                         "Name": "",
    
                         "Driver": "",
    
                         "Relabel": "",
    
                         "Propagation": "rprivate",
    
                         "Named": false,
    
                         "ID": ""
    
                  },
    
                  "/mysql_data": {
    
                         "Source": "/home/server/mysql/data",
    
                         "Destination": "/mysql_data",
    
                         "RW": true,
    
                         "Name": "",
    
                         "Driver": "",
    
                         "Relabel": "",
    
                         "Propagation": "rprivate",
    
                         "Named": false,
    
                         "ID": ""
    
                  },
    
                  "/var/lib/mysql": {
    
                         "Source": "",
    
                         "Destination": "/var/lib/mysql",
    
                         "RW": true,
    
                         "Name": "85d91bff7012b57606af819480ce267449084e81ab386737c80ace9fe75f6621",
    
                         "Driver": "local",
    
                         "Relabel": "",
    
                         "Propagation": "",
    
                         "Named": false,
    
                         "ID": "897cd0152dd152166cb2715044ca4a3915a1b66280e0eb096eb74c2d737d7f77"
    
                  }
    
           },

     

     
  • 启动docker服务
     sudo service docker start
  • 启动所有docker容器
    sudo docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)

     

修改docker默认的存储位置


 

docker 的所有images及相关信息存储位置为:/var/lib/docker

  • 查看默认的docker存储路径
    docker info |grep 'Docker Root Dir'
    WARNING: No swap limit support
    Docker Root Dir: /var/lib/docker
  • 停止所有docker容器
    sudo docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)
  • 停止docker服务
    sudo service docker stop
    cd /var/lib
  • 打包docker目录
    sudo tar -czvf /usr/docker.tar.gz docker/
    cd /usr/
    sudo tar -xzvf docker.tar.gz
  • 修改docker默认的存储位置
    sudo vim /etc/docker/daemon.json
    
    {
        "graph": "/home/server/docker"
    }
  • 启动docker服务
    sudo service docker start
  • 启动所有docker容器
    sudo docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)
  • 查看修改后docker存储路径
    docker info |grep 'Docker Root Dir'
    WARNING: No swap limit support
    Docker Root Dir: /usr/docker
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: docker-compose是一个用于定义和运行多容器Docker应用程序的工具。而JDK1.8是Java Development Kit (Java开发工具包)的缩写,是一款用于开发和执行Java应用程序的软件包。 在使用docker-compose部署应用程序时,如果应用程序需要运行在JDK1.8环境下,可以通过编写docker-compose.yml文件来实现。首先,在文件中定义一个服务,指定使用合适的基础镜像,例如openjdk:8。然后,可以在该服务的配置中指定所需的环境变量和卷挂载等配置项。 具体代码示例如下: ``` version: '3' services: myapp: image: openjdk:8 ports: - 8080:8080 volumes: - ./app:/app environment: - JAVA_OPTS=-Xmx512m ``` 在上述示例中,定义了一个名为myapp的服务,使用openjdk:8镜像。将宿主机的8080端口映射到容器的8080端口,将宿主机上的./app目录挂载到容器的/app目录,并设置了JAVA_OPTS环境变量。 通过运行docker-compose up命令,即可启动应用程序并运行在JDK1.8环境中。可以通过访问宿主机的8080端口,来访问应用程序。这样,就成功将应用程序和JDK1.8集成到了Docker容器中,实现了应用程序的便携性和环境隔离性。 ### 回答2: jdk(Java Development Kit)是一款用于开发和运行Java应用程序的软件开发工具包。JDK 1.8是Java开发工具包的一个版本,它提供了许多功能和库,以支持Java应用程序的开发和执行。 Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用一个YAML文件来配置应用程序的服务,然后可以使用单个命令在多个Docker容器中运行整个应用程序。 在Docker Compose中使用JDK 1.8时,我们可以通过在Docker Compose配置文件中指定相应的镜像,来使用JDK 1.8作为Java应用程序的运行环境。例如,我们可以在配置文件中指定一个基于JDK 1.8的Java镜像,然后定义应用程序的服务,并在服务中指定镜像、传递需要的环境变量和卷等。 使用Docker Compose和JDK 1.8可以带来一些好处。首先,使用Docker可以将应用程序和其依赖项打包成一个独立的容器,使得应用程序在不同环境中的部署更加方便和可靠。其次,JDK 1.8提供了许多功能和性能优化,可以提高Java应用程序的执行效率和稳定性。 总结来说,使用Docker Compose和JDK 1.8可以将Java应用程序与其运行环境隔离开来,并提供了一种简单和可靠的方式来管理和运行多容器的应用程序。 ### 回答3: Docker-compose 是 Docker 官方提供的一个用于定义和运行多容器 Docker 应用程序的工具。而 JDK 1.8 是 Java Development Kit(Java开发工具包)的一个版本,它提供了 Java 编程语言的开发环境以及运行时的支持。 在使用 Docker-compose 来部署应用程序时,如果需要依赖 JDK 1.8 来运行应用程序,可以在 Docker-compose 文件中指定所需的镜像和相关设置。 首先,需要在 Docker-compose 文件的 services 部分定义应用程序服务的镜像。可以选择一个包含 JDK 1.8 的基础镜像,例如官方提供的 openjdk:8 镜像。在服务的定义中,可以指定 image 属性为 openjdk:8 或其他包含 JDK 1.8 的镜像。 接下来,可以在容器的启动命令或入口脚本中使用 JDK 1.8 所提供的 Java 运行命令来执行应用程序。例如,可以执行 java 命令来运行 Java 类,或执行 javac 命令来编译 Java 代码。 需要注意的是,JDK 1.8 可能会与部分应用程序存在兼容性问题,特别是在迁移旧的应用程序时。因此,在使用 Docker-compose 部署应用程序之前,需要确保应用程序与 JDK 1.8 的版本兼容,并进行适当的调试和测试。 总之,通过在 Docker-compose 文件中指定合适的镜像和相应的命令,可以在容器中运行使用 JDK 1.8 开发的应用程序。这种方式可以提供一个独立、可移植的环境,方便开发人员和运维人员快速部署和管理应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值