通过 Docker 化一个博客网站来开启我们的 Docker 之旅

简介:

这篇文章包含 Docker 的基本概念,以及如何通过创建一个定制的 Dockerfile 来 Docker 化Dockerize一个应用。

Docker 是一个过去两年来从某个 idea 中孕育而生的有趣技术,公司组织们用它在世界上每个角落来部署应用。在今天的文章中,我将讲述如何通过“Docker 化Dockerize”一个现有的应用,来开始我们的 Docker 之旅。这里提到的应用指的就是这个博客!

什么是 Docker?

当我们开始学习 Docker 基本概念时,让我们先去搞清楚什么是 Docker 以及它为什么这么流行。Docker 是一个操作系统容器管理工具,它通过将应用打包在操作系统容器中,来方便我们管理和部署应用。

容器 vs. 虚拟机

容器和虚拟机并不完全相似,它是另外一种提供操作系统虚拟化的方式。它和标准的虚拟机还是有所不同。

标准的虚拟机一般会包括一个完整的操作系统、操作系统软件包、最后还有一至两个应用。这都得益于为虚拟机提供硬件虚拟化的管理程序。这样一来,一个单一的服务器就可以将许多独立的操作系统作为虚拟客户机运行了。

容器和虚拟机很相似,它们都支持在单一的服务器上运行多个操作环境,只是,在容器中,这些环境并不是一个个完整的操作系统。容器一般只包含必要的操作系统软件包和一些应用。它们通常不会包含一个完整的操作系统或者硬件的虚拟化。这也意味着容器比传统的虚拟机开销更少。

容器和虚拟机常被误认为是两种对立的技术。虚拟机采用一个物理服务器来提供全功能的操作环境,该环境会和其余虚拟机一起共享这些物理资源。容器一般用来隔离一个单一主机上运行的应用进程,以保证隔离后的进程之间不能相互影响。事实上,容器和 BSD Jails 以及 chroot 进程的相似度,超过了和完整虚拟机的相似度。

Docker 在容器之上提供了什么

Docker 本身不是一个容器运行环境,事实上,只是一个与具体实现无关的容器技术,Docker 正在努力支持Solaris Zones 和 BSD Jails。Docker 提供了一种管理、打包和部署容器的方式。虽然一定程度上,虚拟机多多少少拥有这些类似的功能,但虚拟机并没有完整拥有绝大多数的容器功能,即使拥有,这些功能用起来都并没有 Docker 来的方便或那么完整。

现在,我们应该知道 Docker 是什么了,然后,我们将从安装 Docker,并部署一个公开的预构建好的容器开始,学习 Docker 是如何工作的。

从安装开始

默认情况下,Docker 并不会自动被安装在您的计算机中,所以,第一步就是安装 Docker 软件包;我们的教学机器系统是 Ubuntu 14.0.4,所以,我们将使用 Apt 软件包管理器,来执行安装操作。

 
  1. # apt-get install docker.io
  2. Reading package lists... Done
  3. Building dependency tree
  4. Reading state information... Done
  5. The following extra packages will be installed:
  6. aufs-tools cgroup-lite git git-man liberror-perl
  7. Suggested packages:
  8. btrfs-tools debootstrap lxc rinse git-daemon-run git-daemon-sysvinit git-doc
  9. git-el git-email git-gui gitk gitweb git-arch git-bzr git-cvs git-mediawiki
  10. git-svn
  11. The following NEW packages will be installed:
  12. aufs-tools cgroup-lite docker.io git git-man liberror-perl
  13. 0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
  14. Need to get 7,553 kB of archives.
  15. After this operation, 46.6 MB of additional disk space will be used.
  16. Do you want to continue? [Y/n] y

为了检查当前是否有容器运行,我们可以执行docker命令,加上ps选项

 
  1. # docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

docker命令中的ps功能类似于 Linux 的ps命令。它将显示可找到的 Docker 容器及其状态。由于我们并没有启动任何 Docker 容器,所以命令没有显示任何正在运行的容器。

部署一个预构建好的 nginx Docker 容器

我比较喜欢的 Docker 特性之一就是 Docker 部署预先构建好的容器的方式,就像yumapt-get部署包一样。为了更好地解释,我们来部署一个运行着 nginx web 服务器的预构建容器。我们可以继续使用docker命令,这次选择run选项。

 
  1. # docker run -d nginx
  2. Unable to find image 'nginx' locally
  3. Pulling repository nginx
  4. 5c82215b03d1: Download complete
  5. e2a4fb18da48: Download complete
  6. 58016a5acc80: Download complete
  7. 657abfa43d82: Download complete
  8. dcb2fe003d16: Download complete
  9. c79a417d7c6f: Download complete
  10. abb90243122c: Download complete
  11. d6137c9e2964: Download complete
  12. 85e566ddc7ef: Download complete
  13. 69f100eb42b5: Download complete
  14. cd720b803060: Download complete
  15. 7cc81e9a118a: Download complete

docker命令的run选项,用来通知 Docker 去寻找一个指定的 Docker 镜像,然后启动运行着该镜像的容器。默认情况下,Docker 容器运行在前台,这意味着当你运行docker run命令的时候,你的 shell 会被绑定到容器的控制台以及运行在容器中的进程。为了能在后台运行该 Docker 容器,我们使用了-d (detach)标志。

再次运行docker ps命令,可以看到 nginx 容器正在运行。

 
  1. # docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. f6d31ab01fc9 nginx:latest nginx -g 'daemon off 4 seconds ago Up 3 seconds 443/tcp, 80/tcp desperate_lalande

从上面的输出信息中,我们可以看到正在运行的名为desperate_lalande的容器,它是由nginx:latest image(LCTT 译注: nginx 最新版本的镜像)构建而来得。

Docker 镜像

镜像是 Docker 的核心特征之一,类似于虚拟机镜像。和虚拟机镜像一样,Docker 镜像是一个被保存并打包的容器。当然,Docker 不只是创建镜像,它还可以通过 Docker 仓库发布这些镜像,Docker 仓库和软件包仓库的概念差不多,它让 Docker 能够模仿yum部署软件包的方式来部署镜像。为了更好地理解这是怎么工作的,我们来回顾docker run执行后的输出。

 
  1. # docker run -d nginx
  2. Unable to find image 'nginx' locally

我们可以看到第一条信息是,Docker 不能在本地找到名叫 nginx 的镜像。这是因为当我们执行docker run命令时,告诉 Docker 运行一个基于 nginx 镜像的容器。既然 Docker 要启动一个基于特定镜像的容器,那么 Docker 首先需要找到那个指定镜像。在检查远程仓库之前,Docker 首先检查本地是否存在指定名称的本地镜像。

因为系统是崭新的,不存在 nginx 镜像,Docker 将选择从 Docker 仓库下载之。

 
  1. Pulling repository nginx
  2. 5c82215b03d1: Download complete
  3. e2a4fb18da48: Download complete
  4. 58016a5acc80: Download complete
  5. 657abfa43d82: Download complete
  6. dcb2fe003d16: Download complete
  7. c79a417d7c6f: Download complete
  8. abb90243122c: Download complete
  9. d6137c9e2964: Download complete
  10. 85e566ddc7ef: Download complete
  11. 69f100eb42b5: Download complete
  12. cd720b803060: Download complete
  13. 7cc81e9a118a: Download complete

这就是第二部分输出信息显示给我们的内容。默认情况下,Docker 会使用 Docker Hub 仓库,该仓库由 Docker 公司维护。

和 Github 一样,在 Docker Hub 创建公共仓库是免费的,私人仓库就需要缴纳费用了。当然,部署你自己的 Docker 仓库也是可以的,事实上只需要简单地运行docker run registry命令就行了。但在这篇文章中,我们的重点将不是讲解如何部署一个定制的注册服务。

关闭并移除容器

在我们继续构建定制容器之前,我们先清理一下 Docker 环境,我们将关闭先前的容器,并移除它。

我们利用docker命令和run选项运行一个容器,所以,为了停止同一个容器,我们简单地在执行docker命令时,使用kill选项,并指定容器名。

 
  1. # docker kill desperate_lalande
  2. desperate_lalande

当我们再次执行docker ps,就不再有容器运行了

 
  1. # docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

但是,此时,我们这是停止了容器;虽然它不再运行,但仍然存在。默认情况下,docker ps只会显示正在运行的容器,如果我们附加-a (all) 标识,它会显示所有运行和未运行的容器。

 
  1. # docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. f6d31ab01fc9 5c82215b03d1 nginx -g 'daemon off 4 weeks ago Exited (-1) About a minute ago desperate_lalande

为了能完整地移除容器,我们在用docker命令时,附加rm选项。

 
  1. # docker rm desperate_lalande
  2. desperate_lalande

虽然容器被移除了;但是我们仍拥有可用的nginx镜像(LCTT 译注:镜像缓存)。如果我们重新运行docker run -d nginx,Docker 就无需再次拉取 nginx 镜像即可启动容器。这是因为我们本地系统中已经保存了一个副本。

为了列出系统中所有的本地镜像,我们运行docker命令,附加images选项。

 
  1. # docker images
  2. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
  3. nginx latest 9fab4090484a 5 days ago 132.8 MB

构建我们自己的镜像

截至目前,我们已经使用了一些基础的 Docker 命令来启动、停止和移除一个预构建好的普通镜像。为了“Docker 化(Dockerize)”这篇博客,我们需要构建我们自己的镜像,也就是创建一个 Dockerfile

在大多数虚拟机环境中,如果你想创建一个机器镜像,首先,你需要建立一个新的虚拟机、安装操作系统、安装应用,最后将其转换为一个模板或者镜像。但在 Docker 中,所有这些步骤都可以通过 Dockerfile 实现全自动。Dockerfile 是向 Docker 提供构建指令去构建定制镜像的方式。在这一章节,我们将编写能用来部署这个博客的定制 Dockerfile。

理解应用

我们开始构建 Dockerfile 之前,第一步要搞明白,我们需要哪些东西来部署这个博客。

这个博客本质上是由一个静态站点生成器生成的静态 HTML 页面,这个生成器是我编写的,名为 hamerkop。这个生成器很简单,它所做的就是生成该博客站点。所有的代码和源文件都被我放在了一个公共的 Github 仓库。为了部署这篇博客,我们要先从 Github 仓库把这些内容拉取下来,然后安装 Python 和一些 Python 模块,最后执行hamerkop应用。我们还需要安装 nginx,来运行生成后的内容。

截止目前,这些还是一个简单的 Dockerfile,但它却给我们展示了相当多的 Dockerfile 语法)。我们需要克隆 Github 仓库,然后使用你最喜欢的编辑器编写 Dockerfile,我选择vi

 
  1. # git clone https://github.com/madflojo/blog.git
  2. Cloning into 'blog'...
  3. remote: Counting objects: 622, done.
  4. remote: Total 622 (delta 0), reused 0 (delta 0), pack-reused 622
  5. Receiving objects: 100% (622/622), 14.80 MiB | 1.06 MiB/s, done.
  6. Resolving deltas: 100% (242/242), done.
  7. Checking connectivity... done.
  8. # cd blog/
  9. # vi Dockerfile

FROM - 继承一个 Docker 镜像

第一条 Dockerfile 指令是FROM指令。这将指定一个现存的镜像作为我们的基础镜像。这也从根本上给我们提供了继承其他 Docker 镜像的途径。在本例中,我们还是从刚刚我们使用的 nginx 开始,如果我们想从头开始,我们可以通过指定ubuntu:latest来使用 Ubuntu Docker 镜像。

 
  1. ## Dockerfile that generates an instance of http://bencane.com
  2. FROM nginx:latest
  3. MAINTAINER Benjamin Cane <ben@bencane.com>

除了FROM指令,我还使用了MAINTAINER,它用来显示 Dockerfile 的作者。

Docker 支持使用#作为注释,我将经常使用该语法,来解释 Dockerfile 的部分内容。

运行一次测试构建

因为我们继承了 nginx Docker镜像,我们现在的 Dockerfile 也就包括了用来构建 nginx 镜像的 Dockerfile中所有指令。这意味着,此时我们可以从该 Dockerfile 中构建出一个 Docker 镜像,然后以该镜像运行一个容器。虽然,最终的镜像和 nginx 镜像本质上是一样的,但是我们这次是通过构建 Dockerfile 的形式,然后我们将讲解 Docker 构建镜像的过程。

想要从 Dockerfile 构建镜像,我们只需要在运行 docker 命令的时候,加上 build 选项。

 
  1. # docker build -t blog /root/blog
  2. Sending build context to Docker daemon 23.6 MB
  3. Sending build context to Docker daemon
  4. Step 0 : FROM nginx:latest
  5. ---> 9fab4090484a
  6. Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
  7. ---> Running in c97f36450343
  8. ---> 60a44f78d194
  9. Removing intermediate container c97f36450343
  10. Successfully built 60a44f78d194

上面的例子,我们使用了-t (tag)标识给镜像添加“blog”的标签。实质上我们就是在给镜像命名,如果我们不指定标签,就只能通过 Docker 分配的 Image ID 来访问镜像了。本例中,从 Docker 构建成功的信息可以看出,Image ID值为 60a44f78d194

除了-t标识外,我还指定了目录/root/blog。该目录被称作“构建目录”,它将包含 Dockerfile,以及其它需要构建该容器的文件。

现在我们构建成功了,下面我们开始定制该镜像。

使用 RUN 来执行 apt-get

用来生成 HTML 页面的静态站点生成器是用 Python 语言编写的,所以,在 Dockerfile 中需要做的第一件定制任务是安装 Python。我们将使用 Apt 软件包管理器来安装 Python 软件包,这意味着在 Dockerfile 中我们要指定运行apt-get updateapt-get install python-dev;为了完成这一点,我们可以使用RUN指令。

 
  1. ## Dockerfile that generates an instance of http://bencane.com
  2. FROM nginx:latest
  3. MAINTAINER Benjamin Cane <ben@bencane.com>
  4. ## Install python and pip
  5. RUN apt-get update
  6. RUN apt-get install -y python-dev python-pip

如上所示,我们只是简单地告知 Docker 构建镜像的时候,要去执行指定的apt-get命令。比较有趣的是,这些命令只会在该容器的上下文中执行。这意味着,即使在容器中安装了python-devpython-pip,但主机本身并没有安装这些。说的更简单点,pip命令将只在容器中执行,出了容器,pip命令不存在。

还有一点比较重要的是,Docker 构建过程中不接受用户输入。这说明任何被RUN指令执行的命令必须在没有用户输入的时候完成。由于很多应用在安装的过程中需要用户的输入信息,所以这增加了一点难度。不过我们例子中,RUN命令执行的命令都不需要用户输入。

安装 Python 模块

Python 安装完毕后,我们现在需要安装 Python 模块。如果在 Docker 外做这些事,我们通常使用pip命令,然后参考我的博客 Git 仓库中名叫requirements.txt的文件。在之前的步骤中,我们已经使用git命令成功地将 Github 仓库“克隆”到了/root/blog目录;这个目录碰巧也是我们创建Dockerfile的目录。这很重要,因为这意味着 Docker 在构建过程中可以访问这个 Git 仓库中的内容。

当我们执行构建后,Docker 将构建的上下文环境设置为指定的“构建目录”。这意味着目录中的所有文件都可以在构建过程中被使用,目录之外的文件(构建环境之外)是不能访问的。

为了能安装所需的 Python 模块,我们需要将requirements.txt从构建目录拷贝到容器中。我们可以在Dockerfile中使用COPY指令完成这一需求。

 
  1. ## Dockerfile that generates an instance of http://bencane.com
  2. FROM nginx:latest
  3. MAINTAINER Benjamin Cane <ben@bencane.com>
  4. ## Install python and pip
  5. RUN apt-get update
  6. RUN apt-get install -y python-dev python-pip
  7. ## Create a directory for required files
  8. RUN mkdir -p /build/
  9. ## Add requirements file and run pip
  10. COPY requirements.txt /build/
  11. RUN pip install -r /build/requirements.txt

Dockerfile中,我们增加了3条指令。第一条指令使用RUN在容器中创建了/build/目录。该目录用来拷贝生成静态 HTML 页面所需的一切应用文件。第二条指令是COPY指令,它将requirements.txt从“构建目录”(/root/blog)拷贝到容器中的/build/目录。第三条使用RUN指令来执行pip命令;安装requirements.txt文件中指定的所有模块。

当构建定制镜像时,COPY是条重要的指令。如果在 Dockerfile 中不指定拷贝文件,Docker 镜像将不会包含requirements.txt 这个文件。在 Docker 容器中,所有东西都是隔离的,除非在 Dockerfile 中指定执行,否则容器中不会包括所需的依赖。

重新运行构建

现在,我们让 Docker 执行了一些定制任务,现在我们尝试另一次 blog 镜像的构建。

 
  1. # docker build -t blog /root/blog
  2. Sending build context to Docker daemon 19.52 MB
  3. Sending build context to Docker daemon
  4. Step 0 : FROM nginx:latest
  5. ---> 9fab4090484a
  6. Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
  7. ---> Using cache
  8. ---> 8e0f1899d1eb
  9. Step 2 : RUN apt-get update
  10. ---> Using cache
  11. ---> 78b36ef1a1a2
  12. Step 3 : RUN apt-get install -y python-dev python-pip
  13. ---> Using cache
  14. ---> ef4f9382658a
  15. Step 4 : RUN mkdir -p /build/
  16. ---> Running in bde05cf1e8fe
  17. ---> f4b66e09fa61
  18. Removing intermediate container bde05cf1e8fe
  19. Step 5 : COPY requirements.txt /build/
  20. ---> cef11c3fb97c
  21. Removing intermediate container 9aa8ff43f4b0
  22. Step 6 : RUN pip install -r /build/requirements.txt
  23. ---> Running in c50b15ddd8b1
  24. Downloading/unpacking jinja2 (from -r /build/requirements.txt (line 1))
  25. Downloading/unpacking PyYaml (from -r /build/requirements.txt (line 2))
  26. <truncated to reduce noise>
  27. Successfully installed jinja2 PyYaml mistune markdown MarkupSafe
  28. Cleaning up...
  29. ---> abab55c20962
  30. Removing intermediate container c50b15ddd8b1
  31. Successfully built abab55c20962

上述输出所示,我们可以看到构建成功了,我们还可以看到另外一个有趣的信息---> Using cache。这条信息告诉我们,Docker 在构建该镜像时使用了它的构建缓存。

Docker 构建缓存

当 Docker 构建镜像时,它不仅仅构建一个单独的镜像;事实上,在构建过程中,它会构建许多镜像。从上面的输出信息可以看出,在每一“步”执行后,Docker 都在创建新的镜像。

 
  1. Step 5 : COPY requirements.txt /build/
  2. ---> cef11c3fb97c

上面片段的最后一行可以看出,Docker 在告诉我们它在创建一个新镜像,因为它打印了Image ID :cef11c3fb97c。这种方式有用之处在于,Docker能在随后构建这个 blog 镜像时将这些镜像作为缓存使用。这很有用处,因为这样, Docker 就能加速同一个容器中新构建任务的构建流程。从上面的例子中,我们可以看出,Docker 没有重新安装python-devpython-pip包,Docker 则使用了缓存镜像。但是由于 Docker 并没有找到执行mkdir命令的构建缓存,随后的步骤就被一一执行了。

Docker 构建缓存一定程度上是福音,但有时也是噩梦。这是因为决定使用缓存或者重新运行指令的因素很少。比如,如果requirements.txt文件发生了修改,Docker 会在构建时检测到该变化,然后 Docker 会重新执行该执行那个点往后的所有指令。这得益于 Docker 能查看requirements.txt的文件内容。但是,apt-get命令的执行就是另一回事了。如果提供 Python 软件包的 Apt 仓库包含了一个更新的 python-pip 包;Docker 不会检测到这个变化,转而去使用构建缓存。这会导致之前旧版本的包将被安装。虽然对python-pip来说,这不是主要的问题,但对使用了存在某个致命攻击缺陷的软件包缓存来说,这是个大问题。

出于这个原因,抛弃 Docker 缓存,定期地重新构建镜像是有好处的。这时,当我们执行 Docker 构建时,我简单地指定--no-cache=True即可。

部署博客的剩余部分

Python 软件包和模块安装后,接下来我们将拷贝需要用到的应用文件,然后运行hamerkop应用。我们只需要使用更多的COPY 和 RUN指令就可完成。

 
  1. ## Dockerfile that generates an instance of http://bencane.com
  2. FROM nginx:latest
  3. MAINTAINER Benjamin Cane <ben@bencane.com>
  4. ## Install python and pip
  5. RUN apt-get update
  6. RUN apt-get install -y python-dev python-pip
  7. ## Create a directory for required files
  8. RUN mkdir -p /build/
  9. ## Add requirements file and run pip
  10. COPY requirements.txt /build/
  11. RUN pip install -r /build/requirements.txt
  12. ## Add blog code nd required files
  13. COPY static /build/static
  14. COPY templates /build/templates
  15. COPY hamerkop /build/
  16. COPY config.yml /build/
  17. COPY articles /build/articles
  18. ## Run Generator
  19. RUN /build/hamerkop -c /build/config.yml

现在我们已经写出了剩余的构建指令,我们再次运行另一次构建,并确保镜像构建成功。

 
  1. # docker build -t blog /root/blog/
  2. Sending build context to Docker daemon 19.52 MB
  3. Sending build context to Docker daemon
  4. Step 0 : FROM nginx:latest
  5. ---> 9fab4090484a
  6. Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
  7. ---> Using cache
  8. ---> 8e0f1899d1eb
  9. Step 2 : RUN apt-get update
  10. ---> Using cache
  11. ---> 78b36ef1a1a2
  12. Step 3 : RUN apt-get install -y python-dev python-pip
  13. ---> Using cache
  14. ---> ef4f9382658a
  15. Step 4 : RUN mkdir -p /build/
  16. ---> Using cache
  17. ---> f4b66e09fa61
  18. Step 5 : COPY requirements.txt /build/
  19. ---> Using cache
  20. ---> cef11c3fb97c
  21. Step 6 : RUN pip install -r /build/requirements.txt
  22. ---> Using cache
  23. ---> abab55c20962
  24. Step 7 : COPY static /build/static
  25. ---> 15cb91531038
  26. Removing intermediate container d478b42b7906
  27. Step 8 : COPY templates /build/templates
  28. ---> ecded5d1a52e
  29. Removing intermediate container ac2390607e9f
  30. Step 9 : COPY hamerkop /build/
  31. ---> 59efd1ca1771
  32. Removing intermediate container b5fbf7e817b7
  33. Step 10 : COPY config.yml /build/
  34. ---> bfa3db6c05b7
  35. Removing intermediate container 1aebef300933
  36. Step 11 : COPY articles /build/articles
  37. ---> 6b61cc9dde27
  38. Removing intermediate container be78d0eb1213
  39. Step 12 : RUN /build/hamerkop -c /build/config.yml
  40. ---> Running in fbc0b5e574c5
  41. Successfully created file /usr/share/nginx/html//2011/06/25/checking-the-number-of-lwp-threads-in-linux
  42. Successfully created file /usr/share/nginx/html//2011/06/checking-the-number-of-lwp-threads-in-linux
  43. <truncated to reduce noise>
  44. Successfully created file /usr/share/nginx/html//archive.html
  45. Successfully created file /usr/share/nginx/html//sitemap.xml
  46. ---> 3b25263113e1
  47. Removing intermediate container fbc0b5e574c5
  48. Successfully built 3b25263113e1

运行定制的容器

成功的一次构建后,我们现在就可以通过运行docker命令和run选项来运行我们定制的容器,和之前我们启动 nginx 容器一样。

 
  1. # docker run -d -p 80:80 --name=blog blog
  2. 5f6c7a2217dcdc0da8af05225c4d1294e3e6bb28a41ea898a1c63fb821989ba1

我们这次又使用了-d (detach)标识来让Docker在后台运行。但是,我们也可以看到两个新标识。第一个新标识是--name,这用来给容器指定一个用户名称。之前的例子,我们没有指定名称,因为 Docker 随机帮我们生成了一个。第二个新标识是-p,这个标识允许用户从主机映射一个端口到容器中的一个端口。

之前我们使用的基础 nginx 镜像分配了80端口给 HTTP 服务。默认情况下,容器内的端口通道并没有绑定到主机系统。为了让外部系统能访问容器内部端口,我们必须使用-p标识将主机端口映射到容器内部端口。上面的命令,我们通过-p 80:80语法将主机80端口映射到容器内部的80端口。

经过上面的命令,我们的容器看起来成功启动了,我们可以通过执行docker ps核实。

 
  1. # docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. d264c7ef92bd blog:latest nginx -g 'daemon off 3 seconds ago Up 3 seconds 443/tcp, 0.0.0.0:80->80/tcp blog

总结

截止目前,我们拥有了一个运行中的定制 Docker 容器。虽然在这篇文章中,我们只接触了一些 Dockerfile 指令用法,但是我们还是要学习所有的指令。我们可以检查 Docker's reference page 来获取所有的 Dockerfile 指令用法,那里对指令的用法说明得很详细。

另一个比较好的资源是 Dockerfile Best Practices page,它有许多构建定制 Dockerfile 的最佳练习。有些技巧非常有用,比如战略性地组织好 Dockerfile 中的命令。上面的例子中,我们将articles目录的COPY指令作为 Dockerfile 中最后的COPY指令。这是因为articles目录会经常变动。所以,将那些经常变化的指令尽可能地放在最后面的位置,来最优化那些可以被缓存的步骤。

原文发布时间为:2016-06-08

本文来自云栖社区合作伙伴“Linux中国”

相关文章
|
4月前
|
前端开发 数据库 Docker
一款可以直接使用的招聘网站, react django开发招聘网站,docker 部署
一款可以直接使用的招聘网站, react django开发招聘网站,docker 部署
50 0
|
6月前
|
Linux Docker 容器
用Docker-Compose一分钟搭建Wordpress博客系统
用Docker-Compose一分钟搭建Wordpress博客系统
117 0
用Docker-Compose一分钟搭建Wordpress博客系统
|
1月前
|
SQL Apache 流计算
Apache Flink官方网站提供了关于如何使用Docker进行Flink CDC测试的文档
【2月更文挑战第25天】Apache Flink官方网站提供了关于如何使用Docker进行Flink CDC测试的文档
141 3
|
1月前
|
测试技术 程序员 Linux
【Docker项目实战】使用Docker部署blog轻量级博客系统
【2月更文挑战第16天】使用Docker部署blog轻量级博客系统
157 2
|
12月前
|
Docker 容器
.NET Core 网站项目在Docker中运行无法访问问题处理
.NET Core 网站项目在Docker中运行无法访问问题处理
264 0
|
9月前
|
Cloud Native 关系型数据库 MySQL
云原生之使用Docker部署wordpress网站
云原生之使用Docker部署wordpress网站
218 1
云原生之使用Docker部署wordpress网站
|
JavaScript 前端开发 应用服务中间件
面向WEB开发人员的Docker(六):使用nginx部署静态网站
上一期,介绍了使用docker部署开发wordpress,本期来介绍更加贴近前端开发的Docker使用。现在很多前端项目都使用vue,发布的时候将文件生成到默认的 dist 文件夹,本文将展示vue项目发布部署到Docker容器,文章涉及的命令都是基于配置好了docker环境,如果没有安装部署Docker,可以参阅《面向WEB开发的Docker(三):安装Docker》。
381 0
面向WEB开发人员的Docker(六):使用nginx部署静态网站
|
应用服务中间件 Apache nginx
如何在Docker容器中运行多个网站?底层原理是什么?
如何在Docker容器中运行多个网站?底层原理是什么?
433 0
|
数据安全/隐私保护 容器
【docker-compose】一键部署WordPress博客
【docker-compose】一键部署WordPress博客
304 0
【docker-compose】一键部署WordPress博客
|
Cloud Native 应用服务中间件 nginx
云原生之使用docker部署nginx网站
云原生之使用docker部署nginx网站
208 0
云原生之使用docker部署nginx网站