docker小册学习笔记之三——操作镜像

第十一课:保存和共享镜像

提交容器更改

Docker镜像的本质是多个基于UnionFS的镜像层依次挂载的结果,而容器的文件系统则是在以只读方式挂载镜像后增加的一个可读可写的沙盒环境。
基于这样的结构,Docker中为我们提供了将容器中的这个可读可写的沙盒环境持久化为一个镜像层的方法。也就是,我们能够轻松的将docker里的容器内的修改记录下来,保存为一个新的镜像。

docker commit webapp   // 将名为webapp的容器保存为镜像的命令,可以形象称为提交容器的更改

docker images // 可以从本地镜像列表中找到它
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
<none>                <none>              0bc42f7ff218        3 seconds ago       372MB
## ......

docker commit -m"Configured" webapp  //类似代码提交,提交容器更改时给出一个提交信息,方便查询
为镜像命名
docker tag 0bc42 webapp:1.0    //为一个(未命名)镜像取名
docker tag webapp:1.0 webapp:latest //为已命名的镜像重新取名

//对未命名的惊喜取名后,docker不会显示原未命名镜像,只会显示有命名的镜像。对已命名的镜像再次命名后,则,旧的镜像依旧会存在镜像列表中

docker image
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
webapp                1.0                 0bc42f7ff218        29 minutes ago      372MB
webapp                latest              0bc42f7ff218        29 minutes ago      372MB
## ......


docker commit -m "Upgrade" webapp webapp:2.0 //提交容器更改时直接指定镜像名

镜像的迁移

将容器导出为镜像后,需要迁移镜像。由于Docker是以集中的方式管理镜像的,所以迁移之前,需要从Docker中取出镜像。docker save命令可以将镜像输出,保存镜像Docker外部。

docker save webapp:1.0>webapp-1.0.tar //使用管道进行接收(即 > 符号) 不推荐使用管道

docker save -o ./webapp-1.0.tar webapp:1.0 //-o 指定输出文件
// 输出后,可以在相应路径查看已经存储镜像内容的wepapp-1.0.tar的文件了
导入镜像

当从A机器的docker中导出镜像文件至A机器中后,我们可以通过多种方式将A机器中的文件复制到B机器上。 之后, 我们需要将镜像导入到B机器中运行的Docker中。
与docker save 相对的 docker load

docker load < webapp-1.0.tar    //管道方式 从输入流中读取镜像数据

docker load -i webapp-1.0.tar   //-i 指定输入文件

//镜像导入后,可以通过 docker images 看到它
docker images
批量迁移
docker save -o ./images.tar webapp:1.0 nginx:1.12 mysql:5.7  
// 只需要在docker save中传入多个镜像名做参数,它能够将镜像都打成一个包,便于一次性迁移

导出和导入容器

docker export -o ./webapp.tar webapp  // docker export直接导出容器,docker commit 和 docker save 的结合体

使用docker export导出的容器包,可以使用docker import导入。需要注意,docker import 并非直接将容器导入,而是以镜像形式导入,所以 导入结果仍然是镜像,不是容器。 docker import中 也可以给镜像命名

docker import ./webapp.tar webapp:1.0

思考题

问 : 通过Docker进行的集群部署和其他虚拟化形式中的集群部署有怎样的区别,在部署过程中Docker又是怎样发挥它的优势的。

答:可以将容器直接导出压缩包,利用命令直接导入镜像。

第十二课 通过Dockerfile创建镜像

关于Dockerfile

Dockerfile是Docker中用于镜像自动化构建流程的配置文件,在Dockerfile中,包含了构建镜像过程中需要执行的命令和其他操作。

Dockerfile的内容很简单,主要以两种形式 一种注释行,另一种指令行。

# Comment
INSTRUCTION arguments

Dockerfile在容器体系下能够完成自动构建。无需人工执行每一个搭建流程。

编写Dockerfile

与提交容器的修改,再进行镜像迁移的方式相比,使用Dockerfile的优势

  1. Dockerfile的体积远小于镜像包,更容易进行快速迁移和部署
  2. 环境构建流程记录了Dockerfile中,能够直观的看到镜像构建的顺了和逻辑
  3. 使用Dockerfile来构建镜像能够轻松实现自动部署等自动化流程。
  4. 在修改环境搭建细节时,修改Dockerfile文件要比从新提交镜像来的轻松。

Docker官方提供的Redis镜像的Dockerfile文件

FROM debian:stretch-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r redis && useradd -r -g redis redis

# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.10
RUN set -ex; \
	\
	fetchDeps=" \
		ca-certificates \
		dirmngr \
		gnupg \
		wget \
	"; \
	apt-get update; \
	apt-get install -y --no-install-recommends $fetchDeps; \
	rm -rf /var/lib/apt/lists/*; \
	\
	dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
	wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
	wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
	export GNUPGHOME="$(mktemp -d)"; \
	gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
	gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
	gpgconf --kill all; \
	rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \
	chmod +x /usr/local/bin/gosu; \
	gosu nobody true; \
	\
	apt-get purge -y --auto-remove $fetchDeps

ENV REDIS_VERSION 3.2.12
ENV REDIS_DOWNLOAD_URL http://download.redis.io/releases/redis-3.2.12.tar.gz
ENV REDIS_DOWNLOAD_SHA 98c4254ae1be4e452aa7884245471501c9aa657993e0318d88f048093e7f88fd

# for redis-sentinel see: http://redis.io/topics/sentinel
RUN set -ex; \
	\
	buildDeps=' \
		wget \
		\
		gcc \
		libc6-dev \
		make \
	'; \
	apt-get update; \
	apt-get install -y $buildDeps --no-install-recommends; \
	rm -rf /var/lib/apt/lists/*; \
	\
	wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL"; \
	echo "$REDIS_DOWNLOAD_SHA *redis.tar.gz" | sha256sum -c -; \
	mkdir -p /usr/src/redis; \
	tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1; \
	rm redis.tar.gz; \
	\
# disable Redis protected mode [1] as it is unnecessary in context of Docker
# (ports are not automatically exposed when running inside Docker, but rather explicitly by specifying -p / -P)
# [1]: https://github.com/antirez/redis/commit/edd4d555df57dc84265fdfb4ef59a4678832f6da
	grep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 1$' /usr/src/redis/src/server.h; \
	sed -ri 's!^(#define CONFIG_DEFAULT_PROTECTED_MODE) 1$!\1 0!' /usr/src/redis/src/server.h; \
	grep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 0$' /usr/src/redis/src/server.h; \
# for future reference, we modify this directly in the source instead of just supplying a default configuration flag because apparently "if you specify any argument to redis-server, [it assumes] you are going to specify everything"
# see also https://github.com/docker-library/redis/issues/4#issuecomment-50780840
# (more exactly, this makes sure the default behavior of "save on SIGTERM" stays functional by default)
	\
	make -C /usr/src/redis -j "$(nproc)"; \
	make -C /usr/src/redis install; \
	\
	rm -r /usr/src/redis; \
	\
	apt-get purge -y --auto-remove $buildDeps

RUN mkdir /data && chown redis:redis /data
VOLUME /data
WORKDIR /data

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 6379
CMD ["redis-server"]
Dockerfile的结构

Dockerfile可以理解为一个由上往下执行指令的脚本。
Dockerfile的指令简单分为五大类:

  1. 基础指令:用于定义新镜像的基础和性质
  2. 控制指令:是指导镜像构建的核心部分,用于描述镜像在构建构成中需要执行的指令
  3. 引入指令:用于将外部文件直接引入到构建镜像内部
  4. 执行指令:能够为基于镜像所创建的容器,指定在启动时需要执行的脚本或命令。
  5. 配置指令:对镜像以及基于镜像所创建的容器,可以通过配置指定对其网络、用户等内容进行配置。

常见的Dockerfile指令

FROM

通过FROM指令指定一个基础镜像,接下来的操作都是基本这个镜像所展开的。所以FROM指令经常作为第一条指令。

//FROM指令的三种形式
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]

在Dockerfile中可以多次出现FROM指令,当FROM第二次或者之后出现时,表示此刻需要将当前指出的镜像的内容合并到此刻构建的镜像内容里。直接合并两个镜像。

RUN

RUN指令就是用于向控制台发送命令的指令。

//RUN后面直接拼上需要执行的命令,在构建时,docker会执行这些命令,并将我们对文件系统的修改记录下来,形成镜像的变化。
RUN <commond>
RUN ["execute","param1","param2"]

支持 \ 换行。

ENTRYPOINT和CMD

基于镜像启动的容器,在容器启动时会根据镜像所定义的一条命令来启动容器中进程号为1的进程。而这个命令的定义,就是通过Dockerfile中的ENTRYPOINT和CMD来实现。

ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT command param1 param2

CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2

ENTRYPOINT和CMD指令用法近似,都是给出需要执行的命令,并且都可以为空,或者不再Dockerfile里指出。

EXPOSE

通过EXPOSE指令就可以为镜像指定要暴露的端口

//配置了镜像的端口暴露定义,那么基于这个镜像所创建的容器,
//在被其他容器通过--link连接时,就能够直接允许来自其他容器对这些端口的访问了
EXPOSE <port> [<port>/<protocol>...]
VOLUME

使用VOLUME指令来定义基于此镜像的容器所自动建立的数据卷。

//在VOLUME指令中定义的目录,在基于新镜像创建容器时,会自动建立为数据卷,不需要我们再单独使用-v选项来配置
VOLUME ["/data"]

COPY和ADD
使用COPY或ADD指令能够帮助我们直接从宿主机的文件系统里拷贝内容到镜像里的文件系统中。

COPY [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] <src>... <dest>

COPY [--chown=<user>:<group>] ["<src>",..."<dest>"]
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

两者的区别主要在于ADD能够支持网络端的URL地址作为src源,并且在源文件被识别为压缩包时,自动进行解压,而COPY没有这两个能力。

构建镜像

构建镜像,参数是目录路径(本地路径或URL路径),而非dockerfile的文件路径。
这个目录作为构建的环境目录,我们的很多操作都是基于这个目录进行的。如,使用COPY或是ADD拷贝文件到构建的新环境时,会以这个目录作为基础目录。

docker build ./webapp

默认情况下,docker build也会从这个目录下寻找目录名为Dockerfile的文件,将它作为Dockerfile内容来源。如果我们的Dockerfile文件路径不在目录下,或者有另外的文件名,我们可以通过-f选先单独给出Dockerfile的文件的路径

docker build -t webapp:latest -f ./webapp/a.Dockerfile ./webapp

构建时最好带上-t选项 指定新生成镜像的名称

docker build -t webapp:latest ./webapp

第十三课 常见Dockerfile使用技巧

构建中使用变量

在Dockerfile中,可以用ARG指令来建立一个参数,在构建时通过构建指令传入这个参数变量,并且在Dockerfile里使用它。例如:可以使用ARG占位符来控制Dockerfile中某个程序的版本。

FROM  debian:stretch-slim

## ......

ARG TOMCAT_MAJOR
ARG TOMCAT_VERSION

## ......

RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"

## ......

本例中,将tomcat的版本号通过ARG指令定义为参数变量,在调用下载tomcat包时,使用变量替换掉下载地址中的版本号。通过这样的定义,就可以在不对Dockerfile进行大幅修改的前提下,轻松实现对Tomcat版本的切换并重新构建镜像了。

使用这个Dockerfile文件构建TOmcat时,可以在构建时通过docker build的 --build-arg 选项来设置参数变量

docker build --build-arg TOMCAT_MAJOR --build-arg TOMCAT_VERSION=8.0.53 -t tomcat:8.0 ./tomcat
构建缓存

Docker在镜像构建的过程中,还支持一种缓存策略来提高镜像的构建速度。

原理:由于镜像是多个指令所创建的镜像层组合而得,那么如果我们判断新编译的镜像层与已经存在的镜像层未发生变化,那么我们完全可以直接利用之前构建的结果,而不需要再执行这条构建指令,这就是镜像构建缓存的原理。

在另外一些时候,我们不希望Docker在构建镜像时使用构建缓存,这时我们通过 --no-cache 选项来禁用它。

docker build --no-cache ./webapp

搭配 ENTRYPOINT 和 CMD

ENTRYPOINT 和 CMD 这两个命令的目的,都是用来指定基于此镜像所创建容器里主进程的启动命令的。
两个指令的区别在于,ENTRYPOINT 指令的优先级高于CMD指令。

为了更好的让大家理解,这里索性列出所有的 ENTRYPOINT 与 CMD 的组合,供大家参考。

ENTRYPOINTCMD实际执行
ENTRYPOINT ["/bin/ep", “arge”]/bin/ep arge
ENTRYPOINT /bin/ep arge/bin/sh -c /bin/ep arge
CMD ["/bin/exec", “args”]/bin/exec args
CMD /bin/exec args/bin/sh -c /bin/exec args
ENTRYPOINT ["/bin/ep", “arge”]CMD ["/bin/exec", “argc”]/bin/ep arge /bin/exec argc
ENTRYPOINT ["/bin/ep", “arge”]CMD /bin/exec args/bin/ep arge /bin/sh -c /bin/exec args
ENTRYPOINT /bin/ep argeCMD ["/bin/exec", “argc”]/bin/sh -c /bin/ep arge /bin/exec argc
ENTRYPOINT /bin/ep argeCMD /bin/exec args/bin/sh -c /bin/ep arge /bin/sh -c /bin/exec args

ENTRYPOINT 和 CMD 设计的目的是不同的。ENTRYPOINT指令主要用于对容器进行一些初始化,而CMD指令则用于真正定义容器中主程序的启动命令。

这是redis镜像中对ENTRYPOINT 和 CMD的定义

## ......

COPY docker-entrypoint.sh /usr/local/bin/

ENTRYPOINT ["docker-entrypoint.sh"]

## ......

CMD ["redis-server"]

可以看到,CMD指令定义的正是启动redis的服务程序,而ENTRYPOINT使用的是一个外部引入的脚本文件。

事实上,使用脚本文件来作为ENTRYPOINT的内容是常见的做法,因为对容器运行初始化的命令相对较多,全部放置在ENTRYPOINT后会特别复杂。
我们来看看Redis中ENTRYPOINT脚本,可以看到其中会根据脚本参数进行一些处理,而脚本的参数,其实就是CMD中定义的内容。

#!/bin/sh
set -e

# first arg is `-f` or `--some-option`
# or first arg is `something.conf`
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
	set -- redis-server "$@"
fi

# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
	find . \! -user redis -exec chown redis '{}' +
	exec gosu redis "$0" "$@"
fi

exec "$@"

这里我们要关注脚本最后的一条命令,也就是 exec “$@”。在很多镜像的 ENTRYPOINT 脚本里,我们都会看到这条命令,其作用其实很简单,就是运行一个程序,而运行命令就是 ENTRYPOINT 脚本的参数。反过来,由于 ENTRYPOINT 脚本的参数就是 CMD 指令中的内容,所以实际执行的就是 CMD 里的命令。

所以说,虽然 Docker 对容器启动命令的结合机制为 CMD 作为 ENTRYPOINT 的参数,合并后执行 ENTRYPOINT 中的定义,但实际在我们使用中,我们还会在 ENTRYPOINT 的脚本里代理到 CMD 命令上。

第十四课 使用 Docker Hub 中的镜像

本节可以看看,可记录的东西比较少

PS: 这是学习docker小册的笔记,附上小册地址:https://juejin.im/book/5b7ba116e51d4556f30b476c

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值