部署网站

恭喜你可以看到这里!我们前面学习了 docker run 以及 Docker 容器的知识,还有一些专业术语。有了这些知识,可以玩点新花样了,我们来使用 Docker 来部署一个 Web 应用!

运行静态站点

接下来带你从零来运行一个简单的静态网站。我们将从 Docker Hub 中获取 Docker 镜像,运行容器并查看这个网站,非常容易。

我们要用的镜像是一个单页面的网页,我已经将这个镜像托管在 Docker 注册中心 - prakhar1989/static-site。我们可以使用 docker run 直接下载并运行这个镜像。根据之前的描述,使用 --rm 选项会在容器退出时自动删除容器。

$ docker run --rm prakhar1989/static-site

由于本地没有这个镜像,所以客户端会先从注册中心拉取镜像,然后再运行。如果顺利的话,你会在终端看到 Nginx is running... 的一行输出。不过现在服务器已经运行了,我们如何访问网站呢?网站运行在哪个端口上?还有个问题,我如何从我的主机访问容器?先按下 Ctrl + C 停止容器。

在上面的这种情况下,客户端不会暴露任何端口,所以我们需要重新运行docker run命令来设置端口。我们在操作的时候,应该有一种办法可以在关闭终端后也可以使得容器在运行,这种方式被称为 分离模式

$ docker run -d -P --name static-site prakhar1989/static-site
e61d12292d69556eabe2a44c16cbd54486b2527e2ce4f95438e504afb7b02810

在上面的命令中,使用 -d 选项分离终端让容器在后台运行,-p 会设置端口,在这里会暴露一个随机的端口出来,最后使用 --name 来指定运行容器的名称。现在我们可以运行docker port [CONTAINER]命令来查看端口

$ docker port static-site
80/tcp -> 0.0.0.0:32769
443/tcp -> 0.0.0.0:32768

你可以在浏览器中打开 http://localhost:32769 查看。

注意:如果您使用的是docker-toolbox,那么您可能需要使用它docker-machine ip default来获取IP。

注意:如果你使用的是 docker-toolbox,那么你可能需要使用 docker-machine ip default 来获取 IP。

还可以指定暴露一个特定的端口。

$ docker run -p 8888:80 prakhar1989/static-site
Nginx is running...
静态网站

要停止分离容器(后台运行),你可以使用 docker stop [容器ID] ,也可以使用 docker stop [容器名称]来停止容器。

$ docker stop static-site
static-site

相信你也看到了这非常简单。在实际服务器上部署的话你只需要安装 Docker,然后运行上面的 Docker 命令。现在你已经了解了如何在 Docker 镜像中运行 Web 服务器,你一定想知道 - 如何创建自己的 Docker 镜像呢?我们在下面继续探讨这个问题。

Docker 镜像

在前面我们使用过镜像,下面将深入了解 Docker 镜像是什么以及如何构建自己的镜像!最后我们会使用自己的镜像在本地运行,然后将它部署在 AWS 上让别人可以访问。刺不刺激?来,跟我一起开始吧。

Docker 镜像是容器的基础。在前面的例子中,我们从注册中心中 拉取 Busybox 镜像,并基于该镜像运行了容器。要查看本地可用的镜像列表,可以使用 docker images 命令。

$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
prakhar1989/catnip latest c7ffb5626a50 2 hours ago 697.9 MB
prakhar1989/static-site latest b270625a1631 21 hours ago 133.9 MB
python 3-onbuild cf4002b2c383 5 days ago 688.8 MB
martin/docker-cleanup-volumes latest b42990daaca2 7 weeks ago 22.14 MB
ubuntu latest e9ae3c220b23 7 weeks ago 187.9 MB
busybox latest c51f86c28340 9 weeks ago 1.109 MB
hello-world latest 0a6ba66e537a 11 weeks ago 960 B

上面列出了我从注册中心拉取的镜像列表,以及我自己创建的镜像(马上你就会看到)。IMAGE IDTAG 会构成一个镜像的唯一标识。

为了简单起见,你可以认为镜像类似于一个 Git 仓库,它可以 提交 和修改内容,也可以有多个版本。如果你没有指定版本,默认会认为是latest。例如你可以拉取指定版本的 ubuntu 镜像。

$ docker pull ubuntu:12.04

想要获取最新的 Docker 镜像,可以从注册中心(比如 Docker Hub)获取,也可以自己创建镜像上传。Docker Hub 上有成千上万的镜像,你也可以在命令行使用 docker search 搜索镜像。

我们在谈镜像的时候有一个很重要的区别就是镜像子镜像的差异:

  • 基础镜像 是没有父镜像的镜像,比如像 ubuntu,busybox 或 debian 这种操作系统镜像。

  • 子镜像 是基于基础镜像构建并添加其他功能的镜像。

然后是官方和用户镜像,它们也可以是基础镜像和子镜像。

  • 官方镜像 是由 Docker 官方人员正式维护和支持的图像。在上面的镜像中,pythonubuntubusyboxhello-world 都是官方镜像。

  • 用户镜像 是由你和我这样的用(xian)户(yu)创建和共享的镜像。它们基于基础镜像构建,然后会添加一些其他功能。一般用户镜像的格式为 user/image-name

我们的第一个镜像

现在你对镜像有了一个大致的理解,可以创建一个镜像了。我们的目标是创建一个沙盒化的 Flask 应用的镜像。为了达到我们这次的成果,我已经创建了一个有趣的 Flask 程序,每次加载的时候都会随机的显示一张猫咪的 gif 图片,哪个程序员还不喜欢猫呢?使用 git 克隆这个仓库,如下所示:

$ git clone https://github.com/prakhar1989/docker-curriculum
$ cd docker-curriculum/flask-app

克隆操作在运行 docker 的计算机上,不是 docker 容器中。

下面我们使用这个程序来为它创建一个镜像。如上所述,所有用户镜像都要基于基础镜像。由于我们的应用程序是用 Python 编写的,我们要使用的基础镜像将是 Python 3。更具体地说,我们将使用 python:3-onbuild 这个版本的镜像。

可能你想问 onbuild 是什么鬼?

这些映像包含多个 ONBUILD 触发器,这是引导大多数应用所需的全部触发器。构建时会复制一个requirements.txt文件,在该文件处运行pip install,然后将当前目录复制到/usr/src/app

换句话说,onbuild版本包含自动使应用运行的部分,这些镜像将原本手动的操作自动化。这些镜像不会手动执行这些任务(或编写任务脚本),而是由你编写。我们现在拥有创建自己镜像的所有要素 - 一个功能强大的应用程序和一个基础镜像。接下来该怎么做?答案是 - 使用 Dockerfile

Dockerfile

一个 Dockerfile 是一个简单的文本文件,它包含 Docker 客户端在创建镜像时调用的命令列表。这是实现镜像创建过程自动化的一种简单方法。更重要的是,你在 Dockerfile 中编写的命令和 Linux 下几乎相同。意味着你你用学习新的语法来创建一个 dockerfile。

在你的应用程序中要包含一个 Dockerfile 文件,但由于我们第一次做,所以从头开始创建一个。首先使用你喜欢的编辑器在应用程序目录下创建一个名为 Dockerfile 的空白文件。

我们从指定基础镜像开始。使用FROM关键字:

FROM python:3-onbuild

下一步通常是编写复制文件和安装依赖的命令。好在 onbuild 版本的镜像可以解决这个问题。下一步就是指定一个要暴露的端口号,由于我们的 Flask 应用在 5000 端口上运行,所以我们要加入一行指令暴露它。

EXPOSE 5000

最后一步是编写运行应用的命令,很简单只需要加入 python ./app.py 就可以了。我们使用 CMD 命令来实现:

CMD ["python", "./app.py"]

这里的 CMD 命令主要是告诉容器在启动时运行哪个命令。有了这个,我们的Dockerfile就准备好了。这是它的样子

# our base image
FROM python:3-onbuild
# specify the port number the container should expose
EXPOSE 5000
# run the application
CMD ["python", "./app.py"]

现在我们有了Dockerfile,就可以创建我们的镜像了。使用 docker build 命令可以通过 Dockerfile 来构建一个镜像。

下面显示了运行构建后的输出。在你运行命令前(不要忘记 .),请务必将用户名替换为你自己的。这里的用户名应该和你在 Docker Hub 中注册时是相同的。如果你还没有账户,请赶紧创建!docker build命令非常简单,它可以通过 -t 指定标签的名称,最后的 . 代表一个路径,现在是当前目录。

$ docker build -t prakhar1989/catnip .
Sending build context to Docker daemon 8.704 kB
Step 1 : FROM python:3-onbuild
# Executing 3 build triggers...
Step 1 : COPY requirements.txt /usr/src/app/
---> Using cache
Step 1 : RUN pip install --no-cache-dir -r requirements.txt
---> Using cache
Step 1 : COPY . /usr/src/app
---> 1d61f639ef9e
Removing intermediate container 4de6ddf5528c
Step 2 : EXPOSE 5000
---> Running in 12cfcf6d67ee
---> f423c2f179d1
Removing intermediate container 12cfcf6d67ee
Step 3 : CMD python ./app.py
---> Running in f01401a5ace9
---> 13e87ed1fbc2
Removing intermediate container f01401a5ace9
Successfully built 13e87ed1fbc2

如果你本地没有 python:3-onbuild 镜像,客户端会先拉取镜像,然后创建自定义镜像。所以运行命令的输出可能和我的不一样。仔细看,你会注意到构建触发器已经执行。顺利的话,你的形象应该准备好了!运行docker images来查看你的镜像是否显示。

本节的最后一步是运行镜像并查看它是否真的可用(把用户名替换为你自己的)。

$ docker run -p 8888:5000 prakhar1989/catnip
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

我们刚运行的命令在容器内部开启了一个 5000 的端口,并和宿主机的 8888 端口绑定。所以你可以访问 8888 端口查看应用:

恭喜!你已成功创建第一个 Docker 镜像。

在 AWS 上运行 Docker

现在这个应用程序没法给我的小伙伴们看,我要它有何用?所以在本节目中,我们将了解如何将这个🐂* 闪闪的程序部署到云端,以便可以和我的朋友分(zhuang)享(bi)!我们将使用AWS Elastic Beanstalk只需点击几下即可启动并运行我们的应用。我们还会看到使用 Beanstalk 让我们的应用程序更容易扩展和管理!

国内的小伙伴也可以在阿里云或 Vultr 以及其他的服务器主机上尝试哦!

发布镜像

在将我们的应用程序部署到 AWS 之前,需要做的第一件事是在 AWS 可以访问的注册中心上发布我们的镜像。你可以使用任何 Docker注册中心(甚至可以托管 自己的注册中心)。现在,让我们使用 Docker Hub 发布镜像。要发布只需输入

$ docker push prakhar1989/catnip

如果这是你第一次推送镜像,客户端会提示你登录一下,输入 Docker Hub 的账号和密码就可以了。

$ docker login
Username: prakhar1989
WARNING: login credentials saved in /Users/prakhar/.docker/config.json
Login Succeeded

请记住将上面的登录名换成你自己的。镜像的格式应该以 username/image_name 发布,这个位置非常重要。

完成后,你可以在 Docker Hub 上查看镜像。例如,这是我的镜像 网址

注意:在我们继续之前,我想澄清的一件事是,为了部署到 AWS,在公共注册中心(或任何注册中心)上放置镜像并不是必须的。如果你在为下一个百万融资的独角兽公司写代码,当我没说。我们之所以公开推送镜像,是因为它跳过了一些中间配置步骤,让部署变得更简单。

现在你的镜像已经在线了,任何安装了 Docker 的人都可以通过输入 run 来把玩你的应用。

$ docker run -p 8888:5000 prakhar1989/catnip

摸摸自己的头再想想你之前是怎么部署程序的,就知道头发是怎么消失的!

Beanstalk

AWS Elastic Beanstalk(EB)是 AWS 提供的 PaaS(平台即服务)。如果你用过 Heroku、Google App Engine,你会很快熟悉它。作为开发人员,你只需告诉 EB 如何运行你的应用程序,它将负责其余的工作 - 包括扩展,监控甚至更新。2014 年 4 月,EB 增加了对运行单容器部署的支持,这是我们用来部署应用程序的方法。虽然 EB 具有非常直观的 CLI,但还需要做一些配置,为了简单起见,我们将使用 Web UI 启动应用程序。

要继续,你需要一个有余额的 AWS 账户。如果还没有,请使用信用卡填写相关信息。但不用担心,它是免费的,我们在本教程中所做的任何事情也都是免费的!让我们开始吧。

以下是步骤:

Elastic Beanstalk
  • 单击右上角的 “创建新应用程序”

  • 为你的应用程序提供一个吊炸天(但唯一)的名称,并提供(可选)描述

  • 在“ 新建环境” 中,创建一个新环境并选择 “Web服务器环境”

  • 通过选择域来填写环境信息。这个 URL 是你给朋友分享的内容,请确保它易于记忆。

  • 在基本配置部分。从预定义平台中选择 Docker

Elastic Beanstalk 环境类型
  • 现在我们需要上传我们的应用代码。但是由于我们的应用程序打包在 Docker 容器中,我们只是告诉 EB 我们的 contianer。打开 flask-app 文件夹中的 Dockerrun.aws.json 文件,然后根据镜像的名称编辑镜像。先别急,我马上解释一下这个文件的内容。完成后,单击单选按钮 “上传你自己的” 并选择这个文件。

  • 你看到的最终界面会有一些微调器提示你的环境正在设置中。首次安装通常需要大约 5 分钟。

在我们等待的时候,来快速查看Dockerrun.aws.json文件包含的内容。此文件基本上是 AWS 特定文件,它告诉 EB 有关我们的应用程序和 docker 配置的详细信息。

{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "prakhar1989/catnip",
"Update": "true"
},
"Ports": [
{
"ContainerPort": "5000"
}
],
"Logging": "/var/log/nginx"
}

这个文件一看就懂,你也可以 参考 官方文档获取更多信息。我们提供给 EB 应该使用的镜像名称以及容器应该打开的端口。

希望到现在为止,我们的实例应该准备好了。转到 EB 页面,你应该有一个绿色打勾,表示你的应用程序是存活的。

EB 部署

继续在浏览器中打开 URL,你应该看到应用程序的样子。接下来打开你的 QQ、微信、Telegram 把这个链接发送给你的基佬们,让他们也享受一下可爱的猫咪。

恭喜!你已经部署了第一个 Docker 应用!看起来好像有很多步骤,但使用 EB命令行工具,只要通过几次点击就可以模仿 Heroku 的功能!希望你会同意 Docker 消除了在云端构建和部署应用程序的许多痛苦。我建议你阅读单容器环境中的AWS 文档,以了解存在哪些功能。

在本教程的下一部分(也是最后一部分),我们将稍微提高一点,部署一个接近实际的应用程序; 具有持久后端存储层的应用程序。让我们马上开始吧!