Dockerfile构建镜像
Dockerfile构建镜像的流程
- 首先 ,docker为CS架构(客户端-服务端),在虚拟机使用docker build, docker引擎会将Dockerfile的上下文目录(即Dockerfile所在的目录)的所有数据打包发送给docker服务端。
- 接着,服务端开始运行dockerfile的内容,每执行dockerfile的一行代码,就会生成一个中间镜像层,当执行下一行代码时,就会生成一个在上一层基础上进行修改的中间镜像层,每一层都是在上一层的基础上进行修改而生成的,这也是为什么构建过程有"层叠"的概念。
- 最后,当执行到最后一行,最后一次创建的中间镜像层就会成为最终镜像。它代表了 Dockerfile 中所有指令的执行结果和镜像的最终状态。
- 注意:这些产生的中间镜像层(包括最终镜像)会被缓存到Docker本地镜像仓库里,如果不想要这些缓存,比如说想构建一个同名但是全新(可以理解dockerfile的代码发生变化)的镜像,就要在dockerbuild后加一个参数-no-cache,加了会忽略本地镜像缓存。
Dockerfile命令
FROM
- Dockerfile的第一条指令必须为From,如果创建多个镜像在同一个dockerfile,就可以用多个FROM,一个FROM代表一个镜像。
- 语法格式:
css
FROM 镜像名称
# 或者
FROM 镜像名称:版本号
MAINTAINER
- 用于指定维护者信息,也可以叫作者信息
- 语法格式:
less
MAINTAINER 维护者信息(eg:yourName yourQQ@qq.com)
RUN
- RUN 指令将在当前镜像基础上执行指定命令,并提交为新的中间镜像层,当命令较长时可以使用 \ 来换行
- 语法格式:
bash
RUN 执行的命令
#举例
RUN echo 'Asia/Shanghai' >/etc/timezone
VOLUME
- 基于镜像创建的容器添加数据卷,通过挂载将外部的目录或者其他容器的数据卷映射到该路径上,从而实现对数据的持久化
- 语法格式:
bash
VOLUME 外部路径:容器路径
#举例
VOLUME /tmp
- 举例中只写了一个路径的话就代表容器路径,会创建一个匿名卷挂载到容器的tmp目录,这样,容器中对 /tmp 目录的读写操作将直接映射到匿名卷上。
- 默认情况下,Docker 会将匿名卷存储在位于宿主机的 /var/lib/docker/volumes/ 目录下。每个匿名卷都会以一个随机生成的唯一名称来标识,并在该目录下创建对应的子目录。
ADD
- 将dockerfile所在目录中指定的文件复制到镜像的指定目录中
- 同时可以识别远程URL,会自动下载URL对应的压缩包(不推荐使用)
- 如果指定的文件是压缩包(如 .tar, .gz, .zip 等),会自动解压缩文件复制到镜像的指定目录中(单纯复制压缩包使用COPY指令)
- 语法格式:
bash
#格式1
ADD 外部文件路径 容器路径
#格式2(复制压缩包并重命名)
ADD 压缩包名字 重命名压缩包名字
#格式3(会自动解压缩)
ADD 压缩包名字 容器路径
#举例复制并重命名
ADD target/pms-boot.jar app.jar
#举例解压缩的情况
ADD my.zip /data
- 如果容器路径是一个具体的文件名而非目录名,那么外部文件会复制到镜像并重命名为该具体的文件名
- 就是pms-boot.jar 复制过去后会重命名为app.jar ,该app.jar在镜像根目录。
COPY
- 将dockerfile所在目录中指定文件复制到镜像的指定目录中
- 语法格式
arduino
#shell格式
COPY 源路径 目标路径
ENTRYPOINT
- 指定容器启动时执行的命令
- 与CMD类似
- 不会被docker run的命令覆盖,但是docker run后面加 --entrypoint参数就可以被覆盖
- docker run后面有参数的话会传给entrypoint
- 可以是Shell格式,也可以是exec格式
- 语法格式
bash
ENTRYPOINT 命令
#举例shell格式
ENTRYPOINT java -jar /app.jar
#举例exec格式
ENTRYPOINT ["java","-jar","/app.jar"]
#举例docker run 传参
ENTRYPOINT echo "hello"
docker run 镜像:版本 "world"
最后容器会执行 echo "hello" "world" 输出是"hello world"
CMD
- 指定容器启动后默认执行的命令和参数
- CMD 在 Dockerfile 中可以有多个,但只有最后一个 CMD 会生效。多个ENTRYPOINT的话每个都会执行的。
- docker run后面如果有命令会覆盖dockerfile中CMD的命令
- 语法格式
objectivec
#shell格式(不能传参)
CMD 命令
#exec格式(有两种)
CMD ["可执行文件","参数1","参数2",...]
CMD ["参数1","参数2"](作为ENTRYPOINT的默认参数)
- 可以与ENTRYPOINT结合使用:
-
- 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略(是结合的情况下会被忽略,如果cmd有可执行文件的话还是会执行)。
- 如果 ENTRYPOINT 使用了 exec 模式,CMD 也应该使用 exec 模式,CMD指定的内容会被追加到ENTRYPOINT末尾当参数。
scss
#举例1
ENTRYPOINT ["echo","hello"]
CMD ["world"]
最终会输出"hello world"
#举例2(当cmd的可执行文件跟entrypoint一样时就会传参,不一样就各自执行)
ENTRYPOINT["echo","hello"]
CMD ["echo","world"]
最终会输出"hello world"
#举例3
ENTRYPOINT["echo","hello"]
CMD ["world"]
dokcer run 镜像:版本 "china"
最终会输出"hello china"
WORKDIR
-
指定容器工作目录,容器内没有指定的路径时会创建该路径
-
语法格式
WORKDIR 容器路径
-
直接看个例子就明白了
bash
WORKDIR /app
RUN touch file1.txt
WORKDIR subdir
RUN touch file2.txt
WORKDIR /data
RUN touch file3.txt
- 首先设置了工作目录为 /app,然后在 /app 目录下创建了一个文件 file1.txt。
- 接着 WORKDIR subdir 命令将当前的工作目录更改为 /app/subdir,然后在该目录下创建了文件 file2.txt。
- 最后,WORKDIR /data 将工作目录更改为 /data,并在该目录下创建了文件 file3.txt。
USER
- 指定镜像会以什么样的用户去运行,不指定默认容器会使用root用户来运行。
- 注意:如果使用不存在的 用户或用户id,Docker 构建过程将会报错。
- 语法格式
bash
USER 用户名/用户id
ENV
- 设置环境变量,这个环境变量在构建过程中和运行过程中都是有效的。比如说配置JDK环境
- 语法格式
ini
#格式1
ENV key=value
#格式2
ENV key value
#格式3(允许在多行上设置环境变量。使用 \ 进行换行)
ENV <key>=<value> \
<key>=<value> \
#举例
ENV JAVA_HOME /usr/local/jdk1.8.0_121
ONBUILD
- 在构建镜像的过程中设置一个触发器。这个触发器将在其他镜像继承(通过 FROM)当前镜像并构建新镜像时触发。
- 语法格式
bash
ONBUILD 命令
#举例
ONBUILD COPY . /app
ONBUILD RUN make /app
-
- 这个例子ONBUILD 指令设置了两个触发器。当其他镜像继承(通过 FROM)当前镜像并构建新镜像时,将会按顺序执行这两个触发器。
- 在继承当前镜像的新镜像的构建过程中,首先会复制当前工作目录中的所有文件到新镜像的 /app 目录中,然后通过 make 命令在 /app 目录中运行构建。
EXPOSE
- 声明(暴露的意思)运行时容器提供服务端口,仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。,它并不会实际上打开或监听端口。
- 要容器内的服务可以让外部访问,您需要将容器内部的端口映射到主机上。这可以通过 Docker 命令行的 -p 或者 -P 参数来实现。
- 语法格式:
erlang
EXPOSE 端口1 端口2 ...
实战dockerfile自定义镜像
定义dockefile
bash
FROM openjdk:8-jre
MAINTAINER whisper yourQQ@qq.com
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
# /tmp 目录作为容器数据卷目录,SpringBoot内嵌Tomcat容器默认使用/tmp作为工作目录,任何向 /tmp 中写入的信息不会记录进容器存储层,从而保证容器存储层的无状态化
# 在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录
VOLUME /tmp
#app.jar是自己命名的
ADD target/whisper-gateway.jar app.jar
ENTRYPOINT ["java","-Xmx128m","-Djava.security.egd=file:/dev/./urandom","-Dcsp.sentinel.app.type=1","-jar","/app.jar"]
EXPOSE 9999
- 基础镜像选openjdk:8-jre(如果您的项目需要进行编译和打包操作,建议选择jdk。如果您只需运行已编译好的 Java 代码,可以选择jre)
- 容器建立时运行cp指令,设置容器时区为当前时区
-
- /usr/share/zoneinfo/Asia/Shanghai:操作系统中自带的时区文件
- /etc/localtime :是容器自带的文件,/etc/localtime 文件是随着容器启动在容器内部创建的,它是通过复制来自主机操作系统的对应时区文件来初始化的。
- 创建一个匿名卷挂载到容器的tmp目录,这样容器中对 /tmp 目录的读写操作将直接映射到匿名卷上。
- 复制jar到镜像并重命名为app.jar
- ENTRYPOINT
-
- java:运行 Java 程序
- -Xmx128m:设置 Java 虚拟机的最大堆内存为 128MB
- -Djava.security.egd=file:/dev/./urandom:设置随机数生成器的算法为 /dev/./urandom,以提高随机性和性能
- -Dcsp.sentinel.app.type=1:设置 Sentinel 应用程序的类型为 1。
- -jar:指定要运行的 JAR 文件
- /app.jar:指定要运行的 JAR 文件的路径。
- 暴露端口9999
应用部署
下载Docker插件
配置pom.xml文件
- 添加 spring-boot-maven-plugin 依赖
- 指定 finalName 不带版本号的话,打包出的 jar 包名称就没有版本号
xml
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<excludes>
<!-- 打包忽略lombok(作用于编译阶段)-->
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
Run/Debug Configurations 配置
- Name:填写容器名
- Server:选择你的docker容器(点击右边三个点)
- Dockerfile:你的dockerfile文件路径
- image tag:镜像名字:版本
- container name:容器名字,当你用docker start 后面的容器名就是这个
- Run option: 类似docker run 后面加的参数.
- Before launch:程序执行前执行脚本