一、Docker容器配置进阶
1、容器的自动重启
Docker提供重启策略控制容器退出时或Docker重启时是否自动启动该容器。
容器默认不支持自动重启,要使用 --restart
选项指定重启策略。
[root@localhost ~]# docker run --help
--restart string Restart policy to apply when a container exits (default "no")
容器重启策略选项值:
选项值 | 功能 |
---|---|
no | 容器退出时不重启,默认设置 |
on-failure[:max-retries] | 容器以非0状态退出时重启,max-retries指定重启的次数 |
always | 不管退出状态始终重启,无限次 |
unless-stopped | 不管退出状态始终重启,(Docker守护进程启动时,容器处于运行状态才生效) |
# 案例1:运行一个始终重启的redis容器,容器退出时Docker重启它
[root@localhost ~]# docker run -d --name testrs --restart=always redis
cdf90c50d38f712020f2bd75e10e20c5ba6aa980747176f7bff4b45d4844ab8a
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cdf90c50d38f redis "docker-entrypoint.s…" 7 seconds ago Up 6 seconds 6379/tcp testrs
# 停止Docker后,容器立马重启
[root@localhost ~]# systemctl stop docker
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cdf90c50d38f redis "docker-entrypoint.s…" 2 minutes ago Up Less than a second 6379/tcp testrs
# 案例2:设置容器最大重启次数
# 设置非0状态最大重启十次
[root@localhost ~]# docker run -dti --restart=on-failure:10 redis bash
fe8f308023cfde7a738ba5a22098dbe8d5f35697db85094a2dc7778a7293f1d5
# 另一个终端执行
[root@localhost ~]# systemctl stop docker
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe8f308023cf redis "docker-entrypoint.s…" 20 seconds ago Up Less than a second 6379/tcp serene_pascal
# 案例3:已经运行或创建的容器,用 docker update 更改重启策略
[root@localhost ~]# docker update --restart=on-failure:3 fe8f308
fe8f308
作用:容器自动重启;重启策略能够确保关联的多个容器按照正确的顺序启动。
使用重启策略时的注意事项:
- 重启策略只在容器成功启动后才会生效。(容器运行后生效)
- 如果手动停止一个容器,那么它的重启策略会被忽略,直到Docker守护进程重启或容器手动重启。(手动停止,暂停重启策略)
- Docker Swarm服务的重启策略采用不同的配置方式。(集群采用不同的重启策略)
- 重启策略不同于dockerd命令的
--live-restore
选项,这个选项可使Docker升级中,即使网络和用户输入都终端,容器依然保持运行。 - Docker建议使用重启策略,并避免使用进程管理器启动容器:(1)同时使用两者会产生冲突;(2)进程管理器依赖于操作系统,Docker无法监控。
2、在Docker停止时保持容器继续运行
默认情况下,Docker守护进程终止时,正在运行的容器会关闭。
实时恢复(Live Restore):管理员配置守护进程,让容器在守护进程不可用时依然运行。
实时恢复的作用:减少因Docker守护进程崩溃、计划停机或升级导致的容器停机时间。
(1)启用实时恢复功能
第一种方式是在Docker守护进程配置文件中设置:
sighup(挂断)信号在控制终端或者控制进程死亡时向关联会话中的进程发出,默认进程对SIGHUP信号的处理时终止程序,所以我们在shell下建立的程序,在登录退出连接断开之后,会一并退出。
nohup,故名思议就是忽略SIGHUP信号,一般搭配& 一起使用,&表示将此程序提交为后台作业或者说后台进程组。
[root@localhost ~]# vi /etc/docker/daemon.json
{
"live-restore":true
}
# 配置生效方法一:
# 修改配置后重启守护进程生效
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl restart docker
# 配置生效方法二:
# 重新加载Docker守护进程,避免容器停止
[root@localhost ~]# systemctl reload docker
# 案例1:
# 1.启动两个容器
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1dd65fa55b80 top "/bin/sh -c 'exec to…" 5 weeks ago Up 38 seconds test_exec_entry
52a7de98ccc7 test_shell_entry "/bin/sh -c 'top -b'" 5 weeks ago Up 52 seconds test:
# 2.停止docker守护进程
[root@localhost ~]# systemctl stop docker
Warning: Stopping docker.service, but it can still be activated by:
docker.socket
[root@localhost ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Wed 2022-05-11 00:23:16 CST; 8s ago
Docs: https://docs.docker.com
Process: 1697 ExecReload=/bin/kill -s HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 1853 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock (code=exited, status=0/SUCCESS)
Main PID: 1853 (code=exited, status=0/SUCCESS)
# 3.查看容器是否依然运行
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1dd65fa55b80 top "/bin/sh -c 'exec to…" 5 weeks ago Up About a minute test_exec_entry
52a7de98ccc7 test_shell_entry "/bin/sh -c 'top -b'" 5 weeks ago Up 2 minutes test
# 案例2:apache容器测试实时恢复
# 1.修改守护进程配置文件,启动实时恢复
[root@localhost ~]# vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://nxwgbmaq.mirror.aliyuncs.com"],
"live-restore":true
}
# 2.重启守护进程
[root@localhost ~]# systemctl restart docker
# 3.运行一个apache容器
[root@localhost ~]# docker pull httpd
[root@localhost ~]# docker run --rm -d -p 8080:80 httpd
27c266f2fbc9bb5dd67e442a99b82db440d135506b9d912d624331baae675ca9
# 4.重新加载Docker守护进程
[root@localhost ~]# systemctl reload docker
# 5.查看当前容器
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
27c266f2fbc9 httpd "httpd-foreground" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, :::8080->80/tcp inspiring_robinson 《————容器依然运行
# 6.kill结束进程
[root@localhost ~]# ps -e | grep dockerd
5052 ? 00:00:01 dockerd 《————查看获取进程号
[root@localhost ~]# kill -SIGHUP 5052 《————向进程发送sighup信号
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
27c266f2fbc9 httpd "httpd-foreground" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp, :::8080->80/tcp inspiring_robinson 《————容器依然运行
# 7.访问apache验证服务是否正常
[root@localhost ~]# curl 127.0.0.1:8080
<html><body><h1>It works!</h1></body></html>
另一种恢复方式:在手动启动dockerd进程时指定--live-restore选项。
不建议使用这种方式,因为不会设置systemd或其他进程管理器的环境,会导致意外发生。
(2)升级期间的实时恢复
实时恢复功能支持Docker守护进程在升级期间保持容器的运行。
存在的问题:
- 只支持Docker 补丁版本升级,不支持主要版本和次要版本的升级。
- 升级过程中跳过版本,守护进程可能无法恢复其与容器的连接。
(3)重启时的实时恢复
限定条件:只有Docker守护进程选项未发生变化,实时恢复才能恢复容器。
(4)实时恢复功能对运行容器的影响
守护进程停止,正在运行的容器可能会填满守护进程通常读取的FIFO日志,阻止容器记录更多日志数据。
缓冲区填满,必须重新启动Docker守护进程来刷新。
可以更改/proc/sys/fs/pipe-max=size来修改内核的缓冲区大小。
[root@localhost ~]# cat /proc/sys/fs/pipe-max-size
1048576
3、一个容器中运行多个服务
注意:一个容器可以有多个进程,但为了高效利用Docker,不要让一个容器负责整个应用程序的多个方面,而要通过用户定义网络和共享卷连接多个容器来实现应用程序的多个方面。
容器的主进程负责管理它启动的所有进程。
解决子进程回收:--init
选项可以将一个精简的初始化进程作为主进程插入容器,并在容器退出时回收所有的进程。
解决多进程启停最好方式:设置一个上层的进程统一处理这些进程的生命周期(sysinit\upstart\systemd)
在一个容器运行多个服务的方式:
- 将所有命令放入包装器脚本中,并提供测试和调试信息,使用CMD指令运行包装器脚本。
- 如果有一个主进程需要首先启动并保持运行,但是临时需要运行一些其他进程(可能与主进程交互),可以使用bash脚本的作业控制实现。
- 在容器中使用supervisord等进程管理器。
Supervisor 是 Linux/UNIX系统下的一个进程管理工具,守护进程名为supervisord,可以方便地监听、启动、停止、重启一个或多个进程。
该工具管理进程时,如遇到进程被意外杀死,守护进程监听到后,会自动重启。
# 案例:将supervisord和配置和要管理的应用程序(nginx和tomcat)打包到一个镜像中,一个容器运行多个服务
# 1.创建dockerfile目录
[root@hecs-hqs-01 ~]# mkdir web-supervisord
[root@hecs-hqs-01 web-supervisord]# cd web-supervisord/
# 2.编写Dockerfile
[root@hecs-hqs-01 web-supervisord]# vi Dockerfile 《————dockerfile目录
FROM centos:7
MAINTAINER hqs@163.com
COPY CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
COPY nginx_install.sh /tmp/nginx_install.sh
RUN sh /tmp/nginx_install.sh; \rm -rf /usr/local/src/*
RUN sed -i -e '/worker_processes/a daemon off;' /usr/local/nginx/conf/nginx.conf;
COPY jdk-8u162-linux-x64.tar.gz /usr/local/src/jdk-8u162-linux-x64.tar.gz
COPY tomcat_install.sh /tmp/tomcat_install.sh
RUN sh /tmp/tomcat_install.sh; \rm -rf /usr/local/src/*
COPY supervisor_install.sh /tmp/supervisor_install.sh
COPY supervisord.conf /etc/supervisord.conf
COPY start_tomcat.sh /usr/local/tomcat/bin/mystart.sh
RUN sh /tmp/supervisor_install.sh; \rm -rf /tmp/*.sh
# 3.默认源下载慢且总有问题,更换为163的yum源
[root@hecs-hqs-01 web-supervisord]# vi CentOS-Base.repo
[base]
name=CentOS-$releasever - Base
baseurl=http://mirrors.163.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#released updates
[updates]
name=CentOS-$releasever - Updates
baseurl=http://mirrors.163.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
baseurl=http://mirrors.163.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
baseurl=http://mirrors.163.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
# 4.准备nginx安装脚本
[root@hecs-hqs-01 web-supervisord]# vi nginx_install.sh
yum install -y wget tar gcc gcc-c++ make pcre pcre-devel zlib zlib-devel openssl openssl-devel
cd /usr/local/src
wget 'http://nginx.org/download/nginx-1.12.2.tar.gz'
tar -zxvf nginx-1.12.2.tar.gz
cd nginx-1.12.2
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-stream --with-stream_ssl_module
make
make install
exit 0
# 5.准备tomcat安装脚本
[root@hecs-hqs-01 web-supervisord]# vi tomcat_install.sh
yum install -y wget tar
cd /usr/local/src/
tar -zxvf jdk-8u162-linux-x64.tar.gz
mv jdk1.8.0_162 /usr/local/
#/usr/local/jdk1.8.0_162/bin/java -version
#配置java环境变量
echo 'JAVA_HOME=/usr/local/jdk1.8.0_162/' >>/etc/profile
echo 'PATH=$PATH:$JAVA_HOME/bin' >>/etc/profile
echo 'CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:$CLASSPATH' >>/etc/profile
source /etc/profile
wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.38/bin/apache-tomcat-8.5.38.tar.gz
tar -zxvf apache-tomcat-8.5.38.tar.gz
mv apache-tomcat-8.5.38 /usr/local/tomcat
# 6.下载jdk
# 官网下载地址:https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html 需要注册才能下载
[root@hecs-hqs-01 web-supervisord]# ls
# 7.准备supervisor安装脚本
[root@hecs-hqs-01 web-supervisord]# vi supervisor_install.sh
yum -y install epel-release
yum -y install python2-pip
pip install supervisor
# 8.准备supervisor配置文件
[root@hecs-hqs-01 web-supervisord]# vi supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock ; the path to the socket file
[supervisord]
logfile=/tmp/supervisord.log ; # 日志
logfile_maxbytes=50MB ; # 最大50M日志
logfile_backups=10 ; # 轮循日志备份10个
loglevel=info ; # 日志等级记录info的
pidfile=/tmp/supervisord.pid ;pid
nodaemon=true ; # 在前台启动
minfds=102400 ; # 文件描述符限制
minprocs=2000 ; # 进程数
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
[program:nginx]
command=/usr/local/nginx/sbin/nginx ; # 前台启动nginx
autostart=true ; # 随着supervisor自动启动
startsecs=10 ; # 启动10s后算正常启动
autorestart=true ; # 程序退出后自动重启
startretries=3 ; # 启动失败自动重试次数
stdout_logfile_maxbytes=20MB ;stdout # 日志文件大小最大20Mb
stdout_logfile=/usr/local/nginx/logs/out.log
[program:tomcat]
command=sh /usr/local/tomcat/bin/mystart.sh ; # 前台启动tomcat
autostart=true ; # 随着supervisor自动启动
startsecs=10 ; # 启动10s后算正常启动
autorestart=true ; #程序退出后自动重启
startretries=3 ; # 启动失败自动重试次数
stdout_logfile_maxbytes=20MB ;stdout 日志文件大小最大20Mb
stdout_logfile=/usr/local/tomcat/logs/catalina.out
# 9.tomcat启动脚本准备
# 由于supervisor无法使用source,需要编写个脚本来启动
[root@hecs-hqs-01 web-supervisord]# vi start_tomcat.sh
source /etc/profile
/usr/local/tomcat/bin/catalina.sh run
# 10.构建镜像
[root@hecs-hqs-01 web-supervisord]# docker build -t web-supervisor .
[root@localhost web-supervisord]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
web-supervisor latest 5198c5737571 7 minutes ago 1.44GB
# 11.启动容器测试
[root@localhost web-supervisord]# docker run -d web-supervisor /bin/bash -c 'supervisord -c /etc/supervisord.conf'
a326e2589794c4b2a19c23d177f2c418d0583c87605342fd7445694dc40e2219
# 6.基于镜像启动容器和测试web服务
docker exec -it 76782ab /bin/bash
ifconfig
curl 127.0.0.1
curl 127.0.0.1:8080
4、容器健康检查机制
进程级的健康检查:最简单的,检验进程是否运行。重启策略可以根据检查情况重启已停止的容器。这个检查不足:无法发现应用程序问题。
Docker提供了健康检查机制,可以通过Dockerfile文件在镜像中注入,也可以在启动容器时通过相应选项实现。
(1)在Dockerfile中使用HEALTHCHECK指令
可以在Dockerfile中使用HEALTHCHECK指令声明健康检测配置,用于判断容器主进程的服务状态是否正常,反映容器的实际健康状态。
Dockerfile构建镜像时,加入了HEALTHCHECK指令,基于这样镜像启动的容器,就具备了健康状态检查能力,能自动进行健康检查。
Dockerfile中,只能出现一次HEALTHCHECK指令,出现多次,仅最后一次生效。
一旦有一次健康检查成功,Docker就会确认容器为健康状态。
HEALTHCHECK指令格式:
- 设置检查容器健康状况的命令
HEALTHCHECK [选项] CMD <命令>
# 选项:
--interval:设置容器运行后开始健康检查的时间间隔,默认30s。
--timeout:设置允许健康检查命令允许的最长时间,默认30s。超时即失败。
--start-period:设置容器启动的初始化时间。(启动过程中的健康检查失败不报错)
--retries:设置允许连续重试的次数,默认3次。(连续检查失败后视为不健康)
# CMD指令后的命令:指定执行健康检查的具体命令。可以使用shell格式或exec格式。
# 返回值:CMD指令后面的“命令” 执行完毕返回值表示容器的运行状况。
0:成功。容器是健康且可用的。
1:失败。容器不健康,不能正常工作。
2:保留值。暂时不要使用。
# 案例:每5分钟健康检查一次,访问web服务器主页,每次检查3秒以内
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1 # || 表示上一条命令执行失败后,才执行下一条命令,退出返回值为1,告诉docker健康检查失败
- 表示禁止从基础镜像继承HEALTHCHECK指令设置
HEALTHCHECK NONE
(2)启动容器时通过相应选项实现健康检查
可以在执行 docker run
或 docker create
命令启动创建容器时指定容器的健康检查策略。
[root@localhost ~]# docker run --help
--health-cmd string Command to run to check health # 指定和运行健康检查
--health-interval duration Time between running the check (ms|s|m|h) (default 0s) # 设置容器运行后开始健康检查的时间间隔(单位:ms/s/m/h),默认是0s
--health-retries int Consecutive failures needed to report unhealthy # 设置连续失败需要报告的次数
--health-start-period duration Start period for the container to initialize before starting # 设置容器启动的初始化时间(单位:ms/s/m/h),默认是0s
health-retries countdown (ms|s|m|h) (default 0s)
--health-timeout duration Maximum time to allow one check to run (ms|s|m|h) (default 0s) # 设置允许健康检查命令允许的最长时间(单位:ms/s/m/h),默认是0s
--no-healthcheck Disable any container-specified HEALTHCHECK # 禁用容器健康检查指令
(3)案例:测试容器的健康检查功能
容器启动,开始执行健康检查命令,并周期执行,如果返回0,容器处于健康状态。如果返回非0值,容器不健康。
下面案例使用busybox镜像来测试健康检查功能。
# 1.下载busybox镜像
[root@localhost ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
5cc84ad355aa: Pull complete
Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest
# 2.创建容器设置健康检查,检查间隔20s,失败重复1次;并立即查看容器的健康检查状态
[root@localhost ~]# docker run --rm --name test-health -d \
> --health-cmd 'stat /etc/passwd || exit 1' \
> --health-interval 20s --health-retries 1 \
> busybox sleep 1d; docker inspect --format '{{.State.Health.Status}}' test-health
d8fa5965abc02ec8d01722f98257ba2b8f76d50af05890dc8c6e1a18573ee6a3
starting 《————容器启动后的健康状态为Starting
# 3.延迟20s时间,再次查看容器的健康检查状态
[root@localhost ~]# sleep 20s;docker inspect --format '{{.State.Health.Status}}' test-health
healthy 《————容器处于健康状态
# 4.删除容器的/etc/passwd文件,模拟健康问题
[root@localhost ~]# docker exec test-health rm /etc/passwd
# 5.延迟20s时间,再次查看容器的健康检查状态
[root@localhost ~]# sleep 20s;docker inspect --format '{{.State.Health.Status}}' test-health
unhealthy 《————容器处于不健康状态
# 进一步查看容器的详情
[root@localhost ~]# docker inspect --format '{{json .State.Health}}' test-health
{"Status":"unhealthy","FailingStreak":6,"Log":[{"Start":"2022-05-12T04:06:13.855633253+08:00","End":"2022-05-12T04:06:13.899152317+08:00","ExitCode":1,"Output":"stat: can't stat '/etc/passwd': No such file or directory\n"},{"Start":"2022-05-12T04:06:33.910019299+08:00","End":"2022-05-12T04:06:33.953470925+08:00","ExitCode":1,"Output":"stat: can't stat '/etc/passwd': No such file or directory\n"},{"Start":"2022-05-12T04:06:53.956520997+08:00","End":"2022-05-12T04:06:53.998897439+08:00","ExitCode":1,"Output":"stat: can't stat '/etc/passwd': No such file or directory\n"},{"Start":"2022-05-12T04:07:14.002929368+08:00","End":"2022-05-12T04:07:14.045233889+08:00","ExitCode":1,"Output":"stat: can't stat '/etc/passwd': No such file or directory\n"},{"Start":"2022-05-12T04:07:34.050748795+08:00","End":"2022-05-12T04:07:34.090015044+08:00","ExitCode":1,"Output":"stat: can't stat '/etc/passwd': No such file or directory\n"}]}
# 上述信息中报错:stat: can't stat '/etc/passwd': No such file or directory\n,符合前面模拟的健康问题
# 6.查看容器的当前状态
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d8fa5965abc0 busybox "sleep 1d" 5 minutes ago Up 5 minutes (unhealthy) test-health
5、运行时选项覆盖Dockerfile指令
Dockerfile中的FROM
、MAINTAINER
、RUN
和ADD
这四个指令在运行时是不能被覆盖的,其他的指令在执行docker run(或docker create)命令时都会被相应地覆盖。
从镜像运行一个容器时,可以指定一个新的命令来覆盖Dockerfile的CMD指令。
如果镜像的Dockerfile还声明了ENTRYPOINT指令,则Dockerfile的CMD指令或容器运行时指定的命令均作为参数追加到ENTRYPOINT指令中。
(1)覆盖CMD指令
从镜像运行一个容器的时候,可以指定一个新命令来覆盖Dockerfile中的CMD指令。
[root@localhost ~]# docker history ubuntu
IMAGE CREATED CREATED BY SIZE COMMENT
ff0fea8310f3 7 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B 《——————默认命令
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:1d3b09cf9e041d608… 72.8MB
# 语法
[root@localhost ~]# docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 不覆盖的案例:使用CMD的默认命令
[root@localhost ~]# docker run -tid ubuntu
d6adcb9ca10de89dcb1a1f8ab49b33a715b502e4aa4f1ec240774a7f4d2aa9f5
# 覆盖案例
[root@localhost ~]# docker run -tid ubuntu top 《————top替换默认的bash
0485fee218e64e858ca5b2214b9349b523a3bd41d1ad86c08877240635fa0d32
如果镜像的Dockerfile还声明了ENTRYPOINT指令,则Dockerfile的CMD指令或容器运行时指定的命令均作为参数追加到ENTRYPOINT指令中。
(2)覆盖ENTRYPOINT指令
使用 --entrypoint
选项可以覆盖定义镜像的 Dockerfile 中的 ENTRYPOINT 指令设置。
镜像的 ENTRYPOINT
指令定义容器启动时要执行的命令,在启动容器时不容易被覆盖。
注意:运行时使用--entrypoint
选项将清除镜像胡任何默认命令。
# 语法
[root@localhost ~]# docker run --help
--entrypoint string Overwrite the default ENTRYPOINT of the image
# 案例
[root@localhost ~]# docker pull redis
[root@localhost ~]# docker history redis
IMAGE CREATED CREATED BY SIZE COMMENT
7614ae9453d1 4 months ago /bin/sh -c #(nop) CMD ["redis-server"] 0B
<missing> 4 months ago /bin/sh -c #(nop) EXPOSE 6379 0B
<missing> 4 months ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 4 months ago /bin/sh -c #(nop) COPY file:df205a0ef6e6df89… 374B
<missing> 4 months ago /bin/sh -c #(nop) WORKDIR /data 0B
<missing> 4 months ago /bin/sh -c #(nop) VOLUME [/data] 0B
<missing> 4 months ago /bin/sh -c mkdir /data && chown redis:redis … 0B
...省略
# 默认自动运行redis-server的容器中,再运行一个shell
[root@localhost ~]# docker run -ti --entrypoint /bin/bash redis
root@484cb9c42858:/usr/local/bin# ls
docker-entrypoint.sh redis-benchmark redis-check-rdb redis-sentinel
gosu redis-check-aof redis-cli redis-server
root@bd7640ba7206:/data# exit
exit
# 传递更多参数给ENTRYPOINT
# 默认传递--help给redis-server
[root@localhost ~]# docker run -ti redis --help
Usage: ./redis-server [/path/to/redis.conf] [options] [-]
./redis-server - (read config from stdin)
./redis-server -v or --version
./redis-server -h or --help
...省略
# 传递--help给redis-cli
[root@localhost ~]# docker run -ti --entrypoint redis-cli redis --help
redis-cli 6.2.6
Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]
-h <hostname> Server hostname (default: 127.0.0.1).
-p <port> Server port (default: 6379).
-s <socket> Server socket (overrides hostname and port).
...省略
# 传递空字符串重置容器的入口命令
[root@localhost ~]# docker run -ti --entrypoint="" redis bash
root@5cb647f12d63:/data# exit
exit
(3)覆盖EXPOSE传入端口指令
EXPOSE指令定义对外提供服务的初始传入端口,这些端口可用于容器中的进程。
[root@localhost ~]# docker run --help
--expose list Expose a port or a # 对外暴露容器的一个端口或一个端口范围
range of ports
--link list Add link to another # 添加到其他容器的连接
container
-p, --publish list Publish a container s # 将容器的一个端口或端口范围发布到主机
port(s) to the host
-P, --publish-all Publish all exposed # 将所有的端口发布到主机随机端口
ports to random ports
(4)覆盖ENV指令
linux中的容器,Docker自动设置了如下环境变量(默认值):
- HOME(用户主目录):根据USER值设置
- HOSTNAME(主机名):默认为容器名
- PATH(执行文件的默认路径):常用目录
- TERM(终端):若容器分配了伪TTY,则为xterm。
可以使用若干 -e
选项设置任何环境变量,并可以覆盖上述默认环境变量或Dockerfile中ENV指令定义的环境变量。
# 语法
[root@localhost ~]# docker run --help
-e, --env list Set environment variables # 设置环境变量
--env-file list Read in a file of # 读取文件中环境变量
environment variables
# 案例
[root@localhost ~]# export today=Sunday
[root@localhost ~]# echo $today
Sunday
[root@localhost ~]# docker run -t -e "deep=purple" \
> -e today \
> --rm alpine env
# 输出如下信息
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=40951973766d
TERM=xterm
deep=purple
today=Sunday
HOME=/root
(5)HEALTHCHECK和WORDDIR指令
覆盖HEALTHCHECK的相关选项:
[root@localhost ~]# docker run --help
--health-cmd string Command to run to check health # 指定和运行健康检查
--health-interval duration Time between running the check (ms|s|m|h) (default 0s) # 设置容器运行后,健康检查的时间间隔(单位:ms/s/m/h),默认是0s
--health-retries int Consecutive failures needed to report unhealthy # 设置连续失败需要报告的次数
--health-start-period duration Start period for the container to initialize before starting # 设置容器启动的初始化时间(单位:ms/s/m/h),默认是0s
health-retries countdown (ms|s|m|h) (default 0s)
--health-timeout duration Maximum time to allow one check to run (ms|s|m|h) (default 0s) # 设置允许健康检查命令允许的最长时间(单位:ms/s/m/h),默认是0s
--no-healthcheck Disable any container-specified HEALTHCHECK # 禁用容器健康检查指令
容器中运行二进制文件的默认工作目录是根目录(/),Dockerfile中可以使用WORDDIR指令自定义工作目录。还可以使用 -w
选项覆盖WORKDIR指令设置。
[root@localhost ~]# docker run --help
-w, --workdir string Working directory # 修改容器的工作目录
inside the container
(6)USER
容器默认用户是root(UID=0),Dockerfile的USER指令可以指定容器运行第一个进程时的默认用户。
启动容器的时候,可以使用 -u(--user)
选项指定新的默认用户覆盖镜像的USER指令。
# 语法
[root@localhost ~]# docker run --help
-u, --user string Username or UID # 指定容器运行第一个进程的默认用户,可以使用用户名、UID、组名、GID参数
(format:
<name|uid>[:<group|gid>])
# 案例
[root@localhost home]# docker run -t -u daemon ubuntu env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=88f322b48026
TERM=xterm
HOME=/usr/sbin
[root@localhost ~]# docker run -ti -u ftp alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=a5f4455a2407
TERM=xterm
HOME=/var/lib/ftp
(7)VOLUME指令
Dockerfile中可以使用 VOLUME 指令定义一个或多个与镜像关联的卷。
容器启动时可以使用 -v
、--mount
、--volume-from
选项来指定挂载卷。详见:Docker存储管理
二、限制容器资源使用
1、容器内存资源限制
容器可使用的内容包括:物理内存、交换空间(SWAP)。
Docker 默认没有设置内存限制的,容器进程可以根据需要尽可能多地使用内存和交换空间。
硬限制:仅允许容器使用不超过给定值的用户内存或系统内存。
软限制:允许容器按需使用内存,但不能影响到内存使用或与占用主机内存。
(1)用户内存限制
同时设置内存和交换空间时,交换空间限制必须大于物理内存限制。
[root@localhost ~]# docker run --help
-m, --memory bytes Memory limit # 容器可用内存限制,最低是4MB
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable # 容器可以使用的交换空间,-1为无限
unlimited swap
# 案例1:对容器内存使用不限制
[root@localhost ~]# docker run -ti ubuntu /bin/bash
# 案例2:设置内存限制并取消交换空间的限制
[root@localhost ~]# docker run -ti -m 300M --memory-swap -1 ubuntu /bin/bash
# 案例3:只设置内存限制
[root@localhost ~]# docker run -ti -m 300M ubuntu /bin/bash
[root@localhost ~]# docker inspect --format='{{.HostConfig.Memory}}' a84ca902dbab
314572800 《————314572800/1024/1024=300
# 案例4:同时设置内存和交换空间
[root@localhost ~]# docker run -ti -m 300M --memory-swap 1G ubuntu /bin/bash
(2)内核内存限制
内核内存不能使用交换空间,消耗过多的内核内存会导致系统服务被阻塞。
内核内存不独立于用户内存,一般在用户内存限制的基础上限制内核内存。
限制内核内存的效果:当内核内存太多时,系统会阻止新进程创建。
[root@localhost ~]# docker run --help
--kernel-memory bytes Kernel memory limit # 容器内核内存限制
# 案例1:同时设置用户内存和内核内存
[root@localhost ~]# docker run -ti -m 200M --kernel-memory 50M ubuntu /bin/bash
# 案例2:只设置内核内存,内存不限制
[root@localhost ~]# docker run -ti --kernel-memory 50M ubuntu /bin/bash
(3)设置内存预留实现软限制
作为一个软限制功能,内存预留并不能保证不会超过限制。主要目的是确保当内存争用严重时,内存就按预留设置进行分配。
内存软限制设置效能:确保容器不会长时间消耗过多内存,每次内存回收将容器内存消耗缩减到软限制之下。
软限制设置规则:
- 内存预留值应当始终低于硬限制,否则硬限制会优先触发。
- 将内存预留值设置为0表示不作限制。
[root@localhost ~]# docker run --help
--memory-reservation bytes Memory soft limit # 设置内存预留。0表示无限制
# 示例1:限制内存为500MB,内存预留值(软限制)为200MB
[root@localhost ~]# docker run -ti -m 500M --memory-reservation 200M ubuntu /bin/bash
# 示例2:设置内存软限制为1GB,没有设置内存硬限制
[root@localhost ~]# docker run -ti --memory-reservation 1G ubuntu /bin/bash
2、容器所用CPU资源限制
默认设置是所有容器都可以平等地使用主机CPU资源而不受限制。
(1)CPU份额限制
默认情况,所有容器得到相同比例的CPU周期。
通过设置CPU份额权重,分配容器可以使用的CPU周期。
[root@localhost ~]# docker run --help
-c, --cpu-shares int CPU shares (relative weight) # 设置CPU份额权重,默认值1024
# 案例
[root@localhost ~]# docker run -tid -c 1024 ubuntu /bin/bash
17ea3842fe624a9719817255dba50ba809f3f30c0ffbd1b50e1fd42172e032f0
[root@localhost ~]# docker run -tid -c 512 ubuntu /bin/bash
5c51e13530879ab7ed56bc42c1f71de0b6a2bff9cadbac6fb1364fa4c11c529a
(2)CPU周期限制
默认的CFS(完全公平调度器)周期为100ms(100000us)。
设置CPU周期限制容器CPU资源使用。
[root@localhost ~]# docker run --help
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period # 限制容器CPU CFS周期
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota # 限制容器CPU CFS配额
--cpus decimal Number of CPUs # 指定容器可用CPU资源,浮点数,默认0.000(不受限)
# 案例:
[root@localhost ~]# docker run -ti --cpu-period=50000 --cpu-quota=25000 ubuntu /bin/bash
# 案例2:
[root@localhost web-supervisord]# docker run -ti --cpus=0.5 ubuntu /bin/bash
(3)CPU放置限制
限制容器进程运行在什么CPU上。
[root@localhost web-supervisord]# docker run --help
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
# 案例:
# 容器的进程允许运行在cpu1\cpu2\cpu3
[root@localhost ~]# docker run -ti --cpuset-cpus='1-3' ubuntu /bin/bash
# 容器的进程允许运行在cpu0
[root@localhost ~]# docker run -ti --cpuset-cpus='0' ubuntu /bin/bash
# 容器的进程允许运行在cpu0\cpu4
[root@localhost ~]# docker run -ti --cpuset-cpus='0,4' ubuntu /bin/bash
(4)CPU配额限制
--cpu-quota
限制容器进程的CPU配额。
默认值0
表示容器占用一个CPU 100%的CPU资源。
设为 50000
表示容器至多使用CPU50%的资源。
3、容器所用块I/O带宽的限制
块I/O带宽(Block I/O Bandwith,Blkio):磁盘读写带宽。
Docker 可设置权重,限制容器读写磁盘的带宽。
(1)设置块I/O权重
使用--blkio-weight
选项设置一个容器相对于所有其他正在运行的容器的块I/O带宽权重。
可设置的块I/O带宽权重范围是10-1000。
默认所有的容器的权重值为500。0表示被禁用。
[root@localhost ~]# docker run --help
--blkio-weight uint16 Block IO (relative
weight), between 10
and 1000, or 0 to
disable (default 0)
案例:
[root@localhost ~]# docker run -tid --blkio-weight 300 ubuntu /bin/bash
[root@localhost ~]# docker run -tid --blkio-weight 0 ubuntu /bin/bash
(2)限制设备读写效率
Docker 按照两种指标限制容器的设备读写速率:每秒字节数、每秒I/O次数。
1)每秒字节数
使用--device-read-bps
选项限制指定设备的读取速率,即每秒读取的字节数。
使用--device-write-bps
选项限制指定设备的写入速率
单位可以是kb,mb或gb中的一个。
[root@localhost ~]# docker run --help
--device-read-bps list Limit read rate (bytes
per second) from a
device (default [])
--device-write-bps list Limit write rate
(bytes per second) to
a device (default [])
# 案例:限制/dev/sda的读取速率为1kb/s
[root@localhost ~]# docker run -ti --device-read-bps /dev/sda:1kb ubuntu # /dev/sda1和/dev/sda2会报错,只能控制磁盘IO不能控制分区IO
root@d2003fdad6bc:/#
2)每秒I/O次数
限制指定设备的读取和写入速率,用每秒I/O次数表示。
[root@localhost ~]# docker run --help
--device-read-iops list Limit read rate (IO
per second) from a
device (default [])
--device-write-iops list Limit write rate (IO
per second) to a
device (default [])
# 案例:限制/dev/sda设备的读取速率为每秒1000次
[root@localhost ~]# docker run -ti --device-read-iops /dev/sda:1000 ubuntu
root@8900e9904535:/#
4、资源限制的实现机制
对容器使用的内存、CPU和块I/O带宽资源的限制具体是由控制组的相应子系统来实现的。
- memory子系统设置控制组中的任务所使用的内存限制。
- cpu子系统通过调度程序提供对CPU的控制组任务的访问。
- blkio子系统为块设备(如磁盘、固态硬盘、USB等)设置输入和输出限制。
注意:在docker run命令中使用--cpu-shares、--memory、--device-read-bps等选项实际上就是在配置控制组,相关的配置文件保存在/sys/fs/cgroup
目录中。
# 1.创建容器内存限额300M,CPU权重512
[root@localhost docker]# docker run -tid -p 8080:80 -m 300M --cpu-shares=512 httpd
3cdbdafe0398d40daa8bf334413dc748a3a707cfac96c8ff0ee22a87e5d85e60
# 2.查看/sys/fs/cgroup目录中容器CPU的配置信息
[root@localhost ~]# cd /sys/fs/cgroup/cpu/docker
# 里面有容器ID命名的目录,包含容器CPU相关配置
[root@localhost 3cdbdafe0398d40daa8bf334413dc748a3a707cfac96c8ff0ee22a87e5d85e60]# cat cpu.shares
512
# 3.查看容器内存限制信息
[root@localhost ~]# cd /sys/fs/cgroup/memory/docker/3cdbdafe0398d40daa8bf334413dc748a3a707cfac96c8ff0ee22a87e5d85e60/
[root@localhost 3cdbdafe0398d40daa8bf334413dc748a3a707cfac96c8ff0ee22a87e5d85e60]# cat memory.limit_in_bytes
314572800 《——————换算为300M
5、动态更改容器的资源限制
docker update
命令可以动态地更新容器配置,防止容器在主机上使用太多的资源。
除了kernel-memory
选项,其他选项都是立即生效。
kernel-memory
选项只能应用于停止的容器,在下一次重启时生效。
[root@localhost ~]# docker update --help
Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
Update configuration of one or more containers
Options:
--blkio-weight uint16 Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota
--cpu-rt-period int Limit the CPU real-time period in microseconds
--cpu-rt-runtime int Limit the CPU real-time runtime in microseconds
-c, --cpu-shares int CPU shares (relative weight)
--cpus decimal Number of CPUs
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
--kernel-memory bytes Kernel memory limit
-m, --memory bytes Memory limit
--memory-reservation bytes Memory soft limit
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--pids-limit int Tune container pids limit (set -1 for unlimited)
--restart string Restart policy to apply when a container exits
# 案例:
[root@localhost docker]# docker run -tid -p 8080:80 -m 300M --cpu-shares=512 httpd
[root@localhost ~]# docker update -m 500M --cpu-shares=400 --memory-swap 600M 3cdbdafe0398
3cdbdafe0398