获取镜像的途径有两个, 一是从镜像仓库获取, 如官方的 Docker Hub, 二是自定义. 上文已经介绍如何从镜像仓库获取镜像, 本文基于一个 Springboot 项目, 来介绍自定义一个镜像的基本流程.
1. 定制镜像的本质
我们知道镜像是分层存储的, 镜像的构建也是一层一层进行的, 一层构建完后, 就变为只读, 在其上再构建下一层. 因此定制镜像, 实际上就是定义每一层要干的事, 比如执行某个命令, 设置一个环境变量, 声明一个暴露端口等等. 然后在构建时, 按照各层的定义, 一层一层地完成构建, 最终形成一个包含这些层的镜像.
2. Dockerfile 文件
Docker 中定义各层要干的事的文件叫 Dockerfile, 它是一个文本文件, 包含了一条条的指令, 每一条指令对应一层镜像, 指令的内容就描述了这一层该如何构建. 如下示例了一个非常简单的 Dockerfile,
- FROM nginx
- RUN echo '<h1>Hello jboost!</h1>'> /usr/share/nginx/html/index.HTML
我们定制镜像, 必须要以某一个镜像为基础, 在其上构建自己需要的层, 如上示例中, 我们是以 nginx 镜像为基础, 然后在第二层定制了我们自己的内容 -- 修改 index.HTML 的内容为 < h1>Hello jboost!</h1>, 这样运行容器打开 nginx 主页时就不会显示默认的页面内容了.
上面示例中接触了 Dockerfile 的两个指令
FROM:FROM 指令指定基础镜像, 每一个定制镜像必须要有一个基础镜像, 所以必须要有一条 FROM 指令, 并且是 Dockerfile 的第一条指令
RUN:RUN 指令指定需要执行的命令, 后面接的命令就像是 shell 脚本一样可执行
Dockerfile 还提供了许多其它指令, 后续我们再集中介绍, 本文只对接触到的指令做简单说明.
3. 自定义一个镜像
这部分以一个 Springboot 项目为基础, 介绍自定义一个镜像涉及的基本环节. 项目地址为: https://github.com/ronwxy/swagger-register , 该项目是一个 Swagger API 文档注册服务, 其它项目可将 Swagger API 信息注册到该服务, 进行统一查看与管理.
3.1 定义 Dockerfile 文件
首先, 我们在项目的根目录下创建一个 Dockerfile 文件 (文件名就叫 Dockerfile), 其内容为:
- FROM openjdk:8-jdk-alpine
- ENV PROFILE=dev
- RUN mkdir /App /logs
- COPY ./target/swagger-register-1.0.0-SNAPSHOT.jar /App/App.jar
- WORKDIR /App
- VOLUME /register-data
- EXPOSE 11090
- CMD ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "app.jar"]
从上往下依次介绍如下
第一行: FORM openjdk:8-jdk-alpine, 表示以
openjdk:8-jdk-alpine
这个镜像为基础镜像, 因为这是一个 Springboot 项目所以必须要有 jdk 支持, 我们在定制镜像时, 可以找一个最适合的镜像作为基础镜像.
第二行: ENV PROFILE=dev, 定义了一个环境变量, 这个环境变量可以在后面被引用
第三行: RUN mkdir /App /logs, 通过 mkdir 命令创建了两个目录, 用来保存 jar 执行文件及日志
第四行: COPY ./target/swagger-register-1.0.0-SNAPSHOT.jar /App/App.jar 将 target 目录下的 jar 包复制到 / App 目录下, 并且进行重命名
第五行: WORKDIR /App, 指定工作目录为 / App, 后面各层的当前目录就是指定的工作目录
第六行: VOLUME /register-data, 定义一个匿名数据卷, 前面说过写操作不要直接在容器内进行, 而要改为写挂载的数据卷目录, 这个定义可在运行容器时通过 -v 来覆盖.
第七行: EXPOSE 11090, 声明了运行容器时提供的服务端口, 也仅仅是个声明而已, 只是告诉使用的人要映射这个端口, 通过 -p 可映射端口.
第八行: CMD ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "app.jar"], 指定了容器启动命令, 因为是一个 Springboot 项目, 所以就是一个 java -jar 的执行命令, 容器启动的时候就会执行该命令来运行 Springboot 服务, 这里引用了第二行定义的环境变量 PROFILE
3.2 配置 maven 插件
定义好 Dockerfile 后, 为了方便构建镜像, 我们可以借助 maven 的 dockerfile 插件 dockerfile-maven-plugin, 在 pom.xml 的 build 部分加入配置如下
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- <!-- Docker maven plugin -->
- <plugin>
- <groupId>com.spotify</groupId>
- <artifactId>dockerfile-maven-plugin</artifactId>
- <version>1.4.10</version>
- <configuration>
- <repository>${docker.image.prefix}/${project.artifactId}</repository>
- <buildArgs>
- <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
- </buildArgs>
- </configuration>
- </plugin>
- <!-- Docker maven plugin -->
- </plugins>
- </build>
repository 指定了镜像的名称, docker.image.prefix 需要 properties 部分进行定义, 我这里是 springboot.
3.3 构建镜像
下载源码: https://github.com/ronwxy/swagger-register.git , 然后在项目的根目录下执行如下命令 (前提是本地已经装好了 docker 与 maven 及 jdk)
mvn clean package -Dmaven.test.skip=true dockerfile:build
该命令首先会执行 mvn clean package -Dmaven.test.skip=true 对项目进行打包, 生成./target/swagger-register-1.0.0-SNAPSHOT.jar 文件, 然后基于当前目录下的 Dockerfile 文件进行构建, 如下图所示
由上图可看出, 该镜像构建分八步 (对应 Dockerfile 的八行指令), 每一步生成一个镜像层, 每一层都有唯一的 ID. 由图中也可以看出, 除了 COPY 之类的命令外, 每一层的构建实际上是先基于上一层启动一个容器, 然后执行该层定义的操作, 再移除这个容器来实现的, 如第八步中
- Step 8/8 : CMD ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "app.jar"]
- [INFO]
- [INFO] ---> Running in f4acd0b53bca
- [INFO] Removing intermediate container f4acd0b53bca
- [INFO] ---> a9ee579f2d62
先启动一个 ID 为 f4acd0b53bca 的容器, 在其中执行 CMD 所定义的命令, 然后再移除容器 f4acd0b53bca, 最后生成 ID 为 a9ee579f2d62 的镜像.
构建完后, 我们就可以在本地镜像中通过 docker iamges 看到我们定制的镜像了, 如图
图中 springboot/swagger-register 镜像即为我们刚刚构建好的定制镜像.
3.4 启动容器
我们可以通过以下命令来启动一个刚才定制镜像的容器
docker run -d --name swagger-register -p 11090:11090 -v /home/jenkins/swagger-register/register-data:/register-data -v /home/jenkins/swagger-register/logs:/logs --restart=always springboot/swagger-register:latest
其中:
-d 表示以后台进程方式运行
-name 指定容器名称
-p 指定端口映射, 左边为宿主机端口, 右边为容器服务端口
-v 指定数据卷挂载, 左边为宿主机目录, 右边为容器目录
-restart=always 表示在 docker 启动时自动启动该容器
关于容器相关的内容后面详细介绍, 这里不展开说明了. 启动容器后, 我们就可以浏览器打开地址 http:// 宿主机 ip:11090/doc.HTML 来访问服务了 (打开页面后内容是空的, 因为没有任何服务注册 Swagger API, 相关内容可参考 swagger API 文档集中化注册管理)
4. 总结
本文介绍了一个基于 Springboot 项目的 Docker 镜像定制及使用过程, 对镜像的构建过程, 及 Dockerfile 的基本指令以及容器的运行做了基本介绍. 后续会对 Dockerfile 的其它指令及 Dockerfile 的一些最佳实践进行更为详细的介绍, 欢迎关注.
我的个人博客地址: http://blog.jboost.cn/
来源: https://www.cnblogs.com/spec-dog/p/11204914.html