Docker概述 第二章

yoqz3865 9年前
   <p>在本次介绍Docker的迷你系列 <a href="http://www.open-open.com/lib/view/open1460380578889.html" rel="nofollow,noindex">第一章</a> 中,我们了解了什么使Docker如此特别、虚拟机与容器之间的区别和组成Docker的主要组件。</p>    <p>在本博文中,我们将直接与一些容器接触。尤其是,我们将展示如何启动一个容器,如何使用Dockerfile构建镜像,如何与注册服务(registries)打交道,并介绍数据卷(data volumes)的基本概念</p>    <h2>启动容器</h2>    <p>在启动一个容器之前,你需要先从注册服务中拉取</p>    <pre>  $ docker pull alpine  </pre>    <p>启动容器执行如下简单命令即可:</p>    <pre>  $ docker run <image name> <command>  </pre>    <p>这里的 command 是你想在容器内执行的命令。</p>    <p>如果镜像本地不存在,Docker尝试从公共镜像服务器上取。这个动作是自动执行的,但是你会感受到命令执行的延迟。</p>    <p>需要注意的是在容器内被执行的命令退出后,容器是停止状态的。例如,你执行的命令是 /bin/echo hello world ,容器启动,打印"hello word",然后容器停止运行。</p>    <p>例如,执行</p>    <pre>  $ docker run alpine /bin/echo hello world  </pre>    <p>你会看类似的如下信息:</p>    <pre>  $ docker run alpine /bin/echo hello world    Unable to find image 'alpine:latest' locally    latest: Pulling from alpine    31f630c65071: Pull complete    Digest: sha256:074de05bbd8554cf454a19094df34985c8674f54e14474731427a2a31d1970ec    Status: Downloaded newer image for alpine:latest    hello world  </pre>    <p>我们在容器内启动 <a href="/misc/goto?guid=4958975074418483580" rel="nofollow,noindex">Alpine Linux</a> ,并且安装 openssh 用于ash提示。首先,执行如下命令:</p>    <pre>  $ docker run -t -i alpine /bin/ash  </pre>    <p>参数 -t, -i 意味着分配一个伪终端,并保持接受标准输入(STDIN)即使没有登录。这使得你可以使用容器像一个传统的虚拟机一样,只要ash运行即可。</p>    <p>安装 openssh 如下:</p>    <pre>  apk update    apk add openssh  </pre>    <p>如果我们退出容器,我们在硬盘上所做的改动并没有储存到容器上。所以下次启动容器时,openssh是没有被安装的。</p>    <p>如果我们想保存改动,我们需要提交(commit)他们。</p>    <pre>  $ docker ps  </pre>    <p>你会看到如下信息:</p>    <pre>  $ docker ps    CONTAINER ID    IMAGE     COMMAND       CREATED           STATUS2511d433bb01    alpine    "/bin/ash"    44 minutes ago    Up 44 minutes  </pre>    <p>现在我们可以将改动提交到容器中</p>    <pre>  $ docker commit 2511d433bb01 my/alpine  </pre>    <p>这里的 2511d433bb01 是容器ID, my/alpine 是我们的仓库</p>    <p>可以通过执行下面的命令查看:</p>    <pre>  docker images  </pre>    <p>你会看到如下类似信息:</p>    <pre>  $ docker images    REPOSITORY     TAG        IMAGE ID         CREATED             VIRTUAL SIZE    my/alpine      latest     2511d433bb01     About a minute ago  9.501 MB  </pre>    <h2>使用Dockerfiles构建镜像</h2>    <p>在运行容器上提交改动对于试验来说是可以的。但是在生产场景,使用Dockerfiles来记录必要的命令是更好的,作为 docker build 命令说明的Dockerfiles可以被纳入Git中进行版本控制跟踪。Docker守护进程(Docker daemon)依次执行你的命令,必要的话提交改动到新镜像,最终输出新镜像ID</p>    <p>创建Dockerfiles,并且执行如下命令:</p>    <pre>  # base image    FROM alpine  </pre>    <h2>grab ca-certificates and nginx</h2>    <p>RUN apk update && apk add ca-certificates nginx</p>    <h2>expose ports 80 and 443</h2>    <p>EXPOSE 80 443# start up nginx</p>    <p>CMD ["nginx", "-g", "daemon off;"]</p>    <p>现在我们来构建Docker镜像:</p>    <pre>  $ docker build -t mynginx .  </pre>    <p>-t mynginx 参数指定镜像名为 mynginx ,现在即可运行它:</p>    <pre>  $ docker run -p 8080:80 mynginx  </pre>    <p>-p 8080:80 参数将本机的8080端口映射到容器的80端口。</p>    <p>在浏览器中输入下面网址:</p>    <pre>  http://<ip_of_your_docker_host>:8080  </pre>    <p>你将会看到:</p>    <p><img src="https://simg.open-open.com/show/dd58233336a8126e16ad498ce90775a0.png"></p>    <p>我们构建了一个简单的nginx镜像!</p>    <p>我们可以使用Alpine Linux作为基础镜像。我们安装了nginx,然后开放了端口80和443。最后我们运行了nginx。使用Alpine Linux的原因是它占用空间很小,这使得nginx镜像也小很多,同时能够更快的构建,更快的推送到注册服务器或者从上面拉取。</p>    <p>想进一步了解Dockerfile的命令,请参考 <a href="/misc/goto?guid=4958855529107634770" rel="nofollow,noindex">该文献</a></p>    <h2>与注册服务器协作</h2>    <p>现在我们知道了如何构建Docker镜像,下面我们来学习如何将镜像推送到Docker注册服务上。</p>    <p>在例子中我们使用 <a href="/misc/goto?guid=4958849364237785306" rel="nofollow,noindex">Docker Hub</a> 注册服务。</p>    <p>你需要注册一个账户才能够推送Docker镜像。更多信息请参考 <a href="/misc/goto?guid=4959670226641537840" rel="nofollow,noindex">用户手册</a> 。</p>    <p>上一个例子中,我们使用如下命令构建出Docker镜像:</p>    <pre>  docker build -t mynginx .  </pre>    <p>该命令可以构建出Docker镜像,但是它没有仓库名字,只能在构建它的本机使用。</p>    <p>为了把它推送到Docker Hub注册服务(或者其他私服)上,你需要将你用户名添加到Docker镜像名中:</p>    <pre>  $ docker build -t yourname/mynginx .  </pre>    <p>在docker push前,要确保你已经执行了 docker login 。该命令将你的连接配置保存在你根目录的 .dockercfg 文件中。</p>    <p>推送到Docker注册服务,执行如下命令:</p>    <pre>  $ docker push yourname/mynginx  </pre>    <p>从Docker注册服务中拉取,执行如下命令:</p>    <pre>  $ docker pull yourname/mynginx  </pre>    <p>如果你自己搭建了Docker私服,执行命令类似如下:</p>    <pre>  $ docker push your_registry:5000/mynginx    $ docker pull your_registry:5000/mynginx  </pre>    <h2>数据持久化</h2>    <p>Docker容器是无状态的。然而在某些场景下如使用数据库,你需要持久化的数据。Docker支持两种持久化数据的方式:数据卷和数剧卷容器。这两个我们都将依次介绍。</p>    <h2>数据卷</h2>    <p>数据卷是经过特殊设计的在一个或者多个容器内的目录,它可以绕过统一文件系统( <a href="/misc/goto?guid=4959670226732242672" rel="nofollow,noindex">Union file system</a> )。</p>    <p>数据卷提供了多个有用的特征,来持久化或者共享数据。</p>    <ul>     <li>数据卷在容器创建时候初始化。如果容器的基础镜像在指定的挂载点含有数据,那些已经存在的数据则会在目录初始化时被拷贝到新的卷中</li>     <li>数据卷可以在容器间共享或者重用</li>     <li>对于数据卷的改动是直接生效的</li>     <li>数据卷不受镜像更新的影响,即使这些更新修改了数据卷挂载的路径</li>     <li>即使容器本身已被删除数据卷仍然存在</li>    </ul>    <p>数据卷被设计来用于持久化数据,独立于容器的生命周期。因此当你在删除容器的时候,Docker绝不会自动删除这些卷,同样也不会垃圾回收那些不被容器所引用的卷。</p>    <p>你可以使用 -v 参数执行 docker run 来添加本机目录作为数据卷到容器中。 -v 可以被多次使用,来挂载多个数据卷</p>    <p>我们在web应用容器中挂载一个单独的数据卷如下:</p>    <pre>  $ docker run -p 8080:80 \    -v /data/share/nginx/html:/usr/share/nginx/html mynginx  </pre>    <p>上面的命令挂载了一个本机目录 /data/share/nginx/html 到容器中的 /usr/share/nginx/html 目录。所有我们放到本机目录的文件都能立即在容器中看到。</p>    <p>注意:如果路径 /usr/share/nginx/html 已经在容器镜像中存在,它的内容将被本机上 /data/share/nginx/html 路径中的内容所替代,这样是为了保持挂载操作结果的一致性。</p>    <h2>数据卷容器</h2>    <p>如果你有一些持久化数据想在容器间共享,或者想在非持久化的容器中使用,最佳实践是创建一个数据卷容器,然后从容器中挂载数据。这避免了在某些Linux操作系统上挂载本机目录所导致可能会出现的某些权限问题。</p>    <p>我们来新建一个已命名的共享数据卷容器。可以使用busybox镜像,因为像postgres等其他官方镜像也是基于busybox镜像的。由于这些镜像都有统一文件系统层,Docker就会只保留这些层的一个副本来节省磁盘空间。</p>    <p>执行如下命令:</p>    <pre>  $ docker create -v /dbdata --name dbdata busybox true  </pre>    <p>该命令创建了一个使用busybox基础镜像的叫做dbdata的数据卷容器,并创建了数据目录 /dbdata 。</p>    <p>你可以在其他容器使用 --volumes-from 来挂载 /dbdata 卷。</p>    <pre>  $ docker run -d --volumes-from dbdata --name postgres postgres  </pre>    <p>在这个例子中,如果postgres镜像中也有 /dbdata 的目录,那么从dbdata容器中挂载数据卷就会隐藏postgres镜像中 /dbdata 下的文件。结果就是只显示来自dbdata容器对应目录下的文件。</p>    <p>你可以使用多个 --volumes-from 参数将来自多个容器的多个数据卷整合到一起。</p>    <p>如果你删除了挂载数据卷的容器,包括初始的dbdata容器,这些数据卷将不会被删除。为了将数据卷从硬盘删除,你必须在最后一个引用该数据卷的容器中显式地执行 docker rm -v 。这使得你可以升级或者有效地在容器间聚合数据。</p>    <h2>结论</h2>    <p>在本迷你系列的 <a href="http://www.open-open.com/lib/view/open1460380578889.html" rel="nofollow,noindex">第一章</a> 中,我们对Docker有了一个整体的认识,什么使得Docker如此特别,它的架构组成。在本文我们学习了构建镜像,启动容器,分层的文件系统和如何持久化数据。</p>    <p>请继续期待我们下一章关于Kubernetes的介绍。</p>    <p>原文链接: <a href="/misc/goto?guid=4959670226820166133" rel="nofollow,noindex">Docker Overview, Part Two</a> (翻译:姜俊厚)</p>    <p>来源:http://dockone.io/article/1205</p>