在本次介绍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来记录必要的命令是更好的,作为 docker build
命令说明的Dockerfiles可以被纳入Git中进行版本控制跟踪。Docker守护进程(Docker daemon)依次执行你的命令,必要的话提交改动到新镜像,最终输出新镜像ID
创建Dockerfiles,并且执行如下命令:
# base image
FROM alpine
RUN apk update && apk add ca-certificates nginx
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
你将会看到:
我们构建了一个简单的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 (翻译:姜俊厚)