文章目录
参考:Docker 101 Tutorial、Play with Docker
Play with Docker(PWD)
Play with Docker is an interactive playground that allows you to run Docker commands on a linux terminal, no downloads required.
- Log into https://labs.play-with-docker.com/ to access your PWD terminal
- Type the following command in your PWD terminal:
docker run -dp 80:80 docker/getting-started:pwd
- Wait for it to start the container and click the port 80 badge
- Have fun!
1. Getting Started
The command you just ran
我们已经启动了本教程的容器(container)!我们先来解释一下刚才运行的命令:
docker run -d -p 80:80 docker/getting-started:pwd
你会注意到使用了一些参数。以下是关于它们的意义:
-d
- 在后台运行容器,这句命令不会占用终端,你可以在终端继续使用别的命令-p 80:80
- 将主机的端口 80 映射到容器中的端口 80docker/getting-started:pwd
- 这个教程的 docker 镜像pwd
- Play With Docker
Pro tip
可以组合使用单字符标志来缩短完整命令。例如,上面的命令可以写成:
docker run -dp 80:80 docker/getting-started:pwd
What is a container?
既然已经运行了容器,那么什么是容器?
简单地说,容器只是您机器上与主机上所有其他进程隔离的另一个进程。这种隔离利用了内核名称空间和 cgroups( kernel namespaces and cgroups ),这些特性在 Linux 中已经存在很长时间了。Docker 致力于使这些功能易于使用。
What is a container image?
在运行容器时,它使用一个独立的文件系统。此自定义文件系统由容器映像(container image)提供。由于映像包含容器的文件系统,因此它必须包含运行应用程序所需的所有内容—所有依赖项、配置、脚本、二进制文件等。映像还包含容器的其他配置,例如环境变量、要运行的默认命令和其他元数据。
Info
如果你熟悉 chroot,可以将容器看作 chroot 的扩展版本。文件系统只是来自映像。
2. Our Application
在本教程的其余部分中,我们将使用在 NodeJS 中运行的简单 todo 列表管理器。如果你不熟悉 Node,别担心!不需要真正的 JavaScript 经验!
Getting our App into PWD
在运行应用程序之前,我们需要将应用程序源代码放到 Play with Docker 环境中。对于真正的项目,可以克隆 GitHub 仓库。但是,在本例中,我们将上传一个 ZIP 文件。
- 下载 zip 并将其上传到 Play with Docker。提示,您可以将 zip(或任何其他文件)拖放到 PWD 的终端上。
- 在 PWD 终端中,解压缩 zip 文件。
unzip app.zip
- 将当前工作目录更改为新解压出来的 app 文件夹。
cd app/
- 在这个目录中,应该能看到一个简单的基于 NodeJS 的应用程序。
ls package.json spec src yarn.lock
Building the App’s Container Image
为了构建应用程序,我们需要使用 Dockerfile
。Dockerfile 只是用于创建容器映像的基于文本的指令脚本。如果你以前创建过 Dockerfile,你可能会在下面的 Dockerfile 中看到一些缺陷。别担心!我们来检查一下。
-
创建一个名为 Dockerfile 的文件,包含以下内容。
FROM node:10-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "/app/src/index.js"]
-
使用
docker Build
命令构建容器映像。docker build -t docker-101 .
该命令使用 Dockerfile 来构建新的容器映像。您可能已经注意到下载了许多“图层”。这是因为我们指示构建器要从
node:10-alpine
映像开始。但是,由于我们的计算机上没有该映像,因此需要下载该映像。之后,我们复制了应用程序,并使用
yarn
安装了应用程序的依赖项。CMD
指令指定从该映像启动容器时要运行的默认命令。
Starting an App Container
现在我们有了一个映像,让我们运行我们的 Nodejs 应用程序!为此,我们将使用 docker run
命令(还记得开头的命令吗?)。
-
使用docker run命令启动容器:
docker run -dp 3000:3000 docker-101
还记得
-d
和-p
标志吗?我们正在 “隔离” 模式下运行新容器(在后台),并讲将主机端口 3000 映射到容器端口 3000 。 -
点击 PWD 界面顶部的
OPEN PORT 3000
打开应用程序。一旦打开,你应该看到一个空的 todo 列表!
-
继续,你可以添加一个或两个项目,然后看它能否按预期工作。
此时,你有一个正在运行的 todo 列表管理器,其中包含一些由你构建的项目!现在,让我们做一些更改,学习如何管理容器。
Recap
在这一小节中,我们学习了有关构建容器映像的基础知识,并创建了一个 Dockerfile 来完成此操作。构建图像后,我们启动了容器并看到了正在运行的应用程序!
接下来,我们将对应用程序进行修改,并学习如何使用新映像来更新正在运行的应用程序。在此过程中,我们将学习其他一些有用的命令。
3. Updating our App
作为一项小功能需求,产品团队已要求我们在没有任何 todo 列表项时更改 “空文本框” 。他们希望将其(New Item)转换为以下内容:
You have no todo items yet! Add one above!
很简单吧?让我们改变一下。
Updating our Source Code
- 在
~/app/src/static/js/app.js
文件中,更新第 56 行以使用新的空文本。(在 PWD 中编辑文件的按钮使用教程)- <p className="text-center">No items yet! Add one above!</p> + <p className="text-center">You have no todo items yet! Add one above!</p>
- 让我们使用
docker build
的相同命令来构建映像的更新版本。docker build -t docker-101 .
- 构建完成后重新
run
来更新容器。docker run -dp 3000:3000 docker-101
Uh oh! 你可能看到这样的错误(ID不同):
docker: Error response from daemon: driver failed programming external connectivity on endpoint laughing_burnell
(bb242b2ca4d67eba76e79474fb36bb5125708ebdabd7f45c8eaf16caaabde9dd): Bind for 0.0.0.0:3000 failed: port is already allocated.
所以发生了什么事?其实是我们无法启动新容器,因为旧容器仍在运行。
之所以出现此问题,是因为该容器正在使用主机的端口 3000,并且只有一个进程(包括容器)可以侦听特定的端口。要解决此问题,我们需要删除旧的容器。
Replacing our Old Container
要移除容器,首先需要停止它。然后再移除。
- 使用
docker ps
命令获取容器的ID。docker ps
- 使用
docker stop
命令停止容器。# 在 <the-container-id> 中填入 docker ps 看到的占用了 3000 端口的容器的 id docker stop <the-container-id>
- 容器停止后,可以使用
docker rm
命令将其删除。docker rm <the-container-id>
- 现在,启动更新后的应用程序。
docker run -dp 3000:3000 docker-101
- 打开应用程序,您应该会看到更新后的帮助文本!
Pro tip
你可以通过在docker rm
命令中添加 “ force” 标志来在单个命令中停止和删除容器。例如:docker rm -f <容器标识符>
Recap
虽然我们能够构建更新,但是您可能会注意到两件事:
- 我们的待办事项列表中的所有现有项都不存在了!这不是一个很好的应用程序!我们稍后再谈。
- 如此小的更改涉及很多步骤。在接下来的部分中,我们将讨论如何在每次进行更改时如何查看代码更新而无需重建和启动新容器。
在讨论持久性之前,我们将快速了解如何与其他人共享这些映像。
4. Sharing our App
现在我们已经建立了映像,如果我们要共享 Docker 映像,必须使用 Docker 注册表。默认的注册表是 Docker Hub,这也是我们使用过的所有映像的来源。
Create a Repo
要推送映像,我们首先需要在 Docker Hub 上创建一个仓库。
- 转到 Docker Hub 并登录
- 点击 Create Repository 按钮
- 以
101-todo-app
命名仓库,确保这个仓库的权限是Public
- 点击 Create 按钮
如果你查看页面的右侧,会看到一个名为 Docker commands 的部分。这给出了一个示例命令,我们需要运行该命令才能推送到我们创建的仓库。
Pushing our Image
-
回到 PWD 实例中,尝试运行该命令。你应该得到如下错误:
$
docker push dockersamples/101-todo-app
The push refers to repository [docker.io/dockersamples/101-todo-app]
An image does not exist locally with the tag: dockersamples/101-todo-app为什么失败了?push 命令正在查找名为 dockersamples/101 todo app 的映像,但没有找到。如果运行
docker image ls
,也不会看到。要解决此问题,我们需要 “标记” 我们的映像,这基本上意味着给它起另一个名字。
-
使用命令
docker login -u YOUR-USER-NAME
登录到 Docker Hub。 -
使用
docker tag
命令为docker-101
映像赋予新名称。一定要把YOUR-USER-NAME
换成你的 Docker ID。docker tag docker-101 YOUR-USER-NAME/101-todo-app
-
现在,再试一次您的 push 命令。如果要从 Docker Hub 复制值,则可以删除
tagname
部分,因为我们没有在映像名称中添加标记。docker push YOUR-USER-NAME/101-todo-app
Running our Image on a New Instance
现在,我们的映像已构建并推送到 Docker Hub 注册表中,让我们尝试在从未见过此容器的实例上运行我们的应用程序!
- 返回 PWD 中,单击 Add New Instance 以创建一个新实例。
- 在新实例中,启动我们刚 push 的应用程序。
你应该看到该映像被下载并最终启动!docker run -dp 3000:3000 YOUR-USER-NAME/101-todo-app
- 点击出现的 3000 端口,你应该会看到带有修改的应用程序!Hooray!
Recap
在本节中,我们学习了如何通过将映像推送到 Docker Hub 注册表来实现共享。然后我们转到一个全新的实例,并能够下载并运行新推送的映像。这在 CI pipeline 中非常常见,pipeline 将创建映像并将其推送到注册表,然后生产环境可以下载并使用映像的最新版本。
现在我们已经弄清楚了,让我们回到上一节末尾我们注意到的内容。我们注意到当我们重新启动应用程序时,我们丢失了所有的待办事项列表项。这显然不是一个很好的用户体验,所以让我们学习如何在重启期间持久化数据!