Sevice Computing:玩转 Docker 容器技术

  • 容器概述
  • 第一、二章 安装配置运行第一个容器
  • 搭建实验环境
  • 运行第一个容器
  • docker的常用操作
  • 第三章 镜像
  • hello-world - 最小的镜像
  • base 镜像
  • 构建镜像
  • 第四章 容器
  • 运行容器
  • 工具类容器
  • MySQL与容器化
  • 容器运行小结
  • 第五章 网络
  • none 网络
  • host 网络
  • 第六章存储
  • 容器监控与与日志


容器概述

容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行。开发人员在自己笔记本上创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机、物理服务器或公有云主机上运行。

容器由两部分组成:

  1. 应用程序本身
  2. 依赖:比如应用程序需要的库或其他软件

容器在 Host 操作系统的用户空间中运行,与操作系统的其他进程隔离。这一点显著区别于的虚拟机。

Docker 的核心组件包括:

  1. Docker 客户端 - Client
    最常用的 Docker 客户端是 docker 命令。通过 docker 我们可以方便地在 Host 上构建和运行容器。
  2. Docker 服务器 - Docker daemon
    Docker daemon 是服务器组件,以 Linux 后台服务的方式运行。
  3. Docker 镜像 - Image
    可将 Docker 镜像看着只读模板,通过它可以创建 Docker 容器。
  4. Registry
    Registry 是存放 Docker 镜像的仓库,Registry 分私有和公有两种。
  5. Docker 容器 - Container
    Docker 容器就是 Docker 镜像的运行实例。

第一、二章 安装配置运行第一个容器

搭建实验环境

环境选择

容器需要管理工具、runtime 和操作系统,我们的选择如下:

  1. 管理工具 - Docker Engine
    因为 Docker 最流行使用最广泛。
  2. runtime - runc
    Docker 的默认 runtime
  3. 操作系统 - Ubuntu

安装 Docker

配置 Docker 的 apt 源

  1. 安装包,允许 apt 命令 HTTPS 访问 Docker 源。
  2. 容器技术基于java架构 容器技术教程_Docker


  3. 容器技术基于java架构 容器技术教程_Docker_02

  4. 添加 Docker 官方的 GPG

容器技术基于java架构 容器技术教程_docker_03

  1. 将 Docker 的源添加到 /etc/apt/sources.list

容器技术基于java架构 容器技术教程_docker_04

这里我先开始以为是一行命令所以输在了一起,但是这样会报错,像如下图所示这样一行一行分开输入就可以了。

容器技术基于java架构 容器技术教程_Docker_05


安装 Docker

容器技术基于java架构 容器技术教程_数据_06


容器技术基于java架构 容器技术教程_Docker_07

运行第一个容器

环境就绪,马上运行第一个容器,执行命令:

容器技术基于java架构 容器技术教程_docker_08


由于 Docker Hub 的服务器在国外,下载镜像会比较慢。幸好 DaoCloud 为我们提供了免费的国内镜像服务。

  1. 在 daocloud.io 免费注册一个用户。
  2. 容器技术基于java架构 容器技术教程_数据_09

  3. 登录后,点击顶部菜单“加速器”。
  4. 容器技术基于java架构 容器技术教程_docker_10

  5. copy “加速器”命令并在 host 中执行(你的命令可能跟我的会稍有不同)。
  6. 容器技术基于java架构 容器技术教程_docker_11


  7. 容器技术基于java架构 容器技术教程_数据_12

  8. 重启 Docker deamon,即可体验飞一般的感觉。
  9. 容器技术基于java架构 容器技术教程_容器技术基于java架构_13

docker的常用操作

容器技术基于java架构 容器技术教程_数据_14


容器启动过程如下:

容器技术基于java架构 容器技术教程_容器技术基于java架构_15

  • Docker 客户端执行 docker run 命令。
  • Docker daemon 发现本地没有 httpd 镜像。
  • daemon 从 Docker Hub 下载镜像。
  • 下载完成,镜像 httpd 被保存到本地。
  • Docker daemon 启动容器。

docker images 可以查看到 httpd 已经下载到本地。

docker ps 或者 docker container ls 显示容器正在运行。

容器技术基于java架构 容器技术教程_容器技术基于java架构_16

第三章 镜像

镜像是 Docker 容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。

hello-world - 最小的镜像

hello-world 是 Docker 官方提供的一个镜像,通常用来验证 Docker 是否安装成功。

我们先通过 docker pull 从 Docker Hub 下载它。

容器技术基于java架构 容器技术教程_容器技术基于java架构_17


用 docker images 命令查看镜像的信息。

容器技术基于java架构 容器技术教程_docker_18


通过 docker run 运行。

容器技术基于java架构 容器技术教程_容器技术基于java架构_19

base 镜像

hello-world 虽然是一个完整的镜像,但它并没有什么实际用途。通常来说,我们希望镜像能提供一个基本的操作系统环境,用户可以根据需要安装和配置软件。这样的镜像我们称作 base 镜像。

base 镜像有两层含义:

  1. 不依赖其他镜像,从 scratch 构建。
  2. 其他镜像可以之为基础进行扩展。
    所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。

我们以 CentOS 为例考察 base 镜像包含哪些内容。

下载镜像:

容器技术基于java架构 容器技术教程_容器技术基于java架构_20


查看镜像信息以及后面的运行操作都和上一个最小镜像的操作类似。

构建镜像

某些情况下我们也不得不自己构建镜像,比如:

  1. 找不到现成的镜像,比如自己开发的应用程序。
  2. 需要在镜像中加入特定的功能,比如官方镜像几乎都不提供 ssh。

所以本节我们将介绍构建镜像的方法。同时分析构建的过程也能够加深我们对前面镜像分层结构的理解。

Docker 提供了两种构建镜像的方法:

  1. docker commit 命令
  2. Dockerfile 构建文件

举个例子:在 ubuntu base 镜像中安装 vi 并保存为新镜像。

  1. 第一步, 运行容器

    -it 参数的作用是以交互模式进入容器,并打开终端。cfcf30326357 是容器的内部 ID。
  2. 安装 vi
    确认 vi 没有安装后安装vi。
  3. 保存为新镜像
    在新窗口中查看当前运行的容器。

    nifty_ellis 是 Docker 为我们的容器随机分配的名字。
    执行 docker commit 命令将容器保存为镜像。
    (这里因为中间实验中断了一下电脑重启过,所以容器随机分配的名字变为infallible_villani)

    新镜像命名为 ubuntu-with-vi。
    查看新镜像的属性。

    从 size 上看到镜像因为安装了软件而变大了。
    从新镜像启动容器,验证 vi 已经可以使用。

下面是镜像的常用操作子命令:

  • images 显示镜像列表
  • history 显示镜像构建历史
  • commit 从容器创建新镜像
  • build 从 Dockerfile 构建镜像
  • tag 给镜像打 tag
  • pull 从 registry 下载镜像
  • push 将 镜像 上传到 registry
  • rmi 删除 Docker host 中的镜像
  • search 搜索 Docker Hub 中的镜像

第四章 容器

运行容器

docker run 是启动容器的方法。在讨论 Dockerfile 时我们已经学习到,可用三种方式指定容器启动时执行的命令:

  • CMD 指令。
  • ENDPOINT 指令。
  • 在 docker run 命令行中指定。

    容器启动时执行 pwd,返回的 / 是容器中的当前目录。 执行 docker ps 或 docker container ls 可以查看 Docker host 中当前运行的容器,但是却显示没有容器,用 docker ps -a 或 docker container ls -a 看看。-a 会显示所有状态的容器,可以看到,之前的容器已经退出了,状态为Exited。

因为容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器也就不会退出。

理解了这个原理,我们就可以通过执行一个长期运行的命令来保持容器的运行状态。例如执行下面的命令:
docker run ubuntu /bin/bash -c 'while true ; do sleep 1; done"

while 语句让 bash 不会退出。我们可以打开另一个终端查看容器的状态:

容器技术基于java架构 容器技术教程_容器技术基于java架构_21


可见容器仍处于运行状态。不过这种方法有个缺点:它占用了一个终端。我们可以加上参数 -d 以后台方式启动容器。

容器技术基于java架构 容器技术教程_docker_22


容器启动后回到了 docker host 的终端。这里看到 docker 返回了一串字符,这是容器的 ID。通过 docker ps 查看容器,对于容器的后续操作,我们需要通过 “长ID”、“短ID” 或者 “名称” 来指定要操作的容器。比如下面停止一个容器:

容器技术基于java架构 容器技术教程_docker_23

工具类容器

工具类容器通常给能我们提供一个临时的工作环境,通常以 run -it 方式运行,比如:

容器技术基于java架构 容器技术教程_docker_24


运行 busybox,run -it 的作用是在容器启动后就直接进入。我们这里通过 wget 验证了在容器中访问 internet 的能力。执行 exit 退出终端,同时容器停止。

工具类容器多使用基础镜像,例如 busybox、debian、ubuntu 等。

MySQL与容器化

拉取 MySQL 镜像

容器技术基于java架构 容器技术教程_容器技术基于java架构_25


容器技术基于java架构 容器技术教程_docker_26


使用MySQL容器因为前面占用了这个地址,所以这里会报下面的错。

容器技术基于java架构 容器技术教程_容器技术基于java架构_27


容器技术基于java架构 容器技术教程_容器技术基于java架构_28


解决办法是先停掉这个进程。然后再重新执行这个命令就可以了。

容器技术基于java架构 容器技术教程_docker_29

容器运行小结

容器运行相关的知识点:

  1. 当 CMD 或 Entrypoint 或 docker run 命令行指定的命令运行结束时,容器停止。
  2. 通过 -d 参数在后台启动容器。
  3. 通过 exec -it 可进入容器并执行命令。

指定容器的三种方法:

  1. 短ID。
  2. 长ID。
  3. 容器名称。 可通过 --name 为容器命名。重命名容器可执行docker rename。

容器按用途可分为两类:

  1. 服务类的容器。
  2. 工具类的容器。

更多关于容器操作的概念和其中的关系见这篇博客和其系列博客

第五章 网络

Docker 安装时会自动在 host 上创建三个网络,我们可用 docker network ls 命令查看:

容器技术基于java架构 容器技术教程_数据_30

none 网络

none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过 --network=none 指定使用 none 网络。

容器技术基于java架构 容器技术教程_docker_31


封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。当然大部分容器是需要网络的。

host 网络

连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过 --network=host 指定使用 host 网络。

容器技术基于java架构 容器技术教程_docker_32


直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。

备制支持 ifconfig 和 ping 命令的 ubuntu 容器

容器技术基于java架构 容器技术教程_容器技术基于java架构_33


安装所需的包

容器技术基于java架构 容器技术教程_数据_34


容器技术基于java架构 容器技术教程_Docker_35

第六章存储

Docker 为容器提供了两种存放数据的资源:

  1. 由 storage driver 管理的镜像层和容器层。
  2. Data Volume。

分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker storage driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。

Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。对于 Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,因为:

  1. 没有哪个 driver 能够适应所有的场景。
  2. driver 本身在快速发展和迭代。

不过 Docker 官方给出了一个简单的答案:
优先使用 Linux 发行版默认的 storage driver。

Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。

比如 busybox,它是一个工具箱,我们启动 busybox 是为了执行诸如 wget,ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。

但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。

这就要用到 Docker 的另一种存储机制:Data Volume。

容器监控与与日志

检查docker的状态

容器技术基于java架构 容器技术教程_Docker_36