Docker 生成镜像

140 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Docker 生成镜像

Docker 镜像的生成有两种方式:

  • 使用 commit 命令,以当前容器生成新镜像
  • 使用 Dockerfile 文件生成新镜像

在已有容器基础上生成新镜像

docker commit命令会以当前容器, 当前时刻的状态, 作为原料, 生成新的镜像文件. 该命令提交的只是创建容器的镜像与容器的当前状态之间有差异的部分, 这使得该更新非常轻量.

格式如下 : docker commit -m="描述" -a="作者" 容器名或者容器id 要创建的新镜像的名字:[tag]

docker commit 命令虽然可以用来创建新镜像, 但是并不推荐这样去创建镜像, 不过学习 docker commit 能够帮助我们更加深入地理解构建过程和镜像的分层结构. 因为即使是用 Dockerfile 构建镜像, 底层其实也是执行 docker commit 一层一层构建新镜像的.

演示: 上传 web 项目到 tomcat8 中, 并且将其打包为新的镜像.

① 基础镜像.

使用的基础镜像为 : tomcat:8-jre8

② 启动 tomcat

docker run -d --name="tomcat8" -p 8080:8080 tomcat:8-jre8

③ 复制 war 包到 docker 上的 tomcat 下的 webapps 目录下.

docker cp /usr/local/tomcat/apache-tomcat-8.5.28/webapps/Test.war tomcat8:/usr/local/tomcat/webapps

④ 测试

访问 http:192.168.1.185:8080/Test 成功.

⑤ 将其打包为新镜像

将 有 Test.war 的 tomcat8 容器, 打包为新的镜像.

docker commit tomcat8 tomcat8-test.war:1.0

⑥ 运行新镜像

docker run -d --name="t8war" -p 9090:9090 tomcat8-test.war:1.0

⑦ 测试

访问 http:192.168.1.185:9090/Test 成功.

DockerFile 构建镜像

Dockerfile 文件

Dockerfile 是用来构建 Docker 镜像的脚本文件, 文件内容是镜像构建中每一步需要执行的操作命令.

(1) 构建三步曲 :

  • 手动编写一个名叫 Dockerfile的文件. 注意 : 文件名必须是 Dockerfile, 且没有文件后缀名.
  • 在 Dockerfile 文件所在的目录, 使用 docker build 命令构建自定义的镜像.
  • 从构建出来的镜像, 运行容器.

(2) Dockerfile 内容规范 :

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

DockerFile 保留字指令

DockerFile 有如下保留字指令 :

FROM 表示当前镜像以哪个镜像为基础镜像进行构建. 当基础镜像在本地中不存在时, 会自动从 DockerHub 上下载.

MAINTAINER 镜像编写者的名字和邮箱.

RUN 容器构建时需要执行的命令. RUN 指令会执行命令并创建新的镜像层, 通常用于安装软件包.

EXPOSE 当前容器对外暴露出的端口. 该命令只是声明了容器应该打开的端口, 并不会主动将其打开.

WORKDIR 容器创建完成后, 进入容器终端默认登录进来的工作目录.如果 WORKDIR 目录不存在, Docker 会自动创建.

ENV 在构建镜像过程中, 设置一个可被使用的环境变量. eg:

ENV  MY_PATH  /usr/mytest        #这个环境变量可以在后续的任何指令中使用.
WORKDIR $MY_PATH

ADD 将宿主机目录下的文件拷贝到镜像中. ( ADD 命令会自动处理 URL 和解压压缩包 ). eg:

ADD  xx.txt   /        #拷贝文件到容器的根目录下.

COPY 也是拷贝文件到镜像, 不过不会处理 URL, 不会自动解压.

COPY 支持两种格式:

COPY src dest 
COPY ["src", "dest"]

需要注意:src 只能指定 build context 中的文件或目录. build context 是 docker build 命令运行时指定的目录.

VOLUME 容器数据卷, 用于容器数据的保存和持久化工作.

CMD 设置容器启动后, 默认执行的命令及其参数. 注意 : Dockerfile 中可以有多个 CMD 指令, 但是只有最后一个生效. 当使用 docker run 启动容器, 若该命令中携带了参数, 该 CMD 指令会被参数替换掉.

ENTRYPOINT 配置容器启动时要执行的命令. 和 CMD 一样, 都是在指定容器启动程序和参数. 当使用 docker run 启动容器, 若该命令中携带了参数, 该 ENTRYPOINT 指令会把参数和原来的 ENTRYPOINT 指令组合在一起, 形成新指令.

RUN, CMD 和 ENTRYPOINT 指令都支持两种命令格式 :

  • CMD 和 ENTRYPOINT 推荐使用 Exec 格式, 因为指令可读性更强, 更容易理解. RUN 则两种格式都可以.
  • 如果 Docker 镜像的用途是运行应用程序或服务, 比如运行一个 MySQL, 应该优先使用 Exec 格式的 ENTRYPOINT 指令. CMD 可为 ENTRYPOINT 提供额外的默认参数, 同时可利用 docker run 命令行替换默认参数.
  • 如果想为容器设置默认的启动命令, 可使用 CMD 指令. 用户可在 docker run 命令行中替换此默认命令.
Shell 格式 : 当指令执行时, shell 格式底层会调用 /bin/sh -c <command> . 
<instruction> <command>

Exec 格式 : 当指令执行时, 会直接调用 <command>, 不会被 shell 解析. 
<instruction> ["executable", "param1", "param2", ...]

DockerFile 构建过程解析

Dockerfile 构建镜像的大致流程 :

  • docker 从基本镜像开始, 运行一个容器.
  • 执行 dockerfile 中的一条指令, 并对容器进行修改.
  • 执行类似 docker commit 的操作, 提交一个新的镜像层.
  • docker 再基于刚才提交的镜像运行一个新的容器.
  • 执行 dockerfile 中的下一条指令, 直到所有指令都执行完毕.

详细的构建流程. 以我们自己创建的 ubantu-with-vim 为例:

① Dockerfile 文件内容:

FROM ubuntu
RUN apt-get update && apt-get install -y vim

② 运行 docker build 命令构建镜像, 并详细分析每个细节:

root@ubuntu:~# pwd         #1  
/root  
root@ubuntu:~# ls          #2   
Dockerfile   
root@ubuntu:~# docker build -t ubuntu-with-vi-dockerfile .        #3   
Sending build context to Docker daemon 32.26 kB           #4   
Step 1 : FROM ubuntu           #5   
---> f753707788c5   
Step 2 : RUN apt-get update && apt-get install -y vim           #6   
---> Running in 9f4d4166f7e3             #7   
......   
Setting up vim (2:7.4.1689-3ubuntu1.1) ...   
---> 35ca89798937           #8    
Removing intermediate container 9f4d4166f7e3          #9   
Successfully built 35ca89798937           #10   
root@ubuntu:~#   
  • 当前目录为 /root.
  • 表示 Dockerfile 准备就绪.
  • 运行 docker build 命令, -t 将新镜像命名为 ubuntu-with-vi-dockerfile, 命令末尾的 . 表示 build context 为当前目录. Docker 默认会从 build context 中查找 Dockerfile 文件, 也可以通过 -f 参数指定 Dockerfile 的位置. 所以, dockerfile 中用到的文件都必须能在 build context 中找到.
  • 从这步开始就是镜像真正的构建过程. 首先 Docker 会将 build context 目录中的所有文件发送给 Docker daemon. Dockerfile 中的 ADD, COPY 等命令可以将 build context 中的文件添加到镜像. 此例中, build context 为当前目录 /root, 该目录下的所有文件和子目录都会被发送给 Docker daemon.所以, 使用 build context 就得小心了, 不要将多余文件放到 build context 下, 特别不要把 / 或 /usr 作为 build context, 否则构建过程会相当缓慢.
  • Step 1:执行 FROM, 将 ubuntu 作为 base 镜像. ubuntu 镜像 ID 为 f753707788c5.
  • Step 2:执行 RUN, 安装 vim, 具体步骤为 ⑦, ⑧, ⑨.
  • 启动 ID 为 9f4d4166f7e3 的临时容器, 在容器中通过 apt-get 安装 vim.
  • 安装成功后, 将容器保存为镜像, 其 ID 为 35ca89798937. 这一步底层使用的是类似 docker commit 的命令.
  • 删除临时容器 9f4d4166f7e3.
  • 镜像构建成功.

通过 docker images 查看镜像信息. 镜像 ID 为 35ca89798937, 与构建时的输出一致.

在上面的构建过程中, 我们要特别注意指令 RUN 的执行过程 ⑦, ⑧, ⑨. Docker 会在启动的临时容器中执行操作, 并通过 commit 保存为新的镜像.

DockerFile 案例

目标: 自定义镜像 mycentos. 以 centos 镜像为父镜像, 在它的基础上形成我们自己的新镜像.

① Hub 上默认的 centos 镜像.

初始 centos 运行时的默认工作目录是 / , 我们希望更改其工作目录为 /usr/local,默认不支持 vim, 我们希望给它添加 vim 功能. 默认不支持 ifconfig, 我们希望给它添加 iconfig 功能.

② 编写 mycentos 镜像的 Dockerfile 文件.

FROM centos                                 #在 centos 镜像的基础上操作.
MAINTAINER sanjingye<sanjingye@126.com>
ENV MYPATH /usr/local                       #容器环境变量
WORKDIR $MYPATH                             #更改工作目录
RUN yum -y install vim                      #添加 vim
RUN yum -y install net-tools                #添加 net-tools
EXPOSE 80                                   #暴露出80端口
CMD echo $MYPATH
CMD echo "--------- success -------"
CMD /bin/bash

③ 构建镜像

docker build -t 新镜像的名字:tag . 注意: docker build 命令最后有一个 . 它表示当前目录.

docker build -t mycentos:1.0 .

④ 运行

docker run -it mycentos:1.0

⑤ 镜像历史

docker history 镜像ID

将微服务项目制作为镜像

基础镜像是只有 jdk8 的镜像. 直接从 DockerHub 上 pull 下来. docker pull ascdc/jdk8

① 打包 SpringBoot 项目为 Jar 包.

② 将其上传到 linux 上.

③ 并在该 JAR 包所在目录下创建 Dockerfile 文件.

文件名就叫做 : Dockerfile

文件内容为 :

#基础镜像
FROM ascdc/jdk8
#复制文件到该容器中
ADD ts_eureka_server.jar  /ts_server_eureka.jar
#声明启动端口号
EXPOSE 6868
#配置容器启动后执行的命令
ENTRYPOINT ["java","-jar","/ts_server_eureka.jar"] 

如果想要设置 JVM 参数, 需要如下设置:

#基础镜像
FROM ascdc/jdk8
#复制文件到该容器中
ADD ts_config.jar  /ts_config.jar
#声明启动端口号
EXPOSE 12000
#配置容器启动后执行的命令, 每个参数, 使用一个,
ENTRYPOINT ["java","-XX:MetaspaceSize=128m","-XX:MaxMetaspaceSize=128m","-Xms512m","-Xmx512m","-Xmn256m","-Xss256k","-XX:SurvivorRatio=8","-XX:+UseConcMarkSweepGC","-jar","/ts_config.jar"]

\

④ 打包为 Docker 镜像.

注意 : 镜像名必须小写

docker build -t ts_eureka_server:1.0 .

⑤ 运行

注意 : 防火墙开放相应的端口.

docker run -d -p 6868:6868 ts_eureka_server:1.0

⑥ 测试

Docker 镜像分析工具 Dive

Dive 是一个方便的观察容器各层信息的工具,同时也集成了容器构建命令,方便我们在构建容器镜像的同时查询镜像各层的变动信息. 通过分析 Docker 镜像,可以发现在各个层之间可能重复的文件,并通过移除它们来减小 Docker 镜像的大小.

使用:

1.拉取镜像:

docker pull quay.io/wagoodman/dive:latest

2.运行容器并查看 image

docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock quay.io/wagoodman/dive:latest redis:latest

3.下面是 dive 的快捷键列表:

  • Ctrl+空格 —— 在左右栏之间切换
  • 空格 —— 展开或收起目录树
  • Ctrl+A —— 文件树视图:展示或隐藏增加的文件
  • Ctrl+R —— 文件树视图:展示或隐藏被移除的文件
  • Ctrl+M —— 文件树视图:展示或隐藏被修改的文件
  • Ctrl+U —— 文件树视图:展示或隐藏未修改的文件
  • Ctrl+L —— 层视图:展示当前层的变化
  • Ctrl+A —— 层视图:展示总的变化
  • Ctrl+/ —— 筛选文件
  • Ctrl+C —— 退出