使用镜像

安装docker  

#yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的

yum install -y yum-utils device-mapper-persistent-data lvm2

 

本章将介绍更多关于镜像的内容,包括:

从仓库获取镜像;

命令:

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]     #默认拉取的是dockerhub中的镜像

例如:拉取nginx镜像

[root@localhost ~]# docker pull nginx

管理本地主机上的镜像:

1.列出镜像

docker image ls

列表包含了 仓库名 、 标签 、 镜像 ID 、 创建时间 以及 所占用的空间

2.查看镜像占用硬盘大小

 3.列出某个镜像

[root@localhost ~]# docker image ls nginx

4.以特定的格式显示镜像

 

5.删除本地镜像

命令:$ docker image rm [选项] <镜像1> [<镜像2> ...]

docker image rm ID

docker image rm 镜像名称

使用docker image ls来配合删除

例如:删除一个名为redis的镜像

docker image rm $(docker image ls -q redis)   #-q参数可以显示镜像id

例如:删除redis:2.2之前的镜像

docker image rm $(docker image ls -q -f before=redis:2.2)

例如:删除所有镜像 

 docker image rm $(docker image ls -q )

6.在容器中修改了文件,查看修改了哪些内容:可以使用docker diff命令

 

 

 docker diff显示结果中有三种状态

A 添加了一个文件或目录
B 文件或目录被删除
C原文出自

7.docker commit保存容器为镜像,一般用来保留现场,和虚拟机的快照功能比较相似

docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git

版本控制相似,不过这里这些信息可以省略留空。

8.Dockerfile制作镜像

Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容

 

FROM:

使用docker制作镜像的时候大部分情况下需要在原有的镜像基础上进行部署,当然也有不需要基于基础镜像的

的情况,例如swarm、coreos、etcd.

需要制定基础的镜像的时候可以使用下面命令

#以nginx镜像作为基础镜像进行操作,默认nginx会到dockerhub上下载
FROM nginx   
#不以任何镜像为基础镜像可以使用
FROM scratch

RUN:

 

RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最

常用的指令之一。其格式有两种:

1.shell 格式: RUN <命令>

RUN echo "Hello Docker" > /usr/share/nginx/index/index.html

2.exec 格式: RUN ["可执行文件", "参数1", "参数2"]

RUN ["/bin/bash","-c","echo hello"]

 注意:由于Dockerfile每一个Dockerfile指令都会生成一层镜像,目前限制是127层,所以尽可能的减少指令的层数,如下:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
View Code

构建镜像

在Dockerfile文件所在目录执行:docker build -t nginx:v3 .     #注意有个点   -t参数指定镜像名称

点理解为当前目录并不是特别准确,docker是C/S架构,docker build会通过API连接到server端进行构建的,docker并非在本地构建,

“.”可以理解为上下文目录,例如:

COPY ./package.json /app/

 

这并不是要复制执行 docker build 命令所在的目录下的 package.json ,也不是复制Dockerfile 所在目录下的 package.json ,而是复制 上下文(context) 目录

下的package.json,因此, COPY 这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么COPY ../package.json /app 或者 COPY /opt/xxxx /app

无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。

现在就可以理解刚才的命令 docker build -t nginx:v3 . 中的这个 . ,实际上是在指定上下文的目录, docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

如果观察 docker build 输出,我们其实已经看到了这个发送上下文的过程:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB

 

直接用 Git repo 进行构建

$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
docker build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM gitlab/gitlab-ce:8.14.0-ce.0
8.14.0-ce.0: Pulling from gitlab/gitlab-ce
aed15891ba52: Already exists
773ae8583d14: Already exists
View Code

 

这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/ ,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。

 

用给定的 tar 压缩包构建

 

$ docker build http://server/context.tar.gz

 

如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,

并自动解压缩,以其作为上下文,开始构建。

 

 

COPY 复制文件

 

 

格式:

COPY <源路径>... <目标路径>

COPY ["<源路径1>",... "<目标路径>"]

 

 

RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。

COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路

径> 位置。

COPY package.json /usr/src/app/

COPY 源路径支持“*”通配符和“?”单个字符的正则格式,目标路径可以是绝对路径也可以是WORKDIR工作目录的相对路径

 

ADD 更高级的复制文件

ADD支持URL和压缩包的方式进行文件复制,而且复制到容器后会自动解压

格式:

ADD xxx.tar.gz|http://xxxx/aaa.txt    目标路径

 

 

CMD 容器启动命令

 

 

CMD 指令的格式和 RUN 相似,也是两种格式:

 

shell 格式: CMD <命令>

exec 格式: CMD ["可执行文件", "参数1", "参数2"...]

参数列表格式: CMD ["参数1", "参数2"...] 。在指定了 ENTRYPOINT 指令后,用 CMD

定具体的参数

shell格式举例,

CMD echo $HOME

exec 格式举例

CMD [ "sh", "-c", "echo $HOME" ]

docker不是虚拟机,没有后台运行服务的概念,所以只能在前台运行,例如nginx

错误格式

CMD service nginx start

正确格式

CMD ["nginx","-g","daemon off;"]

 

ENTRYPOINT 入口点

 

 

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。

 

那么有了 CMD 后,为什么还要有 ENTRYPOINT 呢?

场景一:让镜像变成像命令一样使用

 

假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 CMD 来实现:

FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://ip.cn" ]

 

ENV 设置环境变量

 

格式有两种:

ENV <key> <value>

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

用法:

ENV  HOSTNAME  mysql-master 
ENV PackageName  platform-server.jar
ENV PackaheDIR  /opt

  

 

ARG 构建参数

 

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

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的

环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密

码之类的信息,因为 docker history 还是可以看到所有值的。

Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令

docker build 中用 --build-arg <参数名>=<值> 来覆盖。

在 1.13 之前的版本,要求 --build-arg 中的参数名,必须在 Dockerfile 中用 ARG 定义过

了,换句话说,就是 --build-arg 指定的参数,必须在 Dockerfile 中使用了。如果对应参

数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,

而是显示警告信息,并继续构建。这对于使用 CI 系统,用同样的构建流程构建不同的

Dockerfile 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。

VOLUME 定义匿名卷

 

格式为:

VOLUME ["<路径1>", "<路径2>"...]

VOLUME <路径>

 

 

 

 

 
/var/lib/docker/volumes/nginx_date/_data/为宿主机的匿名卷
/data为挂载到容器中的目录

这样凡是写入到容器/data目录中的数据都会保存到宿主机的/var/lib/docker/volumes/nginx_date/_data/目录,不会增加容器大小,
并且可以将宿主机目录挂载到其它容器中,方便容器的数据迁移

 

EXPOSE 声明端口

 

格式为 EXPOSE <端口1> [<端口2>...] 

 

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声

明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助

镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用

随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

举例:

EXPOSE 80

 

WORKDIR 指定工作目录

 

格式为 WORKDIR <工作目录路径>

 

使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改

为指定的目录,如该目录不存在, WORKDIR 会帮你建立目录。

 

如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其

内容不是 hello 。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前

一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令

的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念

不了解所导致的错误。

之前说过每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN

cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任

何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关

系,自然不可能继承前一层构建过程中的内存变化。

因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。

 

举例:

WORKDIR   /nginx

 

USER 指定当前用户

 

格式: USER <用户名>

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目

录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。

 

当然,和 WORKDIR 一样, USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立

好的,否则无法切换。

举例:

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

 

如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来

运行某个服务进程,不要使用 su 或者 sudo ,这些都需要比较麻烦的配置,而且在 TTY 缺

失的环境下经常出错。建议使用 gosu 。

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/
gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]

 

HEALTHCHECK 健康检查

格式:

HEALTHCHECK [选项] CMD <命令> :设置检查容器健康状况的命令

HEALTHCHECK NONE :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指

HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12

引入的新指令。

在没有 HEALTHCHECK 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否

状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程

并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种

状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请

求。

而自 1.12 之后,Docker 提供了 HEALTHCHECK 指令,通过该指令指定一行命令,用这行命令

来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。

当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting ,在

HEALTHCHECK 指令检查成功后变为 healthy ,如果连续一定次数失败,则会变为

unhealthy

HEALTHCHECK 支持下列选项:

 

--interval=<间隔> :两次健康检查的间隔,默认为 30 秒;

--timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被

视为失败,默认 30 秒;

--retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3

次。

CMD , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生

效。

在 HEALTHCHECK [选项] CMD 后面的命令,格式和 ENTRYPOINT 一样,分为 shell 格式,和

exec 格式。命令的返回值决定了该次健康检查的成功与否: 0 :成功; 1 :失败; 2 :

保留,不要使用这个值。

假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否

在正常工作,我们可以用 curl 来帮助判断,其 Dockerfile 的 HEALTHCHECK 可以这么写:

 
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1

 

介绍镜像实现的基本原理:

posted @ 2019-09-17 17:31  什么都不会的小郭  阅读(441)  评论(0编辑  收藏  举报