docker入门 更新镜像 构建镜像

A 构建镜像

1 创建方式

  • 更新镜像:使用docker commit命令

  • 构建镜像:使用docker build命令,需要创建Dockerfile文件

2 Dockerfile

Dockerfile是用来构建Docker镜像的文件,是由一系列指令和参数构成的脚本,从FROM命令开始,紧接着各种命令、参数等,最终会生成一个新的镜像。

第一条指令必须是FROM,指定Base Image基础镜像,指令按从上往下的顺序,依次执行,每条指令都会创建一个新的镜像层并提交

FROM:指定基础镜像,即当前新镜像是基于哪个镜像的
MAINTAINER:指定作者
RUN:指定镜像构建过程中要运行的命令
ENV:设置环境变量
WORKDIR:指定默认的工作目录,即进入容器后默认进入的目录
VOLUME:创建挂载点,也称容器数据卷,用于数据共享和持久化
CMD:指定容器启动时要运行的命令,与RUN不同的是, 这些命令不是在镜像构建过程中执行的
ENTRYPOINT:指定容器启动时要运行的命令
COPY:拷贝文件/目录到镜像中
ADD:拷贝文件到镜像中,且会自动解压缩
EXPOSE:指定对外暴露的端口

分析Dockerfile

分析Dockerfile(tomcat:8.5.16-jre8)如下

原文地址:https://www.cnblogs.com/dfengwei/p/7144937.html

Dockerfile地址:https://github.com/docker-library/tomcat/blob/master/8.5/jre8/Dockerfile

注意,这里以master分支的Dockerfile链接为例,和DockerHub上的Dockerfile链接可能会不一致

#本镜像的基础镜像。有兴趣的话,可以自行在DockerHub上搜索openjdk,分析官方的openjdk镜像的Dockerfile文件。这里为什么不用oracle提供的jdk(jre)?简单地讲,版权问题。
FROM openjdk:8-jre

#声明CATALINA_HOME环境变量,这个变量大家都了解。
ENV CATALINA_HOME /usr/local/tomcat
#将Tomcat下的bin路径加入到PATH环境变量中。
ENV PATH $CATALINA_HOME/bin:$PATH
#创建tomcat路径。
RUN mkdir -p "$CATALINA_HOME"
#指定RUN、CMD、ENTRYPOINT命令的当前工作路径。
WORKDIR $CATALINA_HOME

#Tomcat Native路径配置。
ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib
#将TOMCAT_NATIVE_LIBDIR加入到LD_LIBRARY_PATH环境变量中,这样Tomcat在查找Tomcat Native相关的动态链接库时,会去查找TOMCAT_NATIVE_LIBDIR环境变量指定的路径。
ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR

#检查、更新OpenSSL,这块的细节我没深究。
ENV OPENSSL_VERSION 1.1.0f-3
RUN { \
		echo 'deb http://deb.debian.org/debian stretch main'; \
	} > /etc/apt/sources.list.d/stretch.list \
	&& { \
		echo 'Package: *'; \
		echo 'Pin: release n=stretch'; \
		echo 'Pin-Priority: -10'; \
		echo; \
		echo 'Package: openssl libssl*'; \
		echo "Pin: version $OPENSSL_VERSION"; \
		echo 'Pin-Priority: 990'; \
	} > /etc/apt/preferences.d/stretch-openssl
RUN apt-get update && apt-get install -y --no-install-recommends \
		libapr1 \
		openssl="$OPENSSL_VERSION" \
	&& rm -rf /var/lib/apt/lists/*

#从key服务器导入key,用于验证tomcat压缩文件的签名,这块也没深究。
ENV GPG_KEYS 05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23
RUN set -ex; \
	for key in $GPG_KEYS; do \
		gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
	done

#Tomcat相关文件的版本。
ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.5.16

#Tomcat相关文件下载地址。
ENV TOMCAT_TGZ_URL https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz
ENV TOMCAT_ASC_URL https://www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc

#执行命令
RUN set -x \
	\
	#下载Tomcat压缩文件
	&& wget -O tomcat.tar.gz "$TOMCAT_TGZ_URL" \
	&& wget -O tomcat.tar.gz.asc "$TOMCAT_ASC_URL" \
	#进行签名验证
	&& gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz \
	#解压Tomcat
	&& tar -xvf tomcat.tar.gz --strip-components=1 \
	# 删除供Windows系统使用的.bat文件
	&& rm bin/*.bat \
	# 删除压缩文件
	&& rm tomcat.tar.gz* \
	\
	#安装Tomcat Native
	&& nativeBuildDir="$(mktemp -d)" \
	&& tar -xvf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1 \
	&& nativeBuildDeps=" \
		dpkg-dev \
		gcc \
		libapr1-dev \
		libssl-dev \
		make \
		openjdk-${JAVA_VERSION%%[-~bu]*}-jdk=$JAVA_DEBIAN_VERSION \
	" \
	&& apt-get update && apt-get install -y --no-install-recommends $nativeBuildDeps && rm -rf /var/lib/apt/lists/* \
	&& ( \
		export CATALINA_HOME="$PWD" \
		&& cd "$nativeBuildDir/native" \
		&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
		&& ./configure \
			--build="$gnuArch" \
			--libdir="$TOMCAT_NATIVE_LIBDIR" \
			--prefix="$CATALINA_HOME" \
			--with-apr="$(which apr-1-config)" \
			--with-java-home="$(docker-java-home)" \
			--with-ssl=yes \
		&& make -j "$(nproc)" \
		&& make install \
	) \
	&& apt-get purge -y --auto-remove $nativeBuildDeps \
	&& rm -rf "$nativeBuildDir" \
	&& rm bin/tomcat-native.tar.gz

#验证Tomcat Native是否安装成功
RUN set -e \
	&& nativeLines="$(catalina.sh configtest 2>&1)" \
	&& nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')" \
	&& nativeLines="$(echo "$nativeLines" | sort -u)" \
	&& if ! echo "$nativeLines" | grep 'INFO: Loaded APR based Apache Tomcat Native library' >&2; then \
		echo >&2 "$nativeLines"; \
		exit 1; \
	fi

#暴露8080端口
EXPOSE 8080
#容器启动时执行的命令。
CMD ["catalina.sh", "run"]

此Dockerfile的主要功能可归纳为:

  1. 以openjdk镜像为基础镜像进行构建。
  2. 安装Tomcat Native,及其依赖库(比如APR、OpenSSL等)。验证是否正确安装。
  3. 下载Tomcat,检查签名,解压、清除无用文件等。
  4. 暴露8080端口,配置入口命令。

3 自定义tomcat镜像

原文地址:https://www.cnblogs.com/uncleyong/p/8894133.html

3.1 创建Dockerfile文件

[root@localhost mydocker]# pwd
/data/mydocker
[root@localhost mydocker]# ll
total 193708
-rw-r--r-- 1 root root  10835556 Apr 12  2019 apache-tomcat-9.0.19.tar.gz
-rwxr--r-- 1 root root       446 Dec 20 16:44 Dockerfile
-rwxr--r-- 1 root root 187513052 Dec 20 16:35 openjdk-11.0.2_linux-x64_bin.tar.gz

Dockerfile文件内容

FROM centos
MAINTAINER testname
 
# 拷贝文件
ADD openjdk-11.0.2_linux-x64_bin.tar.gz /usr/local
ADD apache-tomcat-9.0.19.tar.gz /usr/local
 
# 配置环境变量
ENV JAVA_HOME /usr/local/jdk-11.0.2
ENV CLASSPATH .:$JAVA_HOME/lib
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.19
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
 
WORKDIR $CATALINA_HOME
 
EXPOSE 8080
 
# 镜像运行容器就把tomcat启动起来
CMD ["catalina.sh", "run"]

3.2 docker build

查看docker build使用方法

docker build --help

执行docker build -t (tag名)

[root@localhost mydocker]# docker build -t zhang/tomcat:1.0 .               
Sending build context to Docker daemon  198.4MB
Step 1/11 : FROM centos
latest: Pulling from library/centos
a1d0c7532777: Pull complete 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
 ---> 5d0da3dc9764
Step 2/11 : MAINTAINER zhanglei
 ---> Running in df0dfb66a62d
Removing intermediate container df0dfb66a62d
 ---> 1e7053949435
Step 3/11 : ADD openjdk-11.0.2_linux-x64_bin.tar.gz /usr/local
 ---> 9b98a2af2ef3
Step 4/11 : ADD apache-tomcat-9.0.19.tar.gz /usr/local
 ---> 513dc1c01353
Step 5/11 : ENV JAVA_HOME /usr/local/jdk-11.0.2
 ---> Running in 91e95bd841be
Removing intermediate container 91e95bd841be
 ---> 39c98b82022f
Step 6/11 : ENV CLASSPATH .:$JAVA_HOME/lib
 ---> Running in f59e05ae989e
Removing intermediate container f59e05ae989e
 ---> edc68b5e462c
Step 7/11 : ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.19
 ---> Running in b22b8f0298a1
Removing intermediate container b22b8f0298a1
 ---> 2841c3a092c9
Step 8/11 : ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
 ---> Running in f6ebcd63c8c2
Removing intermediate container f6ebcd63c8c2
 ---> 6580c4b7e4e5
Step 9/11 : WORKDIR $CATALINA_HOME
 ---> Running in 8af75ddd7f62
Removing intermediate container 8af75ddd7f62
 ---> caedcd4bd116
Step 10/11 : EXPOSE 8080
 ---> Running in b0353ea35616
Removing intermediate container b0353ea35616
 ---> 27d1ab33db33
Step 11/11 : CMD ["catalina.sh", "run"]
 ---> Running in 7281f2f1e31a
Removing intermediate container 7281f2f1e31a
 ---> b0c823dc19cc
Successfully built b0c823dc19cc
Successfully tagged zhang/tomcat:1.0
[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
zhang/tomcat        1.0                 b0c823dc19cc        9 seconds ago       556MB
centos              latest              5d0da3dc9764        3 months ago        231MB
gogs/gogs           latest              8ec7cc11f4a3        7 months ago        93.9MB

运行容器

docker run \
--name zhang_tomcat \
-p 8181:8080 \
-d zhang/tomcat:1.0

访问url,成功显示tomcat主页,创建镜像成功!

B Dockfile指令详解

参考资料:https://www.yuque.com/grasilife/docker/image-dockerfile-readme

1 COPY 复制文件

格式:

  • COPY <源路径>... <目标路径>
  • COPY ["<源路径1>",... "<目标路径>"]

和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。

COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:

COPY package.json /usr/src/app/

<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

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

<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。
目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。
这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。

2 ADD 更高级的复制文件

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。

比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。
下载后的文件权限自动设置为600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。
所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。
因此,这个功能其实并不实用,而且不推荐使用。

如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。
在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu 中:

FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...

但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 ADD 命令了。

在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。

另外需要注意的是,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。

因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。

3 CMD 容器启动命令

CMD 指令的格式和 RUN 相似,也是两种格式:

  • shell 格式:CMD <命令>
  • exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
  • 参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。

之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。
既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。
CMD 指令就是用于指定默认的容器主进程的启动命令的。

在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。
我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。
这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。

在指令格式上,一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号。

如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:

CMD echo $HOME

在实际执行中,会将其变更为:

CMD [ "sh", "-c", "echo $HOME" ]

这就是为什么我们可以使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理。

提到 CMD 就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。

Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念。

一些初学者将 CMD 写为:

CMD service nginx start

然后发现容器执行后就立即退出了。甚至在容器内去使用 systemctl 命令结果却发现根本执行不了。
这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
而使用 service nginx start 命令,则是希望 upstart 来以后台守护进程形式启动 nginx 服务。
而刚才说了 CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"],因此主进程实际上是 sh。那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。
正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:

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

4 VOLUME 定义匿名卷

  • 格式为:VOLUME ["<路径1>", "<路径2>"...]
  • VOLUME <路径>
    之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
VOLUME /data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data
中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。
比如:

docker run -d -v mydata:/data xxxx

在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

C 更新镜像

1 进入指定容器伪终端

[root@localhost mydocker]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                            NAMES
25fae95b650c        zhang/tomcat:1.0    "catalina.sh run"        3 hours ago         Up 3 hours          0.0.0.0:8181->8080/tcp                           zhang_tomcat


[root@localhost mydocker]# docker exec -it 25fae95b650c  /bin/bash
[root@25fae95b650c apache-tomcat-9.0.19]# pwd
/usr/local/apache-tomcat-9.0.19
[root@25fae95b650c apache-tomcat-9.0.19]# ls
BUILDING.txt  CONTRIBUTING.md  LICENSE  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  bin  conf  lib  logs  temp  webapps  work

2 修改:这里复制了目录webapps,新目录名webapps_bak

[root@25fae95b650c apache-tomcat-9.0.19]# cp -r webapps webapps_bak
[root@25fae95b650c apache-tomcat-9.0.19]# ls
BUILDING.txt  CONTRIBUTING.md  LICENSE  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  bin  conf  lib  logs  temp  webapps  webapps_bak  work
[root@25fae95b650c apache-tomcat-9.0.19]# exit

3 docker commit

# 语法:docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# -a :提交的镜像作者;
# -c :使用Dockerfile指令来创建镜像;
# -m :提交时的说明文字;
# -p :在commit时,将容器暂停。
[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
zhang/tomcat        1.0                 b0c823dc19cc        3 hours ago         556MB
centos              latest              5d0da3dc9764        3 months ago        231MB
gogs/gogs           latest              8ec7cc11f4a3        7 months ago        93.9MB
[root@localhost mydocker]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                            NAMES
25fae95b650c        zhang/tomcat:1.0    "catalina.sh run"        3 hours ago         Up 3 hours          0.0.0.0:8181->8080/tcp                           zhang_tomcat

[root@localhost mydocker]# docker commit -m='cp -r webapps webapps_bak' -a='zhanglei' 25fae95b650c zhang/tomcat:2.0
sha256:f375fea412a39898806339339442b55d8ee8e21b4a5874e518abb84298b1f446
[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
zhang/tomcat        2.0                 f375fea412a3        4 seconds ago       561MB
zhang/tomcat        1.0                 b0c823dc19cc        3 hours ago         556MB
centos              latest              5d0da3dc9764        3 months ago        231MB
gogs/gogs           latest              8ec7cc11f4a3        7 months ago        93.9MB
posted @ 2021-12-20 20:15  默默雷  阅读(266)  评论(0编辑  收藏  举报