【Docker笔记04】【Dockerfile】

一、前言

本系列是根据 B 站 尚硅谷 Docker 视频 学习记录笔记。因为没有视频课件,部分内容摘自 https://www.yuque.com/tmfl/cloud/dketq0

本系列仅为自身学习笔记记录使用,记录存在偏差,推荐阅读原视频内容或本文参考笔记。


二、 Dockerfile 的构建过程

Dockerfile 时用来构建 Docker 镜像的文本文件,时由一条条构建镜像所需的指令和参数构成的脚本。

构建步骤:

  1. 编写Dockerfile文件
  2. docker build命令构建镜像
  3. docker run依据镜像运行容器实例

Dockerfile编写:

  • 每条保留字指令都必须为大写字母,且后面要跟随至少一个参数
  • 指令按照从上到下顺序执行
  • #表示注释
  • 每条指令都会创建一个新的镜像层并对镜像进行提交

Docker引擎执行Docker的大致流程:

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器做出修改
  3. 执行类似docker commit的操作提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个新容器
  5. 执行Dockerfile中的下一条指令,直到所有指令都执行完成

三、 Dockerfile 保留字

  1. FROM :基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板。Dockerfile第一条必须是FROM。格式如下:

    powershell 复制代码
    # 表示从基础镜像  amazoncorretto:8-al2-jdk 开始构建镜像
    FROM amazoncorretto:8-al2-jdk
  2. MAINTAINER :镜像维护者的姓名和邮箱地址(非必须),格式如下:

    powershell 复制代码
    # 非必须
    MAINTAINER ZhangSan zs@163.com
  3. RUN :容器构建时需要运行的命令(即在容器构建时可以指定执行一些 Shell 命令), 存在shell 格式 和 exec 格式两种格式。

    powershell 复制代码
    # 等同于在终端操作的shell命令
    # shell 格式 : RUN <命令行命令>
    RUN yum -y install vim
    
    #  exec 格式:RUN ["可执行文件" , "参数1", "参数2"]
    # 等价于 RUN ./test.php dev offline
    RUN ["./test.php", "dev", "offline"]  
  4. EXPOSE :当前容器对外暴露出的端口, 格式如下:

    powershell 复制代码
    EXPOSE 8080
  5. WORKDIR :指定在创建容器后, 终端默认登录进来的工作目录。格式如下:

    powershell 复制代码
    WORKDIR /usr/local/tomcat
  6. USER :指定该镜像以什么样的用户去执行,如果不指定,默认是root(一般不修改该配置)。格式如下:

    powershell 复制代码
    # USER <user>[:<group>]
    USER patrick
  7. ENV :用来在构建镜像过程中设置环境变量。这个环境变量可以在后续的任何RUN指令或其他指令中使用,如下,和 WORKDIR 命令联合使用:

    powershell 复制代码
    ENV CATALINA_HOME /usr/local/tomcat
    ENV PATH $CATALINA_HOME/bin:$PATH
    RUN mkdir -p "$CATALINA_HOME"
    WORKDIR $CATALINA_HOME
  8. VOLUME :容器数据卷,用于数据保存和持久化工作。类似于 docker run 的-v参数。格式如下:

    powershell 复制代码
    # VOLUME 挂载点
    # 挂载点可以是一个路径,也可以是数组(数组中的每一项必须用双引号)
    VOLUME /var/lib/mysql
  9. ADD :将宿主机目录下(或远程文件)的文件拷贝进镜像,且会自动处理URL和解压tar压缩包。

  10. COPY :类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源路径>的文件目录复制到新的一层镜像内的<目标路径>位置。

    powershell 复制代码
    COPY src dest
    COPY ["src", "dest"]
    # <src源路径>:源文件或者源目录
    # <dest目标路径>:容器内的指定路径,该路径不用事先建好。如果不存在会自动创建
  11. CMD :指定容器启动后要干的事情。也存在shell 格式和 exec 格式。 需要注意的是 Dockerfile 中可以有多个 CMD 命令,但是只有最后一个生效, CMD 会被 docker run 之后的参数替换。

    如下在 Tomcat8 的 Dockerfile 文件中最后两行内容如下

    powershell 复制代码
    # 暴露 8080 端口
    EXPOSE 8080
    # 在容器启动后执行 catalina.sh 脚本
    CMD ["catalina.sh", "run"]

    所以当我们通过 docker run -it -p 8080:8080 [容器ID] 命令启动 tomcat 时,Tomcat 的 Dockerfile 脚本在容器启动后会执行 catalina.sh 脚本完成 Tomcat 的启动。而如果我们通过docker run -it -p 8080:8080 [容器ID] /bin/bash 命令启动 Tomcat 容器时,相当于在原先的 Dockerfile 后追加了一条命令 CMD ["bin/bash", "run"]。而因为多个 CMD 命令只会生效最后一个则会导致 Tomcat 容器通过该命令启动后 Tomcat 服务并未启动。

  12. ENTRYPOINT :用来指定一个容器启动时要运行的命令。类似于CMD命令,但是ENTRYPOINT不会被docker run 后面的命令覆盖,这些命令参数会被当做参数送给ENTRYPOINT指令指定的程序。ENTRYPOINT可以和CMD一起用,一般是可变参数才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行期命令,而是将CMD的内容作为参数传递给ENTRYPOINT指令,它们两个组合会变成 <ENTRYPOINT> "<CMD>"

    powershell 复制代码
    FROM nginx
    
    ENTRYPOINT ["nginx", "-c"]  # 定参
    CMD ["/etc/nginx/nginx.conf"] # 变参

    对于此Dockerfile,构建成镜像 nginx:test后,如果执行;

    • docker run nginx test,则容器启动后,会执行 nginx -c /etc/nginx/nginx.conf。因为当 ENTRYPOINT 和 CMD一起使用时则 CMD 会作为 ENTRYPOINT 的参数使用。
    • docker run nginx:test /app/nginx/new.conf,则容器启动后,会执行 nginx -c /app/nginx/new.conf

Tomcat 官网的 Dockerfile 内容如下 : 可以作为熟悉语法的内容 :

shell 复制代码
#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#

FROM amazoncorretto:8-al2-jdk

ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
RUN mkdir -p "$CATALINA_HOME"
WORKDIR $CATALINA_HOME

# let "Tomcat Native" live somewhere isolated
ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib
ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR

# see https://www.apache.org/dist/tomcat/tomcat-9/KEYS
# see also "versions.sh" (https://github.com/docker-library/tomcat/blob/master/versions.sh)
ENV GPG_KEYS 48F8E69F6390C9F25CFEDCD268248959359E722B A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243

ENV TOMCAT_MAJOR 9
ENV TOMCAT_VERSION 9.0.87
ENV TOMCAT_SHA512 71a64fe805aab89ef4798571d860a3c9a4f751f808921559a9249305abb205836de33ab89bb33b625a77f799f193d6bffbe94aadf293866df756d708f5bfb933

RUN set -eux; \
	\
# http://yum.baseurl.org/wiki/YumDB.html
	if ! command -v yumdb > /dev/null; then \
		yum install -y --setopt=skip_missing_names_on_install=False yum-utils; \
		yumdb set reason dep yum-utils; \
	fi; \
# a helper function to "yum install" things, but only if they aren't installed (and to set their "reason" to "dep" so "yum autoremove" can purge them for us)
	_yum_install_temporary() { ( set -eu +x; \
		local pkg todo=''; \
		for pkg; do \
			if ! rpm --query "$pkg" > /dev/null 2>&1; then \
				todo="$todo $pkg"; \
			fi; \
		done; \
		if [ -n "$todo" ]; then \
			set -x; \
			yum install -y --setopt=skip_missing_names_on_install=False $todo; \
			yumdb set reason dep $todo; \
		fi; \
	) }; \
	_yum_install_temporary gzip tar; \
	\
	ddist() { \
		local f="$1"; shift; \
		local distFile="$1"; shift; \
		local mvnFile="${1:-}"; \
		local success=; \
		local distUrl=; \
		for distUrl in \
# https://apache.org/history/mirror-history.html
			"https://dlcdn.apache.org/$distFile" \
# if the version is outdated, we have to pull from the archive
			"https://archive.apache.org/dist/$distFile" \
# if all else fails, let's try Maven (https://www.mail-archive.com/users@tomcat.apache.org/msg134940.html; https://mvnrepository.com/artifact/org.apache.tomcat/tomcat; https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/)
			${mvnFile:+"https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/$mvnFile"} \
		; do \
			if curl -fL -o "$f" "$distUrl" && [ -s "$f" ]; then \
				success=1; \
				break; \
			fi; \
		done; \
		[ -n "$success" ]; \
	}; \
	\
	ddist 'tomcat.tar.gz' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz"; \
	echo "$TOMCAT_SHA512 *tomcat.tar.gz" | sha512sum --strict --check -; \
	ddist 'tomcat.tar.gz.asc' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz.asc"; \
	export GNUPGHOME="$(mktemp -d)"; \
	for key in $GPG_KEYS; do \
		gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \
	done; \
	gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; \
	tar -xf tomcat.tar.gz --strip-components=1; \
	rm bin/*.bat; \
	rm tomcat.tar.gz*; \
	rm -rf "$GNUPGHOME"; \
	\
# https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html#Default_web_applications
	mv webapps webapps.dist; \
	mkdir webapps; \
# we don't delete them completely because they're frankly a pain to get back for users who do want them, and they're generally tiny (~7MB)
	\
	nativeBuildDir="$(mktemp -d)"; \
	tar -xf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1; \
	_yum_install_temporary \
		apr-devel \
		gcc \
		make \
		openssl11-devel \
	; \
	( \
		export CATALINA_HOME="$PWD"; \
		cd "$nativeBuildDir/native"; \
		aprConfig="$(command -v apr-1-config)"; \
		./configure \
			--libdir="$TOMCAT_NATIVE_LIBDIR" \
			--prefix="$CATALINA_HOME" \
			--with-apr="$aprConfig" \
			--with-java-home="$JAVA_HOME" \
			--with-ssl \
		; \
		nproc="$(nproc)"; \
		make -j "$nproc"; \
		make install; \
	); \
	rm -rf "$nativeBuildDir"; \
	rm bin/tomcat-native.tar.gz; \
	\
# mark any explicit dependencies as manually installed
	find "$TOMCAT_NATIVE_LIBDIR" -type f -executable -exec ldd '{}' ';' \
		| awk '/=>/ && $(NF-1) != "=>" { print $(NF-1) }' \
		| xargs -rt readlink -e \
		| sort -u \
		| xargs -rt rpm --query --whatprovides \
		| sort -u \
		| tee "$TOMCAT_NATIVE_LIBDIR/.dependencies.txt" \
		| xargs -r yumdb set reason user \
	; \
	\
# clean up anything added temporarily and not later marked as necessary
	yum autoremove -y; \
	yum clean all; \
	rm -rf /var/cache/yum; \
	\
# sh removes env vars it doesn't support (ones with periods)
# https://github.com/docker-library/tomcat/issues/77
	find ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +; \
	\
# fix permissions (especially for running as non-root)
# https://github.com/docker-library/tomcat/issues/35
	chmod -R +rX .; \
	chmod 1777 logs temp work; \
	\
# smoke test
	catalina.sh version

# verify Tomcat Native is working properly
RUN set -eux; \
	nativeLines="$(catalina.sh configtest 2>&1)"; \
	nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')"; \
	nativeLines="$(echo "$nativeLines" | sort -u)"; \
	if ! echo "$nativeLines" | grep -E 'INFO: Loaded( APR based)? Apache Tomcat Native library' >&2; then \
		echo >&2 "$nativeLines"; \
		exit 1; \
	fi

EXPOSE 8080
CMD ["catalina.sh", "run"]

四、Dockerfile 构建

1. 基础构建

Dockerfile 的构建通过 docker build 命令实现,如下:

powershell 复制代码
# 注意:定义的TAG后面有个空格,空格后面有个点
# docker build -t 新镜像名字:TAG .
docker build -t ubuntu:1.0.1 .

如下例子:通过 Dockerfile 基于 centos 7 镜像新建一个支持 vim、 net-tools 以及 JDK 的镜像。

powershell 复制代码
# 在 jdk 相对路径下建立
[root@localhost jdk]# ll
总用量 189604
drwxr-xr-x. 7   10  143       245 10月  5 2019 jdk1.8
-rw-r--r--. 1 root root 194151339 10月 14 13:55 jdk-8u231-linux-x64.tar.gz
# 新建编辑 Dockerfile 文件
[root@localhost jdk]# vim Dockerfile
# 查看当前存在镜像
[root@localhost jdk]# docker images 
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
tomcat       latest    fb5657adc892   2 years ago   680MB
mysql        5.7       c20987f18b13   2 years ago   448MB
ubuntu       latest    ba6acccedd29   2 years ago   72.8MB
centos       7         eeb6ee3f44bd   2 years ago   204MB
redis        6.0.8     16ecd2772934   3 years ago   104MB
# 构建新镜像
[root@localhost jdk]# docker build -t ubuntu-plus:1.0.0 .
[+] Building 0.0s (11/11) FINISHED                                                                                                           docker:default
 => [internal] load build definition from Dockerfile                                                                                                   0.0s
 => => transferring dockerfile: 830B                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/centos:7                                                                                            0.0s
 => [internal] load .dockerignore                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                        0.0s
 => [1/6] FROM docker.io/library/centos:7                                                                                                              0.0s
 => [internal] load build context                                                                                                                      0.0s
 => => transferring context: 107B                                                                                                                      0.0s
 => CACHED [2/6] WORKDIR /usr/local                                                                                                                    0.0s
 => CACHED [3/6] RUN yum -y install vim                                                                                                                0.0s
 => CACHED [4/6] RUN yum -y install net-tools                                                                                                          0.0s
 => CACHED [5/6] RUN mkdir /usr/local/java                                                                                                             0.0s
 => CACHED [6/6] ADD jdk-8u231-linux-x64.tar.gz /usr/local/java                                                                                        0.0s
 => exporting to image                                                                                                                                 0.0s
 => => exporting layers                                                                                                                                0.0s
 => => writing image sha256:83f22e20f9477993e092720eee52962161c2a30e32294e61d3fe251b3ad7eebb                                                           0.0s
 => => naming to docker.io/library/ubuntu-plus:1.0.0                                                                                                   0.0s
# 构建成功,可以看到新镜像
[root@localhost jdk]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
ubuntu-plus   1.0.0     83f22e20f947   5 minutes ago   1.12GB
tomcat        latest    fb5657adc892   2 years ago     680MB
mysql         5.7       c20987f18b13   2 years ago     448MB
ubuntu        latest    ba6acccedd29   2 years ago     72.8MB
centos        7         eeb6ee3f44bd   2 years ago     204MB
redis         6.0.8     16ecd2772934   3 years ago     104MB
# 可以正常进,并且支持上述新增功能
[root@localhost jdk]# docker run -it ubuntu-plus:1.0.0 /bin/bash

上面例子中的 Dockerfile 内容如下:

powershell 复制代码
FROM centos:7
# 执行作者和邮箱
MAINTAINER kingfish<kingfish@xxx.com>
# 设置环境变量
ENV MYPATH /usr/local
# 设置容器进入后的默认路径
WORKDIR $MYPATH
# 安装 net-tools 和 vim
RUN yum -y install vim
RUN yum -y install net-tools

# 安装 JDK8
# RUN yum -y install glibc.i868
RUN mkdir /usr/local/java
# 将 宿主机的 jdk 文件拷贝到容器内部. Add 是相对路径 jar, 安装包必须要和 Dockerfile 放同一文件夹下
ADD jdk-8u231-linux-x64.tar.gz /usr/local/java
# 配置环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_231
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH

EXPOSE 80

CMD echo $MYPATH
CMD echo "install ifconfig cmd into ubuntu success ....."
CMD /bin/bash

2. 微服务打包成 Dockerfile

  1. 新建SpringBoot服务并打包为 spring-simple-demo.jar,移动到宿主机上,本文不再展示具体服务搭建过程

  2. 编写 Dockerfile,文件内容如下:

    powershell 复制代码
    # 依赖于 JDK 8
    FROM jdk:8
    MAINTAINER kingfish
    # 在主机 /var/lib/docker目录下创建一个临时文件,并链接到容器的 /tmp
    VOLUME /tmp
    
    # 将jar包添加到容器中,并命名为 spring-simple-demo.jar
    ADD spring-simple-demo.jar /spring-simple-demo.jar
    # 运行jar包
    RUN bash -c 'touch /spring-simple-demo.jar'
    ENTRYPOINT ["java", "-jar", "/spring-simple-demo.jar"]
    
    # 暴露服务端口(服务本身暴露9090端口)
    EXPOSE 9090
  3. 构建镜像

    powershell 复制代码
    [root@192 spring-demo]# vim Dockerfile 
    # 构建镜像,指定镜像名和版本号
    [root@192 spring-demo]#  docker build -t simple_demo_docker:1.0 .
    [+] Building 15.8s (8/8) FINISHED                                                                                      docker:default
     => [internal] load build definition from Dockerfile                                                                             0.0s
     => => transferring dockerfile: 588B                                                                                             0.0s
     => [internal] load metadata for docker.io/library/java:8                                                                       15.2s
     => [internal] load .dockerignore                                                                                                0.0s
     => => transferring context: 2B                                                                                                  0.0s
     => [internal] load build context                                                                                                0.0s
     => => transferring context: 103B                                                                                                0.0s
     => CACHED [1/3] FROM docker.io/library/java:8@sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d           0.0s
     => [2/3] ADD spring-simple-demo.jar /spring-simple-demo.jar                                                                     0.1s
     => [3/3] RUN bash -c 'touch /spring-simple-demo.jar'                                                                            0.3s
     => exporting to image                                                                                                           0.1s
     => => exporting layers                                                                                                          0.1s
     => => writing image sha256:bb3a29e0a820244956ee052546902f06837b2395deb84bab2f4215b838e8fada                                     0.0s
     => => naming to docker.io/library/simple_demo_docker:1.0                                                                        0.0s
     # 查看镜像
    [root@192 spring-demo]# docker images
    REPOSITORY           TAG       IMAGE ID       CREATED         SIZE
    simple_demo_docker   1.0       bb3a29e0a820   6 seconds ago   749MB
  4. 启动镜像并测试

    powershell 复制代码
    # 启动容器
    [root@192 spring-demo]# docker run -p 9090:9090 -it simple_demo_docker:1.0  /bin/bash
    # 调用服务暴露接口,可以正常返回结果
    [root@192 spring-demo]# curl 127.0.0.1:9090/simple-demo/demo/getByDb
    [{"id":232,"name":null,"password":null,"roleId":null,"userId":"1"},{"id":233,"name":null,"password":null,"roleId":null,"userId":"0"},{"id":234,"name":null,"password":null,"roleId":null,"userId":"2"},{"id":235,"name":null,"password":null,"roleId":null,"userId":"3"},{"id":236,"name":null,"password":null,"roleId":null,"userId":"4"},{"id":237,"name":null,"password":null,"roleId":null,"userId":"5"},{"id":238,"name":null,"password":null,"roleId":null,"userId":"7"},{"id":239,"name":null,"password":null,"roleId":null,"userId":"6"},{"id":240,"name":null,"password":null,"roleId":null,"userId":"8"},{"id":241,"name":null,"password":null,"roleId":null,"userId":"9"}][root@192 spring-demo]# 

五、虚悬镜像

虚悬镜像:仓库名、标签名都是 的镜像,称为 dangling images(虚悬镜像)。

在构建或者删除镜像时可能由于一些错误导致出现虚悬镜像。如:

构建时候没有镜像名、tag

powershell 复制代码
docker build .

列出docker中的虚悬镜像:

powershell 复制代码
docker image ls -f dangling=true

虚悬镜像一般是因为一些错误而出现的,没有存在价值,可以删除:

powershell 复制代码
# 删除所有的虚悬镜像
docker image prune

六、参考内容

B 站 尚硅谷 Docker 视频
https://www.yuque.com/tmfl/cloud/dketq0

相关推荐
Hommy8835 分钟前
【开源剪映小助手】Docker 部署
docker·容器·开源·github·aigc
Keep Running *1 小时前
Spring Cloud Alibaba_学习笔记
笔记·学习
我不是懒洋洋1 小时前
AI的影响6
笔记
斯普信云原生组2 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器
喵了几个咪2 小时前
如何在 Superset Docker 容器中安装 MySQL 驱动
mysql·docker·容器·superset
工具罗某人3 小时前
docker compose部署kafka集群搭建
docker·容器·kafka
咖啡忍者4 小时前
【SAP CO】4.COPC产品成本控制-3.WIP后台配置
笔记
CheerWWW6 小时前
深入理解计算机系统——位运算、树状数组
笔记·学习·算法·计算机系统
中屹指纹浏览器6 小时前
2026浏览器指纹检测技术演进与多账号反检测实战策略
经验分享·笔记
独小乐7 小时前
012.整体框架适配SDRAM|千篇笔记实现嵌入式全栈/裸机篇
c语言·汇编·笔记·单片机·嵌入式硬件·arm·gnu