Docker教程
Docker 核心技术
隔离性 Linux namespace
每个用户实例之间相互隔离, 互不影响。 一般的硬件虚拟化方法给出 的方法是VM,而LXC给出的方法是container,更细一点讲就是 kernel namespace。其中pid、net、ipc、mnt、uts、user等 namespace将container的进程、网络、消息、文件系统、 UTS(“UNIX Time-sharing System”)和用户空间隔离开。
- pid namespace。不同用户的进程就是通过pid namespace隔离开的,且不同 namespace 中可以有相同pid。所有的LXC进程在docker中的父 进程为docker进程,每个lxc进程具有不同的namespace。同时 由于允许嵌套,因此可以很方便的实现 Docker in Docker。
- net namespace 有了 pid namespace, 每个namespace中的pid能够相互隔 离,但是网络端口还是共享host的端口。网络隔离是通过net namespace实现的, 每个net namespace有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每个container的网络就能隔离开来。 docker默认采用veth的方式将container中的虚拟网卡同host 上的一个docker bridge: docker0连接在一起。
- ipc namespace container中进程交互还是采用linux常见的进程间交互方法 (interprocess communication - IPC), 包括常见的信号 量、消息队列和共享内存。然而同 VM 不同的是,container 的进程间交互实际上还是host上具有相同pid namespace中的进 程间交互,因此需要在IPC资源申请时加入namespace信息 - 每 个IPC资源有一个唯一的 32 位 ID。
- mnt namespace 类似chroot,将一个进程放到一个特定的目录执行。mnt namespace允许不同namespace的进程看到的文件结构不同,这 样每个 namespace 中的进程所看到的文件目录就被隔离开了。 同chroot不同,每个namespace中的container 在/proc/mounts的信息只包含所在namespace的mount point。
- uts namespace UTS(“UNIX Time-sharing System”) namespace允许每个 container拥有独立的hostname和domain name, 使其在网络 上可以被视作一个独立的节点而非Host上的一个进程。
- user namespace 每个container可以有不同的 user 和 group id, 也就是说 可以在container内部用container内部的用户执行程序而非 Host上的用户。
控制组 Cgroups
cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单, 提供类似文件的接口,在 /cgroup目录下新建一个文件夹即可新建一 个group,在此文件夹中新建task文件,并将pid写入该文件,即可实 现对该进程的资源控制。groups可以限制blkio、cpu、cpuacct、 cpuset、devices、freezer、memory、net_cls、ns九大子系统 的资源,以下是每个子系统的详细说明:
- blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁 盘,光盘以及usb等等。
- cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。
- cpuacct 产生cgroup任务的cpu资源报告。
- cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配 单独的cpu和内存。
- devices 允许或拒绝cgroup任务对设备的访问。
- freezer 暂停和恢复cgroup任务。
- memory 设置每个cgroup的内存限制以及产生内存资源报告。
- net_cls 标记每个网络包以供cgroup方便使用。
- ns 名称空间子系统。
便携性 AUFS
AUFS (AnotherUnionFS) 是一种 Union FS。 简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)的文件系统, 更进一步的理解, AUFS支持为每一个成 员目录(类似Git Branch)设定readonly、readwrite 和 whiteout-able 权限, 同时 AUFS 里有一个类似分层的概念, 对 readonly 权限的 branch 可以逻辑上进行修改(增量地, 不影响 readonly 部分的)。
通常 Union FS 有两个用途, 一方面可以实现不借助 LVM、RAID 将多个disk挂到同一个目录下, 另一个更常用的就是将一个 readonly 的 branch 和一个 writeable 的 branch 联合在一 起,Live CD正是基于此方法可以允许在 OS image 不变的基础上允 许用户在其上进行一些写操作。
安全性 AppArmor,SELinux,GRSEC
安全永远是相对的,这里有三个方面可以考虑Docker的安全特性:
- 由kernel namespaces和cgroups实现的Linux系统固有的安 全标准;
- Docker Deamon的安全接口;
- Linux本身的安全加固解决方案,类如AppArmor, SELinux;
Docker 快速入门
本章节分为四个部分:
-
Docker安装
该部分介绍Docker在Centos7试验环境下的安装方法。 -
Docker镜像
该部分主要介绍 Docker利用Dockerfile来构建构建镜像文件。 通常Dcokerfile构建镜像时需要基础镜像,在文章的开始部分介 绍构建基础镜像的官方方法。随后,详细讲解Dockerfile的文件 结构,以及文件内元素的具体使用方法。 -
Docker容器
该部分介绍容器的一些基础使用。利用简单的docker命令行来创 建docker容器,并且在容器中安装应用等。简单介绍docker命令 行的基础使用方法,调试方法等。介绍如何管理容器内产生的数据 并将其共享和持久化,同时容器通信的管理实现也做来相关介绍。 -
Docker基本指令和用法
通过字典方式来整理docker命令行说明。
Docker 安装 (操作系统为CentOS7 64位)
参考官方文档安装:https://docs.docker.com/engine/install/centos/
卸载原有的环境:
$ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
安装对应的依赖环境和镜像地址:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装过慢设置镜像:
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
直接安装docker CE
sudo yum install -y docker-ce docker-ce-cli containerd.io
启动Docker服务
sudo systemctl start docker
查看docker的版本
sudo docker version
补充:通过官方的镜像地址下载docker会比较慢,
配置阿里云的镜像地址:
yum-config-manager --add-repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker-ce.repo
yum更新下即可:
yum makecache fast
开机启动docker
sudo systemctl enable docker
默认访问的仓库是在国外所以访问速度是没法保证的。为了更好的体验,我们可以设置镜像加速:
修改 /etc/docker/daemon.json 文件并添加上 registry-mirrors 键值。
{
"registry-mirrors": ["https://2lqq34jg.mirror.aliyuncs.com"]
}
添加保存之后,重启服务:
sudo systemctl daemon-reload
sudo systemctl restart docker
至此 Docker 在CentOS下安装步骤就完成了!
Docker 镜像
镜像是 Docker 的三大组件之一。 Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地, Docker 会从镜像仓库下载(默认是 Docker Hub 公共注册服务器 中的仓库),我们也可以搭建一个本地的镜像仓库,但这不是本文的重点。本文将以镜像为中心介绍:
- 如何构建基础镜像
- Dockerfile的基本结构以及详解
- 利用Dockerfile构建镜像
基础结构
Dockerfile由一行行命令语句组成,并且支持以 # 开头的注释行。一般的,Dockerfile 分为四部分:
- 基础镜像信息
- 维护者信息
- 镜像操作指令
- 容器启动时执行指令
例如:
FROM ubuntu
MAINTAINER docker_user docker_user@email.com
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
CMD /usr/sbin/nginx
构建基础镜像
我将应用打包到镜像中形成我们所需的镜像,往往需要一个基础的镜像 作为我们应用服务的外部环境,那么问题来了,基础镜像从何而来?官 方推荐的是直接从官网仓库pull一个,但由于官网被墙的比较厉害, 所以这里介绍一些官方提供以及个人方法。
1.使用Debootstrap来创建Ubuntu的base image
$ sudo debootstrap raring raring > /dev/null
$ sudo tar -C raring -c . | docker import - raring
$ docker run raring cat /etc/lsb-release
2.使用scratch创建base image
在Docker registry中有一个scratch,你可以pull拉取下来,
$ sudo docker pull scratch
甚至可以自己制作:
$ tar cv --files-from /dev/null | docker import - scratch
Scratch镜像很赞,它简洁、小巧而且快速, 它没有bug、安全漏 洞、延缓的代码或技术债务。这是因为它基本上是空的。除了Docker 添加了点的metadata (译注:元数据为描述数据的数据)。总之它是 非常小的一个Docker镜像。
3.下载官方提提供的OS的tar文件
到OPENVZ上下载基础包然后使用docker limport 加载到本地镜 像,这里以ubuntu14.04 为例,从openvz下载一个ubuntu14.04 的模板:
wget http://download.openvz.org/template/precreated/ubuntu-14.04- x86_64.tar.gz
cat ubuntu-14.04-x86_64-minimal.tar.gz | docker import - ubuntu:base
创建镜像
编写完成 Dockerfile 之后,可以通过 docker build 命令来创 建镜像。
基本的格式为 docker build [选项] 路径,该命令将读取指定路径 下(包括子目录)的 Dockerfile,并将该路径下所有内容发送给 Docker 服务端,由服务端来创建镜像。因此一般建议放置 Dockerfile 的目录为空目录。也可以通过 .dockerignore 文件 (每一行添加一条匹配模式)来让 Docker 忽略路径下的目录和文 件。要指定镜像的标签信息,可以通过 -t 选项,例如:
$ sudo docker build -t myrepo/myapp /tmp/test1/
Dockerfile 操作建议
Docker可以读取一个Dockerfile文件来构建所需的镜像,这个文件 里包含所有所需要的指令。Dockerfile文件用特有的格式来设置镜像信息,本文包含Docker官方提供的一些践以及方法,强烈建议你去参照 这些建议。
官方建议:
- 一个Dockerfile文件尽量 越简洁越好 ,这意味着 它可以被停止销 毁,然后被最小化配置安装到另一个地方。
- 在通常情况下,最好把 Dockerfile文件放到一个空目录 ,然后,将构建 镜像所需要动文件添加到该目录。为了提高构建性能,你也可以通 过添加 .dockerignore 文件到该目录以排除文件和目录,该文件支 持排斥的模式类似于.gitignore文件。
- 为了 减少镜像复杂度、依赖、文件大小和构建时间 ,应该尽量避免安装多余的 不需要包,例如:你不需要在一个数据库镜像中添加一个文本编辑器。
- 在绝大多数情况下, 每一个镜像只跑一个process ,应用于多个容器中可 以方边应用横向扩展和重复利用容器。如果该服务依赖于其他服 务,请使用容器互联。
- 你需要在Dockerfile的可读性和镜像层次最小化之间取得平衡, 要有目的且非常谨慎的控制使用分层的数量。
Dockerfile 参数详解
FROM
格式为 FROM 或FROM :。
第一条指令必须为 FROM 指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。
MAINTAINER
格式为 MAINTAINER ,指定维护者信息。
RUN
格式为 RUN 或 RUN [“executable”, “param1”, “param2”]。
前者将在 shell 终端中运行命令,即 /bin/sh -c; 后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN [“/bin/bash”, “-c”, “echo hello”],每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较 长时可以使用 \ 来换行。
CMD
支持三种格式
- CMD [“executable”,“param1”,“param2”] 使用 exec 执行,推荐方式;
- CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
- CMD [“param1”,“param2”] 提供给 ENTRYPOINT 的默认参数;
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
EXPOSE
EXPOSE […]
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P ,Docker 主机会自动分配一个端口转发到指定的端口。
ENV
ENV
指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。
ADD
ADD
该命令将复制指定的 到容器中的 。 其中可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件 (自动解压为目录)。
COPY
COPY
复制本地主机的 (为 Dockerfile 所在目录的相对路径)到容器中 的 。 当使用本地目录为源目录时,推荐使用 COPY。
ENTRYPOINT
两种格式:
ENTRYPOINT [“executable”, “param1”, “param2”]
ENTRYPOINT command param1 param2(shell中执行)
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
每个 Dockerfile 中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。
VOLUME
VOLUME [“/data”]
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据 库和需要保持的数据等。
USER
USER daemon
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以 在之前创建所需要的用户,例如: RUN groupadd -r postgres && useradd - r -g postgres postgres
WORKDIR
WORKDIR /path/to/workdir
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
Docker 容器
容器是 Docker 又一核心概念。 简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环 境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运 行态环境和其他系统环境)和跑在上面的应用。本章节着重介绍了容器 的基础使用方法,以及如何管理容器的数据、如何管理容器的网络,相信你读完本章节将会有一个大致的了解。
容器使用入门
使用info命令检查docker安装程序是否正常运行:
# 检查是否安装好docker
$ docker info
如果提示如下信息: command not found 或者 类似 于/var/lib/docker/repositories: permission denied 可 能安装有问题或者尝试在前面加上sudo。此外,基于docker系统配置,命令行前面应该加上sudo,来确保正常 执行命令,并且此时系统会为Administrar创建一个名叫docker的 Unix 用户组来让其他用户加入该组。
下载一个已经制作好的镜像:
docker pull ubuntu
创建一个带有交互窗口的container:
docker run -i -t ubuntu /bin/bash
-i参数表示启动了一个可以交互的容器,—t参数表示创建了一个附带 标准输入和输出的pseudo-TTY窗口 如果想要退出tty窗口,使用 Ctrl-p + Ctrl-q指令,容器将会退 出并且会持续保持一个停止的状态。如果想查看所有状态的容器,可以 使用docker ps -a 指令。
提交(保存)一个容器的状态到一个镜像文件中:
当提交(commit)容器时,docker仅保存源镜像与当前镜像的差异,如果想列出镜像,请使用docker images指令
docker commit <container> <some_name>
docker images
管理容器工作
我们用docker run指令来运行一个容器:
- 交互容器跑在前端.
- 守护进程跑在后台.
一些常用管理容器的命令:
- docker ps - 列出容器
- docker logs - 输出容器日志
- docker stop - 停止运行容器.
我们以docker version为例查看当前安装的docker客户端以及 deamon进程信息:
docker version
如果你想展示一些帮助信息,可以使用:
docker --help
在docker中跑一个web应用程序
现在你已经掌握了一些基础了,来深入一下吧。之前跑的一些应用没什 么实际用途,让们来跑一个web应用试试吧。 这个web应用包含里 python 应用:
docker run -d -P training/webapp python app.py
以上指令中包含两个参数:
-d 让容器在后台运行
-P 分配一个主机端口到容器端口的映射以供外部访问
training/webapp是一个构建好的镜像,里面包含了一个简单的 Python Flask web应用程序。
管理容器数据
docker管理数据的两种主要方式。 数据卷,以及数据卷容器。数据卷是在一个或多个容器,它绕过Union File System的一个专门 指定的目录。数据卷为持续共享数据提供了一些有用的功能:
- 在创建容器时,卷被初始化。如果容器的基础映像包含指定的数据 装入点,现有的数据复制到在卷初始化新卷。
- 数据卷可以共享和容器之间重复使用。
- 改变数据卷将立刻生效(在所有挂载该容器中)。
- 改变数据卷数据不会影响到容器。
- 即使容器本身被删除。但是数据卷依然存在。
- 数据卷的目的是持久化数据,独立于容器的生命周期。Docker因 此不会自动删除卷,当你删除一个容器,也不会“垃圾回收”直到没 有容器再使用。
添加一个数据卷
你可以在docker run时加上-v参数来添加一个数据卷,-v参数也可 以使用多次,以挂载多个数据卷
docker run -d -P --name web -v /webapp training/webapp python app.py
这条命令将在容器中的/webapp文件夹创建一个数据卷存储数据。 你也可以在构建镜像时在Dockerfile里面定义。 数据卷默认的权限时读写,你也可以定义成只读。
docker run -d -P --name web -v /opt/webapp:ro training/webapp python app.py
查找数据卷路径用docker inspect命令定位:
docker inspect web
挂载一个主机目录作为数据卷
挂载主机目录为数据卷,必须参照 -v hostPATH:containerPATH 这种格式 路径必须为绝对路径,以保证容器的 可移植性。
docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp 目录
docker数据卷的权限是读写,你也可以指定只读:
docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py
挂载一个数据文件作为数据卷
-v 标记也可以从主机挂载单个文件到容器中
docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
管理容器通信
容器与宿主机器采用端口映射的方式通信
之前的例子:
docker run -d -P training/webapp python app.py
我们也可以指定端口映射:
docker run -d -p 80:5000 training/webapp python app.py
-p参数还有其他指定方法:
ip:hostPort:containerPort 映射指定IP的指定端口
ip::containerPort 映射指定IP任意端口
hostPort:containerPort 映射所有主机IP的指定端口
Docker 常用基本指令及用法
镜像命令
镜像命令 | 说明 |
---|---|
docker images | 列出本地主机上的镜像 |
docker search 镜像名称 | 从 docker hub 上搜索镜像 |
docker pull 镜像名称 | 从docker hub 上下载镜像 |
docker rmi 镜像名称 | 删除本地镜像 |
docker images
镜像表格信息说明
选项 | 说明 |
---|---|
REPOSITORY | 表示镜像的仓库源 |
TAG | 镜像的标签 |
IMAGE ID | 镜像ID |
CREATED | 镜像创建时间 |
SIZE | 镜像大小 |
参数 | 说明 |
---|---|
-a | 列出本地所有的镜像 |
-q | 只显示镜像ID |
–digests | 显示镜像的摘要信息 |
–no-trunc | 显示完整的镜像信息 |
docker search
docker hub是Docker的在线仓库,我们可以通过docker search 在上面搜索我们需要的镜像
参数名称 | 描述 |
---|---|
–no-trunc | 显示完整的描述信息 |
–limit | 分页显示 |
-f | 过滤条件 docker search -f STARS=5 tomcat |
Docker pull
从Docker hub 上下载镜像文件
Docker rmi
删除方式 | 命令 |
---|---|
删除单个 | docker rmi -f 镜像ID |
删除多个 | docker rmi -f 镜像1:TAG 镜像2:TAG |
删除全部 | docker rmi -f $(docker images -qa) |
容器命令
创建并启动一个容器的命令
docker run [OPTIONS] IMAGE [COMMAND]
OPTIONS中的一些参数
options | 说明 |
---|---|
–name | “容器新名字”: 为容器指定一个名称 |
-d | 后台运行容器,并返回容器ID,也即启动守护式容器 |
-i | 以交互模式运行容器,通常与 -t 同时使用 |
-t | 为容器重新分配一个伪输入终端,通常与 -i 同时使用 |
-P | 随机端口映射 |
-p | 指定端口映射 |
我们要查看当前正在运行的容器有哪些,可以通过ps 命令来查看:
docker ps [OPTIONS]
OPTIONS | 说明 |
---|---|
-a | 列出当前所有正在运行的容器+历史上运行过的 |
-l | 显示最近创建的容器 |
-n | 显示最近n个创建的容器 |
-q | 静默模式,只显示容器编号 |
–no-trunc | 不截断输出 |
我们启动了一个容器后,如何退出容器
退出方式 | 说明 |
---|---|
exit | 容器停止退出 |
ctrl+p+q | 容器不停止退出 |
启动容器:
docker start 容器ID或者容器名称
重启容器:
docker restart 容器id或者名称
停止容器:
docker stop 容器ID或者名称
还可以通过强制停止方式处理:
docker kill 容器ID或者名称
删除容器:
有时候容器使用完成就没有作用了,我们想要删除掉容器,这时可以通过rm命令
docker rm 容器ID
docker rm -f $(docker ps -qa)
docker ps -a -q | xargs docker rm
其他命令
查看容器运行的日志:
docker logs -t -f --tail 3 容器ID
查看容器中运行的进程:
docker top 容器ID
查看容器细节:
docker inspect 容器ID
进入运行的容器:
进入方式 | 说明 |
---|---|
exec | 在容器中打开新的终端,并且可以启动新的进程 |
attach | 直接进入容器启动命令的终端,不会启动新的进程 |
文件复制:
我们有时需要从容器中拷贝内容到宿主机中
docker cp 容器ID:容器内路径 目的地路径