转载

Docker概述 第二章

在本次介绍Docker的迷你系列 第一章 中,我们了解了什么使Docker如此特别、虚拟机与容器之间的区别和组成Docker的主要组件。

在本博文中,我们将直接与一些容器接触。尤其是,我们将展示如何启动一个容器,如何使用Dockerfile构建镜像,如何与注册服务(registries)打交道,并介绍数据卷(data volumes)的基本概念

启动容器

在启动一个容器之前,你需要先从注册服务中拉取

$ docker pull alpine

启动容器执行如下简单命令即可:

$ docker run <image name> <command>

这里的 command 是你想在容器内执行的命令。

如果镜像本地不存在,Docker尝试从公共镜像服务器上取。这个动作是自动执行的,但是你会感受到命令执行的延迟。

需要注意的是在容器内被执行的命令退出后,容器是停止状态的。例如,你执行的命令是 /bin/echo hello world ,容器启动,打印"hello word",然后容器停止运行。

例如,执行

$ docker run alpine /bin/echo hello world

你会看类似的如下信息:

$ 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

我们在容器内启动 Alpine Linux ,并且安装 openssh 用于ash提示。首先,执行如下命令:

$ docker run -t -i alpine /bin/ash

参数 -t, -i 意味着分配一个伪终端,并保持接受标准输入(STDIN)即使没有登录。这使得你可以使用容器像一个传统的虚拟机一样,只要ash运行即可。

安装 openssh 如下:

apk update
apk add openssh

如果我们退出容器,我们在硬盘上所做的改动并没有储存到容器上。所以下次启动容器时,openssh是没有被安装的。

如果我们想保存改动,我们需要提交(commit)他们。

$ docker ps

你会看到如下信息:

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS2511d433bb01 alpine "/bin/ash" 44 minutes ago Up 44 minutes

现在我们可以将改动提交到容器中

$ docker commit 2511d433bb01 my/alpine

这里的 2511d433bb01 是容器ID, my/alpine 是我们的仓库

可以通过执行下面的命令查看:

docker images

你会看到如下类似信息:

$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
my/alpine latest 2511d433bb01 About a minute ago 9.501 MB

使用Dockerfiles构建镜像

在运行容器上提交改动对于试验来说是可以的。但是在生产场景,使用Dockerfiles来记录必要的命令是更好的,作为 docker build 命令说明的Dockerfiles可以被纳入Git中进行版本控制跟踪。Docker守护进程(Docker daemon)依次执行你的命令,必要的话提交改动到新镜像,最终输出新镜像ID

创建Dockerfiles,并且执行如下命令:

# base image
FROM alpine

grab ca-certificates and nginx

RUN apk update && apk add ca-certificates nginx

expose ports 80 and 443

EXPOSE 80 443# start up nginx

CMD ["nginx", "-g", "daemon off;"]

现在我们来构建Docker镜像:

$ docker build -t mynginx .

-t mynginx 参数指定镜像名为 mynginx ,现在即可运行它:

$ docker run -p 8080:80 mynginx

-p 8080:80 参数将本机的8080端口映射到容器的80端口。

在浏览器中输入下面网址:

http://<ip_of_your_docker_host>:8080

你将会看到:

Docker概述 第二章

我们构建了一个简单的nginx镜像!

我们可以使用Alpine Linux作为基础镜像。我们安装了nginx,然后开放了端口80和443。最后我们运行了nginx。使用Alpine Linux的原因是它占用空间很小,这使得nginx镜像也小很多,同时能够更快的构建,更快的推送到注册服务器或者从上面拉取。

想进一步了解Dockerfile的命令,请参考 该文献

与注册服务器协作

现在我们知道了如何构建Docker镜像,下面我们来学习如何将镜像推送到Docker注册服务上。

在例子中我们使用 Docker Hub 注册服务。

你需要注册一个账户才能够推送Docker镜像。更多信息请参考 用户手册 。

上一个例子中,我们使用如下命令构建出Docker镜像:

docker build -t mynginx .

该命令可以构建出Docker镜像,但是它没有仓库名字,只能在构建它的本机使用。

为了把它推送到Docker Hub注册服务(或者其他私服)上,你需要将你用户名添加到Docker镜像名中:

$ docker build -t yourname/mynginx .

在docker push前,要确保你已经执行了 docker login 。该命令将你的连接配置保存在你根目录的 .dockercfg 文件中。

推送到Docker注册服务,执行如下命令:

$ docker push yourname/mynginx

从Docker注册服务中拉取,执行如下命令:

$ docker pull yourname/mynginx

如果你自己搭建了Docker私服,执行命令类似如下:

$ docker push your_registry:5000/mynginx
$ docker pull your_registry:5000/mynginx

数据持久化

Docker容器是无状态的。然而在某些场景下如使用数据库,你需要持久化的数据。Docker支持两种持久化数据的方式:数据卷和数剧卷容器。这两个我们都将依次介绍。

数据卷

数据卷是经过特殊设计的在一个或者多个容器内的目录,它可以绕过统一文件系统( Union file system )。

数据卷提供了多个有用的特征,来持久化或者共享数据。

  • 数据卷在容器创建时候初始化。如果容器的基础镜像在指定的挂载点含有数据,那些已经存在的数据则会在目录初始化时被拷贝到新的卷中
  • 数据卷可以在容器间共享或者重用
  • 对于数据卷的改动是直接生效的
  • 数据卷不受镜像更新的影响,即使这些更新修改了数据卷挂载的路径
  • 即使容器本身已被删除数据卷仍然存在

数据卷被设计来用于持久化数据,独立于容器的生命周期。因此当你在删除容器的时候,Docker绝不会自动删除这些卷,同样也不会垃圾回收那些不被容器所引用的卷。

你可以使用 -v 参数执行 docker run 来添加本机目录作为数据卷到容器中。 -v 可以被多次使用,来挂载多个数据卷

我们在web应用容器中挂载一个单独的数据卷如下:

$ docker run -p 8080:80 /
-v /data/share/nginx/html:/usr/share/nginx/html mynginx

上面的命令挂载了一个本机目录 /data/share/nginx/html 到容器中的 /usr/share/nginx/html 目录。所有我们放到本机目录的文件都能立即在容器中看到。

注意:如果路径 /usr/share/nginx/html 已经在容器镜像中存在,它的内容将被本机上 /data/share/nginx/html 路径中的内容所替代,这样是为了保持挂载操作结果的一致性。

数据卷容器

如果你有一些持久化数据想在容器间共享,或者想在非持久化的容器中使用,最佳实践是创建一个数据卷容器,然后从容器中挂载数据。这避免了在某些Linux操作系统上挂载本机目录所导致可能会出现的某些权限问题。

我们来新建一个已命名的共享数据卷容器。可以使用busybox镜像,因为像postgres等其他官方镜像也是基于busybox镜像的。由于这些镜像都有统一文件系统层,Docker就会只保留这些层的一个副本来节省磁盘空间。

执行如下命令:

$ docker create -v /dbdata --name dbdata busybox true

该命令创建了一个使用busybox基础镜像的叫做dbdata的数据卷容器,并创建了数据目录 /dbdata

你可以在其他容器使用 --volumes-from 来挂载 /dbdata 卷。

$ docker run -d --volumes-from dbdata --name postgres postgres

在这个例子中,如果postgres镜像中也有 /dbdata 的目录,那么从dbdata容器中挂载数据卷就会隐藏postgres镜像中 /dbdata 下的文件。结果就是只显示来自dbdata容器对应目录下的文件。

你可以使用多个 --volumes-from 参数将来自多个容器的多个数据卷整合到一起。

如果你删除了挂载数据卷的容器,包括初始的dbdata容器,这些数据卷将不会被删除。为了将数据卷从硬盘删除,你必须在最后一个引用该数据卷的容器中显式地执行 docker rm -v 。这使得你可以升级或者有效地在容器间聚合数据。

结论

在本迷你系列的 第一章 中,我们对Docker有了一个整体的认识,什么使得Docker如此特别,它的架构组成。在本文我们学习了构建镜像,启动容器,分层的文件系统和如何持久化数据。

请继续期待我们下一章关于Kubernetes的介绍。

原文链接: Docker Overview, Part Two (翻译:姜俊厚)

原文  http://dockone.io/article/1205
正文到此结束
Loading...