五、Docker 镜像管理
a.Docker镜像简介
- docker镜像基于union file system将多个目录合并挂载至一个目录给容器使用
- docker镜像只有rootfs而没有内核、运行使用的是宿主机的bootfs
- rootfs(root file system),文件系统
- bootfs(boot file system),主要包含 bootloader 和 Kernel
- 一个镜像是有一层或者多层合并而成,每一层称为是一个layer
- 镜像可以基于其它镜像进行重新构建,被引用的镜像称为父镜像
- 一个镜像可以同时被创建为多个容器
- 镜像是只读的,在容器的任何更改都不会直接修改镜像
镜像即创建容器的模版,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快速的创建并启动容器
b. Dockerfile简介
Dockerfile 可以理解为构建docker 镜像的源码。docker 可以通过读取一个dockerfile来自动构建docker image ,Dockerfile是一个文本文件,其中包含了按顺序排列的构建指定镜像所需要的全部命令, Dockerfile可以放在任意路径,但文件名必须为Dockerfile,且首字母D必须大写。使用Dockerfile构建镜像可以实现镜像的自动化构建,提高部署速度和环境的一致性。
c. Dockerfile 指令
Dockerfile格式要求
- #号开头的行为注释行
- 可以设置.dockeringore指定不打包进镜像的文件列表
- docker build 中执行的shell命令是由基础镜像所包含的命令集合
- 指令不区分大小写,一般默认都写成大写
- 第一个非注释行,必须是“FROM”指令,指出基于哪个基础镜像
常用的Dockerfile指令及说明
指令 | 说明 |
---|---|
FROM | 设置镜像使用的基础镜像 |
MAINTAINER | 设置镜像的作者信息,已被淘汰,建议使用LABEL标签代替 |
RUN | 编译镜像时运行的脚本 |
CMD | 设置容器的启动命令 |
LABEL | 设置镜像的标签 |
EXPOESE | 设置镜像暴露的端口,备注作用 |
ENV | 设置容器的环境变量 |
ADD | 编译镜像时复制文件到镜像中 |
COPY | 编译镜像时复制文件到镜像中 |
ENTRYPOINT | 设置容器的入口程序 |
VOLUME | 设置容器的挂载卷 |
USER | 设置运行RUN CMD ENTRYPOINT的用户名 |
WORKDIR | 设置RUN CMD ENTRYPOINT COPY ADD指令的工作目录,类似cd命令 |
ARG | 设置编译镜像时加入的参数 |
ONBUILD | 设置镜像的ONBUILD指令 |
d. Dockerfile 相关指令详解
1、FROM: 指定基础镜像
FROM指令是最重要的一个并且必须为Dockerfile文件开篇的第一个非注释行,用于为镜像文件构建过程中指定基准镜像,后续的指令运行于此基础镜像所提供的运行环境。 基准镜像可以是任何可用的镜像文件,默认情况下,docker build时候会在docker主机上查找指定的镜像,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件,如果找不到指定的镜像文件,docker build 会返回一个错误信息
如何选择合适的镜像
对于不同的软件官方都提供了相关的docker镜像,比如: nginx、redis、mysql、httpd、tomcat、jdk等服务类的镜像,也有操作系统类,如: centos、ubuntu、debian等。建议使用官方镜像,比较安全。
格式:
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
示例:
FROM scratch #所有镜像的起源镜像,相当于Object类
FROM ubuntu
FROM ubuntu:bionic
FROM centos:2009
2、LABEL: 指定镜像元数据
可以给镜像添加标签,来帮助项目组织镜像、记录许可信息、帮助自动化等。对于每个标签,添加一行以LABEL开头并带有一个或多个键值对的行
注意:
- 如果字符串中包含空格,则必须用双引号引起来或转义这个空格。如果字符串中包含双引号,必须转义
- 一个镜像可以有多个标签
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL "org.label-schema.name": "CentOS Base Image"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
#一行格式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
#多行格式
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
docker inspect
3、MAINTAINER: 指定维护者信息
此指令已过时,用LABEL代替
格式:
MAINTAINER <name>
示例:
MAINTAINER yuhao@20080808@qq.com
#用LABEL代替
LABEL maintainer="yuhao@20080808@qq.com"
4、RUN: 执行 shell命令
RUN 指令用来在构建镜像阶段需要执行 FROM 指定镜像所支持的Shell命令。
注意: RUN 可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多个shell命令通过 && 连接一起成为在一条指令
每个RUN都是独立运行的,和前一个RUN无关
格式:
#shell 格式: 相当于 /bin/sh -c <命令> 此种形式支持环境变量
RUN <命令>
#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号
RUN ["executable","param1","param2"...]
#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
多个 前后RUN 命令独立无关和shell命令不同
#world.txt并不存放在/app内
RUN cd /app
RUN echo "hello" > world.txt
5、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 .....
#变量支持高级赋值格式
${key:-word} # 如果key变量值为空,返回word字符串
${key:+word} # 如果key变量为空,什么都不做,否则word返回
如果运行容器时如果需要修改变量,可以执行下面通过基于 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
6、COPY: 复制文件至容器内
复制本地宿主机的文件到容器中的
格式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,建议使用此格式
说明:
可以是多个,可以使用通配符 必须是build上下文中的路径(为 Dockerfile 所在目录的相对路径),不能是其父目录中的文件 如果是目录,则其内部文件或子目录会被递归复制,但 目录自身不会被复制 - 如果指定了多个
, 或在 中使用了通配符,则 必须是一个目 录,且必须以 / 结尾 可以是绝对路径或者是 WORKDIR 指定的相对路径 - 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
- 如果
事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录
示例:
COPY hom* /mydir/
COPY hom?.txt /mydir/
7、ADD: 复制和解包文件
该命令可认为是增强版的COPY,不仅支持COPY,还支持自动解压缩。可以将文件复制指定的到容器中
格式:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
说明:
可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压) 可以是绝对路径或者是 WORKDIR 指定的相对路径 如果是目录,只复制目录中的内容,而非目录本身 如果是一个 URL ,下载后的文件权限自动设置为 600 如果为URL且不以/结尾,则 指定的文件将被下载并直接被创建为 ,如果以 / 结尾,则文件名URL指定的文件将被直接下载并保存为 /< filename> - 如果
是一个本地文件系统上的打包文件,如: gz, bz2 ,xz ,它将被解包 ,其行为类似于”tar -x”命令,但是通过URL获取到的tar文件将不会自动展开 - 如果
有多个,或其间接或直接使用了通配符,则 必须是一个以/结尾的目录路径;如果 不以/结尾,则其被视作一个普通文件, 的内容将被直接写入到
示例:
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/
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
8、 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;"]
CMD ["/bin/sh","-c","/opt/start.sh"]
9、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,当指定多个时,只有最后一个生效
- 通常会利用ENTRYPOINT指令配合脚本,可以为CMD指令提供环境配置
示例1:
FROM centos:centos7.9-v10.0
LABEL maintainer="yuhao@20080808@qq.com"
ENV version=1.18.0
ADD nginx-$version.tar.gz /usr/local/
RUN cd /usr/local/nginx-$version && ./configure --prefix=/apps/nginx && make &&
make install && rm -rf /usr/local/nginx* && sed -i 's/.*nobody.*/user nginx;/'
/apps/nginx/conf/nginx.conf && useradd -r nginx
COPY index.html /apps/nginx/html
VOLUME ["/apps/nginx/html"]
EXPOSE 80 443
CMD ["-g","daemon off;"]
ENTRYPOINT ["/apps/nginx/sbin/nginx"]
#上面两条指令相当于ENTRYPOINT ["/apps/nginx/sbin/nginx","-g","daemon off;"]
示例2:
利用脚本实现指定环境变量动态生成配置文件内容
# 编辑Dockerfile文件,内容如下
[root@centos7 ~]#cat Dockerfile
FROM nginx:1.16-alpine
LABEL maintainer="yuhao@20080808@qq.com"
ENV DOC_ROOT='/data/website/'
ADD index.html ${DOC_ROOT}
ADD entrypoint.sh /bin/
EXPOSE 80/tcp 8080
#HEALTHCHECK --start-period=3s CMD wget -0 - -q http://${IP:-0.0.0.0}:
{PORT:-80}/
CMD ["/usr/sbin/nginx","-g", "daemon off;"] #CMD指令的内容都成为了ENTRYPOINT的参数
ENTRYPOINT [ "/bin/entrypoint.sh"]
#编辑entrypoint.sh脚本文件
[root@centos7 ~]#cat entrypoint.sh
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf <<EOF
server {
server_name ${HOSTNAME};
listen ${IP:-0.0.0.0}:${PORT:-80};
root ${DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@"
## 构建镜像
[root@centos7 ~]# chmod +x entrypoint.sh
[root@centos7 ~]# docker build -t nginx:v1.0 .
[root@centos7 ~]# docker run --name n1 --rm -P -e "PORT=8080" -e "HOSTNAME=www.yuhao.org" nginx:v1.0
10、ARG: 构建参数
ARG指令在build 阶段指定变量,和ENV不同的是,容器运行时不会存在这些环境变量
格式:
ARG <name>[=<default value>]
说明:
- 如果和ENV同名,ENV覆盖ARG变量
- 可以用 docker build –build-arg <参数名>=<值> 来覆盖
示例:
FROM busybox
ARG author="yuhao@20080808@qq.com"
LABEL maintainer="${author}"
11、VOLUME: 匿名卷
在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,默认会将宿主机上的目录挂载至VOLUME 指令指定的容器目录。即使容器后期被删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。而匿名卷则是由Docker自动生成的,并且没有自定义名字,只能通过自动分配的ID进行引用。匿名卷在容器删除时会被自动删除。
为了保留数据,可以使用命名卷代替匿名卷。
docker run -v my_volume:/path/in/container my_image
匿名卷格式:
VOLUME <容器内路径>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]
注意:
<容器内路径>如果在容器内不存在,在创建容器时会自动创建
<容器内路径>如果是存在的,同时目录内有内容,将会把此目录的内容复制到宿主机的实际目录
注意:
- Dockerfile中的VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系
- 通过docker rm -fv <容器ID> 可以删除容器的同时删除VOLUME指定的卷
示例:
[root@centos7 ~]#cat Dockerfile
FROM alpine
LABEL maintainer="yuhao20080808@qq.com"
COPY repositories /etc/apk/repositories
VOLUME [ "/test1","/test2" ]
12、 EXPOSE: 暴露端口
指定服务端的容器需要对外暴露(监听)的端口号,以实现容器与外部通信。EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会真正暴露端口,即不会自动在宿主进行端口映射。因此,在启动容器时需要通过 -P 或 -p ,Docker 主机才会真正分配一个端口转发到指定暴露的端口才可使用
注意: 即使 Dockerfile 没有 EXPOSE 端口指令,也可以通过docker run -p 临时暴露容器内程序真正监听的端口,所以EXPOSE 相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露
格式:
EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ..]
#说明
<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
示例:
[root@centos7 dockerfile]# echo Website in Dockerfile > index.html
[root@centos7 dockerfile]# vim Dockerfile
FROM busybox
LABEL maintainer="yuhao20080808@qq.com"
COPY index.html /data/website/
EXPOSE 80
[root@centos7 dockerfile]# docker build -t test:v1 .
[root@centos7 dockerfile]# docker run --rm -P --name test1 test:v1 /bin/httpd -f -h /data/website
13、WORKDIR: 指定工作目录
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,当容器运行后,进入容器内WORKDIR指定的默认目录
WORKDIR 指定工作目录(或称当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会自行创建,相当于创建一个目录然后cd到了创建的目录
格式:
WORKDIR /path/to/workdir
示例:
#如果想实现相同目录可以使用WORKDIR
WORKDIR /app
RUN echo "hello" > world.txt
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
#则最终路径为 /a/b/c
14、USER: 指定当前用户
指定运行容器的用户名或 UID,在后续dockerfile中的 RUN ,CMD和ENTRYPOINT指令时使用此用户
当服务不需要管理员权限时,可以通过该命令指定运行用户
这个用户必须是事先建立好的,否则无法切换。如果没有指定 USER,默认是 root 身份执行
格式:
USER <user>[:<group>]
USER <UID>[:<GID>]
示例:
RUN useradd loan -u 1911
RUN groupadd -r mysql && useradd -r -g mysql mysql
USER mysql
15、HEALTHCHECK: 健康检查
检查容器的健康性
格式:
HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令,如果命令执行失败,则返回1,即
unhealthy
HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK 支持下列选项:
--interval=<间隔> #两次健康检查的间隔,默认为 30 秒
--timeout=<时长> #健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默
认 30 秒
--retries=<次数> #当连续失败指定次数后,则将容器状态视为 unhealthy,默认3次
--start-period=<FDURATION> #default: 0s
#检查结果返回值:
0 #success the container is healthy and ready for use
1 #unhealthy the container is not working correctly
2 #reserved do not use this exit code
示例:
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/
e. Dockerfile 缓存机制
Dockerfile的缓存机制是指Docker在构建镜像时会缓存之前处理过的层(layer)以提高构建效率。具体地说,每条Dockerfile指令都会生成一个层(layer),并只有在这个层发生改变时才会重新运行该指令。因此,如果构建过程中某些指令没有改变,则Docker会直接使用之前生成的缓存层,从而加速构建过程。这种缓存机制对于频繁构建、频繁调试的场景尤其有用,它可以避免重复地执行较慢的构建命令,从而节省时间和资源。
注意:Dockerfile的缓存机制只是适用于构建过程中的单个镜像,对于多个镜像之间的依赖关系无法处理。如果多个镜像之间存在依赖关系,则需要手动保证每个镜像都重新构建以确保依赖关系正确。为了进一步减小缓存失效的影响,可以将经常变化的指令往后放置,将不变的指令往前放置,以提高重用率。
f、构建镜像docker build 命令
docker build命令使用Dockerfile文件创建镜像
命令格式:
docker build [OPTIONS] PATH | URL | -
说明:
PATH | URL | - #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取Dockerfile的内容
-f, --file string #Dockerfile文件名,默认为 PATH/Dockerfile
--force-rm #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache #不使用之前构建中创建的缓存
-q --quiet=false #不显示Dockerfile的RUN运行的输出结果
--rm=true #创建镜像成功时,删除临时容器
-t --tag list #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签默认为latest)
示例:
docker build . #构建出REPOSITORY和TAG都为<none>的镜像
docker build /usr/local/src/nginx #指定Dockerfile路径的方式
docker build -f /path/to/a/Dockerfile . # 指定Dockerfile绝对路径
docker build -t test/myapp . #默认标签为latest
docker build -t test/myapp:1.0.1 . #指定单个标签
docker build -t test/myapp:1.0.2 -t shykes/myapp:latest . #为同一个镜像创建两个标签
g、Dockerfile 制作基于基础镜像的Base镜像
准备目录结构,下载镜像并初始化系统
#按照业务类型或系统类型等方式划分创建目录环境,方便后期镜像比较多的时候进行分类
[root@ubuntu1804 ~]#mkdir /data/dockerfile/{web/{nginx,jdk},system/{centos,ubuntu}} -p
[root@ubuntu1804 ~]#tree /data/dockerfile/
/data/dockerfile/
├── system
│ ├── centos
│ └── ubuntu
└── web
├── jdk
├── nginx
#下载基础镜像
[root@ubuntu1804 ~]#docker pull centos:centos7.9.2009
#先制作基于基础镜像的系统base镜像
[root@ubuntu1804 ~]#cd /data/dockerfile/system/centos/
[root@ubuntu1804 centos]#vim Dockerfile
FROM centos:centos7.9.2009
LABEL maintainer="yuhao20080808@qq.com"
RUN yum -y install wget && rm -f /etc/yum.repos.d/* && \
wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/Centos-7.repo \
&& wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo \
&& sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/Centos-7.repo \
&& yum -y install vim-enhanced tcpdump lrzsz tree telnet bash-completion net-tools wget curl bzip2 lsof zip unzip nfs-utils gcc make gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel zlib-devel \
&& yum clean all \
&& rm -f /etc/localtime \
&& ln -s ../usr/share/zoneinfo/Asia/Shanghai /etc/localtime
[root@ubuntu1804 centos]# docker build -t centos7-base:v1 .
[root@ubuntu1804 centos]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7-base v1 19deb727d5c7 21 seconds ago 433MB
centos centos7.9.2009 eeb6ee3f44bd 20 months ago 204MB
最后编辑:于浩 更新时间:2024-05-20 16:57