Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
引自:https://yeasy.gitbooks.io/docker_practice/content/introduction/what.html
本文内容大部分是来源于Docker — 从入门到实践,并且添加了部分自己的理解。算是读书总结和笔记吧。
现在的Docker,人们对他可谓是推崇备至,评价极高。传统的虚拟机技术被取代的趋势已很明显。再加上其开源免费,受到大量开发者的追捧,Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟。
其实docker出身于法国某公司的一个内部项目。开源以后就在github上进行维护。之后渐渐受到越来越广泛的关注。他在2013年才改名为docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
为什么使用docker?
详见:https://yeasy.gitbooks.io/docker_practice/content/introduction/why.html
Docker的三个基本概念
学习docker前需要先了解三个非常重要的概念。理解了这三个概念,就理解了 Docker 的整个生命周期。
- 镜像(Image)
Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:14.04 就包含了完整的一套 Ubuntu 14.04 最小系统的 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。 - 容器(Container)
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会把容器和虚拟机搞混。 - 仓库(Repository)
镜像构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,14.04, 16.04。我们可以通过 ubuntu:14.04,或者 ubuntu:16.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。
仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
安装
请参考官方文档
使用镜像
获取镜像
Docker Hub 上有大量的高质量的镜像可以用
从 Docker Registry 获取镜像的命令是 docker pull。其命令格式为:
- 可以通过 docker pull –help 查看用法
- Docker Registry地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
仓库名:如之前所说,这里的仓库名是两段式名称,既 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
比如:12$ docker pull ubuntu:14.0414.04: Pulling from library/ubuntu上面的命令中没有给出 Docker Registry 地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:14.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 14.04 的镜像。
运行
|
|
docker run 就是运行容器的命令。
- -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
- –rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 –rm 可以避免浪费空间。
- ubuntu:14.04:这是指用 ubuntu:14.04 镜像为基础来启动容器。
- bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 14.04.5 LTS 系统。
最后我们通过 exit 退出了这个容器。
一般 我们执行的收带-it就好了:
|
|
|
|
这条命令会用 nginx 镜像启动一个容器,命名为 webserver ,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器。
启动这个容器,并且打开bash窗口
运行以后可以从浏览器访问:http://localhost
列出镜像
- docker images
默认的 docker images 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a 参数。1$ docker images -a
搜索镜像
|
|
commit生成镜像
当你修改了容器的内容之后,执行$ docker diff webserver
可以看到你对镜像的修改。
docker commit 的语法格式为:docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
我们可以用下面的命令将容器保存为镜像:
执行$ docker images nginx
可以看到自己commit的镜像。
运行新的镜像,容器命名为web2,并且映射到 81 端口。访问 http://localhost:81 看到结果。
查看镜像内的历史记录
退出来之后再运行容器:
慎用 docker commit
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念, 但是实际环境中并不会这样使用。
首先,如果仔细观察之前的 的结果,你会发现除了真 正想要修改的 文件外,由于命令的执 行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件 包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜 像极为臃肿。
此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜 像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎 么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间 后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索, 但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦 的。
而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层 都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添 加、修改,而不会改动上一层。如果使用 docker commit 制作镜像,以及后期 修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢 失,会一直如影随形的跟着这个镜像,即使根本无法访问到TM。这会让镜像更加臃 肿。
docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保 存现场等。但是,不要使用 docker commit 定制镜像,定制行为应该使用
Dockerfile 来完成。
使用 Dockerfile 定制镜像
参考链接
示例:
创建一个Dockerfile文件:
其内容为:
FROM 指定基础镜像。
RUN 执行命令,并且构建一层镜像。
如果需要执行多条命令,不要执行多个RUN。应该这样编辑:
- 构建
在 Dockerfile 文件所在目录执行:1$ docker build -t nginx:v3 .
-t nginx:v3
指定了镜像名字
注意以上命令最后的那个 .
,它的意思是指定上下文的路径为 .
,执行COPY命令的时候相当于以此路径为上下文:COPY ./package.json /app/
docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
docker build的其他用法
直接用 Git repo 进行构建
|
|
#
后面的是注释
用给定的 tar 压缩包构建
|
|
从标准输入中读取 Dockerfile 进行构建
|
|
或
从标准输入中读取上下文压缩包进行构建
|
|
Dockerfile 指令概览
上面已经介绍了FROM,RUN,下面再看一下其他的指令。
格式:
* COPY <源路径>... <目标路径>
* COPY ["<源路径1>",... "<目标路径>"]
|
|
这个package.json必须在上下文目录中,上下文的概念上面也提过了。
Add
不常用,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD
CMD 容器启动命令
CMD 指令的格式和 RUN 相似,也是两种格式:
- shell 格式:CMD <命令>
- exec 格式:CMD [“可执行文件”, “参数1”, “参数2”…]
- 参数列表格式:CMD [“参数1”, “参数2”…]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
ubuntu 镜像默认的 CMD 是 /bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。
|
|
实际执行中,会将其变更为:
一般推荐使用 exec 格式。
ENTRYPOINT 入口点
当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传给 ENTRYPOINT
比如:
|
|
这里 -i 就是新的 CMD,因此会作为参数传给 curl
ENV 设置环境变量
格式有两种:
- ENV
- ENV
= = … 12ENV VERSION=1.0 DEBUG=on \NAME="Happy Feet"
\
用来换行,含有空格的值要用双引号括起来。
后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。
ARG 构建参数
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
VOLUME 定义匿名卷
|
|
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
不太明白
运行时可以覆盖这个挂载设置:
EXPOSE 声明端口
WORKDIR 指定工作目录
USER 指定当前用户
HEALTHCHECK 健康检查
ONBUILD
只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。