标签 容器 下的文章

容器是未来在共有云和私有云进行应用开发的主要趋势,但是容器到底是什么,为什么它们成为了一种广受欢迎的部署机制,而且你需要怎样来修改你的应用来为容器化的环境优化它?

什么是容器?

容器技术的历史始于 2000 年的 SELinux 和 2005 年的 Solaris zones。今天,容器是由包括 SELinux、Linux 命名空间和控制组(cgroup)等几项内核特性构成,提供了用户进程、网络空间和文件系统空间的隔离。

为什么它们如此流行?

最近容器技术大规模的应用在很大程度上是由于旨在使容器更加易于使用的标准的发展,例如 Docker 镜像格式和分布模型,这个标准使用 不可变镜像 immutable image ,这正是容器运行时环境的起点,不可变镜像可以保证开发团队发布的镜像就是经过测试的,和部署到生产环境中的镜像是同样的镜像。

容器所提供的轻量级隔离为一个应用组件提供了一个更好的抽象。在容器中运行的组件将不会干扰其它可能直接运行在虚拟机上的应用。它们可以避免对系统资源的争夺,而且除非它们共享一个持久卷,否则不会阻止对同一个文件的写请求。容器使得日志和指标采集的实践得以标准化,而且它们可以在物理机和虚拟机上支持更大的用户密度,所有的这些优点将导致更低的部署成本。

我们应该如何构建一个基于容器的应用呢?

将应用改为运行在容器中并不是什么很高的要求。主要的 Linux 发行版都有提供了基础镜像,任何可以在虚拟机上运行的程序都可以在上面运行。但是容器化应用的趋势是遵循如下最佳实践:

1. 实例是一次性的

你的应用的任何实例都不需要小心地保持运行。如果你的一个运行了许多容器的系统崩溃了,你还能够转移到其它可用的系统去创建新的容器。

2. 重试而不是崩溃

当你的应用的一个服务依赖于另一个服务的时候,在另一个服务不可用的时候它应该不会崩溃。例如,你的 API 服务正在启动而且监测到数据库不能连接。你应该设计它使得其不断重试连接,而不是运行失败和拒绝启动。当数据库连接断开的时候 API 可以返回 503 状态码,告诉客户端服务现在不可用。应用应该已经遵守了这个实践,但是如果你正在一个一次性实例的容器环境中工作,那么对这个实践的需要会更加明显。

3. 持久性数据是特殊的

容器是基于共享镜像启动,它使用了写时复制(COW)文件系统。如果容器的进程选择写入文件,那么这些写的内容只有在直到容器存在时才存在。当容器被删除的时候,写时复制文件系统中的那一层会被删除。提供给容器一个挂载的文件系统目录,使之在容器存活之外也能持久保存,这需要另外的配置,而且会额外消耗物理存储。明确的抽象定义了什么存储是持久的,催生出了实例是一次性的观点。拥有一个抽象层也使得容器编制引擎可以处理挂载和卸载持久卷的复杂请求,以便这些持久卷可以用于容器。

4. 使用 stdout 而不是日志文件

现在你或许会思考,如果持久的数据是特殊的,那么我用日志文件来做什么事情?容器运行时环境和编制引擎项目所采用的方法是进程应该写入 stdout/stderr,而且具有归档和维护容器日志的基础设施。

5. 敏感信息(以及其它配置信息)也是特殊的

你绝不应该将敏感信息例如密码、密钥和证书硬编码到你的镜像中。通常在你的应用与开发服务、测试服务,或者生产服务相交互时,这些敏感信息通常都是不同的。大多数开发者并没有访问生产环境的敏感信息的权限,所以如果敏感信息被打包到镜像中,那么必须创建一个新的镜像层来覆盖这个开发服务的敏感信息。基于这一点来看,你再也不能使用与你们开发团队所创建的和质量测试所测试的相同的镜像了,而且也失去了不可修改的镜像的好处。相反的,这些值应该被存储在环境变量中文件中,它们会在容器启动时导入。

6. 不要假设服务的协同定位

在一个编排好的容器环境中,你会希望让编排器将你的容器发送到任何最适合的节点。最适合意味着很多事情:它应该基于那个节点现在拥有最多的空间、容器所需的服务质量、容器是否需要持久卷,等等。这可能意味这你的前端、API 和数据库容器最终都会放在不同的节点。尽管给每个节点强制分配一个 API 容器是可以做到的(参考 Kubernetes 的 DaemonSets),但这种方式应该留给执行监控节点自身这类任务的容器。

7. 冗余/高可用计划

即使你没有那么多负载需要高可用性的配置,你也不应该以单路方式编写服务,否则会阻止它运行多份拷贝。这将会允许你运用滚动式部署,使得将负载从一个节点移动到另外一个节点非常容易,或者将服务从一个版本更新到下一个版本而不需要下线。

8. 实现就绪检查和灵活性检查

应用在响应请求之前会有一定的启动时间是一件很正常的事情,例如,一个 API 服务器需要填充内存数据缓存。容器编排引擎需要一种方法来检测你的容器是否准备好服务用户请求。为一个新的容器提供就绪检查可以允许我们进行滚动式部署,使得旧容器可以继续运行直到不再需要它,这可以防止服务宕机。类似的,一个存活检查也是一种容器编排引擎持续检查容器是否在健康可用状态的方法。决定容器健康或者说“存活”应该由容器应用的创建者说了算。一个不再存活的容器将会被结束,而且一个新的容器会被创建来替代它。

想查找更多资料?

我将会出席十月份的格雷丝霍普计算机女性峰会(Grace Hopper Celebration of Women in Computing),你可以在这里来看一下关于我的访谈:应用的容器化:是什么,为什么,和如何实现。今年不去 GHC 吗?那你可以在 OpenShiftKubernetes 的项目站点来了解关于容器、编排和应用的相关内容。


via: https://opensource.com/life/16/9/8-best-practices-building-containerized-applications

作者:Jessica Forrester 译者:LinuxBars 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我在红帽工作的期间,每天在 Fedora Atomic host 上使用 Docker 容器。 来自 原子项目 Project Atomic 原子主机 Atomic Host 是一个轻量级容器操作系统,可以以 Docker 格式运行 Linux 容器。它专门为提高效率而定制,使其成为用于云环境的 Docker 运行时系统的理想选择。

幸运的是,我发现一个很好的方式来管理在主机上运行的容器:Cockpit。 它是一个具有漂亮的 Web 界面的 GNU/Linux 服务器远程管理工具。它可以帮我管理在主机上运行的服务器和容器。你可以在从之前发布在这里的这篇概述中了解 Cockpit 的更多信息。不过,我也希望在主机上可以自动运行容器,我可以使用 Ansible 来完成这个工作。

请注意,我们不能在原子主机上使用 dnf 命令。原子主机并没有设计为通用操作系统,而是更适合容器和其他用途。但在原子主机上设置应用程序和服务仍然非常容易。这篇文章向您展示了如何自动化和简化这个过程。

设置组件

开始之前,请确保你的系统上安装了 Ansible。

sudo dnf -y install ansible

首先,我们需要在原子主机上运行 cockpit 容器。在你的机器上从 https://github.com/trishnaguha/fedora-cloud-ansible 下载它的源代码。

$ git clone https://github.com/trishnaguha/fedora-cloud-ansible.git

现在切换到 cockpit 的目录,并如下编辑 inventory 文件:

$ cd fedora-cloud-ansible
$ cd cockpit
$ vim inventory

完成如下修改:

  1. 使用你的原子主机的 IP 替换掉 IP_ADDRESS_OF_HOST
  2. 用您的 SSH 私钥文件的路径替换 ansible_ssh_private_key_file ='PRIVATE_KEY_FILE' 行中的PRIVATE_KEY_FILE

然后保存并退出 inventory 文件编辑。

接下来,编辑 ansible 配置文件:

$ vim ansible.cfg

替换 remote_user=UserUser 为你的原子主机上的远程用户名。然后保存并退出文件编辑。

结合起来

现在是运行 Ansible 的 PlayBook 的时候了。此命令开始运行原子主机上的 Cockpit 容器:

$ ansible-playbook cockpit.yml

Cockpit 现在运行在原子主机上了。使用浏览器去访问你的实例的公网 IP 的 9090 端口——这是 Cockpit 的默认端口。举个例子,如果该实例的 IP 地址是 192.168.1.4,就去访问 192.168.1.4:9090,你将会看到如下的 Web 界面:

管理你的容器

使用原子主机的登录信息或以 root 用户身份登录。然后访问 Cockpit 管理器上的 Containers 部分来查看原子主机上运行的容器。在下面的示例中,您会看到我还设置了其他容器,如 httpdredis

注意,该界面允许您直接在 Cockpit 管理器中使用 Run 和 Stop 按钮启动和停止容器。您还可以使用 Cockpit 管理器管理您的原子主机。转到 Tools -> Terminals,在这里里你可以使用原子主机的终端:

如果您打算在原子主机上部署容器化的应用程序,则可以简单地为其编写一个 PlayBook。然后,您可以使用 ansible-playbook 命令进行部署,并使用 Cockpit 来管理容器。

欢迎你对这个仓库进行分支或添加容器的 PlayBook。


via: https://fedoramagazine.org/deploy-containers-atomic-host-ansible-cockpit/

作者:trishnag 译者:Bestony 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

有如此之多的各种新的云计算技术、工具和技术需要我们跟进,到底从哪里开始学习是一个艰难的决定。这一系列下一代云计算技术的文章旨在让你快速了解新兴和快速变化领域的重大项目和产品,比如软件定义网络(SDN)、容器,以及其交叉领域:容器网络。

对于企业容器部署,容器和网络之间的关系仍然是一个挑战。容器需要网络功能来连接分布式应用程序。根据一篇最新的企业网络星球的文章,一部分的挑战是“以隔离的方式部署容器,在提供隔离自己容器内数据的所需功能的同时,保持有效的连接性”。

流行的容器平台 Docker,使用了软件定义虚拟网络来连接容器与本地网络。此外,它使用 Linux 的桥接功能和虚拟可扩展局域网(VXLAN)技术,可以在同一 Swarm 或容器集群内互相沟通。Docker 的插件架构也支持其他网络管理工具来管理容器网络,比如下面的提到的工具。

容器网络上的创新使得容器可以跨主机连接到其他容器上。这使开发人员可以在开发环境中,在一个主机上部署一个容器来运行一个应用,然后可以过渡到测试环境中,进而到生产环境中,使应用可以持续集成,敏捷开发,快速部署。

容器网络工具有助于实现容器网络的可扩展性,主要是通过:

  1. 使复杂的,多主机系统能够跨多个容器主机进行分发。
  2. 允许构建跨越多个公有云和私有云平台上的大量主机的容器系统。

John Willis speaking 在 Open Networking Summit 2016.

要获取更多信息,查看 Docker 网络教程,是由 Brent Salisbury 和 John Willis 在最近的 Open Networking Summit (ONS)讲演的。更多关于 ONS 的演讲内容可以在这里找到。

你应该知道的容器网络工具和项目包括下述:

  • Calico -- Calico 项目(源自 Metaswitch)利用边界网关协议(BGP)和集成的云编排系统来保证虚拟机和容器之间的 IP 通信安全。
  • Flannel -- Flannel (之前叫 rudder) 源自 CoreOS,它提供了一个覆盖网络,可以作为一个现有的 SDN 解决方案的替代品。
  • Weaveworks -- Weaveworks 项目管理容器的工具包括 Weave Net、Weave Scope、Weave Flux。Weave Net 是一种用于构建和部署 Docker 容器的网络工具。
  • Canal -- 就在本周,CoreOS 和 Tigera 宣布了新的开源项目 Canal 的信息。据其声明,Canal 项目旨在结合部分 Calico 和 Flannel,“构造网络安全策略到网络架构和云管理平台之中”。

你可以通过 Linux 基金会的免费“云基础设施技术”课程来了解更多关于容器管理、软件定义网络和其他下一代云技术,这是一个在 edX 上提供的大规模公开在线课程。课程注册目前已经开放,课程内容于 6 月开放。


via: https://www.linux.com/news/4-container-networking-tools-know

作者:AMBER ANKERHOLZ 译者:Bestony 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你正在运行 Swarm 模式的集群,或者只运行单台 Docker,你都会有下面的疑问:

我如何才能监控到它们都在干些什么?

这个问题的答案是“很不容易”。

你需要监控下面的参数:

  1. 容器的数量和状态。
  2. 一台容器是否已经移到另一个节点了,如果是,那是在什么时候,移动到哪个节点?
  3. 给定节点上运行着的容器数量。
  4. 一段时间内的通信峰值。
  5. 孤儿卷和网络(LCTT 译注:孤儿卷就是当你删除容器时忘记删除它的卷,这个卷就不会再被使用,但会一直占用资源)。
  6. 可用磁盘空间、可用 inode 数。
  7. 容器数量与连接在 docker0docker_gwbridge 上的虚拟网卡数量不一致(LCTT 译注:当 docker 启动时,它会在宿主机器上创建一个名为 docker0 的虚拟网络接口)。
  8. 开启和关闭 Swarm 节点。
  9. 收集并集中处理日志。

本文的目标是介绍 Elasticsearch + Kibana + cAdvisor 的用法,使用它们来收集 Docker 容器的参数,分析数据并产生可视化报表。

阅读本文后你可以发现有一个监控仪表盘能够部分解决上述列出的问题。但如果只是使用 cAdvisor,有些参数就无法显示出来,比如 Swarm 模式的节点。

如果你有一些 cAdvisor 或其他工具无法解决的特殊需求,我建议你开发自己的数据收集器和数据处理器(比如 Beats),请注意我不会演示如何使用 Elasticsearch 来集中收集 Docker 容器的日志。

“你要如何才能监控到 Swarm 模式集群里面发生了什么事情?要做到这点很不容易。” —— @fntlnz

我们为什么要监控容器?

想象一下这个经典场景:你在管理一台或多台虚拟机,你把 tmux 工具用得很溜,用各种 session 事先设定好了所有基础的东西,包括监控。然后生产环境出问题了,你使用 tophtopiotopjnettop 各种 top 来排查,然后你准备好修复故障。

现在重新想象一下你有 3 个节点,包含 50 台容器,你需要在一个地方查看整洁的历史数据,这样你知道问题出在哪个地方,而不是把你的生命浪费在那些字符界面来赌你可以找到问题点。

什么是 Elastic Stack ?

Elastic Stack 就一个工具集,包括以下工具:

  • Elasticsearch
  • Kibana
  • Logstash
  • Beats

我们会使用其中一部分工具,比如使用 Elasticsearch 来分析基于 JSON 格式的文本,以及使用 Kibana 来可视化数据并产生报表。

另一个重要的工具是 Beats,但在本文中我们还是把精力放在容器上,官方的 Beats 工具不支持 Docker,所以我们选择原生兼容 Elasticsearch 的 cAdvisor。

cAdvisor 工具负责收集、整合正在运行的容器数据,并导出报表。在本文中,这些报表被到入到 Elasticsearch 中。

cAdvisor 有两个比较酷的特性:

  • 它不只局限于 Docker 容器。
  • 它有自己的 Web 服务器,可以简单地显示当前节点的可视化报表。

设置测试集群,或搭建自己的基础架构

和我以前的文章一样,我习惯提供一个简单的脚本,让读者不用花很多时间就能部署好和我一样的测试环境。你可以使用以下(非生产环境使用的)脚本来搭建一个 Swarm 模式的集群,其中一个容器运行着 Elasticsearch。

如果你有充足的时间和经验,你可以 搭建自己的基础架构 Bring Your Own Infrastructure,BYOI

如果要继续阅读本文,你需要:

  • 运行 Docker 进程的一个或多个节点(docker 版本号大于等于 1.12)。
  • 至少有一个独立运行的 Elasticsearch 节点(版本号 2.4.X)。

重申一下,此 Elasticsearch 集群环境不能放在生产环境中使用。生产环境也不推荐使用单节点集群,所以如果你计划安装一个生产环境,请参考 Elastic 指南

对喜欢尝鲜的用户的友情提示

我就是一个喜欢尝鲜的人(当然我也已经在生产环境中使用了最新的 alpha 版本),但是在本文中,我不会使用最新的 Elasticsearch 5.0.0 alpha 版本,我还不是很清楚这个版本的功能,所以我不想成为那个引导你们出错的关键。

所以本文中涉及的 Elasticsearch 版本为最新稳定版 2.4.0。

测试集群部署脚本

前面已经说过,我提供这个脚本给你们,让你们不必费神去部署 Swarm 集群和 Elasticsearch,当然你也可以跳过这一步,用你自己的 Swarm 模式引擎和你自己的 Elasticserch 节点。

执行这段脚本之前,你需要:

创建集群的脚本

现在万事俱备,你可以把下面的代码拷到 create-cluster.sh 文件中:

#!/usr/bin/env bash
#
# Create a Swarm Mode cluster with a single master and a configurable number of workers

workers=${WORKERS:-"worker1 worker2"}

#######################################
# Creates a machine on Digital Ocean
# Globals:
#   DO_ACCESS_TOKEN The token needed to access DigitalOcean's API
# Arguments:
#   $1 the actual name to give to the machine
#######################################
create_machine() {
  docker-machine create \
    -d digitalocean \
    --digitalocean-access-token=$DO_ACCESS_TOKEN \
    --digitalocean-size 2gb \
    $1
}

#######################################
# Executes a command on the specified machine
# Arguments:
#   $1     The machine on which to run the command
#   $2..$n The command to execute on that machine
#######################################
machine_do() {
  docker-machine ssh $@
}

main() {

  if [ -z "$DO_ACCESS_TOKEN" ]; then
    echo "Please export a DigitalOcean Access token: https://cloud.digitalocean.com/settings/api/tokens/new"
    echo "export DO_ACCESS_TOKEN=<yourtokenhere>"
    exit 1
  fi

  if [ -z "$WORKERS" ]; then
    echo "You haven't provided your workers by setting the \$WORKERS environment variable, using the default ones: $workers"
  fi

  # Create the first and only master
  echo "Creating the master"

  create_machine master1

  master_ip=$(docker-machine ip master1)

  # Initialize the swarm mode on it
  echo "Initializing the swarm mode"
  machine_do master1 docker swarm init --advertise-addr $master_ip

  # Obtain the token to allow workers to join
  worker_tkn=$(machine_do master1 docker swarm join-token -q worker)
  echo "Worker token: ${worker_tkn}"

  # Create and join the workers
  for worker in $workers; do
    echo "Creating worker ${worker}"
    create_machine $worker
    machine_do $worker docker swarm join --token $worker_tkn $master_ip:2377
  done
}

main $@

赋予它可执行权限:

chmod +x create-cluster.sh

创建集群

如文件名所示,我们可以用它来创建集群。默认情况下这个脚本会创建一个 master 和两个 worker,如果你想修改 worker 个数,可以设置环境变量 WORKERS。

现在就来创建集群吧。

./create-cluster.sh

你可以出去喝杯咖啡,因为这需要花点时间。

最后集群部署好了。

现在为了验证 Swarm 模式集群已经正常运行,我们可以通过 ssh 登录进 master:

docker-machine ssh master1

然后列出集群的节点:

docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
26fi3wiqr8lsidkjy69k031w2 *  master1   Ready   Active        Leader
dyluxpq8sztj7kmwlzs51u4id    worker2   Ready   Active
epglndegvixag0jztarn2lte8    worker1   Ready   Active

安装 Elasticsearch 和 Kibana

注意,从现在开始所有的命令都运行在主节点 master1 上。

在生产环境中,你可能会把 Elasticsearch 和 Kibana 安装在一个单独的、大小合适的实例集合中。但是在我们的实验中,我们还是把它们和 Swarm 模式集群安装在一起。

为了将 Elasticsearch 和 cAdvisor 连通,我们需要创建一个自定义的网络,因为我们使用了集群,并且容器可能会分布在不同的节点上,我们需要使用 overlay 网络(LCTT 译注:overlay 网络是指在不改变现有网络基础设施的前提下,通过某种约定通信协议,把二层报文封装在 IP 报文之上的新的数据格式,是目前最主流的容器跨节点数据传输和路由方案)。

也许你会问,“为什么还要网络?我们不是可以用 link 吗?” 请考虑一下,自从引入用户定义网络后,link 机制就已经过时了。

以下内容摘自 Docker 文档

在 Docker network 特性出来以前,你可以使用 Docker link 特性实现容器互相发现、安全通信。而在 network 特性出来以后,你还可以使用 link,但是当容器处于默认桥接网络或用户自定义网络时,它们的表现是不一样的。

现在创建 overlay 网络,名称为 monitoring:

docker network create monitoring -d overlay

Elasticsearch 容器

docker service create --network=monitoring \
  --mount type=volume,target=/usr/share/elasticsearch/data \
  --constraint node.hostname==worker1 \
  --name elasticsearch elasticsearch:2.4.0

注意 Elasticsearch 容器被限定在 worker1 节点,这是因为它运行时需要依赖 worker1 节点上挂载的卷。

Kibana 容器

docker service create --network=monitoring --name kibana -e ELASTICSEARCH_URL="http://elasticsearch:9200" -p 5601:5601 kibana:4.6.0

如你所见,我们启动这两个容器时,都让它们加入 monitoring 网络,这样一来它们可以通过名称(如 Kibana)被相同网络的其他服务访问。

现在,通过 routing mesh 机制,我们可以使用浏览器访问服务器的 IP 地址来查看 Kibana 报表界面。

获取 master1 实例的公共 IP 地址:

docker-machine ip master1

打开浏览器输入地址:http://[master1 的 ip 地址]:5601/status

所有项目都应该是绿色:

让我们接下来开始收集数据!

收集容器的运行数据

收集数据之前,我们需要创建一个服务,以全局模式运行 cAdvisor,为每个有效节点设置一个定时任务。

这个服务与 Elasticsearch 处于相同的网络,以便于 cAdvisor 可以推送数据给 Elasticsearch。

docker service create --network=monitoring --mode global --name cadvisor \
  --mount type=bind,source=/,target=/rootfs,readonly=true \
  --mount type=bind,source=/var/run,target=/var/run,readonly=false \
  --mount type=bind,source=/sys,target=/sys,readonly=true \
  --mount type=bind,source=/var/lib/docker/,target=/var/lib/docker,readonly=true \
  google/cadvisor:latest \
  -storage_driver=elasticsearch \
  -storage_driver_es_host="http://elasticsearch:9200"
注意:如果你想配置 cAdvisor 选项,参考这里

现在 cAdvisor 在发送数据给 Elasticsearch,我们通过定义一个索引模型来检索 Kibana 中的数据。有两种方式可以做到这一点:通过 Kibana 或者通过 API。在这里我们使用 API 方式实现。

我们需要在一个连接到 monitoring 网络的正在运行的容器中运行索引创建命令,你可以在 cAdvisor 容器中拿到 shell,不幸的是 Swarm 模式在开启服务时会在容器名称后面附加一个唯一的 ID 号,所以你需要手动指定 cAdvisor 容器的名称。

拿到 shell:

docker exec -ti <cadvisor-container-name> sh

创建索引:

curl -XPUT http://elasticsearch:9200/.kibana/index-pattern/cadvisor -d '{"title" : "cadvisor*",  "timeFieldName": "container_stats.timestamp"}'

如果你够懒,可以只执行下面这一句:

docker exec $(docker ps | grep cadvisor | awk '{print $1}' | head -1) curl -XPUT http://elasticsearch:9200/.kibana/index-pattern/cadvisor -d '{"title" : "cadvisor*",  "timeFieldName": "container_stats.timestamp"}'

把数据汇总成报表

你现在可以使用 Kibana 来创建一份美观的报表了。但是不要着急,我为你们建了一份报表和一些图形界面来方便你们入门。

访问 Kibana 界面 => Setting => Objects => Import,然后选择包含以下内容的 JSON 文件,就可以导入我的配置信息了:

[
  {
    "_id": "cAdvisor",
    "_type": "dashboard",
    "_source": {
      "title": "cAdvisor",
      "hits": 0,
      "description": "",
      "panelsJSON": "[{\"id\":\"Filesystem-usage\",\"type\":\"visualization\",\"panelIndex\":1,\"size_x\":6,\"size_y\":3,\"col\":1,\"row\":1},{\"id\":\"Memory-[Node-equal->Container]\",\"type\":\"visualization\",\"panelIndex\":2,\"size_x\":6,\"size_y\":4,\"col\":7,\"row\":4},{\"id\":\"memory-usage-by-machine\",\"type\":\"visualization\",\"panelIndex\":3,\"size_x\":6,\"size_y\":6,\"col\":1,\"row\":4},{\"id\":\"CPU-Total-Usage\",\"type\":\"visualization\",\"panelIndex\":4,\"size_x\":6,\"size_y\":5,\"col\":7,\"row\":8},{\"id\":\"Network-RX-TX\",\"type\":\"visualization\",\"panelIndex\":5,\"size_x\":6,\"size_y\":3,\"col\":7,\"row\":1}]",
      "optionsJSON": "{\"darkTheme\":false}",
      "uiStateJSON": "{}",
      "version": 1,
      "timeRestore": false,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"filter\":[{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}}}]}"
      }
    }
  },
  {
    "_id": "Network",
    "_type": "search",
    "_source": {
      "title": "Network",
      "description": "",
      "hits": 0,
      "columns": [
        "machine_name",
        "container_Name",
        "container_stats.network.name",
        "container_stats.network.interfaces",
        "container_stats.network.rx_bytes",
        "container_stats.network.rx_packets",
        "container_stats.network.rx_dropped",
        "container_stats.network.rx_errors",
        "container_stats.network.tx_packets",
        "container_stats.network.tx_bytes",
        "container_stats.network.tx_dropped",
        "container_stats.network.tx_errors"
      ],
      "sort": [
        "container_stats.timestamp",
        "desc"
      ],
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"index\":\"cadvisor*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"fragment_size\":2147483647},\"filter\":[]}"
      }
    }
  },
  {
    "_id": "Filesystem-usage",
    "_type": "visualization",
    "_source": {
      "title": "Filesystem usage",
      "visState": "{\"title\":\"Filesystem usage\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.filesystem.usage\",\"customLabel\":\"USED\"}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"machine_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":false}},{\"id\":\"3\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.filesystem.capacity\",\"customLabel\":\"AVAIL\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"container_stats.filesystem.device\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
      "uiStateJSON": "{\"vis\":{\"colors\":{\"Average container_stats.filesystem.available\":\"#E24D42\",\"Average container_stats.filesystem.base_usage\":\"#890F02\",\"Average container_stats.filesystem.capacity\":\"#3F6833\",\"Average container_stats.filesystem.usage\":\"#E24D42\",\"USED\":\"#BF1B00\",\"AVAIL\":\"#508642\"}}}",
      "description": "",
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"index\":\"cadvisor*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"filter\":[]}"
      }
    }
  },
  {
    "_id": "CPU-Total-Usage",
    "_type": "visualization",
    "_source": {
      "title": "CPU Total Usage",
      "visState": "{\"title\":\"CPU Total Usage\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.cpu.usage.total\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"container_stats.timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"container_Name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"machine_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}}],\"listeners\":{}}",
      "uiStateJSON": "{}",
      "description": "",
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"index\":\"cadvisor*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
      }
    }
  },
  {
    "_id": "memory-usage-by-machine",
    "_type": "visualization",
    "_source": {
      "title": "Memory [Node]",
      "visState": "{\"title\":\"Memory [Node]\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.memory.usage\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"container_stats.timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"machine_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
      "uiStateJSON": "{}",
      "description": "",
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"index\":\"cadvisor*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
      }
    }
  },
  {
    "_id": "Network-RX-TX",
    "_type": "visualization",
    "_source": {
      "title": "Network RX TX",
      "visState": "{\"title\":\"Network RX TX\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":true,\"addTooltip\":true,\"defaultYExtents\":false,\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.network.rx_bytes\",\"customLabel\":\"RX\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"container_stats.timestamp\",\"interval\":\"s\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.network.tx_bytes\",\"customLabel\":\"TX\"}}],\"listeners\":{}}",
      "uiStateJSON": "{\"vis\":{\"colors\":{\"RX\":\"#EAB839\",\"TX\":\"#BF1B00\"}}}",
      "description": "",
      "savedSearchId": "Network",
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"filter\":[]}"
      }
    }
  },
  {
    "_id": "Memory-[Node-equal->Container]",
    "_type": "visualization",
    "_source": {
      "title": "Memory [Node=>Container]",
      "visState": "{\"title\":\"Memory [Node=>Container]\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"container_stats.memory.usage\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"container_stats.timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"container_Name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"machine_name\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}}],\"listeners\":{}}",
      "uiStateJSON": "{}",
      "description": "",
      "version": 1,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"index\":\"cadvisor*\",\"query\":{\"query_string\":{\"query\":\"* NOT container_Name.raw: \\\\\\\"/\\\\\\\" AND NOT container_Name.raw: \\\\\\\"/docker\\\\\\\"\",\"analyze_wildcard\":true}},\"filter\":[]}"
      }
    }
  }
]

这里还有很多东西可以玩,你也许想自定义报表界面,比如添加内存页错误状态,或者收发包的丢包数。如果你能实现开头列表处我没能实现的项目,那也是很好的。

总结

正确监控需要大量时间和精力,容器的 CPU、内存、IO、网络和磁盘,监控的这些参数还只是整个监控项目中的沧海一粟而已。

我不知道你做到了哪一阶段,但接下来的任务也许是:

  • 收集运行中的容器的日志
  • 收集应用的日志
  • 监控应用的性能
  • 报警
  • 监控健康状态

如果你有意见或建议,请留言。祝你玩得开心。

现在你可以关掉这些测试系统了:

docker-machine rm master1 worker{1,2}

via: https://blog.codeship.com/monitoring-docker-containers-with-elasticsearch-and-cadvisor/

作者:Lorenzo Fontana 译者:bazz2 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

从根本上说,几乎所有的主要软件,即使是开源软件,都是在基于镜像的容器技术出现之前设计的。这意味着把软件放到容器中相当于是一次平台移植。这也意味着一些程序可以很容易就迁移,而另一些就更困难

我大约在三年半前开展基于镜像的容器相关工作。到目前为止,我已经容器化了大量应用。我了解到什么是现实情况,什么是迷信。今天,我想简要介绍一下 Linux 容器是如何设计的,以及谈谈镜像签名。

Linux 容器是如何设计的

对于基于镜像的 Linux 容器,让大多数人感到困惑的是,它把操作系统分割成两个部分:内核空间与用户空间。在传统操作系统中,内核运行在硬件上,你无法直接与其交互。用户空间才是你真正能交互的,这包括所有你可以通过文件浏览器或者运行ls命令能看到的文件、类库、程序。当你使用ifconfig命令调整 IP 地址时,你实际上正在借助用户空间的程序来使内核根据 TCP 协议栈改变。这点经常让没有研究过 Linux/Unix 基础的人大吃一惊。

过去,用户空间中的类库支持了与内核交互的程序(比如 ifconfig、sysctl、tuned-adm)以及如网络服务器和数据库之类的面向用户的程序。这些所有的东西都堆积在一个单一的文件系统结构中。用户可以在 /sbin 或者 /lib 文件夹中找到所有操作系统本身支持的程序和类库,或者可以在 /usr/sbin 或 /usr/lib 文件夹中找到所有面向用户的程序或类库(参阅文件系统层次结构标准)。这个模型的问题在于操作系统程序和业务支持程序没有绝对的隔离。/usr/bin 中的程序可能依赖 /lib 中的类库。如果一个应用所有者需要改变一些东西,就很有可能破坏操作系统。相反地,如果负责安全更新的团队需要改变一个类库,就(常常)有可能破坏面向业务的应用。这真是一团糟。

借助基于镜像的容器,比如 Docker、LXD、RKT,应用程序所有者可以打包和调整所有放在 /sbin、/lib、/usr/bin 和 /usr/lib 中的依赖部分,而不用担心破坏底层操作系统。本质上讲,容器技术再次干净地将操作系统隔离为两部分:内核空间与用户空间。现在开发人员和运维人员可以分别独立地更新各自的东西。

然而还是有些令人困扰的地方。通常,每个应用所有者(或开发者)并不想负责更新这些应用依赖:像 openssl、glibc,或很底层的基础组件,比如,XML 解析器、JVM,再或者处理与性能相关的设置。过去,这些问题都委托给运维团队来处理。由于我们在容器中打包了很多依赖,对于很多组织来讲,对容器内的所有东西负责仍是个严峻的问题。

迁移现有应用到 Linux 容器

把应用放到容器中算得上是平台移植,我准备突出介绍究竟是什么让移植某些应用到容器当中这么困难。

(通过容器,)开发者现在对 /sbin 、/lib、 /usr/bin、 /usr/lib 中的内容有完全的控制权。但是,他们面临的挑战是,他们仍需要将数据和配置放到 /etc 或者 /var/lib 文件夹中。对于基于镜像的容器来说,这是一个糟糕的想法。我们真正需要的是代码、配置以及数据的隔离。我们希望开发者把代码放在容器当中,而数据和配置通过不同的环境(比如,开发、测试或生产环境)来获得。

这意味着我们(或者说平台)在实例化容器时,需要挂载 /etc 或 /var/lib 中的一些文件或文件夹。这会允许我们到处移动容器并仍能从环境中获得数据和配置。听起来很酷吧?这里有个问题,我们需要能够干净地隔离配置和数据。很多现代开源软件比如 Apache、MySQL、MongoDB、Nginx 默认就这么做了。但很多自产的、历史遗留的、或专有程序并未默认这么设计。对于很多组织来讲,这是主要的痛点。对于开发者来讲的最佳实践是,开始架构新的应用,移植遗留代码,以完成配置和数据的完全隔离。

镜像签名简介

信任机制是容器的重要议题。容器镜像签名允许用户添加数字指纹到镜像中。这个指纹随后可被加密算法测试验证。这使得容器镜像的用户可以验证其来源并信任。

容器社区经常使用“容器镜像”这个词组,但这个命名方法会让人相当困惑。Docker、LXD 和 RKT 推行获取远程文件来当作容器运行这样的概念。这些技术各自通过不同的方式处理容器镜像。LXD 用单独的一层来获取单独一个容器,而 Docker 和 RKT 使用基于开放容器镜像(OCI)格式,可由多层组成。糟糕的是,会出现不同团队和组织对容器镜像中的不同层负责的情况。容器镜像概念下隐含的是容器镜像格式的概念。拥有标准的镜像格式比如 OCI 会让容器生态系统围绕着镜像扫描、签名,和在不同云服务提供商间转移而繁荣发展。

现在谈到签名了。

容器存在一个问题,我们把一堆代码、二进制文件和类库放入其中。一旦我们打包了代码,我们就要把它和必要的文件服务器(注册服务器)共享。代码只要被共享,它基本上就是不具名的,缺少某种密文签名。更糟糕的是,容器镜像经常由不同人或团队控制的各个镜像层组成。每个团队都需要能够检查上一个团队的工作,增加他们自己的工作内容,并在上面添加他们自己的批准印记。然后他们需要继续把工作交给下个团队。

(由很多镜像组成的)容器镜像的最终用户需要检查监管链。他们需要验证每个往其中添加文件的团队的可信度。对于最终用户而言,对容器镜像中的每一层都有信心是极其重要的。

作者 Scott McCarty 于 8 月 24 日在 ContainerCon 会议上作了题为 Containers for Grownups: Migrating Traditional & Existing Applications 的报告,更多内容请参阅报告幻灯片


via: https://opensource.com/bus/16/8/introduction-linux-containers-and-image-signing

作者:Scott McCarty 译者:Tanete 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

这是 LXD 2.0 系列介绍文章的第三篇博客。

  1. LXD 入门
  2. 安装与配置
  3. 你的第一个 LXD 容器
  4. 资源控制
  5. 镜像管理
  6. 远程主机及容器迁移
  7. LXD 中的 Docker
  8. LXD 中的 LXD
  9. 实时迁移
  10. LXD 和 Juju
  11. LXD 和 OpenStack
  12. 调试,及给 LXD 做贡献

由于在管理 LXD 容器时涉及到大量的命令,所以这篇文章的篇幅是比较长的,如果你更喜欢使用同样的命令来快速的一步步实现整个过程,你可以尝试我们的在线示例

创建并启动一个新的容器

正如我在先前的文章中提到的一样,LXD 命令行客户端预配置了几个镜像源。Ubuntu 的所有发行版和架构平台全都提供了官方镜像,但是对于其他的发行版也有大量的非官方镜像,那些镜像都是由社区制作并且被 LXC 上游贡献者所维护。

Ubuntu

如果你想要支持最为完善的 Ubuntu 版本,你可以按照下面的去做:

lxc launch ubuntu:

注意,这里意味着会随着 Ubuntu LTS 的发布而变化。因此,如果用于脚本,你需要指明你具体安装的版本(参见下面)。

Ubuntu14.04 LTS

得到最新更新的、已经测试过的、稳定的 Ubuntu 14.04 LTS 镜像,你可以简单的执行:

lxc launch ubuntu:14.04

在该模式下,会指定一个随机的容器名。

如果你更喜欢指定一个你自己的名字,你可以这样做:

lxc launch ubuntu:14.04 c1

如果你想要指定一个特定的体系架构(非主流平台),比如 32 位 Intel 镜像,你可以这样做:

lxc launch ubuntu:14.04/i386 c2

当前的 Ubuntu 开发版本

上面使用的“ubuntu:”远程仓库只会给你提供官方的并经过测试的 Ubuntu 镜像。但是如果你想要未经测试过的日常构建版本,开发版可能对你来说是合适的,你需要使用“ubuntu-daily:”远程仓库。

lxc launch ubuntu-daily:devel c3

在这个例子中,将会自动选中最新的 Ubuntu 开发版本。

你也可以更加精确,比如你可以使用代号名:

lxc launch ubuntu-daily:xenial c4

最新的Alpine Linux

Alpine 镜像可以在“Images:”远程仓库中找到,通过如下命令执行:

lxc launch images:alpine/3.3/amd64 c5

其他

全部的 Ubuntu 镜像列表可以这样获得:

lxc image list ubuntu:
lxc image list ubuntu-daily:

全部的非官方镜像:

lxc image list images:

某个给定的原程仓库的全部别名(易记名称)可以这样获得(比如对于“ubuntu:”远程仓库):

lxc image alias list ubuntu:

创建但不启动一个容器

如果你想创建一个容器或者一批容器,但是你不想马上启动它们,你可以使用lxc init替换掉lxc launch。所有的选项都是相同的,唯一的不同就是它并不会在你创建完成之后启动容器。

lxc init ubuntu:

关于你的容器的信息

列出所有的容器

要列出你的所有容器,你可以这样这做:

lxc list

有大量的选项供你选择来改变被显示出来的列。在一个拥有大量容器的系统上,默认显示的列可能会有点慢(因为必须获取容器中的网络信息),你可以这样做来避免这种情况:

lxc list --fast

上面的命令显示了另外一套列的组合,这个组合在服务器端需要处理的信息更少。

你也可以基于名字或者属性来过滤掉一些东西:

stgraber@dakara:~$ lxc list security.privileged=true
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| NAME |  STATE  |        IPV4         |                       IPV6                    |    TYPE    | SNAPSHOTS |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| suse | RUNNING | 172.17.0.105 (eth0) | 2607:f2c0:f00f:2700:216:3eff:fef2:aff4 (eth0) | PERSISTENT | 0         |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+

在这个例子中,只有那些特权容器(禁用了用户命名空间)才会被列出来。

stgraber@dakara:~$ lxc list --fast alpine
+-------------+---------+--------------+----------------------+----------+------------+
|    NAME     |  STATE  | ARCHITECTURE |      CREATED AT      | PROFILES |    TYPE    |
+-------------+---------+--------------+----------------------+----------+------------+
| alpine      | RUNNING | x86_64       | 2016/03/20 02:11 UTC | default  | PERSISTENT |
+-------------+---------+--------------+----------------------+----------+------------+
| alpine-edge | RUNNING | x86_64       | 2016/03/20 02:19 UTC | default  | PERSISTENT |
+-------------+---------+--------------+----------------------+----------+------------+

在这个例子中,只有在名字中带有“alpine”的容器才会被列出来(也支持复杂的正则表达式)。

获取容器的详细信息

由于 list 命令显然不能以一种友好的可读方式显示容器的所有信息,因此你可以使用如下方式来查询单个容器的信息:

lxc info <container>

例如:

stgraber@dakara:~$ lxc info zerotier
Name: zerotier
Architecture: x86_64
Created: 2016/02/20 20:01 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 31715
Processes: 32
Ips:
 eth0: inet 172.17.0.101
 eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
 eth0: inet6 fe80::216:3eff:feec:65a8
 lo: inet 127.0.0.1
 lo: inet6 ::1
 lxcbr0: inet 10.0.3.1
 lxcbr0: inet6 fe80::c0a4:ceff:fe52:4d51
 zt0: inet 29.17.181.59
 zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
 zt0: inet6 fe80::79:e7ff:fe0d:5123
Snapshots:
 zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)

生命周期管理命令

这些命令对于任何容器或者虚拟机管理器或许都是最普通的命令,但是它们仍然需要讲到。

所有的这些命令在批量操作时都能接受多个容器名。

启动

启动一个容器就向下面一样简单:

lxc start <container>

停止

停止一个容器可以这样来完成:

lxc stop <container>

如果容器不合作(即没有对发出的 SIGPWR 信号产生回应),这时候,你可以使用下面的方式强制执行:

lxc stop <container> --force

重启

通过下面的命令来重启一个容器:

lxc restart <container>

如果容器不合作(即没有对发出的 SIGINT 信号产生回应),你可以使用下面的方式强制执行:

lxc restart <container> --force

暂停

你也可以“暂停”一个容器,在这种模式下,所有的容器任务将会被发送相同的 SIGSTOP 信号,这也意味着它们将仍然是可见的,并且仍然会占用内存,但是它们不会从调度程序中得到任何的 CPU 时间片。

如果你有一个很占用 CPU 的容器,而这个容器需要一点时间来启动,但是你却并不会经常用到它。这时候,你可以先启动它,然后将它暂停,并在你需要它的时候再启动它。

lxc pause <container>

删除

最后,如果你不需要这个容器了,你可以用下面的命令删除它:

lxc delete <container>

注意,如果容器还处于运行状态时你将必须使用“-force”。

容器的配置

LXD 拥有大量的容器配置设定,包括资源限制,容器启动控制以及对各种设备是否允许访问的配置选项。完整的清单因为太长所以并没有在本文中列出,但是,你可以从[这里]获取它。

就设备而言,LXD 当前支持下面列出的这些设备类型:

  • 磁盘 既可以是一块物理磁盘,也可以只是一个被挂挂载到容器上的分区,还可以是一个来自主机的绑定挂载路径。
  • 网络接口卡 一块网卡。它可以是一块桥接的虚拟网卡,或者是一块点对点设备,还可以是一块以太局域网设备或者一块已经被连接到容器的真实物理接口。
  • unix 块设备 一个 UNIX 块设备,比如 /dev/sda
  • unix 字符设备 一个 UNIX 字符设备,比如 /dev/kvm
  • none 这种特殊类型被用来隐藏那种可以通过配置文件被继承的设备。

配置 profile 文件

所有可用的配置文件列表可以这样获取:

lxc profile list

为了看到给定配置文件的内容,最简单的方式是这样做:

lxc profile show <profile>

你可能想要改变文件里面的内容,可以这样做:

lxc profile edit <profile>

你可以使用如下命令来改变应用到给定容器的配置文件列表:

lxc profile apply <container> <profile1>,<profile2>,<profile3>,...

本地配置

有些配置是某个容器特定的,你并不想将它放到配置文件中,你可直接对容器设置它们:

lxc config edit <container>

上面的命令做的和“profile edit”命令是一样。

如果不想在文本编辑器中打开整个文件的内容,你也可以像这样修改单独的配置:

lxc config set <container> <key> <value>

或者添加设备,例如:

lxc config device add my-container kvm unix-char path=/dev/kvm

上面的命令将会为名为“my-container”的容器设置一个 /dev/kvm 项。

对一个配置文件使用lxc profile setlxc profile device add命令也能实现上面的功能。

读取配置

你可以使用如下命令来读取容器的本地配置:

lxc config show <container>

或者得到已经被展开了的配置(包含了所有的配置值):

lxc config show --expanded <container>

例如:

stgraber@dakara:~$ lxc config show --expanded zerotier
name: zerotier
profiles:
- default
config:
 security.nesting: "true"
 user.a: b
 volatile.base_image: a49d26ce5808075f5175bf31f5cb90561f5023dcd408da8ac5e834096d46b2d8
 volatile.eth0.hwaddr: 00:16:3e:ec:65:a8
 volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":100000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":100000,"Nsid":0,"Maprange":65536}]'
devices:
 eth0:
  name: eth0
  nictype: macvlan
  parent: eth0
  type: nic
  limits.ingress: 10Mbit
  limits.egress: 10Mbit
 root:
  path: /
  size: 30GB
  type: disk
 tun:
  path: /dev/net/tun
  type: unix-char
ephemeral: false

这样做可以很方便的检查有哪些配置属性被应用到了给定的容器。

实时配置更新

注意,除非在文档中已经被明确指出,否则所有的配置值和设备项的设置都会对容器实时发生影响。这意味着在不重启正在运行的容器的情况下,你可以添加和移除某些设备或者修改安全配置文件。

获得一个 shell

LXD 允许你直接在容器中执行任务。最常用的做法是在容器中得到一个 shell 或者执行一些管理员任务。

和 SSH 相比,这样做的好处是你不需要容器是网络可达的,也不需要任何软件和特定的配置。

执行环境

与 LXD 在容器内执行命令的方式相比,有一点是不同的,那就是 shell 并不是在容器中运行。这也意味着容器不知道使用的是什么样的 shell,以及设置了什么样的环境变量和你的家目录在哪里。

通过 LXD 来执行命令总是使用最小的路径环境变量设置,并且 HOME 环境变量必定为 /root,以容器的超级用户身份来执行(即 uid 为 0,gid 为 0)。

其他的环境变量可以通过命令行来设置,或者在“environment.”配置中设置成永久环境变量。

执行命令

在容器中获得一个 shell 可以简单的执行下列命令得到:

lxc exec <container> bash

当然,这样做的前提是容器内已经安装了 bash。

更复杂的命令要求使用分隔符来合理分隔参数。

lxc exec <container> -- ls -lh /

如果想要设置或者重写变量,你可以使用“-env”参数,例如:

stgraber@dakara:~$ lxc exec zerotier --env mykey=myvalue env | grep mykey
mykey=myvalue

管理文件

因为 LXD 可以直接访问容器的文件系统,因此,它可以直接读取和写入容器中的任意文件。当我们需要提取日志文件或者与容器传递文件时,这个特性是很有用的。

从容器中取回一个文件

想要从容器中获得一个文件,简单的执行下列命令:

lxc file pull <container>/<path> <dest>

例如:

stgraber@dakara:~$ lxc file pull zerotier/etc/hosts hosts

或者将它读取到标准输出:

stgraber@dakara:~$ lxc file pull zerotier/etc/hosts -
127.0.0.1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

向容器发送一个文件

发送以另一种简单的方式完成:

lxc file push <source> <container>/<path>

直接编辑一个文件

编辑是一个方便的功能,其实就是简单的提取一个给定的路径,在你的默认文本编辑器中打开它,在你关闭编辑器时会自动将编辑的内容保存到容器。

lxc file edit <container>/<path>

快照管理

LXD 允许你对容器执行快照功能并恢复它。快照包括了容器在某一时刻的完整状态(如果-stateful被使用的话将会包括运行状态),这也意味着所有的容器配置,容器设备和容器文件系统也会被保存。

创建一个快照

你可以使用下面的命令来执行快照功能:

lxc snapshot <container>

命令执行完成之后将会生成名为snapX(X 为一个自动增长的数)的记录。

除此之外,你还可以使用如下命令命名你的快照:

lxc snapshot <container> <snapshot name>

列出所有的快照

一个容器的所有快照的数量可以使用lxc list来得到,但是具体的快照列表只能执行lxc info命令才能看到。

lxc info <container>

恢复快照

为了恢复快照,你可以简单的执行下面的命令:

lxc restore <container> <snapshot name>

给快照重命名

可以使用如下命令来给快照重命名:

lxc move <container>/<snapshot name> <container>/<new snapshot name>

从快照中创建一个新的容器

你可以使用快照来创建一个新的容器,而这个新的容器除了一些可变的信息将会被重置之外(例如 MAC 地址)其余所有信息都将和快照完全相同。

lxc copy <source container>/<snapshot name> <destination container>

删除一个快照

最后,你可以执行下面的命令来删除一个快照:

lxc delete <container>/<snapshot name>

克隆并重命名

得到一个纯净的发行版镜像总是让人感到愉悦,但是,有时候你想要安装一系列的软件到你的容器中,这时,你需要配置它然后将它分支成多个其他的容器。

复制一个容器

为了复制一个容器并有效的将它克隆到一个新的容器中,你可以执行下面的命令:

lxc copy <source container> <destination container>

目标容器在所有方面将会完全和源容器等同。除了新的容器没有任何源容器的快照以及一些可变值将会被重置之外(例如 MAC 地址)。

移动一个快照

LXD 允许你复制容器并在主机之间移动它。但是,关于这一点将在后面的文章中介绍。

现在,“move”命令将会被用作给容器重命名。

lxc move <old name> <new name>

唯一的要求就是当容器应该被停止,容器内的任何事情都会被保存成它本来的样子,包括可变化的信息(类似 MAC 地址等)。

结论

这篇如此长的文章介绍了大多数你可能会在日常操作中使用到的命令。

很显然,这些如此之多的命令都会有不少选项,可以让你的命令更加有效率,或者可以让你指定你的 LXD 容器的某个具体方面。最好的学习这些命令的方式就是深入学习它们的帮助文档( -help)。

更多信息

如果你不想或者不能在你的机器上安装 LXD,你可以试试在线版本!


作者简介:我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者,目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。


来自于:https://www.stgraber.org/2016/03/19/lxd-2-0-your-first-lxd-container-312/ 作者:Stéphane Graber 译者:kylepeng93 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出