六、Docker镜像制作和管理
六、Docker镜像制作和管理

六、Docker镜像制作和管理

6.1 Docker 镜像说明

6.1.1 Docker 镜像中没有内核

从镜像大小上面来说,一个比较小的镜像只有1MB多点,或几MB,而内核文件需要几十MB,因此镜像里面是没有内核的,镜像在被启动为容器后将直接使用宿主机的内核,而镜像本身则只提供相应的rootfs,即系统正常运行所必须的用户空间的文件系统,比如/dev/,/proc/,/bin/,/etc/等,容器当中/boot目录是空的,而/boot当中保存的就是与内核相关的文件和目录

6.1.2 为什么没有内核

由于容器启动和运行过程中是直接使用了宿主机的内核,不会直接调用物理机硬件,所以也不会涉及到硬件驱动,因此也无需容器内拥有自己的内核和驱动。而如果使用虚拟机技术,对应每个虚拟机都有自己独立的内核

6.1.3 容器中的程序后台运行,会导致此容器启动后立即退出

Docker容器如果希望启动后能持续运行,就必须有一个能前台持续运行的进程,如果在容器中启动传统的服务,如:httpd,php-fpm等均为后台进程模式运行,就导致 docker 前台没有运行的应用,这样的容器启动后,会立即自杀。所以一般会将服务程序以前台方式运行,对于有一些可能不知道怎么前台运行的程序,只需要在你启动的命令之后添加类似于 tail ,top 这种可以前台运行的程序即可,比较常用的方法,如tail -f /etc/hosts

6.1.4 docker镜像的生命周期

6.1.5 制作镜像方式

Docker 镜像制作类似与虚拟机的镜像(模板)制作,即按照公司的实际业务需求将需要安装的软件、相关配置等基础环境配置完成,然后将其做成镜像,最后再批量从镜像批量生产容器示例,这样可以极大的简化相同环境的部署工作。

Docker的images制作分为手动制作(基于容器)和自动制作(基于dockerfile),企业通常都是基于dockerfile制作镜像

docker commit    #通过修改现有容器,将之手动构建为镜像
docker build     #通过dockerfile文件,批量构建为镜像

6.2 通过docker commit 将现有容器手动构建镜像

docker commit格式:

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

#选项
-a, --author string              Author (e.g., "John Hannibal Smith <hannibal@ateam.com>")
-c, --change list                Apply Dockerfile instruction to the created image
-m, --message string             Commit message
-p, --pause                      Pause container during commit (default true)

#说明:
制作镜像和CONTAINER状态无关,停止状态也可以制作镜像
如果没有指定[REPOSITORY[:TAG]],REPOSITORY和TAG都为<none>
提交的时候标记TAG号: 生产当中常用,后期可以根据TAG标记创建不同版本的镜像以及创建不同版本的容器

6.2.1 基于容器手动制作镜像步骤

基于容器手动制作镜像步骤具体如下:

  1. 下载一个基础的官方镜像,如:alpine
  2. 基于基础镜像启动一个容器,并入到容器
  3. 在容器里面做配置操作
    1. 安装基础命令
    2. 配置运行环境
    3. 安装服务和配置服务
    4. 放入业务程序代码
  4. 提交为一个新镜像 docker commit
  5. 基于自己的镜像创建容器并测试访问

6.2.1.1 基于alpine制作nginx镜像

root@ubuntu-2010:~# docker run -ti --name a1 alpine
/ # vi /etc/apk/repositories
https://mirrors.aliyun.com/alpine/v3.14/main    
https://mirrors.aliyun.com/alpine/v3.14/community
/ # apk update
/ # apk add nginx
/ # nginx -v
nginx version: nginx/1.20.1
/ # grep include /etc/nginx/nginx.conf 
include /etc/nginx/modules/*.conf;
# Uncomment to include files with config snippets into the root context.
#include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/mime.types;
	include /etc/nginx/http.d/*.conf;
#include /etc/nginx/stream.conf;
/ # vi /etc/nginx/http.d/default.conf 
server {
	listen 80 default_server;
	listen [::]:80 default_server;

	# Everything is a 404
	location / {
            root /var/www/html;
            index index.html;
	}

	# You may need this to prevent return 404 recursion.
	location = /404.html {
		internal;
	}
}
/ # mkdir /var/www/html -p
/ # echo "nginx website in alpine" > /var/www/html/index.html
/ # exit

#提交为镜像
方式一:
root@ubuntu-2010:~# docker commit -a 'zhouqihao' -m 'nginx-alpline:3.14' -c 'CMD nginx -g "daemon off;"' -c "EXPOSE 80" a1 nginx-alpline:v1.0
sha256:193772669e495c7add07b5dd1a5d415a2b81d8829bfa9406bf923e390d3fdfce
root@ubuntu-2010:~# docker run -d -p 80:80 nginx-alpline:v1.0 
4f7ef8bd8be94bdb944deb89318bf5790de7e603ad62f635837b081d06efb307
root@ubuntu-2010:~# curl localhost
nginx website in alpine

方式二:
root@ubuntu-2010:~# docker commit -a "zhouqihao" -c 'CMD ["nginx","-g","daemon off;"]' -c "EXPOSE 80" a1 nginx-alpline:v2.0
sha256:a4aa7cdee05e141486b7f12b02f8bf43330433f50c2f87919f8f5c112fc2319e
root@ubuntu-2010:~# docker run -d -p 81:80 nginx-alpline:v2.0 
d6e00818d71dca6f6083a4f4f1bfde0ae1ed31ff604060b397e2ca8d129723ee
root@ubuntu-2010:~# curl localhost:81
nginx website in alpine

6.3 利用dockerfile文件执行docker build自动构建镜像

6.3.1 构建镜像docker build 命令

docker build命令使用Dockerfile文件创建镜像

docker build [OPTIONS] PATH | URL | -

说明:
PATH | URL | -          #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取Dockerfile的内容
-f, --file string       #Dockerfile文件名 (Default is 'PATH/Dockerfile')
--force-rm              #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache              #不使用之前构建中创建的缓存
-q --quiet=false        #不显示Dockerfile的RUN运行的输出结果
--rm=true               #创建镜像成功时,删除临时容器
-t --tag=""             #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签默认为latest)
docker build .
docker build /usr/local/src/nginx
docker build -f /path/to/a/Dockerfile .
docker build -t shykes/myapp .
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
docker build -t test/myapp .
docker build -t nginx:v1 /usr/local/src/nginx

查看镜像的构建历史:

docker history 镜像ID

6.3.2 dockerfile使用详解

6.3.2.1 dockerfile介绍

DockerFile 是一种被Docker程序解释执行的脚本,由一条条的命令组成的,每条命令对应linux下面的一条命令,Docker程序将这些DockerFile指令再翻译成真正的 linux命令,其有自己的书写方式和支持的命令,Docker程序读取DockerFile并根据指令生成Docker镜像,相比手动制作镜像的方式,DockerFile更能直观的展示镜像是怎么产生的,有了DockerFile,当后期有额外的需求时,只要在之前的DockerFile添加或者修改响应的命令即可重新生成新的Docke镜像,避免了重复手动制作镜像的麻烦,类似与shell脚本一样,可以方便高效的制作镜像

Docker守护程序Dockerfile 逐一运行指令,如有必要,将每个指令的结果提交到新映像,然后最终输出新映像的ID。Docker守护程序将自动清理您发送的上下文

请注意,每条指令都是独立运行的,并会导致创建新映像,比如RUN cd /tmp 对下一条指令不会有任何影响。

Docker将尽可能重用中间映像(缓存),以显著加速docker build 命令的执行过程,这由Using cache 控制台输出中的消息指示

6.3.2.2 dockerfile镜像制作和使用流程

6.3.2.3 dockerfile文件的制作镜像分层结构

6.3.2.4 dockerfile文件格式

dockerfile是一个有特定语法格式的文本文件

dockerfile 官方说明: https://docs.docker.com/engine/reference/builder/

帮助: man 5 dockerfile

dockerfile文件说明:

  • 每一行以dockerfile的指令开头,指令不区分大小写,但是惯例用大写
  • 使用#开始作为注释
  • 每一行只支持一条指令,每条指令可以携带多个参数
  • 指令按文件的顺序从上至下执行
  • 每个指令的执行会生成一个新的镜像层,为了减少分层和镜像大小,尽可能将多条指令合并成一条指令
  • 制作镜像一般可能反复多次,每次执行dockerfile都按顺序执行,从头开始,已经执行过的指令已经缓存,不需要再执行,如果后续有一行指令没执行过,其往后的指令都将重新执行,所以为了加速镜像制作,将最常变化的内容放在dockerfile的文件后面

6.3.3 dockerfile相关指令

dockerfile文件中的常见指令:

ADD
COPY
ENV
EXPOSE
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR

6.3.3.1 FROM:指定基础镜像

定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。

FROM就是指定基础镜像,此指令必须放在dockerfile文件第一个非注释行。后续的指令都是运行与此基准镜像所提供的运行环境

基础镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件.如果找不到指定的镜像文件,docker build会返回一个错误信息

格式:

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

#说明:
--platform 指定镜像的平台,比如: linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest

说明:关于scratch镜像

FROM scratch
参考链接:
https://hub.docker.com/_/scratch?tab=description
https://docs.docker.com/develop/develop-images/baseimages/
该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
该镜像在构建基础镜像(例如debian和busybox)或超最小镜像(仅包含一个二进制文件及其所需内容,例
如:hello-world)的上下文中最有用。
FROM scratch
FROM alpine
FROM ubuntu:bionic

6.3.3.2 LABEL:指定镜像元数据

可以指定镜像元数据,如:镜像作者等

LABEL <key>=<value> <key>=<value> <key>=<value> ...

一个镜像可以有多个label,还可以写在一行中,即多标签写法,可以减少镜像的大小

范例:多标签写法

#一行格式
LABEL multi.label1="value1" multi.label2="value2" other="value3"

#多行写法
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

docker inspect命令可以查看LABEL

6.3.3.3 RUN:执行shell命令

RUN指令用来在构建镜像阶段需要执行FROM指定的镜像所支持的shell命令。

通常各种基础镜像一般都支持丰富的shell命令

注意:RUN可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多个shell命令通过&&连在一条指令

#shell 格式: 相当于 /bin/sh -c <命令> 此种形式支持环境变量
RUN <命令>

#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号

RUN ["可执行文件", "参数1", "参数2"]

#exec格式可以指定其它shell
RUN ["/bin/bash","-c","echo hello world"]

范例:

RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
RUN ["/bin/bash", "-c", "echo hello world"]
RUN yum -y install epel-release \
    && yum -y install nginx \
    && rm -rf /usr/share/nginx/html/*
    && echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html

6.3.3.4 ENV:设置环境变量

ENV可以定义环境变量和值,会被后续指令(如:ENV,ADD,COPY,RUN等)通过$KEY或${KEY}进行引用,并在容器运行时保持

#变量赋值格式1
ENV <key> <value> #此格式只能对一个key赋值,<key>之后的所有内容均会被视作其<value>的组成
部分

#变量赋值格式2
ENV <key1>=<value1> <key2>=<value2> \
<key3>=<value3> ...

#此格式可以支持多个key赋值,定义多个变量建议使用,减少镜像层
#如果<value>中包含空格,可以以反斜线\进行转义,也可通过对<value>加引号进行标识;另外,反斜线也
可用于续行

#只使用一次变量
RUN <key>=<value> <command>

#引用变量
RUN $key .....

如果运行容器时如果需要修改变量,可以执行下面通过基于exec 机制实现

注意: 下面方式只影响容器运行时环境,而不影响构建镜像的过程,即只能覆盖docker run时的环境变量,而不会影响docker build时环境变量的值

docker run -e|--env <key>=<value>

#说明
-e, --env list        #Set environment variables
--env-file filename   #Read in a file of environment variables

范例:两种格式功能相同

#格式1
ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy

#格式2
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

范例:

root@ubuntu-2010:/data# cat dockerfile 
FROM alpine
ENV NAME=hao AGE=27
RUN echo "hello $NAME,age=$AGE" > /root/$NAME.txt

root@ubuntu-2010:/data# docker build -t test:v1.0 .
root@ubuntu-2010:/data# docker run --rm test:v1.0 ls /root/
hao
root@ubuntu-2010:/data# docker run --rm test:v1.0 cat /root/hao
hello hao,age=27

root@ubuntu-2010:/data# docker run --rm -e NAME=john -e AGE=18 test:v1.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d40c87855506
NAME=john
AGE=18
HOME=/root

6.3.3.5 COPY:复制文本

复制本地宿主机的 到容器中的 。一般用于将在宿主机上事先准备的配置文件复制到镜像中

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,建议使用此格式
COPY --from=[step num] ["<src>",... "<dest>"] #从指定步骤的镜像拷贝文件到当前容器目录

说明:

  • 可以是多个,可以使用通配符,通配符规则满足Go的filepath.Match 规则,filepath.Match 参考链接: https://golang.org/pkg/path/filepath/#Match
  • 必须是build上下文中的路径(为dockerflie所在目录的相对路径),不能是其父目录中的文件
  • 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
  • 如果指定了多个, 或在中使用了通配符,则必须是一个目录,且必须以 / 结尾
  • 可以是绝对路径或者是WORKDIR 指定的相对路径
  • 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
  • 如果事先不存在,它将会被自动创建,这包括其父目录路径

范例:

COPY hom* /mydir/
COPY hom?.txt /mydir/

6.3.3.6 ADD:复制和解包文件

ADD可以认为是增强版的COPY,不仅支持COPY,还支持解压缩。

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

说明:

  • 可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
  • 可以是绝对路径或者是WORKDIR 指定的相对路径
  • 如果是目录,只复制目录中的内容,而非目录本身
  • 如果是一个 URL ,下载后的文件权限自动设置为 600
  • 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以 / 结尾,则文件名URL指定的文件将被直接下载并保存为/< filename>
  • 如果是一个本地文件系统上的打包文件,如: gzip, bzip2 ,xz ,它将被解包 ,其行为类似于”tar -xf”命令,但是通过URL获取到的tar文件将不会自动展开
  • 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径

范例:

ADD nginx-1.20.1.tar.gz /apps
ADD test relativeDir/            # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/           # adds "test" to /absoluteDir/
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

6.3.3.7 CMD:容器启动命令

一个容器中需要持续运行的进程一般只有一个,CMD 用来指定启动容器时默认执行的一个命令,且其运行结束后,容器也会停止,所以一般CMD 指定的命令为持续运行且为前台命令.

  • 如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么开启容器时就会使用执行CMD指定的默认的命令
  • 而前面的 RUN 命令是在构建镜像进执行的命令,注意二者的不同之处
  • 每个 Dockerfile 只能有一条 CMD 命令。如指定了多条,只有最后一条被执行
  • 如果用户启动容器时用 docker run xxx 指定运行的命令,则会覆盖 CMD 指定的命令
# 使用 exec 执行,推荐方式,第一个参数必须是命令的全路径,此种形式不支持环境变量
CMD ["executable","param1","param2"]

# 在 /bin/sh 中执行,提供给需要交互的应用;此种形式支持环境变量
CMD command param1 param2

# 提供给 ENTRYPOINT 命令的默认参数;
CMD ["param1","param2"]

范例:

CMD ["nginx", "-g", "daemon off;"]

6.3.3.8 ENTRYPOINT:入口点

功能类似于CMD,配置容器启动后默认执行的命令及参数

# 使用 exec 执行
ENTRYPOINT ["executable", "param1", "param2"]

# shell中执行
ENTRYPOINT command param1 param2
  • ENTRYPOINT 不能被 docker run 提供的参数覆盖,而是追加,即如果docker run 命令有参数,那么参数全部都会作为ENTRYPOINT的参数
  • 如果docker run 后面没有额外参数,但是dockerfile中的CMD里有(即上面CMD的第三种用
  • 法),即Dockerfile中即有CMD也有ENTRYPOINT,那么CMD的全部内容会作为ENTRYPOINT的参数
  • 如果docker run 后面有额外参数,同时Dockerfile中即有CMD也有ENTRYPOINT,那么docker run后面的参数覆盖掉CMD参数内容,最终作为ENTRYPOINT的参数
  • 可以通过docker run –entrypoint string 参数在运行时替换,注意string不要加空格
  • 使用CMD要在运行时重新写命令本身,然后在后面才能追加运行参数,ENTRYPOINT则可以运行时无需重写命令就可以直接接受新参数
  • 每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效
root@ubuntu-2010:/data# cat dockerfile 
FROM alpine
RUN echo "hello" > /root/test
ENTRYPOINT ["cat","/root/test"]
root@ubuntu-2010:/data# docker build -t test:v3.0 .
root@ubuntu-2010:/data# docker run -ti --rm test:v3.0 
hello

#追加-A参数
root@ubuntu-2010:/data# docker run -ti --rm test:v3.0 -l
root@ubuntu-2010:/data# cat dockerfile 
FROM alpine
RUN echo "hello" > /root/test
CMD ["-A"]
ENTRYPOINT ["cat","/root/test"]
root@ubuntu-2010:/data# docker run -ti --rm test:v3.1 
hello$

6.3.3.9 ARG:构建参数

ARG指令在build阶段指定变量,和ENV不同的是,容器运行时不会存在这些环境变量

ARG <name>[=<default value>]

如果和ENV同名,ENV覆盖ARG变量

可以用 docker build –build-arg <参数名>=<值> 来覆盖

root@ubuntu-2010:/data# cat dockerfile 
FROM centos
ARG pkg="httpd"
RUN yum -y install ${pkg}

root@ubuntu-2010:/data# docker build --build-arg pkg=nginx -t centos:nginx .

说明:ARG和FROM

FROM指令支持由第一个FROM之前的任何ARG指令声明的变量
#示例:
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras

#在FROM之前声明的ARG在构建阶段之外,因此,FROM之后的任何指令都不能使用它。 要使用在第一个FROM
之前声明的ARG的默认值,请使用ARG指令,在构建阶段内部不带任何值

#示例:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

6.3.3.10 VOLUME:挂载点

在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,默认会将宿主机上的目录挂载至VOLUME指令指定的容器目录。即使容器后期被删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。

宿主机目录为

/var/lib/docker/volumes/<id>/_data
VOLUME <path>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]

注意:Dockerfile中的VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系

范例:

root@ubuntu-2010:/data# cat dockerfile 
FROM alpine
VOLUME [ "/data1", "/data2" ]


root@ubuntu-2010:/data# docker build -t alpine:volume .
root@ubuntu-2010:/data# docker run -ti alpine:volume sh
/ # ls /data1
/ # cp /etc/issue /data1/f1.txt
/ # cp /etc/issue /data2/f2.txt

root@ubuntu-2010:/data# tree /var/lib/docker/volumes/
/var/lib/docker/volumes/
├── 0cf46a1fffea894b41cd3a476a37f59beff92ef7ba7a60b7dc9ccb1f5081555a
│   └── _data
├── 2c3b5614b01cfbff4431c4f2f6070c5659f2aef8d8d94b1f59726ab96417b318
│   └── _data
│       └── f2.txt
├── 45731fcc24c6c4969b55c37651659df2be48c153cd310b16cf1e59696f645901
│   └── _data
│       └── f1.txt

#-v选项会随着容器的删除而自己删除匿名卷

6.3.3.11 EXPOSE:暴露端口

指定服务端的容器需要对外暴露(监听)的端口号,以实现容器与外部通信。

EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会真正的暴露端口,即不会主动在宿主机进行端口映射

因此,在容器启动时需要通过-P 或-p,Docker主机才会真正分配一个端口转发到指定暴露的端口才可使用

注意:即使dockerfile没有EXPOSE端口指令,也可以通过docker run -p 临时暴露容器内程序真正监听的端口,所以,EXPOSE相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露

EXPOSE <port>[/<protocol>] [<prot>[/<protocol>] ..]

#<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为tcp协议
EXPOSE 80 443
EXPOSE 323/udp 323/tcp

6.3.3.12 WORKDIR:指定工作目录

为后续的RUN、CMD、ENTRYPOINT指令配置工作目录,当容器运行后,进入容器内的默认目录

WORKDIR /path/to/workdir
#两次run不在同一个环境内,可使用WORKDIR
RUN cd /app
RUN echo "hello" > world.txt

可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

#则最终路径为 /a/b/c

6.3.3.13 ONBUILD:子镜像引用父镜像

可以用来配置当构建当前镜像的子镜像时,会自动触发执行的指令,但在当前镜像构建时,并不会执行

root@ubuntu-2010:/data# cat dockerfile 
FROM alpine
ONBUILD ADD container.txt /root
ONBUILD echo hello > test.txt
root@ubuntu-2010:/data# docker build -t alpine:A .
root@ubuntu-2010:/data# docker run -ti --rm alpine:A
/ # ls /root

root@ubuntu-2010:/data# cat dockerfile 
FROM alpine:A
root@ubuntu-2010:/data# docker build -t alpine:s .
root@ubuntu-2010:/data# docker run -ti --rm alpine:s
/ # ls /root
container.txt
/ # cat test.txt 
hello

使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild

6.3.3.14 USER:指定当前用户

指定运行容器的用户名或 UID,在后续dockerfile中的 RUN ,CMD和ENTRYPOINT指令时使用此用户

当服务不需要管理员权限时,可以通过该命令指定运行用户

这个用户必须是事先建立好的,否则无法切换

如果没有指定 USER,默认是 root 身份执行

USER <user>[:<group>]
USER <UID>[:<GID>]

范例:

RUN groupadd -r mysql && useradd -r -g mysql mysql
USER mysql

6.3.3.15 HEALTHCHECK: 健康检查

容器的健康性检查

HEALTHCHECK [选项] CMD <命令>    #设置检查容器健康状况的命令
HEALTHCHECK NONE                 #如果基础镜像有健康性检查指令,使用这行可以屏蔽掉其健康性检查指令

HEALTHCHECK 支持下列选项:
--interval=<间隔> : 两次健康检查的间隔,默认为 30 秒;
--timeout=<时长> : 健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默
认 30 秒;
--retries=<次数> : 当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3次。
root@ubuntu-2010:/data# cat dockerfile 
FROM centos:nginx
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhos/
root@ubuntu-2010:/data# docker build -t centos-nginx:healthcheck .
root@ubuntu-2010:/data# docker run -ti --rm centos-nginx:healthcheck 
[root@51e10ce58b42 /]# nginx
[root@51e10ce58b42 html]# mv index.html index.html.bak
[root@51e10ce58b42 html]# curl localhost -I 
HTTP/1.1 403 Forbidden
Server: nginx/1.14.1
Date: Sun, 19 Sep 2021 06:03:28 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

[root@51e10ce58b42 html]# curl -fs localhost
[root@51e10ce58b42 html]# echo $?
22

root@ubuntu-2010:/data# docker ps
CONTAINER ID   IMAGE                      COMMAND       CREATED         STATUS                            PORTS     NAMES
51e10ce58b42   centos-nginx:healthcheck   "/bin/bash"   7 seconds ago   Up 7 seconds (health: starting)             cranky_taussig
root@ubuntu-2010:/data# docker ps
CONTAINER ID   IMAGE                      COMMAND       CREATED              STATUS                        PORTS     NAMES
51e10ce58b42   centos-nginx:healthcheck   "/bin/bash"   About a minute ago   Up About a minute (healthy)             cranky_taussig

root@ubuntu-2010:/data# docker ps -a
CONTAINER ID   IMAGE                      COMMAND       CREATED         STATUS                     PORTS     NAMES
51e10ce58b42   centos-nginx:healthcheck   "/bin/bash"   2 minutes ago   Up 2 minutes (unhealthy)             cranky_taussig

#查看健康状态
root@ubuntu-2010:/data# docker inspect -f '{{.State.Health.Status}}' 51
unhealthy

6.3.3.16 dockerfile构建过程和指令总结

dockerfile 构建过程

  • 从基础镜像运行一个容器
  • 执行一条指令,对容器做出修改
  • 执行类似docker commit的操作,提交一个新的中间镜像层(可以利用中间层镜像创建容器进行调试和排错)
  • 再基于刚提交的镜像运行一个新的容器
  • 执行Dockerfile中的下一条指令,直至所有指令执行完毕

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注