写给前端开发者的 Docker 指南

Akanksha Sharma 原作,授权 New Frontend 翻译。

为什么用 docker?

很久很久以前,业务上需要开一个新应用的时候,运维团队会去买一台新服务器。这时运维团队并不清楚新应用的性能需求,很大程度上靠猜,会造成资金和资源的浪费。

虚拟机让我们可以在服务器上运行多个应用,但是每个虚拟机都需要完整的操作系统才能运行。每个操作系统都会占用 CPU 和 RAM 资源,需要购买授权,需要更新补丁,这些都意味着成本和麻烦。

Google 很早以前就开始使用容器模型,以克服虚拟机模型的不足。大致上容器模型意味着同一台宿主机上可以运行多个容器,减少了 CPU 和 RAM 占用。

但是,这对我们开发者有什么帮助?

它可以保证所有开发者、所有服务器(测试、预备、生产)的环境是一致的。

任何人都可以快捷地配置项目,无需折腾配置文件、安装库和依赖。

简单来说,docker 是一个让我们能够以容器模型开发、部署、运行应用的平台。

让我们回头说下容器系统到底是什么样的,和虚拟机有什么不一样。

虚拟机和 docker 的区别

如你所见,和虚拟机不同,容器能更好地共享宿主机的资源。

对 docker 大概有所了解后,让我们深入一点。

怎么用 docker?

首先需要熟悉一些术语。

docker 镜像和容器

docker 镜像是一个可执行文件,其中包含裁剪过的操作系统,以及运行应用所需的所有库和配置。镜像分为多层,每层是一个单独的对象。docker 镜像基于 Dockerfile 创建(后面会介绍)。

docker 容器是运行 docker 镜像的实例。可能有许多容器运行同一个 docker 镜像。

容器化简单的 Node.js 应用

我们将尝试容器化一个非常简单的 node.js 应用,创建相应的镜像:

Node.js 应用

先创建一个文件夹 my-node-app

mkdir my-node-app
cd my-node-app

来写一个简单的 node 服务 index.js

var express = require('express')
var app = express()
app.get('/', function (req, res) {  
 res.send('Hello World!')  
})
app.listen(8081, function () {  
  console.log('应用监听 8081 端口')  
})

创建相应的 package.json 文件:

{
    "name": "helloworld",  
    "version": "1.0.0",  
    "description": "容器化 node.js 应用",  
    "main": "index.js",  
    "author": "",  
    "license": "ISC",  
    "dependencies": {  
      "express": "^4.16.4"  
    }
}

到目前为止,我们并没有在宿主机上安装 express 或者 npm,因为 Dockerfile 会处理所有依赖、库、配置。

Dockerfile

让我们在 my-node-app 文件夹中创建 Dockerfile(注意这个文件没有扩展名):

FROM node:8
WORKDIR /app  
COPY package.json /app  
RUN npm install  
COPY . /app  
EXPOSE 8081  
CMD node index.js

现在解释一下上面这些是什么意思:

FROM node:8 从 docker hub 拉取 node.js 的 docker 镜像(https://hub.docker.com/_/node/

WORKDIR /app 设置镜像的工作目录,下面的所有指令(COPYRUNCMD 等)都在这一目录下执行。

COPY package.json /app 从宿主机复制 package.json 文件至镜像中的 /app 文件夹。

RUN npm install 在镜像中运行这一命令,为应用安装依赖(node_modules)。

COPY . /app 从 my-node-app 目录复制文件至镜像的 /app 目录。

EXPOSE 8081 开放容器的 8081 端口。为什么是 8081?因为 index.js 里写了监听 8081 端口。基于 node.js 镜像创建的容器默认关闭所有端口。

构建 docker 镜像

使用如下命令在宿主机上构建一个名为 hello-world 的镜像:

docker build -t hello-world .

输出如下:

Sending build context to Docker daemon  4.096kB  
Step 1/7 : FROM node:8  
  ---> 4f01e5319662  
Step 2/7 : WORKDIR /app  
  ---> Using cache  
  ---> 5c173b2c7b76  
Step 3/7 : COPY package.json /app  
  ---> Using cache  
  ---> ceb27a57f18e  
Step 4/7 : RUN npm install  
  ---> Using cache  
  ---> c1baaf16812a  
Step 5/7 : COPY . /app  
  ---> 4a770927e8e8  
Step 6/7 : EXPOSE 8081  
  ---> Running in 2b3f11daff5e  
Removing intermediate container 2b3f11daff5e  
  ---> 81a7ce14340a  
Step 7/7 : CMD node index.js  
  ---> Running in 3791dd7f5149  
Removing intermediate container 3791dd7f5149  
  ---> c80301fa07b2  
Successfully built c80301fa07b2  
Successfully tagged hello-world:latest

如你所见,docker 运行 Dockerfile 中的指令并输出 docker 镜像。第一次运行时需要等一段时间,再次运行会利用缓存以加速构建(上面的输出即为利用缓存时的输出)。

运行以下命令测试镜像是否存在:

docker images

以上命令会输出宿主机上的镜像列表,就像这样:

REPOSITORY    TAG      IMAGE ID      CREATED         SIZE  
hello-world   latest   c80301fa07b2  22 minutes ago  896MB

运行 docker 容器

构建好镜像后,我们可以基于镜像运行容器:

docker container run -p 4000:8081  hello-world

其中,-p 4000:8081 将宿主机的 4000 端口绑定到容器的 8081 端口(我们之前在 Dockerfile 中使用 EXPOSE 指令开放了这个端口)。

输出类似这样:

应用监听 8081 端口

以下命令可以进入容器,在容器中运行 bash:

docker exec -ti <容器 id> /bin/bash

以下命令则可以查看容器是否在运行:

docker ps

输出:

CONTAINER ID    IMAGE        COMMAND                  CREATED    
`<容器 id>`  hello-world  "/bin/sh -c 'node in…"   11 seconds ago

STATUS              PORTS                    NAMES  
Up 11 seconds       0.0.0.0:4000->8081/tcp   some-random-name

在浏览器中打开 http://localhost:4000/ 可以看到如下页面:

Hello World!

乌拉,初次容器化应用成功!

最新文章

评论

正在加载评论