0%

Docker 容器基础知识

容器学习

基础知识

  • 容器: 容器是镜像的运行实例。可以使用同一个镜像运行多个容器。
  • 镜像: 镜像是文件系统层和一些元数据的集合。它们合在一起,以 Dokcer 容器的形式运行
  • 层: 层是文件变更的集合。 MyApplication v1 和 v2 的不同之处保存在这个层里。

简而言之,容器运行着由镜像定义的系统。这些镜像由一个或多个层(或差异集)加上一些 docker 的元数据组成。

命令

命令 作用
docker build 构建一个 docker 镜像
docker run 以容器形式运行一个 docker 镜像
docker commit 将一个 docker 容器作为镜像提交
docker tag 给一个 docker 镜像打标签

创建 docker 镜像的方式

方法 描述
docker 命令/“手工” 使用 docker run 启动一个容器,并在命令行输入命令来创建镜像。使用 docker commit 来创建一个镜像
Dockerfile 从一个已知基础镜像开始构建,并制定一组优先的简单命令来构建
Dockerfile 及配置管理(configuration management, CM)工具 与 Dockerfile 相同,不过将构建的控制权交给了更为复杂的 CM 工具
从头构建镜像并导入一组文件 从一个空白镜像开始,导入一个含有所需文件的 TAR 文件

手工创建

通过编写 Dockerfile 文件,然后通过 docker build . 来创建镜像,可以通过打标签来方便查找新的镜像 docker tag xxx(镜像ID) tag_name

示例:

1
2
3
4
5
6
7
FROM node
MAINTAINER test@exmpale.com
RUN git clone -q https::/github.com/docker-in-practice/todo.git
WORKDIR todo
RUN npm install > /dev/null
EXPOSE 8000
CMD ["npm", "start"]
  • FROM: 定义基础镜像
  • MAINTAINER: 声明维护人员
  • 克隆代码
  • WORKDIR: 切换路径
  • RUN: 运行命令
  • EXPOSE: 监听端口
  • CMD: 运行启动程序的命令,引号只能使用双引号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 常用命令
docker build . # 根据 dockerfile 创建镜像
docker tag container_id tag_name # container_id 为镜像id, tag_name 为标签名称
docker run -p 8000:8000 --name example1 todo_app # 运行镜像 todo_app, 并命名为 example1 同时开放端口 8000 映射到镜像 8000 端口上
docker ps -a # 查看已停止和运行中的容器
docker start example1 # 启动容器
docker diff example1 # 显示镜像被实例化成一个容器以来哪些文件受了影响

docker run -d -i -p 1234:1234 --name daemon ubuntu nc -l 1234
# -d 方式将以守护进程方式运行容器
# -i 标志则赋予容器与 Telnet 回话交互的能力
# -p 将容器 1234 端口公布到宿主机上
# --name 标志赋予容器一个名称

docker search container_name # 通过镜像名称查找镜像

docker inspect container_id # 查看镜像的更多信息
# docker inspect --fromat='{{.NetworkSettings.IPAddress}}' | xargs -l1 ping -c1
docker ps -q # 查看运行着的容器的 container id

# docker 镜像和容器清理方式
docker ps -a -q | xargs docker rm
docker images -f "dangling=true" -q | xargs docker rmi

Docker 分层

默认情况下,Docker 在内部使用写时复制(copy-on-write)机制来减少所需的硬盘空间量。

  • 写时复制
    是计算机技术中使用的一种标准的优化策略。在从模板创建一个新的(任意类型)对象时,只在数据发生变化时才能将其复制进来,而不是复制整个所需的数据集。

理解 Docker

Docker 的架构

Docker 守护进程

  • 守护进程与服务器
    守护进程是运行在后台的一个进程,不在用户的直接控制之下。服务器是负责接受客户端请求,并执行用于满足该请求所需的操作的一个进程。守护进程通常也是 服务器,接收来自客户端的请求,为期执行操作。

默认的 Docker 配置,限制只能通过 /var/run/docker.sock 域套接字访问。宿主机外的进程无法获取 Docker 的访问权限。

注意: 若对外开放了 docker 守护进程的访问权限,务必 只向特定 IP 范围开放,同时不要绑定到 0.0.0.0。

  • 链接容器实现端口隔离
    不讲服务暴露给宿主机或外界,可以通过 docker 链接 --link 实现。启动顺序非常重要

  • 私有 Docker 注册中心(仓库)

Dockerfile 优化

每条 Dockerfile 命令都会在之前的基础上创建一个单一的新镜像层。

  • 在 RUN 语句中使用命令链,即 && 可以确保命令作为一条命令执行,可以保持镜像不至于太大。
  • 通过在 COPY 命令中,将 COPY 的内容拆分到应用的各个部分,这样可以利用缓存复用代码修改后未经变更的交付文件。
    但是由于缓存功能箱单简单粗糙。解决这一个问题的唯一途径辨识抛弃原有顺序配置的步骤,创建多份 Dockerfile

管理容器内服务的启动

  • 使用 Supervisor 应用来管理进程的启动

通过 commit 来保存状态

  • 状态是无法捕获的! 提交一个容器只会保存在提交那个时刻容器的文件系统的状态,而不是进程。

构建镜像

  • 使用 ADD 将文件注入到镜像
    打包并压缩文件,然后在 Dockerfile 里使用 ADD 指令。

  • 重新构建时不使用缓存
    在构建镜像时加上 --no-cache 表示不使用缓存

  • 拆分缓存 在命令后面增加一条无害的注释,从而让缓存失效。原理:Docker 将费控的更改均当做一行新的命令来对待,因此每次都需要更改来实现拆分缓存。

清理容器

  • 删除所有容器

    1
    2
    3
    docker ps -a -q | xargs --no-run-if-empty docker rm -f 
    # --no-run-if-empty 避免前面的命令还没有完全输出执行后面的命令
    # docker rm -f 强制删除
  • 删除所有已经退出的容器

    1
    2
    docker ps -a -q --filter status=exited | xargs --no-run-if-empty docker rm
    # --filter 过滤容器
  • 更高级用例

    1
    2
    3
    4
    comm -3 \
    < (docker ps -a -q --filter=status=exited | sort) \
    < (docker ps -a -q --filter=exited=0 | sort) | \
    xargs --no-run-if-empty docker inspect > error_containers

dockerui 管理 docker 守护进程

为 Docker 镜像生成一个依赖图

对容器执行命令

方式: docker exec 三种基本模式:

模式 描述
基本 在命令行上对容器同步的执行命令
守护进程 在容器的后台运行命令
交互 运行命令并允许用户与其交互

配置管理

配置管理和 Dockerfile

Dockerfile 被认为是构建 Docker 镜像的标准方式。

  • 使用 ENTRYPOINT 创建可靠的定制工具

  • 镜像扁平化

    1
    2
    # 获取镜像历史分层
    docker history container_id
  • 用 alien 管理外来软件包
    alien 是一款命令行工具,转为转换不同格式的软件包文件设计

  • 把镜像逆向工程得到 Dockerfile 使用 docker history 命令并通过查看分层的方式确定更改过的地方

  • 从源到镜像的构建
    使用 Red Hat 的 Source to Image (S2I或者STI)框架来构建 Docker 镜像

S2I 的优点

  • 灵活性
    可以很容易嵌到任何现有的软件交付流程中,而且几乎可以使用任意 Docker 镜像作为基础镜像层
  • 速度
    可以比 Dockerfile 构建更快,因为任意数量的复杂操作都可以添加到构建过程中而无需在每个步骤创建出一个新的分层。S2I 还是用户能够在构建之间复用 组件以节省时间
  • 业务解耦
    由于源代码和 Docker 镜像均是清晰且明确分离的,开发人员可以专注于代码而基础架构团队则专注于 Docker 镜像及其交付。因为基础的底层镜像和代码
    是分离的,一些升级和补丁也更容易交付。
  • 安全性
    流程可以将构建中执行的操作限制为特定用户,而 Dockerfile 则允许以 root 身份运行人以命令
  • 生态系统 框架的结构允许用户建立一个镜像和代码分离模式的共享生态系统

小即是美

保持构建镜像更小的 Dockerfile 技巧

  • 减少 Docker 构建时镜像分层的开销
    • 使用一个更小的基础镜像
    • 事后处理: 通过从镜像里删除一些软件包和信息来进一步缩减镜像的大小但是 RUN 命令会在最终镜像里创建一个新的写时复制分层
    • 将一系列命令设置为一行
    • 编写一个脚本来完成安装

疑问

  • 什么是写时复制: 写时复制是一项在处理文件时最小化资源占用的技术。

让镜像变得更小的技巧

删除不必要的软件包和文档文件 小也不一定是更好的

流程:

  • 运行该镜像

  • 进入容器内部

  • 删除不必要的文件

  • 提交容器作为一个新景祥

  • 扁平化该镜像

  • 通过BusyBox 和 Alpine 来精简 Docker 镜像

  • Go 模型的最小容器

  • 使用 inotifywait 给容器瘦身

    • 使用 inotify-tools 来确定应用需要哪些文件,然后删除所有其他文件。
  • 降低由于 Docker 镜像导致的磁盘空间占用和网络贷款

    • 为组织内部创建一个统一的、较大的、单体的基础镜像

持续集成:加快开发流水线

持续集成: 指用于加快开发流水线的一个软件生命周期策略

Docker Hub 自动构建

更有效的构建

CI 意味着软件和测试更频繁的重新构建。

  • 使用 eatmydata 为 I/O 密集型构建体术

    • 在镜像上安装 eatmydata – eatmydata 是一个使用系统调用来写入数据,并通过绕开持久化变更所需工作从而大大提升速度的程序。
      但这个会造成部分安全性的缺失,不建议作为常规使用。
  • 设置一个软件包缓存用于加快构建速度

    • 通过为包管理器安装一个 Squid 代理,减少网络 I/O 来加快构建速度
  • 在 Docker 内部运行 Selenium 测试

容器化 CI 过程

  • 在一个 Docker 容器里运行 Jenkins 主服务器
  • 使用 Jenkins 的 Swarm 插件扩展 CI

持续交付:与 Docker 原则完美契合

推动 Docker 镜像的部署

  • 手动同步注册中心镜像
  • 通过受限连接交付镜像
    • 导出镜像,使用 bup 进行拆分,传输 bup 块,并在另一端导入重新组合的镜像
  • 以 TAR 文件方式共享 Docker 对象
    • 使用 docker export 或者 docker save 创建 TAR 文件,然后经由 SSH 使用 docker import 或 docker load 来使用
    命令 创建的文件 目标类型 目标类型来源
    export TAR 文件 容器文件系统 容器
    import Docker 镜像 平面文件系统 TAR 包
    save TAR 文件 Docker 镜像(带历史记录) 镜像
    load Docker 镜像 Docker 镜像(带历史记录) TAR 包

为不同环境配置镜像

  • 使用 etcd 通知容器
    • etcd 是一个分布式键值存储系统–保存信息片段,可作为实现弹性的多节点集群的一部分。

升级运行在的容器

  • 使用 confd 启用零停机时间切换
    • 升级面向 Web 的应用程序做到零停机时间–在宿主机上使用 nginx 和 confd 来执行两阶段切换。

网络模拟:无痛的现实环境测试

容器通信–超越手工链接

  • Docker Compose 集群
  • 使用 Docker Compose 的 SQLite 服务器
  • 使用 Resolvable 通过 DNS 查找容器

使用 Docker 来模拟真实世界的网络

  • 使用 Comcast 模拟有问题的网络
  • 使用 Blockade 模拟有问题的网络

Docker 和虚拟网络

  • 使用 Weave 建立一个基底网络

容器编排:管理多个 Docker 容器

简单的单台宿主机

  • 使用 systemd 管理宿主机上的容器
  • 使用 systemd 编排宿主机上的容器

多宿主机 Docker

  • 使用 Helios 手动管理多宿主机 Docker
  • 基于 Swarm 的无缝 Docker 集群
  • 使用 Kubernetes 集群
  • 在 Mesos 上构建框架
  • 使用 Marathon 细粒度管理 Mesos

服务发现:我们有什么

  • 使用 Consul 来发现服务
    • 分发消息给一组容器、在一组容器中发现服务及监控一组容器–每台 Docker 宿主机上启动带有 Consul 的容器,从而提供服务目录和配置通信系统