Docker容器回顾之运维篇

一、背景

接上一篇《Docker复习之部署篇》,本文继续对Docker之日常维护常用操作做回顾总结。

在这里插入图片描述
相关资源: 官方文档菜鸟教程Docker 基础教程DockerfileDocker Engine for Linux安装脚本Docker博客

1.1 Docker之namespace回顾

Docker依赖于Linux内核技术 chroot 、namespace 和 cgroup。Docker通过namespace实现了资源隔离,通过cgroups实现了资源限制,通过写时复制机制(copy-on-write)实现了高效的文件操作。首先,我们回顾下 namespace ;

我们知道,Docker本质上是宿主机上的进程(容器进程),而容器的资源隔离也就是指进程资源的隔离。而实现这种资源隔离的核心技术就是 Linux namespace。这种隔离是一种逻辑层面的,更像是抽象出多个轻量级的内核(容器进程),这些进程可以充分利用宿主机的资源,宿主机拥有的所有资源,这些容器进程都可以享有,但容器进程之间是隔离的,对应的,不同容器进程之间使用资源也是隔离的,这样,即使彼此之间进行相同的操作,也不会互相干扰,安全性得到保障。

Linux namespace 实现了 6 项资源隔离,基本上涵盖了一个小型操作系统的运行要素,包括主机名、用户权限、文件系统、网络、进程号、进程间通信,这6 项资源隔离分别对应 6 种系统调用,通过传入上表中的参数,调用 clone() 函数来完成。如下表:

在这里插入图片描述
查看当前进程下有哪些 namespace 隔离,可以查看文件 /proc/[pid]/ns ,/proc/[pid]/ns里设置这些符号链接的另一个作用是,一旦这些链接文件被打开,那么就算该namespace下的所有进程都已经结束,这个namespace也会一直存在,后续进程也可以再加进来。把/proc/[pid]/ns目录文件使用–bind方式挂载可以直到同样的作用,比如mount --bind /proc/2454/ns/uts uts;在Docker中,我们可以在进程都结束的情况下,通过这种形式把namespace保留下来,为以后有进程加入做准备。在Docker中,使用docker exec命令可以使用setns()函数加入已经存在的命名空间,在已经运行着的容器中执行一个新命令。如下所示:

在这里插入图片描述
可以看到,每一项 namespace 都附带一个编号,这是唯一标识 namespace 的,如果两个进程指向的 namespace 编号相同,则表示它们同在该 namespace 下。另外,在4.6 版本的内核中,namespace会多一个cgroup;其中:

1>UTS namespace 提供了主机名和域名的隔离,这样每个容器就拥有独立的主机名和域名了,在网络上就可以被视为一个独立的节点,在容器中对 hostname 的命名不会对宿主机造成任何影响。

2>IPC namespace 实现了进程间通信的隔离,包括常见的几种进程间通信机制,如信号量,消息队列和共享内存。我们知道,要完成 IPC,需要申请一个全局唯一的标识符,即 IPC 标识符,所以 IPC 资源隔离主要完成的就是隔离 IPC 标识符。(ipcs -q可查看消息队列情况;ipcmk -Q可创建一个消息队列)

3>PID namespace 完成的是进程号的隔离,它对进程PID重新标号; 为了做到进程空间的隔离,会首先在容器内创建出PID为1的进程(root namespace),其后它会再创建的新PID namespace被称为child namespace(树的子节点),而原来的PID namespace就是新创建的PID namespace的parent namespace(树的父节点)。这样,不同的PID namespace会形成一个层级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。但子节点却不能看到父节点PID namespace中的任何内容。另外,会发现容器内shell里执行ps,top等命令,还是可以看得到所有进程。说明并没有完全隔离。这是因为,像ps, top这些命令会去读/proc文件系统,因为/proc文件系统在父进程和子进程都是一样的,所以这些命令显示的东西都是一样的,当然我们可以临时重新挂载/proc来实现只看到PID namespace本身应该看到的进程。因此,我们还需要对文件系统进行隔离。

4>mount namespace:通过隔离文件系统挂载点对隔离文件系统来提供支持的,通过CLONE_NEWNS创建mount namespace后,父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统,而不对外界产生任何影响,即隔离后,不同的mount namespace中的文件结构发生变化也互不影响。这样可以做到比较严格地隔离。我们可以通过/proc/[pid]/mounts查看到所有挂载在当前namespace中的文件系统,通过/proc/[pid]/mountstats看到mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统的类型、挂载位置等。需要注意的是,文件系统结构复制这种隔离存在例外,比如父节点namespace中的进程挂载了一张CD-ROM,这时子节点namespace复制的目录结构是无法自动挂载上这张CD-ROM的,因为这种操作会影响到父节点的文件系统。

5>cgroup:控制组

cgroup ,它提供了一套机制用于控制一组特定进程对资源的使用。cgroup绑定一个进程集合到一个或多个子系统上。使​​​用​​​ cgroup,可​​​更​​​具​​​体​​​地​​​控​​​制​​​对​​​系​​​统​​​资​​​源​​​的​​​分​​​配​​​、​​​优​​​先​​​顺​​​序​​​、​​​拒​​​绝​​​、​​​管​​​理​​​和​​​监​​​控​​​。​​​可​​​更​​​好​​​地​​​根​​​据​​​任​​​务​​​和​​​用​​​户​​​分​​​配​​​硬​​​件​​​资​​​源​​​,提​​​高​​​总​​​体​​​效​​​率​​​。关于cgroup定义,参考官方解释

在这里插入图片描述

cgroups, are a Linux kernel feature which allow processes to be organized into
hierarchical(层级树) groups whose usage of various types of resources canthen be limited and monitored.  The kernel's cgroup interface is provided through a pseudo-filesystem called cgroupfs.  Grouping is implemented in the core cgroup kernel code, while resource tracking and limits are implemented in a set of per-resource-type subsystems (memory, CPU, and so on).
       
cgroups,是 Linux内核的功能,允许将进程组织按不通层级分配使用各种类型资源并且红纸和监控这些资源的使用。内核的 cgroup 接口就是通过名为 cgroupfs 的伪文件系统提供。分组在核心 cgroup 内核代码中实现,而资源跟踪和控制则在一组各类资源子系统中实现(如内存、CPU 等)。subsystem,可理解为一个通过cgroup提供的工具和接口来管理进程集合的模块。一个子系统就是一个典型的“资源控制器”,用来调度资源或者控制资源使用的上限。每种资源可以看作是一个子系统。子系统可以是以进程为单位的任何东西,

Cgroups主要由task,cgroup,subsystem及hierarchy构成:

●Task : 在Cgroups中,task就是系统的一个进程。
●Cgroup : Cgroups中的资源控制都以cgroup为单位实现的。cgroup表示按照某种资源控制标准划分而成的任务组,包含一个或多个Subsystems。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。
●Subsystem : Cgroups中的subsystem就是一个资源调度控制器(Resource Controller)。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。
●Hierarchy(层级树) : hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy。

cgroup主要提供了如下功能:

1>>Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
2>>Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
3>>Accounting: 一些审计或一些统计,主要目的是为了计费。
4>>Control: 挂起进程,恢复执行进程。

【cgroup术语解析】:

kio: 这个subsystem可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。
cpu: 这个subsystem使用调度程序控制task对cpu的使用。
cpuacct: 这个subsystem自动生成cgroup中task对cpu资源使用情况的报告。
cpuset: 这个subsystem可以为cgroup中的task分配独立的cpu(此处针对多处理器系统)和内存。
devices 这个subsystem可以开启或关闭cgroup中task对设备的访问。
freezer 这个subsystem可以挂起或恢复cgroup中的task。
memory 这个subsystem可以设定cgroup中task对内存使用量的限定,并且自动生成这些task对内存资源使用情况的报告。
perfevent 这个subsystem使用后使得cgroup中的task可以进行统一的性能测试。{![perf: Linux cpu性能探测器,详见https://perf.wiki.kernel.org/index.PHP/MainPage]}
*net_cls 这个subsystem Docker没有直接使用,它通过使用等级识别符(classid)标记网络数据包,从而允许 Linux 流量控制程序(TC:Traffic Controller)识别从具体cgroup中生成的数据包。

查看/proc/[pid]/cgroup进程获取cgroup信息,输出内容以hierarchy-ID:controller-list:cgroup-path来显示。

查看/sys/fs/cgroup/目录,可查看每项资源的使用情况。比如:cpuacct/cpuacct.usage_percpu、cpuset/cpuset.cpus、cpu/cpu.cfs_period_us、cpu/cpu.cfs_quota_us、cpuacct/cpuacct.usage

查看/proc/stat文件,可获取所有CPU活动的信息,该文件中的所有值都是从系统启动开始累计到当前时刻

eg:cat /proc/stat | grep 'cpu '(周期/时间片/jiffies) ##得到的数字相加/HZ(cat /boot/config-`uname -r` | grep ‘^CONFIG_HZ=’,然后乘以10*9就是系统时间(纳秒)。

实践中,一般利用CGroup会做下面这些事:

●隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
●为这组进程分配其足够使用的内存
●为这组进程分配相应的网络带宽和磁盘存储限制
●限制访问某些设备(通过设置设备的白名单)

eg:cat /boot/config-${uname -r} | grep CGROUP //查看linux是否启用了linux cgroups,“y”代表已经打开linux cgroups功能
在这里插入图片描述

更多参考Linux cgroup详解

在这里插入图片描述

二、操作命令

2.1 Docker 容器查看

首先,管理员对docker做日常维护,需要了解现有环境有多少docker实例,并连接进入docker实例虚拟机进行对应的管理维护;

我们执行 docker ps 命令来查看容器名称:docker ps -a或docker ps //显示当前正在运行的容器,输出如下:

在这里插入图片描述
参数概览:

在这里插入图片描述
示例:

1)显示最后被创建的容器:docker ps -l //相当于 docker ps -n 1
2) 详细输出,长内容不会截断:docker ps --no-trunc //其中 trunc 是 truncate 的缩写
3)只显示容器 ID:$ docker ps -q
4)显示容器文件大小:$ docker ps -s //输出结果可获得 2 个数值:一个是容器真实增加的大小,一个是整个容器的虚拟大小(容器虚拟大小 = 容器真实增加大小 + 容器镜像大小)。

在这里插入图片描述

5)docker ps –format参数可以实现格式化输出自定义列,提供了基于 Go模板 的日志格式化输出辅助功能。

-format=“TEMPLATE”
Pretty-print containers using a Go template.
Valid placeholders:
.ID - Container ID
.Image - Image ID
.Command - Quoted command
.CreatedAt - Time when the container was created.
.RunningFor - Elapsed time since the container was started.
.Ports - Exposed ports.
.Status - Container status.
.Size - Container disk size.
.Names - Container names.
.Labels - All labels assigned to the container.
.Label - Value of a specific label for this container. For example {{.Label “com.docker.swarm.cpu”}}
.Mounts - Names of the volumes mounted in this container

示例: docker ps --format “table {{.ID}}\t{{.Names}}\t{{.Ports}}\t{{.Status}}” //点号表示当前对象及上下文,直接通过{{.}}获取当前对象。

Go模板可用的可用的占位符如下:

在这里插入图片描述

eg:docker ps --format “{{.ID}}: {{.Command}}” // 当使用了 --format 选项,那么 ps 命令只会输出 template 中指定的内容;如果想带上表格列头,需要再 template 中加上 table 指令:docker ps --format “table {{.ID}}: {{.Command}}”

6)过滤显示:Filter

当容器数量过多,或者想排除干扰容器,可以通过 --filter 或 -f 选项,过滤需要显示的容器。支持的过滤条件非常丰富,包括:

在这里插入图片描述
使用该选项时,可按以下 3 条准则来进行过滤操作:

1>选项后跟的都是键值对 key=value (可不带引号),如果有多个过滤条件,就多次使用 filter 选项。例如:

eg1:docker ps --filter id=b61c9e9040e0 --filter name=vpm.redis.1
在这里插入图片描述
2>相同条件之间的关系是或,不同条件之间的关系是与。例如:

eg:docker ps --filter name=vpm.redis.1 --filter name=indexserver --filter status=running

以上过滤条件会找出 name 包含 vpm.redis.1 或 indexserver 并且 status 为 running 的容器。

3> id 和 name,支持正则表达式

eg:docker ps --filter name=^/bingohuang$   //精确匹配 name 为 bingohuang 的容器。注意,容器实际名称,开头是有一个正斜线 / ,可用 docker inspect看到
eg:docker ps --filter name=\.*bingohuang.\*   //匹配 name 包含 bingohuang 的容器,和 --filter name=bingohuang 一个效果

eg:docker rm $(docker ps -q --filter name=.*bingohuang.* --filter status=exited --filter status=dead2>/dev/null)    //清理名称包含 bingohuang,且状态为 exited 或 dead 的容器

其他知识:容器的 metadata 在 /var/lib/docker/containers/containerId/ 目录下,其中 containerId-json.log 文件中记录了回写的内容。

7)容器与宿主机端口映射查看

除docker ps 外,我们还可使用 docker port 可以查看指定 (ID 或者名字)容器的某个确定端口映射到宿主机的端口号。

docker port container_id
docker port container_name

2.2 Docker 容器连接

连接指定容器,实际是运行一个容器或在已运行的容器中执行命令来连接进入容器,执行:

1)运行:docker run -d -P --name runoob training/webapp python app.py //–name 标识来命名容器,为让外部也可以访问这些应用,可以通过 -P(大) 或 -p(小) 参数来指定端口映射。P :是容器内部端口随机映射到主机的高端口。-p : 是容器内部端口绑定到指定的主机端口。

docker run -d -p 5000:5000 training/webapp python app.py
docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py  //-d(–detach)容器后台运行,并且打印容器id

验证:docker port 命令可以查看端口的绑定/映射情况,如:

docker port CONTAINER [PRIVATE_PORT[/PROTO]]

eg:docker port adoring_stonebraker 5000

在这里插入图片描述
2)连接docker exec -it 容器名称/容器ID sh //-i(–interactive)表即使没有连接,也要保持标准输入保持打开状态,一般与 -t (–tty,分配一个伪tty)连用;-t 指示 docker 要创建一个伪 tty 终端,连接容器的标准输入接口,之后用户就可以通过终端进行输入。退出时直接exit即可。

当然也可以使用:docker attach 容器名称,区别如下:

attach进入终端后,没办法退出而不停止容器,要退出只能输入exit,但这样就将容器停止了,另外一个缺点是,如果多个容器同时attach到相同的容器,在一个窗口中操作的结果,会同步显示到所有窗口。因此我们才用docker exec -it 容器名称 sh;

docker attach [container_id] //连接一个正在运行的container实例(即实例必须为start状态,可以多个窗口同时attach一个container实例)

eg:docker exec nginx ip a     //在容器内执行命令,查看容器的ip

docker start -i //启动一个container并进入交互模式(相当于先start,在attach);使用start是启动已经创建过得container,使用run则通过image开启一个新的container。

在容器上做了一堆操作后,比如在ubuntu的基础上安装了一些软件、部署了一些应用之类,希望分发到其它机器,最简单的办法就是把容器重新生成一个新镜像,然后其它人或到新的主机上直接docker pull你的新镜像就可以了。

docker commit -a 作者名字 -m 提交原因 -p 容器ID 镜像名称:版本号

eg:docker commit -m="你的镜像声明" -a="junn" 基于的当前container_ID junn/ubuntu:v2(你要发布的镜像名称:标签)
docker run -t -i junn/ubuntu:v2 /bin/bash  //用上述镜像启动新的容器即可,这样就无缝迁移到新环境了

提交完成后查看执行:docker images

附:查看docker容器初始登录后的root密码,因该密码是随机分配的,执行:

docker logs  [container_id]  2>&1 | grep 'User: ' | tail -n1 

2.3 容器镜像

当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。

1)可以使用 docker images 来列出本地主机上的镜像;使用-q :只显示镜像ID;

在这里插入图片描述
其中,REPOSITORY:表示镜像的仓库源;TAG:镜像的标签;IMAGE ID:镜像ID;CREATED:镜像创建时间;SIZE:镜像大小;同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本;

docker images --tree :显示镜像的所有层(layer)'
docker rmi  <image ID>: 删除一个或多个image

2)利用镜像创建容器实例

docker run -t -i ubuntu:14.04 /bin/bash   //-i: 交互式操作,-t为实例分配一个终端,bash表进入容器进入shell环境,这也是默认的

3)在线查找镜像

可以从 Docker Hub 网站来搜索镜像,Docker Hub 网址为: https://hub.docker.com/;也可以使用 docker search 命令来搜索镜像,–automated :只列出 automated build类型的镜像;-s :列出收藏数不小于指定值的镜像。

docker search image_name   //从Docker HUB上搜索镜像

在这里插入图片描述
然后找到需要的镜像,然后pull下载即可。

4)获取/下载一个新的镜像:

当想下载这个镜像,我们可以使用 docker pull 命令来拉取下载它。

eg:docker pull ubuntu:13.10 

5)自定义创建镜像

当我们从 docker 镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。

A、从已经创建的容器中更新镜像,并且提交这个镜像;

B、使用 Dockerfile 指令来创建一个新的镜像,Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

这里只讨论第2种,使用命令 docker build 从零开始来创建一个新的镜像。首先这需要创建一个 Dockerfile 文件,它包含了一组指令,告诉 Docker 该如何构建我们的镜像。Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。如下就是一个dockerfile的示例:

FROM    centos:6.7   ##指明创建镜像基于的原始镜像,即你要使用哪个镜像源
MAINTAINER      Fisher "fisher@sudops.com"

RUN     /bin/echo 'root:123456' |chpasswd  //告诉docker启动后在镜像内要执行的命令
RUN     useradd runoob  //RUN后跟的<命令行命令> 等同于,在终端操作的 shell 命令
RUN     /bin/echo 'runoob:123456' |chpasswd
RUN     /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
EXPOSE  22
EXPOSE  80
CMD     /usr/sbin/sshd -D

确认后,执行:docker build -t runoob/centos:6.7 -f ./dockerfile //-t :指定要创建的目标镜像名,创建过程会打印到标准输出; -f:读取的镜像配置

注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。如是多条语句,可用&&,只创建一层镜像。

镜像配置文件参数

–build-arg=[ ] :设置镜像创建时的变量;

–cpu-shares :设置 cpu 使用权重;

–cpu-period :限制 CPU CFS周期;

–cpu-quota :限制 CPU CFS配额;

–cpuset-cpus :指定使用的CPU id;

–cpuset-mems :指定使用的内存 id;

–disable-content-trust :忽略校验,默认开启;

-f :指定要使用的Dockerfile路径;

–force-rm :设置镜像过程中删除中间容器;

–isolation :使用容器隔离技术;

–label=[] :设置镜像使用的元数据;

-m :设置内存最大值;

–memory-swap :设置Swap的最大值为内存+swap,"-1"表示不限swap;

–no-cache :创建镜像的过程不使用缓存;

–pull :尝试去更新镜像的新版本;

–quiet, -q :安静模式,成功后只输出镜像 ID;

–rm :设置镜像成功后删除中间容器;

–shm-size :设置/dev/shm的大小,默认值是64M;

–ulimit :Ulimit配置。

–tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。

–network: 默认 default。在构建期间设置RUN指令的网络模式

eg:docker build -t nginx:v3 .  //其中最后的.表构建容器镜像的上下文路径,即当前路径下所有文件都会被当做构建镜像的文件,发给docker引擎(Sending build context to Docker daemon)

注意: 上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成镜像构建过程缓慢。

6)设置镜像标签

使用 docker tag 命令,为镜像添加一个新的标签。

eg: docker tag 860c279d2fec junn/centos:v2   //860c279d2fec是镜像ID,后跟用户名称、镜像源名(repository name)和新的标签名(tag)

7)Docker镜像的加速器

国内从 DockerHub 拉取镜像会比较困难,正常都会失败;比如我的docker EE 18.09.0版的仓库地址是:https://index.docker.io/v1/,因此Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
Docker 官方提供的中国镜像库:https://registry.docker-cn.com;但docker在2020年11月01日起将逐步向Docker Hub匿名和免费用户实施速率和拉取请求次数限制,更多详见download-rate-limit。国内的如下所示:新建 /etc/docker/daemon.json 文件,可配置多个仓库,比如写入如下内容:

 
{
"insecure-registries": ["https://index.docker.io"],
"registry-mirrors": [
        "http://hub-mirror.c.163.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://cr.console.aliyun.com",
        "https://hub.daocloud.io",
        "https://registry.docker-cn.com"
  ]
}

其中:

“insecure-registries”: [], # 这个私库的服务地址,如docker仓库不是基于HTTPS的,那么需要配置insecure-registries参数,以允许docker客户端向其发送非安全传输请求
“registry-mirrors”: [], # 私库加速器

完成后systemctl daemon-reload,systemctl restart docker

/daemon.json 配置文件示例:

{
 "api-cors-header":"", 
 "authorization-plugins":[],
 "bip": "",
 "bridge":"",
 "cgroup-parent":"",
 "cluster-store":"",
 "cluster-store-opts":{},
 "cluster-advertise":"",
  #  启用debug的模式,启用后,可以看到很多的启动信息。默认false
 "debug": true,                     
 "default-gateway":"",
 "default-gateway-v6":"",
 "default-runtime":"runc",
 "default-ulimits":{},
 "disable-legacy-registry":false,
 #  设定容器DNS的地址,在容器的 /etc/resolv.conf文件中可查看。
 "dns": ["192.168.1.1"],
 #  容器 /etc/resolv.conf 文件,其他设置             
 "dns-opts": [],   
 # 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的 主机时,    
 # DNS不仅搜索host,还会搜索host.example.com 。 注意:如果不设置, 
 # Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。             
 "dns-search": [],                  
                                          

"exec-opts": [],
 "exec-root":"",
 "fixed-cidr":"",
 "fixed-cidr-v6":"",
 # 已废弃,使用data-root代替,这个主要看docker的版本
 "graph":"/var/lib/docker", 
 #  Docker运行时使用的根路径,根路径下的内容稍后介绍,默认/var/lib/docker        
 "data-root":"/var/lib/docker",   
 # Unix套接字的属组,仅指/var/run/docker.sock  
 "group": "",  
 # 设置容器hosts                     
 "hosts": [],                        
 "icc": false,
 # 配置docker的私库地址
 "insecure-registries": [],         
 "ip":"0.0.0.0",
 "iptables": false,
 "ipv6": false,
 # 默认true, 启用 net.ipv4.ip_forward ,进入容器后使用 sysctl -a | grepnet.ipv4.ip_forward 查看
 "ip-forward": false,               

 "ip-masq":false,
 # docker主机的标签,很实用的功能,例如定义:–label nodeName=host-121
 "labels":["nodeName=node-121"],     

 "live-restore": true,
 "log-driver":"",
 "log-level":"",
 "log-opts": {},
 "max-concurrent-downloads":3,
 "max-concurrent-uploads":5,
 "mtu": 0,
 "oom-score-adjust":-500,
 # Docker守护进程的PID文件
 "pidfile": "",                    
 "raw-logs": false,
 # 镜像加速的地址,增加后在 docker info中可查看。
 "registry-mirrors":["xxxx"],      
 "runtimes": {
 "runc": {
 "path": "runc"
 },
 "custom": {
 "path":"/usr/local/bin/my-runc-replacement",
 "runtimeArgs": [
 "--debug"
 ]
 }
 },
 # 默认 false,启用selinux支持
 "selinux-enabled": false,        

 "storage-driver":"",
 "storage-opts": [],
 "swarm-default-advertise-addr":"",
 "tls": true,                      # 默认 false, 启动TLS认证开关
 "tlscacert": "",                  # 默认 ~/.docker/ca.pem,通过CA认证过的的certificate文件路径
 "tlscert": "",                    # 默认 ~/.docker/cert.pem ,TLS的certificate文件路径
 "tlskey": "",                     # 默认~/.docker/key.pem,TLS的key文件路径
 "tlsverify": true,                # 默认false,使用TLS并做后台进程与客户端通讯的验证
 "userland-proxy":false,
 "userns-remap":""
 }

2.4 容器创建

1)创建一个容器

docker run :创建一个新的容器并运行一个命令

用法: docker run [OPTIONS] IMAGE [COMMAND] [ARG…]或

eg:docker run -itd --name ubuntu-test ubuntu /bin/bash //交互模式运行一个容器并分配一个伪终端且启动加载bash环境,这样就可以交互式登录,-d指定容器后台运行,–name为容器自定义个名称 ,采用一个名称为ubuntu的镜像来启动加载OS;

eg:docker pull training/webapp # 载入镜像
docker run -d -P training/webapp python app.py //利用上述镜像启动web服务容器,-P指定将容器内部使用的网络端口随机映射到外部使用的主机上(宿主机)。
docker ps //验证,输出如下,我们看到容器内5000 端口(默认 位Python Flask 的端口)映射到宿主机端口 32769 上,我们外部访问32769就可将服务映射/转发到容器内部真正的服务上
CONTAINER ID IMAGE COMMAND … PORTS
d3d5e39ed9d3 training/webapp “python app.py” … 0.0.0.0:32769->5000/tcp

docker run -d -p 5000:5000 training/webapp python app.py //或者-p直接指定容器与宿主间端口映射关系

参数:

-d: 后台运行容器,并返回容器ID;

–name=“nginx-lb”: 为容器指定一个名称;

-p: 指定端口映射,格式为:主机(宿主)端口:容器端口;-p 参数可以出现多次,绑定多个端口号

–volume , -v: 绑定一个卷

-i: 以交互模式运行容器,通常与 -t 同时使用,-it

-a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;

-P: 随机端口映射,容器内部端口随机映射到主机的高端口

-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;

–dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;

–dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;

-h “mars”: 指定容器的hostname;

-e username=“ritchie”: 设置环境变量;Docker容器中的用户默认是非root用户

–env-file=[]: 从指定文件读入环境变量;

–env key=value 添加配置变量

–cpuset=“0-2” or --cpuset=“0,1,2”: 绑定容器到指定CPU运行;

-m :设置容器使用内存最大值;

–net=“bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container:<name|id> 四种类型;

–link=[]: 添加链接到另一个容器;

–expose=[]: 开放一个端口或一组端口;

eg1:docker run -it busybox /bin/bash    //以交互方式运行shell命令行

eg2:docker run -d -p 80:80 --name nginx nginx   //以后台方式运行nginx,并把容器的80端口映射到宿主机的80端口,容器名称nginx

eg3:docker run -d -p 80:80 --name nginx --link tomcat:tomcat -v /logs/:/var/logs/nginx nginx   //以后台方式运行nginx,并把容器的80端口映射到宿主机的80端口,容器名称nginx,连接tomcat容器,把宿主机的/logs目录挂载到容器中的/var/logs/nginx目录

eg4:docker run -itd --name bridge -p 8081:8080  -p 8082:8080 nginx-web

#root身份登录容器
docker exec -it --user=root container_id /bin/bash
docker exec -u 0 -it  container_id  /bin/bash
#或在Docker启动时,可以使用–privileged=true和-u参数来给Docker容器授权
docker run -it --privileged=true -u=root container_id /bin/bash
#以上配置还是会受限ystemctl命令的执行权限问题,收到Failed to get D-Bus connection: Operation not permitted
docker run -it --privileged=true -u root docker.io/centos/systemd:apache /usr/sbin/init
#然后在另一台终端上,执行命令后,就可以再容器执行systemctl了
docker exec -it 【上述已经运行的容器ID】 /bin/bash
#也可已在容器内安装sudo
apt-get update && apt-get install -y sudo
#调试容器,使用--entrypoint选项可覆盖ENTRYPOINT命令对容器调试

2)容器使用卷(volumn)

通过使用(挂载)卷到容器,可以把hostd机上的某个目录"插入"到容器中,然后容器中就能直接访问host机上的文件了,即使容器删除掉,卷里的数据仍然可能持久保存。

#注意-v挂载是可以配置:ro或:rw(默认用)
eg:docker run -it -v /Users/yjmyzz/docker_volumn:/opt/webapp --name myubuntu ubuntu /bin/bash   //-v /Users/yjmyzz/docker_volumn:/opt/webapp的部分,意思就是将机/Users/yjmyzz/docker_volumn这个目录映射到容器中的/opt/webapp

#注意当采用devicemapper存储驱动时,挂载data卷,注意大小,当使用达到最大时,会导致无法写入,影响容器使用
#创建一个大的数据卷
dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/data bs=1G count=0 seek=1000  #将会创建一个虚拟的1000G大小的data文件,如果不加seek参数count直接为1000的话则是创建了一个`结结实实的1000G的文件

3)启停容器实例

docker start container_ID  //容器ID从docker ps -a获取
docker stop container_ID
docker restart container_ID
docker rm -f container_ID  //删除容器时,容器必须是停止状态
docker rm -f $(docker ps -aq)  //删除所有容器
docker container prune(修剪/削减)  //清理掉所有处于终止状态的容器

4)容器的导出和导入

比如,我们要迁移容器时,需要导出本地某个容器,可以使用 docker export 命令:

docker export container_ID > ubuntu16.04.tar  //导出容器快照到本地文件

迁移到目标主机后,再使用 docker import 从容器快照文件中恢复为新的容器启动镜像:

cat docker/ubuntu.tar | docker import - New/ubuntu:v1
docker images  //验证,这时可看到上述新的镜像,然后就可以以该镜像从新启动容器了

2.5 Docker新建网络

docker network create -d bridge test-net   //-d:参数指定 Docker 网络类型,有 bridge、overlay。其中 overlay 网络类型用于 Swarm mode

docker network ls //查看当前网络

可以在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS,配置完,需要重启 docker 才能生效:

{
“dns” : [
“114.114.114.114”,
“8.8.8.8”
]
}
设置后,启动容器的 DNS 会自动配置为 114.114.114.114 和 8.8.8.8。

查看容器的 DNS 是否生效请执行以下命令,它会输出容器的 DNS 信息:

$ docker run -it --rm  ubuntu  cat etc/resolv.conf   //--rm:容器退出时自动清理容器内部的文件系统

如果手动手动指定容器的DNS配置,可执行:

docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=blue.cn ubuntu //-h HOSTNAME 或者 --hostname=HOSTNAME: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。–dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。–dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。如果在容器启动时没有指定 --dns 和 --dns-search,Docker 会默认用宿主主机上的 /etc/resolv.conf 来配置容器的 DNS。

2.6 Docker 镜像仓库

容器中,仓库(Repository)就是集中存放容器的基础,即镜像(images)的地方。其中,Docker 官方维护了一个公共仓库 Docker Hub;

在这里插入图片描述
在这里插入图片描述

1)仓库的登录和退出

docker login  //输入之前注册的用户名和密码
docker logout
docker search ubuntu //查找相关镜像
docker pull ubuntu:18.04
docker tag ubuntu:18.04 username/ubuntu:18.04  //完成一些初始化和定制后,可重新打标签
docker push username/ubuntu:18.04  //上传到官方仓库

2)仓库Registry

仓库注册服务器(registry)是用来存放仓库的地方,里面存放着多个仓库,而在每个仓库当中集中存放这某一类镜像,它们通过不同的标签(tag)来进行区分。

3)本地仓库创建

#在一台可访问 Docker Hub 的机器上搭建一个本地 Docker Registry
docker run -d -p 5000:5000 --restart=always --name registry registry:2
#在搭建好的 Docker Registry 机器上,使用 docker pull 和 docker push 命令将 Docker Hub 上的镜像同步到本地 Registry
docker pull ubuntu:latest
docker tag ubuntu:latest localhost:5000/ubuntu:latest
docker push localhost:5000/ubuntu:latest
#再内外机器上配置镜像加速
vim /etc/docker/daemon.json   //修改如下

{
  "registry-mirrors": ["<your-内网registry-address>"]
}

docker network create video-network

2.7 Docker 更新

docker update 命令可动态更新容器配置,可以对单个容器或多个容器进行限制,但它主要用于更新正在运行的容器的配置,‌如资源限制、‌重启策略等,而非容器内文件。要指定多个容器,请提供以空格分隔的容器名称或 ID 列表。注:Windows 容器不支持 docker update命令;–kernel-memory 选项只能在已停止的容器或已初始化内核内存的正在运行的容器上进行更新,该选项自 Docker 20.10 起已被弃用;其他可在正在运行或已停止的容器上update。初次部署时,把容器的配置目录等一次性挂载好;非必要一般建议不要更新;
重要Docker升级前务必要备份。Docker的容器更新机制是通过重新启动容器来实现的。当我们执行docker update命令时,Docker会先停止容器,然后使用更新后的配置重新启动容器。这样,容器就会应用新的配置项。容器的更新只会影响到正在运行的容器,对于已经停止的容器是不会有任何影响,这时需要先启动这个容器,然后再执行docker update命令。

在这里插入图片描述

#更新容器映射关系,需先stop容器
docker update --volume /path/on/host:/path/on/container $container_id

补充:当然也可以手动修改,进入容器目录:/var/lib/docker/containers/[容器id]/下修改文件hostconfig.json和config.v2.json,将旧端口后更新为新端口号即可。然后重启docker服务,启动容器即可,同样不适用不能重启容器的场景。因此,对于线上环境,我们常使用commit将容器打包为新镜像:docker commit 容器id 镜像名:tag。使用新镜像启动新容器即可,验证无误后切换即可。

另外我们如果想更新容器内的文件内容,是通过vi/vim修改宿主机的映射文件时,会发现容器内文件并没有同步修改成功,这是因为Docker 中,使用-v,即mount volume 的原理是借用了 Linux Namespace 中的 Mount NameSpace,它实际只是隔离系统中不同进程的挂载点视图,我们使用vi/vim修改宿主机映射到容器内的对应文件时,实际会在源文件上创建一个副本,我们实际修改的是一个副本文件,可用stat看修改前后的inode是不一样的,即源映射文件的宿主实际文件是没有变化的,该副本会存储在内存中,等到容器restart 时,容器才会重新读取新的文件,宿主机上修改的内容才会应用到容器内完成更新。我们可用echo命令更新文件或授权文件为666权限;另外最佳做法是:容器映射时,挂载目录,不要挂载文件。这样就不会出现宿主机文件更新,而容器中文件没有更新的状况;

反之,默认容器内的文件更新也不会自动同步到外部,为了实现容器内文件更新能够更新到外部,我们需要进行数据持久化操作。可以使用docker cp mycontainer:/path/to/file /path/on/host命令将容器内的文件拷贝到宿主机上;但是如果容器具备超管权限时,无需这样;

2.8 Docker其他命令

1)获取日志:使用docker logs : 获取容器的日志

用法docker logs [OPTIONS] CONTAINER

参数

-f : 跟踪日志输出

-t : 显示时间戳

–tail :仅列出最新N条容器日志

–since :显示某个开始时间的所有日志

eg:docker logs nginx

docker logs -f nginx    #追踪模式

2)使用docker inspect:获取容器/镜像的元数据

docker inspect命令可查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息。

用法:docker inspect [OPTIONS] NAME|ID [NAME|ID…]

参数

-f :指定返回值的模板文件。

-s :显示总的文件大小。

–type :为指定类型返回JSON。

3)使用docker top 查看容器中运行的进程信息,支持 ps 命令参数

4)使用docker cp 用于容器与主机之间的数据拷贝

用法:

docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|
docker cp [OPTIONS] SRC_PATH| CONTAINER:DEST_PATH

参数:-L :保持源目标中的链接

5)删除container

用法:docker rm 镜像名称或者容器id(container)

批量删除:

docker rm -f | $(docker ps -a -q)
docker ps -a -q | xargs docker rm
docker rm -f container_ID

6)启停:

docker start/stop/restart (docker 启动、停止、重启)<container>

docker kill CONTAINER  强制停止

【docker stop 与 docker kill 的区别】

docker stop命令来停掉容器的时候,docker默认会允许容器中的应用程序有10秒的时间用以终止运行。在docker stop命令执行的时候,会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的10秒,会继续发送SIGKILL的系统信号强行kill掉进程。在容器中的应用程序,可以选择忽略和不处理SIGTERM信号,不过一旦达到超时时间,程序就会被系统强行kill掉,因为SIGKILL信号是直接发往系统内核的,应用程序没有机会去处理它。在使用docker stop命令的时候,我们唯一能控制的是超时时间,比如设置为20秒超时:

docker stop --time=20 container_name

docker kill命令不会给容器中的应用程序有任何gracefully shutdown的机会。它会直接发出SIGKILL的系统信号,以强行终止容器程序的运行。docker kill没有任何的超时时间设置,它会直接发送SIGKILL信号,以及用户通过signal参数指定的其他信号。对比以上,docker kill适合用来强行终止程序并实现快速停止容器。而如果希望程序能够gracefully shutdown的话,应用docker stop;

docker kill --signal=SIGINT container_name  //向docker中的程序发送SIGINT信号

7)Dockerfile中CMD命令的用法回顾

1>用法1:CMD command param1 param2

这种方式,其实是以shell的方式运行程序。最终程序被执行时,类似于/bin/sh -c的方式运行了我们的程序,这样会导致/bin/sh以PID为1的进程运行,而我们的程序只不过是它fork/execs出来的子进程而已。

2>用法2;使用 CMD [“executable”,”param1”,”param2”]

这种方式启动程序,执行和启动时,我们的程序会被直接启动执行,而不是以shell的方式,这样我们的程序就能以PID=1的方式开始执行了。

8)查看docker 容器启动的完整命令

可以借助python模块runlike来实现,执行:

1>yum install -y python-pip //OS里默认有pip3,也可以直接用,可pip install --upgrade pip升级到21.2.4

2>pip install runlike

在这里插入图片描述

3> which runlike //验证

4>命令语法:runlike -p <容器名>|<容器ID>

帮助手册:https://github.com/lavie/runlike

5> pip执行报错:

pip -V //报错如下

Traceback (most recent call last):
  File "/usr/local/bin/pip", line 7, in <module>
    from pip._internal.cli.main import main
  File "/usr/local/lib/python3.5/dist-packages/pip/_internal/cli/main.py", line 57
    sys.stderr.write(f"ERROR: {exc}")
                                   ^
SyntaxError: invalid syntax

这是因为默认的python运行为2.7版本,而pip默认也是python2.7来安装,故会报3.5错误;
如果没有python3,只需执行:sudo apt-get remove python-pip;sudo apt-get install python-pip
如果没有
如果是3.5 需下载3.5的pip支持包:
apt-get remove python-pip
wget https://bootstrap.pypa.io/pip/3.5/get-pip.py
python3.5 get-pip.py
在这里插入图片描述
在这里插入图片描述
9)docker top

用来查看一个容器里面的进程信息的,用法:

docker top 容器ID

docker stats  //查看当前主机下所有容器占用内存和cpu的情况

10)镜像导出,导入
docker load && docker save

eg1:docker save registry:2.7.1 >registry-2.7.1.tar //可以把一个镜像保存到tar文件中

eg2:docker load < registry-2.7.1.tar  //把镜像从tar文件导入到docker中

11)docker events

用以实时获取docker的各种事件信息

12)docker update

当我们docker run 了之后却发现里面有一些参数并不是想要的状态,比如配置的nginx容器cpu或者内存太小,这个时候就可以使用docker update去修改这些参数

docker update nginx --cpus 2

13)docker history

当修改了一个镜像,但是忘记了每一层的修改命令,或者想查看一个镜像是怎么构建的时候就可以使用这个命令

docker history --no-trunc image  #查看镜像的dockerfile文件,检查最后容器启动主进程的命令是什么样的,ENTRYPOINT?CMD?还是它们的组合

14)docker wait: 查看容器的退出状态,了解指定容器是正常退出的还是异常退出

15)docker diff

当运行了一个容器,但是不知道容器里修改了哪一些文件的时候可以使用这个命令

跟该命令类似的还有个docker system df,可以查看容器内文件系统使用情况;

16)docker system

三、实例应用

3.1、Docker 容器误退出后重新进入的几种方式:

1>借助工具nsenter

nsenter是一个命令行工具,用来进入到进程的linux namespace中。docker提供了exec命令可以进入到容器中,nsenter具有跟docker exec差不多的执行效果,但是更底层,特别是docker daemon进程异常的时候,nsenter的作用就显示出来了,常用于排查线上的docker问题。

在这之前,要连接到容器,需要找到容器的第一个进程的PID,通过这个PID,就可以连接到这个容器,执行:

docker inspect --format “{{ .State.Pid }}” //–format参数可简写为-f;docker ps获取容器id;

然后借助linux自带命令nsenter,它可以访问另一个进程的名字空间,正常工作需要有 root 权限。通过该工具我们就可以重新连接到容器。

如果系统中没有安装的话,可下载安装,下载地址:wget https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.32/util-linux-2.32.tar.gz(或wget https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.36/util-linux-2.36.tar.gz);从util-linux版本2.23开始,nsenter工具就包含在其中。安装如下:

tar -xzvf util-linux-2.32.tar.gz
cd util-linux-2.32/
./configure --without-ncurses
make nsenter  //编译nsenter
cp nsenter /usr/local/bin

完成后,执行:

nsenter --target $PID --mount --uts --ipc --net --pid,连接容器

示例:nsenter --target 23592 --mount --uts --ipc --net --pid   //这样就成功进入pid=23592的容器了

2> 借助libcontainer中的nsinit工具:

另外,从0.9版本开始,Docker自身就具有一个管理容器的库,名字为 libcontainer。libcontainer中的nsinit工具允许用户直接访问linux名字空间和cgroup内核。

nsinit读取的是位于/var/lib/docer/execdriver/native/容器目录下的配置数据。要运行nsinit,你需要切换到容器目录下。由于/var/lib/docker目录对于root用户是只读权限,因此你还需要root权限。通过docker的ps命令,你可以确定容器ID。一旦你进入/var/lib/docker目录,你就可以连接容器了:

nsinit exec /bin/bash

3.2、docker打包发布java项目

1)创建一个新目录作为项目根目录,将所有与Java相关的文件(如源代码、配置文件等)放入该目录中。

2)在项目根目录下创建一个名为Dockerfile的文件,用于docker发布镜像时读取它来构建java项目镜像时需要的指令集合,完成后保存并关闭Dockerfile文件。

#Dockerfile文件
# Docker image for springboot file run
# VERSION 0.0.1
# Author: 
# 基础镜像使用java
# 指定使用的基础镜像
FROM openjdk:8-jdk-alpine   #11采用openjdk:11-jdk-slim

# 设置工作目录
WORKDIR /app

#VOLUME 指定了临时文件目录为/tmp,在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp

# 将本地应用程序 JAR 文件复制到容器中
COPY 本地target/<项目文件名>.jar 容器内/app.jar

#上一步如果是源文件的话,复制项目文件到容器工作目录
COPY . ./
# 编译Java源代码(按需)
RUN javac Main.java

# 安装依赖项(如果需要)
RUN apt-get update && apt-get install -y <依赖项名称>

# 设置环境变量
ENV ENVIRONMENT=production

# 暴露应用程序的端口

EXPOSE <应用程序的端口号>

# 启动应用程序,如果是项目文件,CMD ["java", "Main"]
CMD ["java", "-jar", "app.jar"] 
#写全一点的话
CMD ["java","-jar","-Dlogging.file.path=/data/web_log/java","-Dfile.base.path=/data/files/your-java","your-java.jar"]


示例2:参考1参考2

# 将jar包添加到容器中并更名为web.jar
ADD myapp.jar web.jar  #前者为打包的文件 后者为修改后的自定义名字
# 运行jar包
RUN bash -c 'touch /web.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/web.jar"]

3)在项目根目录下使用命令行构建 Docker 镜像

mvn package  #按需,一般不需要这一步,因为一般项目里的jar包一构建好可直接java运行,直接下一步就好
#创建容器,最后一个点号表示使用当前目录下的 Dockerfile 文件进行构建
docker build -t <镜像名称> .
#验证
docker images

4) 启动 Docker 容器并运行 Java 应用程序

#<容器端口号>` 是指应用程序所使用的端口号,要与 Dockerfile 文件中的 `EXPOSE` 暴露的端口号保持一致
docker run -p <宿主机端口号>:<容器端口号> <镜像名称>
docker logs -f your-java

5)自定义项目镜像保存和导入

docker image tag 镜像 my_java:v1
docker save my_java:v1 > /Users/xxxxxx/data/your-java-image.tar
docker load < /Users/xxxxxx/data/your-java-image.tar

四、相关概念和重要内容

4.1、Dockfile文件编写说明

在这里插入图片描述
配置指令说明:

FROM 现有容器镜像 //指明定制的镜像都是基于 FROM 的镜像名称/ID
RUN <命令行命令> //其中<命令行命令> 等同于,在终端操作的 shell 命令。
RUN [“可执行文件”, “参数1”, “参数2”] //比如:RUN [“./test.php”, “dev”, “offline”] 等价于 RUN ./test.php dev offline
COPY [–chown=:] <源路径1>… <目标路径:容器内的目标位置>
COPY [–chown=:] [“<源路径1>”,… “<目标路径>”] //等同与docker copy 其中,[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。
CMD <shell 命令>
CMD [“<可执行文件或命令>”,“”,“”,…] //推荐使用该形式
CMD [“”,“”,…] / /该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
ENTRYPOINT [“”,“”,“”,…] //ENTRYPOINT类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序,它试验定参,而一般变参使用 CMD,且 CMD 多用于给 ENTRYPOINT 传参。但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。且如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。示例如下:
ENTRYPOINT [“nginx”, “-c”] # 定参
CMD [“/etc/nginx/nginx.conf”] # 变参 ,传给ENTRYPOINT使用,相当于执行:nginx -c /etc/nginx/nginx.conf
ENV //设置定义了环境变量,这样在后续的指令中,就可以使用这个环境变量了
ENV = =…
ARG <参数名>[=<默认值>] //构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只对 docker build 的过程中有效,不影响容器内部,构建好的镜像内不存在此环境变量。但可在构建命令 docker build 中使用 --build-arg <参数名>=<值> 来覆盖。
VOLUME [“<路径1>”, “<路径2>”…] //定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷;这样可以避免重要的数据,因容器重启而丢失,这是非常致命的;避免容器不断变大
VOLUME <路径> //在启动容器 docker run 的时候,我们可通过 -v 参数修改挂载点
EXPOSE <端口1> [<端口2>…] //声明端口;在运行时使用docker run -P 自动随机映射 EXPOSE 的端口
WORKDIR <工作目录路径> //指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
USER <用户名>[:<用户组>] //用于指定容器启动后执行后续命令的用户和用户组(用户和用户组必须提前已经存在)
HEALTHCHECK [选项] CMD <命令> //设置检查容器健康状况的命令
HEALTHCHECK NONE //如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK [选项] CMD <命令> //这边 CMD 后面跟随的命令使用
ONBUILD <其它指令> //用于延迟构建命令的执行。在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
LABEL <key>=<value> <key>=<value> <key>=<value> … //用来以键值对的形式给镜像添加一些元数据(标签说明)

![在这里插入图片描述](https://img-blog.csdnimg.cn/92123c791ab7467a80000203c9d07cfc.png

4.2、Docker容器的分层结构

首先我们来说下UnionFS(联合文件系统); Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以构建出各种具体的应用镜像。
在这里插入图片描述

特性: 一次同时加载多个文件系统,但对外面,只能看到一个文件系统,*UnionFS加载后会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker镜像的加载原理:
在这里插入图片描述

docker镜像实际上由一层层的文件系统组成,这种层级的文件系统UnionFS。 bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。Docker镜像层都是只读的,容器层是可写的;当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

Docker 镜像是由多个文件系统(只读层)叠加而成,每个层仅包含了前一层的差异部分。当我们启动一个容器的时候,Docker 会加载镜像层并在其上添加一个可写层。容器上所做的任何更改,譬如新建文件、更改文件、删除文件,都将记录与可写层上。容器层与镜像层的结构如下图所示:
在这里插入图片描述
容器与镜像最大的区别就在于可写层上。如果运行中的容器修改了现有的一个已存在的文件,那该文件将会从可写层下的只读层复制到可写层,该文件的只读版本仍然存在,只是已经被可写层中该文件的副本所隐藏。其中,多个容器共享镜像的结构如下所示:
在这里插入图片描述

4.3、容器数据卷

为防止放在容器中的数据,当容器删除以后,数据出现丢失。引入了将数据持久化的需求,即容器之间数据共享:将Docker容器中产生的数据同步到本地。这就是容器数据卷的技术:也叫目录的挂载,将容器内的目录挂载到宿主机上面。

这样的话,即使容器没有启动,要修改容器内的数据也只需在本地操作即可,同时容器间也可以进行数据的共享。其中:

1)匿名挂载: 就是我们在进行挂载时不写主机内路径,只写容器内路径,docker会自动帮我们在主机生成一个目录用于容器的数据挂载;使用docker volume ls查看可看到OLUME_NAME中没有具体的名字,只有一串地址值
2)具名挂载:容器创建前使用-v 卷名:容器内路径来进行挂载
docker run -d -it -v centos:/home/test:rw //还可配合ro/rw 改变读写权限
docker volume ls //但是此时只知道将容器内的数据卷挂载到了主机,具体挂载到了哪个位置则需要使用inspect来查看
docker volume inspect centos

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羌俊恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值