Docker之基础及Dockerfile详解

文章目录

        • 1.使用 Docker 镜像
          • 1.1.获取镜像
          • 1.2.管理本地主机上的镜像
        • 2.操作 Docker 容器
        • 3. commit
        • 4. Dockerfile
        • 5. Dockerfile 指令详解
          • COPY/ADD 复制文件
          • CMD 容器启动命令
          • ENTRYPOINT 入口点
          • ENV 设置环境变量& ARG 构建参数
          • VOLUME 定义匿名卷
          • USER 指定当前用户

1.使用 Docker 镜像

Docker 运行容器前需要本地存在对应的镜像。本文包括:

  • 从仓库获取镜像;
  • 管理本地主机上的镜像;
1.1.获取镜像

从 Docker 镜像仓库获取镜像的命令是 docker pull 。其命令格式为:

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号] 。默认地址是 Docker
    Hub。

  • 仓库名: <软件名> 。对于 DockerHub,如果不给出用户名,则默认为 library ,也就是官方镜像,。如果不给出标签,将以 latest 作为默认标签。

比如:

 docker pull ubuntu:16.04

运行这部分内容在容器中详细介绍

$ docker run -it --rm \
ubuntu:16.04 \
bash
1.2.管理本地主机上的镜像
  • 列出镜像
docker image ls
  • 删除本地镜像
$ docker image rm [选项] <镜像1> [<镜像2> ...]

用 ID、镜像名、摘要删除镜像,可以用镜像的完整 ID,来删除镜像。 也可以使用短ID(3个字符以上)来删除镜像。

$ docker image rm 7aa

Docker之基础及Dockerfile详解_第1张图片

Untagged 和 Deleted:

到删除行为分为两类,一类是Untagged ,另一类是 Deleted 。镜像的唯一标识是其 ID 和摘要,而一个
镜像可以有多个标签。

当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。

2.操作 Docker 容器

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。启动容器有两种方式,一种是基于镜像新建一个容器,另外一个是将在终止状态( stopped )的容器重新启动。定制镜像应该使用 Dockerfile 来完成,这部分内容我们在后面介绍

新建并启动
用镜像Ubunut16.04创建容器并启动,然后终止容器。

	docker run ubuntu:16.04 /bin/echo 'Hello world!'

还可以则启动一个 bash 终端,允许用户进行交互。

   docker run -t -i ubuntu:16.04 /bin/bash

-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i则让容器的标准输入保持打开。

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

后台运行容器

    docker run -d ubuntu:16.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

这条命令是无限的输出hello word,此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用docker logs 查看)。

查看容器

	docker container ls 

docker

-a 参数可以查看停止的容器

查看docker日志: docker container logs [container ID or NAMES]

	docker logs jovial_blackwell

终止和启动
start启动已经停止的容器
Docker之基础及Dockerfile详解_第2张图片

进入容器

在使用 -d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作:

docker attach 是 Docker 自带的命令。下面示例如何使用该命令。
Docker之基础及Dockerfile详解_第3张图片
Ctrl+c会导致容器的停止。使用docker container ls就不会看到刚才进入的容器了。这就引出了exec命令

	docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。

当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。如果从这个 stdin 中 exit,不会导致容器的停止。

导出/入容器

如果要导出本地某个容器,可以使用 docker export 命令。

 docker export

docker
可以使用 docker import 从容器快照文件中再导入为镜像,例如
Docker之基础及Dockerfile详解_第4张图片

可以通过指定 URL 或者某个目录来导入,例如

	docker import http://example.com/exampleimage.tgz example/imagerepo

删除容器

用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器。可以使用 docker container rm 来删除一个处于终止状态的容器。清理掉所有处于终止状态的容器:docker container prune

3. commit

镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。举个例子:

	 docker run --name webserver -d -p 80:80 nginx

这条命令会用 nginx 镜像启动一个容器,命名为 webserver ,并且映射了 80 端口,可以用浏览器去访问这个nginx服务器

Docker之基础及Dockerfile详解_第5张图片
如果需要修改页面中的文字,该怎么办?可以使用docker exec 命令进入容器,修改其内容。

	docker exec -it webserver bash
	echo '

Hello, Docker!

'
> /usr/share/nginx/tml/index.html exit

最终变成了:
Docker之基础及Dockerfile详解_第6张图片
为什么能?使用的命令就是将hello docker 写入了一个文件中,但是请注意这是容器的文件,而不是linux中的文件,也就是说修改了容器的文件,也就是改动了容器的存储层。

Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。

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

比如:

	docker commit --author four --message '修改主页' webserver nginx:v1

可以docker image ls 中看到这个新定制的镜像。当然这个镜像也是可以使用的,和普通的没什么区别。

commit的缺点:

  • commit命令会使大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿
  • 无法记录操作具体内容

4. Dockerfile

在上面的commit一节中了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这个脚本就是 Dockerfile。Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。至于Dockerfile的优点我们后面再说。

举个例子:

	 mkdir mynginx
	 cd mynginx
	 touch Dockerfile

内容为:

	FROM nginx
	RUN echo 'Hello, Docker!' > /usr/share/nginx/html/index.html
  • FROM就是指定基础镜像,这个很容易理解,以一个镜像为基础,在其上进行定制。
  • RUN指令是用来执行命令行命令的,格式有两种:
    • shell 格式: RUN <命令>,就像上面例子中用到的
    • exec 格式: RUN [“可执行文件”, “参数1”, “参数2”] ,这更像是函数调用中的格式。

构建镜像:
Docker之基础及Dockerfile详解_第7张图片
其中-t指定了最终镜像的名称。返回的结果描述了镜像构建的过程,在 Step2 中,RUN 指令启动了一个容器。RUN 指令启动了一个容器 22829ea4af92 ,执行了所要求的命令,并最后提交了这一层 e133641a2a62 ,随后删除了所用到的这个容器 22829ea4af92 。

Dockerfile 中每一个指令都会建立一层。每一个 RUN 的行为,就和刚才我们commit建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。因此构建环境等命令尽量使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。

**注意:**每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。比如下面例子:

	RUN cd /app
	RUN echo "hello" > world.txt

第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任
何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。当然可以使用WORKDIR 指定工作目录WORKDIR <工作目录路径>

docker build 的工作原理

docker build 命令进行镜像构建。其格式为:

	docker build [选项] <上下文路径/URL/->

命令最后有一个’.’ ,’.’ 表示当前目录,这是在指定上下文路径。docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

实际上 Dockerfile 的文件名并不要求必须为 Dockerfile ,而且并不要求必须位于上下文目录中,比如可以用 -f …/Dockerfile.php 参数指定某个文件作为Dockerfile 。

Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 DockerRemote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。

5. Dockerfile 指令详解

COPY/ADD 复制文件

和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。

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

<源路径> 可以是多个,甚至可以是通配符,<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。比如 <源路径> 可以是一个 URL

CMD 容器启动命令

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

	 CMD <命令>
	 CMD ["可执行文件", "参数1", "参数2"...]

Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。 CMD 指令就是用于指定默认的容器主进程的启动命令(参数)。

ENTRYPOINT 入口点

ENTRYPOINT 的目的和 CMD一样,都是在指定容器启动程序及参数。 ENTRYPOINT 在运行时也可以替代,不过比 shell 要略显繁琐。

练习
复习一下curl命令:
Docker之基础及Dockerfile详解_第8张图片
如上图所示,curl -si url 命令打印出了响应的内容。

编辑如下Dockerfile,构建docker build -t myip .

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

然后执行:

	docker run myip
	当前 IP:1.180.209.251 来自:内蒙古自治区呼和浩特市 电信

那么如果我们希望显示 HTTP头信息,就需要加上 -i 参数。但是直接加载上面的命令后面会报错,跟在镜像名后面的是 command ,运行时会替换 shell 的默认值。因此这里的 -i 替换了原来的shell ,而不是添加在原来的 curl -s http://ip.cn 后面。而 -i 根本不是命令。

ENTRYPOINT的应用,编辑Dokerfile文件:

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

然后使用docker run myip -i

这是因为当存在 ENTRYPOINT 后, 我们的命令参数将会作为参数传给ENTRYPOINT ,而这里 -i 参数传给 curl ,从而达到了我们预期的效果。

ENV 设置环境变量& ARG 构建参数

设置环境变量格式有两种:

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

如:

	ENV VERSION=1.0 DEBUG=on \
		NAME="Happy Feet"

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的

VOLUME 定义匿名卷

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。

格式为:

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

比如VOLUME /data命令中, /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:

	docker run -d -v mydata:/data xxxx

在这行命令中,就使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

USER 指定当前用户

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , shell 以及 ENTRYPOINT 这类命令的身份。

HEALTHCHECK 健康检查

	HEALTHCHECK [选项] shell <命令> :设置检查容器健康状况的命令
	HEALTHCHECK NONE :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting ,在HEALTHCHECK 指令检查成功后变为 healthy ,如果连续一定次数失败,则会变为unhealthy。和 shell , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

假设我们有个镜像是个最简单的 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 \
	shell curl -fs http://localhost/ || exit 1

–interval=<间隔> :两次健康检查的间隔为5秒,–timeout=<时长> :健康检查命令运行超时时间为3s。并且使用 curl -fs http://localhost/ || exit1 作为健康检查命令。。命令的返回值决定了该次健康检查的成功与否: 0 :成功; 1 :失败; 2 :保留,不要使用这个值。

ONBUILD

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

	ONBUILD [INSTRUCTION]

你可能感兴趣的:(Docker)