docker_file就是docker脚本 链接到标题

基础知识:

  1. 每个docker_file必须以FROM开头,指定基础镜像,例如:FROM ubuntu:20.04
  2. 每个保留关键字必须大写,例如:RUN、COPY、CMD等
  3. 执行从上到下的顺序构建镜像,每个命令都会创建一个新的镜像层
  4. #表示注释,注释内容不会被执行
  5. 每个命令都会创建一个新的镜像层,尽量减少层数可以优化镜像大小和构建速度
FROM #指定基础镜像,一切都从这个镜像开始构建,例如:FROM ubuntu:20.04

# MAINTAINER #指定镜像的维护者,例如:MAINTAINER uogog 邮箱 已经废除

RUN #在镜像构建过程中执行命令,例如:RUN apt-get update && apt-get install -y nginx
注意:一个RUN命令会创建一个新的镜像层,尽量合并多个命令为一个RUN命令可以减少层数和优化镜像大小

ADD #将文件或目录添加到镜像中,例如:ADD ./app /app

WORKDIR #设置工作目录,例如:WORKDIR /app

EXPOSE #声明容器运行时监听的端口,例如:EXPOSE 80

CMD #指定容器启动时执行的命令,例如:CMD ["nginx", "-g", "daemon off;"]
注意:CMD可以被docker run命令行参数覆盖,例如:docker run myimage echo "Hello World"会覆盖CMD中的命令
多个CMD命令只有最后一个会生效,前面的CMD命令会被覆盖.
例如:CMD ["echo", "Hello"] CMD ["echo", "World"] 最终只会执行CMD ["echo", "World"]

ENTRYPOINT #指定容器启动时执行的命令,类似于CMD,但更适合不可变的命令,例如:ENTRYPOINT ["python", "app.py"]
甚至可以结合CMD定义默认参数
例如:ENTRYPOINT ["python", "app.py"] CMD ["--help"] 这样运行容器时默认会执行python app.py --help
但如果docker run myimage --version则会覆盖CMD中的参数,最终执行python app.py --version

ONBUILD #指定一个命令,当这个镜像被用作基础镜像时会执行,例如:ONBUILD RUN echo "This is an onbuild command"

COPY #将文件或目录复制到镜像中,例如:COPY ./app /app

ENV #设置环境变量,例如:ENV ENV_VAR=value
本参数使用很多,需要结合实际

VOLUME ["/data",""] # 注意所有对VOLUME的路径的更改比如RUN mkdir /data都要在VOLUME之前
否则会被覆盖掉,VOLUME会创建一个匿名卷并挂载到指定路径上
如果路径已经存在数据,则会将数据复制到匿名卷中,如果路径不存在,则会创建一个空目录并挂载到匿名卷中

ARG TT="d  a" # 这个是构建参数,可以在构建时通过--build-arg TT="value"来传递值
默认值是"d  a",注意ARG只能在构建阶段使用,不能在运行阶段使用
ARG可以不提供=后面的默认值,但如果在构建时没有传递值,则该参数的值将为空字符串

CMD和ENTRYPOINT的区别在于CMD可以被docker run命令行参数覆盖,而ENTRYPOINT则不行,ENTRYPOINT更适合定义一个固定的命令,而CMD更适合定义默认参数

***实战:***创建一个自己的UBUNTU

docker build -f my_dockerfile -t myubuntu:0.1 . # 这里.不能省,注意-t后面是镜像名字和版本号,-f后面是docker_file的名字
# 1. 明确版本
FROM ubuntu:22.04

# 2. 使用 LABEL 替代 MAINTAINER
LABEL maintainer="uogog <2011951@qq.com>"

# 3. 环境变量和工作目录
ENV MYPATH=/usr/local
WORKDIR $MYPATH

# 4. 合并安装(单层,无upgrade,清理缓存)
RUN apt-get update && \
    apt-get install -y \
        vim \
        net-tools \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# 5. 暴露端口(如有需要)
EXPOSE 80

# 6. 使用 ENTRYPOINT 脚本实现多命令(如果需要交互式bash)
COPY <<'EOF' /entrypoint.sh
#!/bin/bash
echo "MYPATH is: $MYPATH"
echo "...end..."
exec "$@"
EOF

RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/bash"]

还可以用docker history命令查看镜像的历史层信息,了解每个命令创建了哪些层,以及每层的大小和创建时间等信息

docker history image_name:tag # 查看镜像历史层信息

***例子:***部署tomcat

FROM ubuntu
LABEL maintainer="uogog <2011951@qq.com>"

COPY readme.md /readme.md

# ADD 文件名 目的路径 # ADD命令会自动解压tar文件,而COPY命令不会
ADD jdb-8.0.0.tar.gz /opt/tomcat # 将当前目录下的jdb-8.0.0.tar.gz文件添加到镜像的/opt/tomcat目录下,并自动解压
ADD apache-tomcat-8.5.73.tar.gz /opt/tomcat # 将当前目录下的apache-tomcat-8.5.73.tar.gz文件添加到镜像的/opt/tomcat目录下,并自动解压

RUN apt install vim -y

ENV MYPATH /opt/tomcat
WORKDIR $MYPATH

ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

EXPOSE 80 8080/tcp # 这个命令只是声明容器运行时监听的端口,并不会实际开放端口,只有在运行容器时使用-p或-P参数才会将容器端口映射到主机端口上

镜像发布

docker login --help # 先登录到Docker Hub,输入用户名和密码
# 然后
docker push uogog/myubuntu:0.1 # 将本地的myubuntu:0.1镜像推送到Docker Hub上的uogog账号下
# 这里需要tag一下,tag的格式是:docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

其实很多命令都没有细说,在真正的项目中会有很多细节需要注意,最好看看这个大佬的书 整体操作

RUN 执行命令 链接到标题

RUN 基本语法 链接到标题

RUN <command>
RUN ["executable", "param1", "param2"]
RUN 指令是 Dockerfile 中最常用的指令之一。它在 当前镜像层 之上创建一个新层,执行指定的命令,并提交结果。

注意:

  • shell形式的RUN指令会在一个新的shell中执行命令,之前的export的环境变量在新的shell中不可用,优点是管道和重定向等shell特性可以正常使用;
  • 而exec形式的RUN指令会直接执行指定的命令,不会启动新的shell,优点是更高效和更安全,但不支持shell特性,就是说环境变量不可用。
  • 实际应用中,注意apt-get update和apt-get install命令要放在同一个RUN指令中,后面最好接上清楚缓存的命令,以减少镜像层数。
  • 在使用shell方法时,建议先设置SHELL ["/bin/bash", “-o”,“pipefail”,"-c"] 来启用pipefail选项,这样可以确保管道中任何一个命令失败都会导致整个RUN指令失败,避免出现错误被忽略的情况。
  • 高级技巧,可以使用–mount=type=cache,target=/var/cache/apt来缓存apt-get的下载文件和–mount来安全使用ssh密钥。

COPY 复制操作 链接到标题

基本语法 链接到标题

COPY [选项] <源路径>... <目标路径>
COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]

这里体现了为什么build时要指定一个工作目录,因为COPY命令中的源路径是相对于这个工作目录的,如果不指定工作目录,默认就是当前目录。

ENTRYPOINT 入口点 链接到标题

入口点和CMD格式类似。 使用入口点只有很少的场景:

  • 要容器当作一个可执行文件来使用,例如:ENTRYPOINT [“python”, “app.py”] 这样运行容器时默认会执行python app.py
  • 要容器当启动脚本来使用,例如:ENTRYPOINT ["/entrypoint.sh"] 这样运行容器时默认会执行/entrypoint.sh脚本
  • 和CMD结合使用,定义默认参数,例如:ENTRYPOINT [“python”, “app.py”] CMD ["–help"] 这样运行容器时默认会执行python app.py –help 但如果docker run myimage –version则会覆盖CMD中的参数,最终执行python app.py –version
  • 启动前需要执行一些初始化操作,例如:ENTRYPOINT ["/entrypoint.sh"] 这样运行容器时会先执行/entrypoint.sh脚本进行初始化,然后再执行CMD中的命令

其实ENTRYPOINT很少使用,除非你需要定义一个固定的命令,并且不希望被覆盖,否则CMD就足够了。

HEALCHCHECK 健康检查 链接到标题

基本语法 HEALCHCHECK 链接到标题

HEALTHCHECK [选项] CMD <命令> # 这个命令会在容器运行时定期执行,用来检查容器的健康状态
HEALCHCHECK NONE

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -fs http://localhost/ || exit 1
# interval:指定健康检查的间隔时间,默认是30秒
# timeout:指定健康检查的超时时间,默认是30秒
# retries:指定健康检查失败后的重试次数,默认是3次
# start-period:指定容器启动后的宽限期,在此期间健康检查失败不会被计入重试次数,默认是0秒
# 返回0表示健康,返回非0表示不健康,返回1表示容器正在启动中

e.g. HTTP服务

HEALCHCHECK CMD curl -f http://localhost/ || exit 1
HEALCHCHECK CMD wget -q  --spider http://localhost/ || exit 1
HEALCHCHECK CMD ["脚本"]

一定不要搞一个超级复杂的健康检查命令,这样会增加容器的负担,甚至可能导致容器崩溃。健康检查应该尽量简单,快速,可靠,最好是一个简单的HTTP请求或者一个简单的脚本。