使用 Docker 加速开发工作流

使用 Docker 加速开发工作流

在开发工作流中使用 Docker 可以极大提高生产力,它消除了"它在我机器上都可以运行"这类典型的错误,在不同的机器上运行也只需要一个 Docker 守护进程,而不需要其他组件。

什么是 Docker

Docker 是一个可以运行容器平台,为了运行这些容器,Docker 使用了操作系统级的虚拟化技术,你可以把容器看作是一个轻量级版本的虚拟机。

你在 Docker 平台上运行的所有容器都是相互隔离的。要启动一个容器,你需要一个 Docker 镜像,这个镜像是你的容器的模板,你可以从 Docker Hub 中获取已经预定义的镜像,或者通过编写 Dockerfile 文件来配置自己的镜像。

为什么要 Docker 化开发工作流

上面我已经提到了在你的开发环境中使用 Docker 的好处。这是一个事实,它摆脱了典型的”它在我的机器上可以工作”的问题,除此之外,还有其他一些好处:

  • 让团队成员之间的开发工作流程更加标准化
  • 如果你也使用 Docker 进行部署,则减少了针对生产环境的 bug(生产和开发之间的配置可以很相似)。

开始

首先创建一个新的文件夹,将我们的项目放在其中,然后我创建一个 Dockerfile 文件:

$ mkdir node-docker && cd node-docker
$ touch Dockerfile

我们将在 Dockerfile 中配置一个 express 应用,内容如下所示:

FROM node:latest

WORKDIR /usr/src/app
COPY package*.json ./
ENV PORT 5000

RUN npm cache clear --force && npm install

FROM 是告诉 Docker 从 Docker Hub 获取一个名为 node(版本:latest)的镜像。

WORKDIR 设置所有即将执行的命令的目录。

COPY 的作用就是复制文件到 WORDIR 中来。

ENV 在容器中设置一个环境变量,名称为 PORT,值为 5000

RUN 执行我们传递进来的命令,在这里会清除 npm 缓存,然后安装package.json 中的所有依赖项。

ENTRYPOINT 会在 Docker 容器启动的时候执行你在这里插入的命令。

现在,我们已经准备好了我们的 Dockerfile 文件,我们需要一个简单的 express 应用,可以在容器内运行。为此,我们创建两个新的文件。

$ touch server.js package.json

package.json 文件中新增两个依赖关系,一个是 express,另外一个是nodemon。

{
  "name": "node-docker",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "nodemon server.js"
  },
  "author": "Jakob Klamser",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.4"
  }
}

express 应用在点击主页面时,返回一个简单的 HTML。对应的 server.js 内容如下所示。

const express = require('express');

const app = express();

const PORT = process.env.PORT || 5000;

app.get('/', (req, res) => {
  res.send(`
    <h1>Express + Docker</h1>
    <span>This projects runs inside a Docker container</span>
  `);
});

app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}!`);
});

在我们开始将 MongoDB 容器与我们的 express 容器一起设置之前,我们希望将一些文件从运行的容器中排除,这个时候就可以使用 .dockerignore 来进行配置,.dockerignore 文件的语法与 .gitignore 文件完全相同。

# Git
.git
.gitignore

# Docker
Dockerfile
docker-compose.yml

# NPM dependencies
node_modules

最后同样重要的是我们需要定义一个 docker-compose.yml。这个文件将包含两个不同容器,同时运行 express 应用和 MongoDB,先创建这个文件。

$ touch docker-compose.yml

然后配置如下所示的该文件内容:

version: '3'
services:
  api:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - mongo
    volumes:
      - "./:/usr/src/app"
      - "reserved:/usr/src/app/node_modules"
  mongo:
    image: "mongo" 
    ports:
      - "27017:27017"
volumes:
  reserved:

version: 定义了我们要使用的 docker-compose 的版本,版本3和版本2之间有不少差异,所以在选择版本时要注意!

services: 这是定义服务的部分,这里我们定义了 express api 和 mongo 两个服务。

build & image: build 告诉 Docker 从 Dockerfile 中构建一个镜像。在我们的例子中,我们希望它使用当前目录下的 Dockerfile,这就是为什么我们把.作为一个参数,因为这定义了当前的目录。image 告诉 Docker 从 Docker Hub 中拉取一个已经存在的镜像。

ports & volumes: 如 ports 的名字所示,我们在这里定义端口,冒号是一个映射操作符,我们将容器的5000端口映射到主机系统的5000端口,在本例中,我们就可以在容器之外访问应用程序。同样的道理也适用于 MongoDB 的端口映射。volumes 也做类似的事情,我们将本地代码的目录映射到容器的 WORKDIR 中,这样一来,如果我们修改了源代码中的任何内容,容器就会立即做出反应。

reserved: 这是一个特殊的卷,如果本地的 node_modules 文件夹存在,则不会覆盖容器内部的 node_modules 文件夹。

然后现在我们可以运行如下所示的命令,Docker 将根据我们的 Dockerfile 文件配置创建一个镜像,然后同时运行两个容器(api和mongo)。

$ docker-compose up

如果你想停止这些容器,可以执行下面的命令:

$ docker-compose down

总结

这里我们只是介绍的一个简单的 Docker 开发环境配置,当然也可以很容易地进行扩展。如果你想改变数据库或添加一个 Nginx 来渲染你的前端,只需在 docker-compose.yml 中添加一个新的服务或改变一个现有的服务即可。当然同样地我们也可以很容易地对 .Net Core、Java 或者 Golang 应用进行 Docker 容器化。

原文链接:https://klamser.dev/dockerize-your-development-environment-for-nodejs