Docker实践(15) – 通过Docker Hub分享镜像

如果你与其他人共享你的镜像,使用描述性名称tag镜像会更有帮助。为了满足这个需求,Docker能够轻松地将镜像移动到其它地方,Docker Inc则创建了免费的Docker Hub服务来鼓励这种共享。

为了使用Docker Hub服务,你需要注册一个Docker Hub帐号来使用docker login登录。注册地址为http://hub.docker.com。

问题

你想公开分享一个Docker镜像。

解决方法

使用Docker Hub registry分享你的镜像。

讨论

下面有几个术语需要理解,以免造成混乱。

  • Username – Docker registry用户名
  • Registry – registries保存镜像。你可以上传镜像到registry和从它下载镜像。Registries可以是公开或私有的。
  • Registry host – 运行Docker registry的主机
  • Docker Hub – 默认公开的registry,位于https://hub.docker.com
  • Index – 与registry host一样。待废弃的术语。
  • 正如你以前看到的,你可以根据需要对镜像进行多次tag。这对复制镜像是有用的,这样你可以管理它。
    我们假设你Docker Hub的用户名为”adev”。下面的三个命令展示如何复制来自Docker Hub的debian:wheezy镜像到你自己用户下。

    1. docker pull debian:wheezy
    2. docker tag debian:wheezy adev/debian:mywheezy1
    3. docker push adev/debian:mywheezy1

    你现在有一个Debian wheezy镜像的引用,你可以自己维护它了。
    如果你有一个私有的repository需要推送,除了在tag之前需要指定registry的地方外,其它步骤一样。我们假设你repository的地址为http://mycorp.private.dockerregistry。下面的示例tag和推送镜像。

    1. docker pull debian
    2. docker tag debian:wheezy mycorp.private.dockerregistry/adev/debian:mywheezy1
    3. docker push mycorp.private.dockerregistry/adev/debian:mywheezy1

    以上的命令不会把镜像推送到公共的Docker Hub,会推送到你的私有repository,所以有这个repository的访问权限的用户能拉取这个镜像。

    Docker实践(14) – Docker tag

    上一篇文章中你通过docker commit保存了容器的状态,并且得到一个随机的镜像ID。记住和管理巨大数量的镜像ID非常困难。使用Docker的tag功能可以给镜像设置一个可读的名称,提醒你镜像创建的目的是什么。掌握这个技术使你对镜像的用途一目了然,使得管理你机器上的镜像变得非常容易。

    问题

    你想方便地引用和存储一个Docker commit。

    解决方法

    使用docker tag命名你的提交。

    讨论

    tag的基本用法很简单:

    1. $ docker tag 071f6a36c23a19801285b82eafc99333c76f63ea0aa0b44902c6bae482a6e036 imagename

    创建了一个可以引用此镜像的名称,如:

    1. docker run imagename

    这个名称比随机的ID容易记多了。
    如果你想与他人共享你的镜像,还需要了解更多的tag知识。tag的相关术语可能会有点混乱。像image,name和repository有时候可互相交换使用。如下定义:

  • Image – 一个只读层
  • Name – 镜像的名称,如“todoapp”
  • Tag – 作为动词,指的是给镜像设置一个名称,作为名词,是镜像名称的修饰符
  • Repository – 已tag的镜像集合一起组成容器的文件系统
  • 可能最容易引起混乱的术语是image和repository。我们一直使用术语image来宽松地表示产生容器的层集合,但技术上,image是递归地引用其父层的单个层。repository是托管的,意味着它存储在某个地方(不管是在你的Docker daemon或者一个registry上)。此外,repository是tagged的镜像的集合来组成容器的文件系统。
    可以用Git类比帮助我们理解。当克隆一个Git repository时,你check out了当时请求点的文件状态。这类似于image。repository是文件每次提交的整体历史记录。因为check out是指向了最上层。当你克隆时也会包含其它所有层。
    我们来看下Ubuntu镜像。当你拉取Ubuntu镜像并执行docker images时,你会看到如下输出:

    1. $ docker images
    2. REPOSITORY  TAG    IMAGE ID        CREATED       VIRTUAL SIZE
    3. ubuntu    trusty   8eaa4ff06b53    4 weeks ago    192.7 MB
    4. ubuntu    14.04    8eaa4ff06b53    4 weeks ago    192.7 MB
    5. ubuntu    14.04.1  8eaa4ff06b53    4 weeks ago    192.7 MB
    6. ubuntu    latest   8eaa4ff06b53    4 weeks ago    192.7 MB

    Repository列列出托管层集合称为“ubuntu”。通常称为镜像。Tag列列出四个不同的名称(trusty, 14.04, 14.04.1, 和latest)。Image ID列列出相同的镜像ID。因为这些不同的tag名称指向同一个镜像。这说明了你可以对同一个镜像设置多个tag。

    理解SaltStack(3) – 通信与安全

    本节介绍Salt通信模型以及认证和安全性的基本概述。

    架构模型

    salt使用server-agent通信模型,(可以作为独立的单台服务器管理工具,也能无代理通过SSH工作)。这个服务器组件称为salt master,代理称为salt minion。
    运维自动化
    salt master负责发送命令给salt minons,然后聚合显示这些命令的结果。单个salt master能管理数千个系统。

    通信模型

    Salt使用发布-订阅模式与受管系统通信。连接由salt minion发起,意味着你不需要在那些系统上监听任何端口来接收请求(从而减少攻击的可能性)。salt master使用4505和4506端口,必须向minion开放接收请求。
    运维自动化

  • Publisher: (4505端口)所有的salt minions与publisher端口建立持久连接。通过此端口在所有的连接上异步发送命令,这使得能够在大量的系统上同时执行命令。
  • Request Server:(4506端口) salt minions连接request server,当需要时发送结果给salt master和安全地请求文件,minion特定的数据值(称为salt pillar)。salt master和salt minion之间的连接是1:1的,且不是异步。
  • SALT MINION认证

    当minion首次启动时,通过网络搜索名称为salt的系统(不过这很容易更改为一个IP或者其它的主机名)。当发现后,minion发起一个握手然后发送它的公钥给salt master。
    运维自动化
    此次连接发起后,salt minion的公钥就存储在了服务器上,然后必须在salt master上执行salt-key命令接受这个minion(或者通过一些自动机制)。当salt minion的公钥被接受时,salt master才会提供解码消息所需的安全密钥(意味着在minion的key还没被接受时,minion不会运行任何命令)。
    minion的key被接受后,salt master给minion返回公钥和AES key,用来加密和解密由master发送的消息。AES key使用minion发送过来的公钥来加密,因为AES key只能由minion解密。

    安全通信

    salt master和salt minion接下来之间的通信都是使用轮换的AES key来加密。AES加密密钥使用TLS的显式初始化向量和CBC块链接算法。

    轮换的安全密钥

    轮换的AES密钥用来加密由salt master发送给salt minion的作业和加密与salt master文件服务器的连接。每次salt master重启和每次salt minion的密钥被删除都会使用salt-key命令来重新生成一个新的密钥。
    当新密码生成后,所有的salt minions必须重新发起认证请求来接收更新的AES密钥。这就使得AES的更新不会中断minion的连接。

    加密通信信道

    Salt master和Salt minion之间的发布通信使用轮换AES密钥加密。Salt master和Salt minion之间的直接通信使用唯一AES密钥来加密每个会话。
    例如,发布作业使用轮换AES密钥来加密,然后minion特定的数据(如salt pillar)使用唯一的AES密钥加密。

    用户访问控制

    在一个命令发送给minions之前,salt对Publiser ACL执行一些内部的检查以确保用户有权限执行当前的命令。如果用户被授权对指定的目标运行指定的命令,则发送命令。否则返回错误。

    Docker实践(13) – 保存开发环境状态

    如果你曾经开发过软件,你可能至少一次地像这样呼叫过,”奇怪了,之前明明正常的!”不过没办法快速地恢复到之前的正常状态,你只能匆忙地去修改代码以尽快完成任务而不至于延期。这就浪费了许多时间。
    版本控制软件已经能帮助你快速恢复到指定的正常版本,不过但下面的两个特殊情况会存在问题:

  • 代码无法体现你工作环境系统的状态
  • 你可能还不愿意提交代码
  • 第一个问题比第二个值得关注。虽然像Git这样的现代源代码控制工具可以轻松地创建分支,不过捕获整个开发文件系统的状态不是Git的目的。
    Docker通过它的commit功能提供了一个成本低廉和快速的方法来保存容器开发系统的状态,这就是我们下面将要探讨的。

    问题

    你想保存你开发环境的状态。

    解决方法

    使用docker commit保存状态

    讨论

    假如你对你的to-do应用做更改。ToDoCorp的CEO对浏览器显示的标题”Swarm+React – TodoMVC.”不满意,要改为“ToDo- Corp’s ToDo App”。
    你不确定如何完成这个任务,所以你可能需要启动应用,并更改文件做试验看会怎样:

    1. $ docker run -d -p 8000:8000 –name todobug1 dockerinpractice/todoapp  3c3d5d3ffd70d17e7e47e90801af7d12d6fc0b8b14a8b33131fc708423ee4372
    2. $ docker exec -i -t todobug1 /bin/bash

    docker run命令后台(-d)启动to-do应用容器,映射容器的8000端口到主机的8000端口(-p 8000:8000),命名为todobug1(–name todobug1)。
    第二个命令在已运行的容器中启动/bin/bash。-i激活交互模式,-t创建一个TTY。
    现在你已经进入容器了,所以试验前先安装编辑器。我们喜欢用vim,所以用以下命令:

    1. apt-get update
    2. apt-get install vim

    经过了一翻努力你意识到需要更改local.html,因为你更改如下:
    ToDoCorp’s ToDo App
    不过CEO想让标题使小写字符,因为她听说这看起来更现代。你想把现在的更改保存下,在另一个终端执行如下命令:

    1. $ docker commit todobug1
    2. ca76b45144f2cb31fda6a31e55f784c93df8c9d4c96bbeacd73cad9cd55d2970

    你现在已经提交了刚才的更改,所以之后你可以从镜像运行包含此更改的容器。
    下一步你更改local.html:
    todocorp’s todo app
    再次提交:

    1. $ docker commit todobug1
    2. 071f6a36c23a19801285b82eafc99333c76f63ea0aa0b44902c6bae482a6e036

    现在在我们这个示例已经有两个镜像ID(ca76b45144f2cb31fda6a31e55f784c93df8c9d4c96bbeacd73cad9cd55d2970和071f6a36c23a19801285b82eafc99333c76f63ea0aa0b44902c6bae482a6e036)。当CEO来评估她想要哪个时,你可以运行任意一个镜像来让你决定。
    你可以在新终端运行如下命令来启动两个镜像:

    1. $ docker run -p 8001:8000 ca76b45144f2cb31fda6a31e55f784c93df8c9d4c96bbeacd73cad9cd55d2970
    2. $ docker run -p 8002:8000 071f6a36c23a19801285b82eafc99333c76f63ea0aa0b44902c6bae482a6e036

    这样你可以打开http://localhost:8001显示大写的标题页面和http://localhost:8002显示小写页面。
    你肯定想知道有没有更好的方法来引用这两个镜像,而不是需要输入这么长的ID。下一篇文章我们会给这些镜像一个名称来更好的引用它。

    Docker实践(12) – 管理容器服务启动

    当尝试Docker作为VM的替代品在容器内运行多个服务可能会比较方便,或者完成VM到容器的初始转换后,运行重要服务是有必要的。
    不管是什么原因,当想尝试管理容器内的进程时尽量避免重复造轮子。

    问题

    你想管理容器内的多个进程。

    解决方法

    使用Supervisor应用(http://supervisord.org/)来管理你的进程启动。

    讨论

    我们准备演示如何创建一个包含Tomcat和Apache web服务器的容器,并以Supervisor的管理方式启动应用。
    首先在一个新的空的目录创建一个Dockerfile,如下:
    虚拟化技术
    需要一个用来指定启动哪些应用的supervisord配置文件,如下:
    虚拟化技术
    虚拟化技术
    使用刚才创建的Dockerfile生成镜像:

    1. docker build -t supervised .

    开始运行容器:
    虚拟化技术
    如果你打开http://localhost:9000,你应该能看到Apache的默认页面。
    如果要清除容器,执行如下命令:

    1. docker rm -f supervised

    理解SaltStack(2) – 插件

    没有涉及salt插件的讨论那所有关于salt方法的讨论都是不完整的。理解插件和salt的可插拔架构往往就像发现新大陆的感觉,把salt研究者变成salt的福音传道者。
    基本的解释是:salt的核心框架提供了高速通信和事件总线。
    此框架连接并验证受管系统,并为这些系统提供发送通知的方法。
    在这个核心框架上层,Salt的其余功能暴露为一组松散耦合的可插入子系统。

    可插拔的子系统

    salt包含超过20个的可插拔子系统,但大多数用户只对用于直接管理系统的少数感兴趣。下表包含Salt中一些更常见的子系统的列表:
    运维自动化

    “可插拔是什么意思”
    Salt没有定义一个内置的方式来执行任何子系统的任务,每个子系统仅仅是把它的工作委托给插件。salt为每个系统提供一系列的插件,不过更改一个插件在大多数情况下跟更新一个配置文件那样的简单。这种可插拔性使salt非常灵活。

    为了说明,下图显示了几个常见的子系统以及每个子系统最常见的插件。
    运维自动化
    此图表只显示了一些可用的子系统和插件,但应该能给你对salt一般架构有一个初步了解。

    作业运行期间的子系统

    当一个作业运行时,几个salt子系统被调用用来处理作业。下面的图表显示一个经典的状态运行或远程执行作业的子系统流程:
    运维自动化

    插件?听起来像讨论salt模块!
    在salt中,插件就是Python中的模块,既然每个插件就是一个Python模块,大多数时候,他们被简单地称为模块,或更准确地,Salt子系统模块(Salt auth模块,Salt文件服务器模块等)。只要你明白每个Salt模块都是一个扩展Salt中许多子系统之一的插件,你就会很好地理解这种关系。

    在每一步中,子系统委托它的工作给配置的插件。例如,在第7步的job returner插件可能是30个插件中的一个,包含MySQL,redis或者根本没有配置的(在第4步后job returner插件也可以直接在受管系统上运行)。
    在每一步中,有许多可用的插件用于完成一个任务,从而可以产生数百种可能的salt配置和工作流。

    伸缩性

    伸缩性使salt成为一个非常强大和可定制的工具,不过当你在学习这个工具时使得很难回答一些标准的问题。
    为了有趣些,我们采取“技术上正确”的方法,并回答几个关于Salt的常见问题:

  • 你如何开始salt作业?- 从任何可以调用Python,REST API,命令行或使用salt内置调度程序的接口
  • salt如何格式化结果? – YAML,JSON,纯文本,python数据结构和其它一些格式,你可以在任何时候使用单个参数来更改格式。
  • Salt使用什么格式来配置声明? – 根据你的使用情况来从15个支持的格式中选择一个,你还可以选择模板语言。格式是基于每个文件指定的,因此你可以同时使用多个格式。
  • 结果保存在哪里? – 你想存储的任何地方,你有30个选择。
  • 除了让你非常讨厌的对话,以这种方式回答这些问题确实说了一些关于salt方法管理的事情。你了解你的基础设施,在目前复杂的环境中,没有做任何事情最好的方式。
    没有必要为这些事情烦恼,salt为大多数用户设置了出色的默认值。主要是,当你什么时候需要伸缩性,它就在那里。

    salt组件

    有了salt的可插拔子系统的新知识,希望你开始明白,salt组件实际上是salt中的可插拔子系统,具有相应的插件。Salt grains? Salt pillar? Salt runners?所有可插拔子系统都很容易扩展。

    虚拟模块

    我们在这里已经讨论了很多,但是还有一个我们需要讨论的模块。还记得早些时候我们解释了salt如何抽象操作系统的底层细节吗?salt实现这种抽象的一种方式是使用虚拟模块。
    一些管理任务在不同的操作系统之间非常的不一样,在编写插件时,它们之间能使用的重复代码很少。
    例如,Debian系统上的包管理使用一个名为aptpkg的执行模块完成。在Redhat上,它使用一个名为yumpkg的执行模块完成(原因很明显)。如果你一直使用Salt,你会知道salt调用pkg远程执行模块进行包管理,它可以在所有操作系统上使用。
    为了启用这种类型的抽象,Salt使用虚拟模块名称加载这些类型的模块。aptpkg模块包含的说明基本上说“如果你是Debian系统,请将此模块加载为pkg。 否则,不要加载这个!类似的代码存在于对Redhat或CentOS的检查决定使用yumpkg中。这样可以存在多个模块来执行相同的任务,但只加载了一个虚拟名称。
    在阅读模块文档时,请记住这一点,因为你经常需要阅读非虚拟模块的文档以了解其行为。你可以在Salt模块中搜索__virtualname__,以找到Salt在加载该模块时使用的名称。
    下一篇文章我们讨论salt的通信。

    Docker实践(11) – 将系统拆分为微服务容器

    我们已经探讨了如何作为一个整体使用容器(像一个经典的服务器),并解释它可以是一个快速移动系统架构到Docker的好方法。不过在Docker世界中,通常认为最好的做法是尽可能多地分割系统,直到每个容器只运行一个服务,然后通过links连接所有容器。因为这是推荐的Docker方法,你会发现Docker Hub中的大多数容器都是这种方法,理解如何以这种方式构建镜像对于与Docker生态系统进行交互非常重要。
    一个容器一个服务的主要原因是通过单一责任原则更容易分离关注点。
    如果一个容器只做一项工作,那么将更容易地把容器放到开发,测试和生产的软件开发生命周期中,而不用担心它与其它组件的交互。这使得交付更灵活和软件项目更可扩展。不过它增加了维护开销,所以最好考虑在你的用例中是否值得这样做。

    问题

    你希望将你的应用程序分解成更易于管理的服务

    解决方法

    使用Docker将你的应用程序集分解为基于容器的服务

    讨论

    在Docker社区中,关于如何严格地遵循“一容器一服务”规则的一些争论,其中一部分源于对定义的不同意见 – 是一个单独的进程,还是根据需要把一组进程放到一起完成一项服务?它往往归结为一个声明,给予从头重新设计系统的能力,微服务是最可能的选择。但有时候,实用性与理想主义相克 – 当我们为我们的组织评估Docker时,我们发现自己处于必须走整条路线的位置,以便让Docker尽可能快速和轻松地工作。 让我们来看看在Docker中跑多个进程的一个缺点。首先我们需要展示如何创建一个带有数据库,应用和web服务器的容器。

    这些示例是为了阐明目的,所以简化了。尝试直接运行它们不一定能用。

    设置简单的PostgreSQL,NodeJS和Nginx应用:

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install postgresql nodejs npm nginx
    3. WORKDIR /opt
    4. COPY . /opt/                             # {*}
    5. RUN service postgresql start &&
    6.     cat db/schema.sql | psql &&
    7.     service postgresql stop
    8. RUN cd app && npm install
    9. RUN cp conf/mysite /etc/nginx/sites-available/ &&
    10.     cd /etc/nginx/sites-enabled &&
    11.     ln -s ../sites-available/mysite

    在RUN语句中使用&&有效地确保了几个命令作为一个命令运行。这对于减小你的镜像大小会有用。每个Dockerfile命令都会在上一个层之上创建一个新层。如果你以这种方式 运行软件包更新命令(如apt-get update)和install命令,你可以确保无论何时安装软件包,它们都将来自更新的软件包缓存。

    前面的示例是一个概念上简单的Dockerfile,它在容器中安装我们需要的一切,然后设置数据库,应用程序和Web服务器。不过在你想快速重载容器时会有一个问题 – 对存储库中任何文件的任何更改都将从{*}处开始重建所有内容,因为缓存无法重复使用。如果你的一些步骤需要时间比较久(如数据库创建或npm安装),你可能需要一段时间等待容器重建。
    解决方案是拆分COPY . /opt指令为三部分,对应数据库,应用程序和Web设置。

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install postgresql nodejs npm nginx
    3. WORKDIR /opt
    4. COPY db /opt/db                                     -+
    5. RUN service postgresql start &&                    |- db setup
    6.     cat db/schema.sql | psql && |
    7.     service postgresql stop                         -+
    8. COPY app /opt/app                                   -+
    9. RUN cd app && npm install                            |- app setup
    10. RUN cd app && ./minify_static.sh                    -+
    11. COPY conf /opt/conf                                 -+
    12. RUN cp conf/mysite /etc/nginx/sites-available/ &&   +
    13.     cd /etc/nginx/sites-enabled &&                  |- web setup
    14.     ln -s ../sites-available/mysite                 -+

    在上面的代码中,COPY命令分成三个单独的指令。这意味着数据库将不会在每次代码更改时重建,因为在COPY app /opt/app之前缓存可以重用。
    不过由于缓存功能相当简单,容器仍然必须在每次对schema.sql进行更改时完全重建 – 解决这个问题的唯一方法是按顺序为三个应用程序分别创建Dockerfile,如下:
    Database Dockerfile

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install postgresql
    3. WORKDIR /opt
    4. COPY db /opt/db
    5. RUN service postgresql start &&
    6.     cat db/schema.sql | psql &&
    7.     service postgresql stop

    App Dockerfile

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install nodejs npm
    3. WORKDIR /opt
    4. COPY app /opt/app
    5. RUN cd app && npm install
    6. RUN cd app && ./minify_static.sh

    Web server Dockerfile

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install nginx
    3. WORKDIR /opt
    4. COPY conf /opt/conf
    5. RUN cp conf/mysite /etc/nginx/sites-available/ &&
    6.     cd /etc/nginx/sites-enabled &&
    7.     ln -s ../sites-available/mysite

    不管何时db,app或conf目录中的哪一个更改,仅仅有一个容器需要重建。当你有三个以上的容器或者需要大量时间的设置步骤时这种方法非常有用 – 你可以在每步中添加最少必要的文件以便获得更有用的Dockerfile缓存。在app Dockerfile中的npm install的操作中,只依赖了package.json文件,所以我们可以更改这个Dockerfile以充分利用dockerfile层缓存。

    1. FROM ubuntu:14.04
    2. RUN apt-get update && apt-get install nodejs npm
    3. WORKDIR /opt
    4. COPY app/package.json /opt/app/package.json
    5. RUN cd app && npm install
    6. COPY app /opt/app
    7. RUN cd app && ./minify_static.sh

    不过事情没有那么简单,从单个dockerfile文件分割为多个dockerfile,增加了不少重复代码。你可以通过添加另一个dockerfile作为基础镜像来部分解决这个问题。此外,启动你的镜像还有一些复杂的问题 – 除了EXPOSE步骤使适当的端口可用于链接和更改Postgres配置之外,你还需要确保在每次启动时链接容器。 幸运的是,有一个叫做docker-compose(以前的fig)的工具来帮我们完成这件事。

    Docker实践(10) – 类主机容器

    我们现在把讨论转到Docker社区最具争议性的领域之一 – 运行一个包含多个进程的类主机镜像。
    这个在Docker社区中部分人认为是一种不好的形式。容器不是虚拟机 – 它们有显著的差异 – 假装不会造成混乱和没有问题。
    不管是好还是坏,本文展示如何运行一个类主机镜像,讨论这其中的一些问题。

    运行一个类主机镜像是一个说服Docker反对者的好方法,Docker很有用。当他们更多地使用Docker,他们会更理解Docker范式,微服务方法对他们更有意义。在我们介绍Docker的公司中,我们发现这种单一的方法是将人们从开发服务器和笔记本电脑上的开发移动到更加包容和可管理的环境的好方法。

    虚拟机与Docker容器的不同之处
    这些是VM和Docker容器之间的一些区别:
    Docker是面向应用的,而VM是面向操作系统的;
    Docker容器与其他Docker容器共享操作系统,相比之下,VM各自有它们自己的操作系统;
    Docker容器设计为运行一个主要进程,而不是管理多个进程集。

    问题

    你需要为容器设置一个类主机的环境,并设置多个进程和服务。

    解决方法

    使用旨在模拟主机的镜像,并为其提供所需的应用程序。

    讨论

    在下面的示例中我们使用phusion/baseimage Docker镜像,一个设计用来运行多个进程的镜像。
    第一步是运行镜像并使用docker exec进入到其bash环境:

    1. user@docker-host$ docker run -d phusion/baseimage
    2. 3c3f8e3fb05d795edf9d791969b21f7f73e99eb1926a6e3d5ed9e1e52d0b446e
    3. user@docker-host$ docker exec -i -t 3c3f8e3fb05d795 /bin/bash
    4. root@3c3f8e3fb05d:/#

    在这段代码中,docker run命令后台启动一个镜像(第一行),默认命令启动镜像并返回了新建容器的ID(第二行)。
    然后传容器ID给docker exec命令(第三),这个命令是在已运行的容器中启动一个新的进程。-i参数允许你与新进程交互,-t参数设置一个TTY,允许你在容器内部创建一个终端(/bin/bash)(第四行)。
    如果你等一分钟然后查看进程列表,输出类似如下:
    虚拟化技术
    虚拟化技术
    你可以看到容器启动起来很像一个主机,初始化服务如cron和sshd使它看起来像一个标准的Linux主机。这对于给新的Docker工程师初始演示很有用。
    这是否构成违反“一个容器一个服务”的微服务原则是Docker社区内部的一个争论。类主机镜像支持者认为这不违反这一原则,因为容器仍然可以实现其运行所在的系统的单个离散函数。

    Docker实践(9) – 虚拟机转换为容器

    Docker Hub没有所有可能的基本镜像,所以对于一些小众的Linux发行版本和用例,人们需要自己来创建它。如果你想把一个存在状态的虚拟机放入Docker上层迭代,或者受益于Docker生态系统,同样的原则也适用。
    理想情况下,你希望使用标准的Docker技术从头开始构建一个等效的VM,例如Dockerfiles与标准配置管理工具。然而现实是,许多VM没有仔细配置管理。这个有可能发生,因为一个VM已经发展成为一个有机体,人们已经在用它了,以一个更结构化的方式来重新创建它的投资可能不值得。

    问题

    你想把一个VM转换为一个Docker镜像

    解决办法

    通过ssh使用qemu-nbd,tar或者其它方法把VM文件系统打包成一个TAR文件,然后在Dockerfile中使用ADD命令添加TAR文件来创建镜像。

    讨论

    首先我们要将虚拟机分为两大类:本地(VM磁盘镜像和VM的执行在你的电脑)和远程(VM磁盘镜像的存储和VM执行在其它地方)。两组虚拟机(和任何你想创建Docker镜像)转换为Docker镜像的原理是一样的 – 打包文件系统为TAR文件并添加TAR文件到scratch镜像的根目录。
    ADD命令 – ADD Dockerfile命令(不像COPY命令)当把TAR文件(也可以是gzipped文件和其它类似的文件类型)放置到镜像时会自动解压。
    SCRATCH镜像 – scratch镜像是你在上面创建镜像的一个零字节的假镜像,通常它用于你想使用Dockerfile复制(或添加)一个完整文件系统到一个镜像的情况。
    我们现在来看一个你有一个本地Virtualbox VM的情况。
    在我们开始之前,你需要做如下事件:

  • 安装qemu-nbd工具(在Ubuntu系统的qemu-utils包里)
  • 记下VM磁盘映像的路径
  • 关闭VM
  • 如果你的VM磁盘映像是.vdi或.vmdk格式,应该能使用qemu-nbd成功挂载。其它的格式也有可能可行。
    以下代码演示了如何将虚拟机文件转换为虚拟磁盘,然后你可以从中复制所有文件:
    虚拟化技术
    如果你的VM是在远程,你可以请求你的运维团队来对你想要的分区作一个转储,或者在VM运行时创建TAR。
    如果你得到一个分区转储,可以很容易的挂载并打包成一个TAR文件:

    1. $ sudo mount -o loop partition.dump /mnt
    2. $ sudo tar cf $(pwd)/img.tar -C /mnt .
    3. $ sudo umount /mnt

    或者你可能在正在运行的系统上创建TAR文件:

    1. $ cd /
    2. $ sudo tar cf /img.tar –exclude=/img.tar –one-file-system /

    你现在得到了包含文件系统的TAR文件,可以通过scp命令传输到其它机器。

    从正在运行的系统上创建TAR文件看起来是一个最简单的方法(不用关机,安装软件或者请求其他团队),不过它有一个严重的缺点 – 你可能会一个不一致的状态下复制文件,这样当使用Docker镜像时会遇到奇怪的问题。如果你必须这样做,那么就尽可能的关闭应用和服务。

    一旦你得到了文件系统TAR文件,你就可以添加到镜像了。这是最简单的一步,只有Dockerfile的两行:

    1. FROM scratch
    2. ADD img.tar /

    然后执行docker build .就生成镜像了。
    现在你得到了一个新的镜像,你可以运行一个容器,然后在上面做你想要的修改,比如删除不需要的包来缩减容器大小并重新导出为一个新的小的镜像,下面是流程图:
    虚拟化技术

    Docker实践(8) – 搜索和运行一个Docker镜像

    Docker registries启用了类似于GitHub的社交编程文化。如果你有兴趣尝试一个新的软件应用,或者搜索一个新的软件用于特定目的。那么Docker镜像可以是一个简单的试验方式,而不会干扰你的主机,调配VM或者担心安装步骤。

    问题

    你想找到以Docker镜像形式的应用或工具,并尝试使用它。

    方法

    使用docker search命令来找到镜像来拉取并运行它。

    讨论

    我们假设你有兴趣尝试Node.js。在下面的命令中我们使用docker search命令来搜索带有node字符的镜像:
    虚拟化技术
    虚拟化技术
    一旦你选择好镜像,你可以通过执行docker pull imagename命令来下载它。
    虚拟化技术
    然后你可以使用-t和-i参数以交互模式运行它。-t参数创建一个tty设备(一个终端),-i参数指定Docker会话为可交互的。

    1. $ docker run -t -i node /bin/bash
    2.         root@c267ae999646:/# node
    3.         > process.version
    4.         ‘v0.12.0’
    5. >

    从镜像维护者经常会有关于如何运行镜像的特别建议。在http://hub.docker.com网站搜索这个镜像会把你带到这个镜像的页面。Description tab会给你更多的信息。