Docker版本控制实战

# 一、从容器中构建镜像

# 1.1 打包git

# 从ubuntu中运行一个名为image-dev的容器
# -i 交互式操作 -t 终端 -d 后台运行
docker run -itd --name image-dev ubuntu:latest /bin/bash
# 进入容器终端
docker exec -it image-dev /bin/bash
# 更新软件包并安装git
apt-get update
apt-get -y install git
git version
# git version 2.25.1

# 1.2 审查改动

docker diff image-dev

A add
C 修改
D delete

# 1.3 Commit——创建新镜像

从修改后的容器创建一个名为ubuntu-git的镜像

# -a "author" -m "message"
docker commit -a "@tim" -m "Added git" image-dev ubuntu-git
# sha256:e32f545e7714f9dbc6eae4f21e3d542fcf3a7bae6067b135bf319f97eceb2a7c

ubuntu-git将出现在本地镜像列表

docker images
# REPOSITORY                   TAG                     IMAGE ID       CREATED              SIZE
# ubuntu-git                   latest                  e32f545e7714   About a minute ago   207MB
# ubuntu                       latest                  d13c942271d6   4 days ago           72.8MB

# 1.4 镜像体积和层数限制

  1. 分配新标签生成新的版本
docker tag ubuntu-git:latest ubuntu-git:2.55.1
  1. 在新镜像中卸载git
# 从源镜像中启动
docker run --name image-dev2 -itd ubuntu-git:latest /bin/bash
# 进入容器终端
docker exec -it image-dev2 /bin/bash
# 卸载git
apt-get remove -y git
git version
# bash: /usr/bin/git: No such file or directory
  1. 提交镜像
docker commit image-dev2 ubuntu-git:removed
docker tag ubuntu-git:removed ubuntu-git:latest
  1. 查看本地镜像
docker images
# REPOSITORY                   TAG                     IMAGE ID       CREATED          SIZE
# ubuntu-git                   latest                  39361ad385b0   58 seconds ago   208MB
# ubuntu-git                   removed                 39361ad385b0   58 seconds ago   208MB
# ubuntu-git                   2.55.1                  e32f545e7714   23 minutes ago   207MB
  1. 查看镜像历史
docker history ubuntu-git:removed
#IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
#39361ad385b0   27 minutes ago   /bin/bash                                       1.06MB    
#e32f545e7714   50 minutes ago   /bin/bash                                       134MB     Added git
#d13c942271d6   4 days ago       /bin/sh -c #(nop)  CMD ["bash"]                 0B        
#<missing>      4 days ago       /bin/sh -c #(nop) ADD file:122ad323412c2e70b…   72.8MB

**问题所在:**卸载了git的的镜像会比源镜像要大的多(208MB > 207MB),这是由于使用docker commit命令会向源镜像提交一个新的文件层,导致镜像体积变大。

且联合文件系统(UFS)是有层数限制的,一般为42层。

所以在将产品上线到部署环境时,总是从容器构建新镜像来发布是不实际的做法。

# 二、导入和导出扁平镜像

# 2.1 导出镜像

 docker export image-dev2 > ubuntu-git_removed.tar
 ls -lh
 # -rw-rw-r--  1 tim tim 171M 1月  11 16:32 ubuntu-git_removed.tar
 gzip ubuntu-git_removed.tar

镜像大小被扁平。

# 2.2 导入镜像

  1. 导入
docker import - new_ubuntu-git:removed < ubuntu-git_removed.tar
docker images
# REPOSITORY                   TAG                     IMAGE ID       CREATED          SIZE
# new_ubuntu-git               removed                 81f8545c2dbe   7 seconds ago    174MB
# ubuntu-git                   latest                  39361ad385b0   33 minutes ago   208MB
# ubuntu-git                   removed                 39361ad385b0   33 minutes ago   208MB
# ubuntu-git                   2.55.1                  e32f545e7714   56 minutes ago   207MB
  1. 查看镜像历史
docker history new_ubuntu-git:removed 
# IMAGE          CREATED              CREATED BY   SIZE      COMMENT
# 81f8545c2dbe   About a minute ago                174MB     Imported from -

问题所在: 会丢失所有改动信息,无法在生产环境中使用。而生产环境中的镜像(ubuntu-git:latest)随着改动的增加会越来越大,以G为单位的镜像并不少见,非常难以迁移和扩展。

# 三、Dockerfile构建

Dockerfile是一个文件,它由构建镜像的指令组成。

指令由Docker镜像构建者自上而下排列,能够被用来修改镜像的任何信息。

使用Dockerfile构建镜像使得很多任务变得非常简单。

# 3.1 使用Dockerfile打包Git

  1. 创建Dockerfile
vim Dockerfile

输入以下内容

# An example Dockerfile for installing Git on Ubuntu
FROM ubuntu:latest
MAINTAINER "@tim"
RUN apt-get update
RUN apt-get install -y git
ENTRYPOINT ["git"]
  1. 从Dockerfile中创建镜像
# .表示执行的上下文环境
docker build -t ubuntu-git:auto .
  1. 查看镜像
docker images
# REPOSITORY                   TAG                     IMAGE ID       CREATED         SIZE
# ubuntu-git                   auto                    7132d02527dc   6 seconds ago   207MB
# new_ubuntu-git               removed                 81f8545c2dbe   2 hours ago     174MB
# ubuntu-git                   latest                  39361ad385b0   2 hours ago     208MB
# ubuntu-git                   removed                 39361ad385b0   2 hours ago     208MB
# ubuntu-git                   2.55.1                  e32f545e7714   3 hours ago     207MB
docker history ubuntu-git:auto
# IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
# 7132d02527dc   About a minute ago   /bin/sh -c #(nop)  ENTRYPOINT ["git"]           0B        
# 684f0c017f49   About a minute ago   /bin/sh -c apt-get install -y git               102MB     
# 0629ea9477b2   2 minutes ago        /bin/sh -c apt-get update                       32.7MB    
# 96f904e8c083   About an hour ago    /bin/sh -c #(nop)  MAINTAINER "@tim"            0B        
# d13c942271d6   4 days ago           /bin/sh -c #(nop)  CMD ["bash"]                 0B        
# <missing>      4 days ago           /bin/sh -c #(nop) ADD file:122ad323412c2e70b…   72.8MB    
  1. 运行镜像
docker run -itd --name auto-image ubuntu-git:auto version
docker logs auto-image
# git version 2.55.1

如上,使用一个极小的Dockerfile即可自动化构建一个大小为百MB级别的Docker镜像

# 3.2 Dockerfile指令

# 3.2.1 FROM:指定基础镜像

FROM ubuntu:latest

# 3.2.2 MAINTAINER:指定作者

MAINTAINER <name> <email>
MAINTAINER <tim> <tim@example.com>

# 3.2.3 RUN:执行命令

# shell格式
RUN <command>
RUN apt-get install git
# exec格式
RUN ["apt-get","install","git"]

多行命令不要写多个RUN,原因是Dockerfile中每一个指令都会建立一层

多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错,RUN书写时的换行符是 \

# 3.2.4 COPY:复制文件

将宿主机器上的的文件复制到镜像内,如果目的位置不存在,Docker会自动创建。

但宿主机器用要复制的目录必须是和Dockerfile文件统计目录下

COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
COPY package.json /usr/src/app/

# 3.2.5 CMD:容器启动命令

CMD命令用于容器启动时需要执行的命令,CMD在Dockerfile中只能出现一次,如果出现多个,那么只有最后一个会有效。

其作用是在启动容器的时候提供一个默认的命令项。如果用户执行docker run的时候提供了命令项,就会覆盖掉这个命令,没提供就会使用构建时的命令。

# shell 格式
CMD <命令>
# exec 格式
CMD ["可执行文件", "参数1", "参数2"...]
# 如容器启动时进入bash:
CMD ["/bin/bash"]
  • CMD 在 docker run 时运行。
    
  • RUN 在 docker build 时运行。
    

# 3.2.6 EXPOSE:暴露端口

设置容器对外映射的容器端口号,如tomcat容器内使用的端口8081,则用EXPOSE命令可以告诉外界该容器的8081端口对外

EXPOSE <端口1> [<端口2>...]
EXPOSE 8081

EXPOSE 8081 等价于 docker run -p 8081

当需要把8081端口映射到宿主机中的某个端口(如8888)以便外界访问时,则可以用docker run -p 8888:8081

# 3.2.7 WORKDIR:配置工作目录

WORKDIR命令是为RUN、CMD、ENTRYPOINT指令配置工作目录。其效果类似于Linux命名中的cd命令,用于目录的切换,但是和cd不一样的是:如果切换到的目录不存在,WORKDIR会为此创建目录。

WORKDIR path
##进入/usr/local/nginx目录下
WORKDIR /usr/local/nginx

##进入/usr/local/nginx中的html目录下
WORKDIR html

## 在html目录下创建了一个hello.txt文件
RUN echo 'hello' > hello.txt

# 3.2.8 ENTRYPOINT:容器启动执行命名

作用与用法和CMD一致

# shell 格式
ENTRYPOINT <命令>
# exec 格式
ENTRYPOINT ["可执行文件", "参数1", "参数2"...]
# 如容器启动时进入bash:
ENTRYPOINT ["/bin/bash"]

CMD的命令会被docker run的命令覆盖而ENTRYPOINT不会

CMD和ENTRYPOINT都存在时,CMD的指令变成了ENTRYPOINT的参数,并且此CMD提供的参数会被 docker run 后面的命令覆盖

# 3.2.9 VOLUME:定义匿名数据卷

创建一个可以从本地主机或其他容器挂载的挂载点。

例如我们知道tomcat的webapps目录是放web应用程序代码的地方,此时我们要把webapps目录挂载为匿名卷,这样任何写入webapps中的数据都不会被记录到容器的存储层,让容器存储层无状态化。

  •   避免重要的数据,因容器重启而丢失,这是非常致命的。
    
  •   避免容器不断变大。
    
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
# 创建tomcat的webapps目录的一个挂载点
VOLUME /usr/local/tomcat/webapps
# 运行容器时,通过docker run -v来把匿名挂载点都挂载在宿主机器上的某个目录
docker run -d -v /home/tomcat_webapps:/usr/local/tomcat/webapps

# 3.2.10 USER:指定执行用户

用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

USER <用户名>[:<用户组>]
USER daemon

# 3.2.11 ADD:添加文件

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

# 3.2.12 ONBUILD:延迟构建

ONBUILD用于配置当前所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

即当这个镜像创建后,若其它镜像以这个镜像为基础,会先执行这个镜像的ONBUILD命令

ONBUILD <其它指令>

# 3.2.13 ENV:永久环境变量

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

ENV JAVA_HOME /opt/jdk
ENV PATH $PATH:$JAVA_HOME/bin
  1. ENV具有传递性,也就是当前镜像被用作其它镜像的基础镜像时,新镜像会拥有当前这个基础镜像所有的环境变量
  2. ENV定义的环境变量,可以在dockerfile被后面的所有指令(CMD除外)中使用,但不能被docker run 的命令参数引用

# 3.2.14 ARG:临时环境变量

构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效。即只在 docker build的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

ARG <参数名>[=<默认值>]

# 3.2.15 LABEL:添加元数据

LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL org.opencontainers.image.authors="runoob"

# 四、Docker常用命令

# 4.1 复制文件

从主机复制到容器

sudo docker cp host_path containerID:container_path

从容器复制到主机

sudo docker cp containerID:container_path host_path

# 4.2 Tag

docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

#将镜像ubuntu:15.10标记为 runoob/ubuntu:v3 镜像
root@runoob:~# docker tag ubuntu:15.10 runoob/ubuntu:v3

# 参考

Docker 教程 | 菜鸟教程 (opens new window)

《Docker实战》

如何用Dockerfile构建镜像 (opens new window)