Node(Express&Mongo)应用程序的生产与开发Docker设置

10
我正在尝试将一个Node应用程序转换为使用Docker,但遇到了一些问题/疑问,我无法回答。
但是为了简单起见,我包含了一些非常基本的示例文件,以保持问题的准确性。实际上,下面的示例仅链接到一个Mongo容器,但不在代码中使用它,以使其更加简单。
主要是,在本地(OS X)开发和生产构建中,需要哪些Dockerfile和docker-compose.yml设置才能成功使用Node + Express + Mongo应用程序的Docker呢?
FROM node:6.3.0

# Create new user to avoid using root - is this correct practise?
RUN useradd --user-group --create-home --shell /bin/false app

COPY package.json /home/app/code/
RUN chown -R app:app /home/app/*

USER app
WORKDIR /home/app/code
# Should this even be set here or use docker-compose instead?
# And should there be:
#  - docker-compose.yml setting it to production by default
#  - docker-compose.dev.yml setting it to production?
# Or reverse it? (docker-compose.prod.yml instead with default being development?)
# Commenting below out or it will always run as production
#ENV NODE_ENV production
RUN npm install

USER root
COPY . /home/app/code
# Running chown to ensure new 'app' user owns files
RUN chown -R app:app /home/app/*
USER app

EXPOSE 3000

# What CMD should be here to ensure development versus production is simple?
# Development - Restart server and refresh browser on file changes
# Production  - Ensure uptime. 
CMD ["npm", "start"]

docker-compose.yml

version: "2"
services:
  web:
    build: .
    # I would normally use a .env file but for this example will set explicitly
    # env_file: .env
    environment:
      - NODE_ENV=production
    volumes:
      - ./:/home/app/code
      - /home/app/code/node_modules
    ports:
      - "3000:3000"
    links:
      - mongo
  mongo:
    image: mongo
    ports:
      - "27017:27017"

docker-compose.dev.yml

version: "2"
services:
  web:
    # I would normally use a .env file but for this example will set explicitly
    # env_file: .env
    environment:
      - NODE_ENV=development

package.json

{
  "name": "docker-node-test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app.js"
  },
  "dependencies": {
    "express": "^4.14.0",
    "mongoose": "^4.6.1",
    "nodemon": "^1.10.2"
  },
  "devDependencies": {
    "mocha": "^3.0.2"
  }
}

1. 如何处理不同的NODE_ENV(dev, production, staging)?

这是我的主要问题和难题。

在我使用的示例中,NODE_ENV在Dockerfile中设置为production,并且有两个docker-compose文件:

  • docker-compose.yml将默认值包括NODE_ENV设置为production
  • docker-compose.dev.yml覆盖了NODE_ENV并将其设置为development

1.1.建议反转顺序,将开发设置作为默认设置,而使用docker-compose.prod.yml进行覆盖?

1.2.如何处理node_modules目录?

我真的不确定如何在本地开发需求和运行生产环境之间完全处理node_modules目录。(也许我有一个基本的误解?)


编辑:

我添加了一个.dockerignore文件,并包含node_modules目录作为一行。这确保在复制等过程中忽略了node_modules目录。

然后我编辑了docker-compose.yml以将node_modules作为卷包含。

volumes:
  - ./:/home/app/code
  - /home/app/code/node_modules

我还在本问题开头的完整docker-compose.yml文件中添加了上述更改,以确保在进行本地开发时能够包括dev-dependencies。当运行docker-compose up时,会在Docker容器内拉取仅供生产使用的节点模块(因为默认的docker-compose.yml设置为NODE_ENV=production)。但是,在运行docker-compose -f docker-compose.yml build时似乎没有考虑到2个docker-compose文件内设置的NODE_ENV:/,我希望它发送NODE_ENV = production,但重新安装了所有的node_modules(包括dev-dependencies)。
我们是否应该使用2个Dockerfiles?(Dockerfile用于生产环境;Dockerfile.dev用于本地开发)
(我觉得这是我在设置中缺少的基本逻辑/知识)

2. Nodemon vs PM2

如何在本地开发机器上使用nodemon,但在生产构建上使用PM2?

3. 是否应该在docker容器内创建用户,然后将该用户设置为在Dockerfile中使用?

默认情况下使用root用户,但我没有看到许多文章谈论在容器内创建专用用户。我的安全做法是正确的吗?在非Docker构建中,我肯定不会使用root运行应用程序。

感谢阅读,任何帮助和建议都将不胜感激 :)
2个回答

1

我可以分享我的经验,不代表这是最好的解决方案。

  1. I have Dockerfile and dockerfile.dev. In dockerfile.dev I install nodemon and run the app with nodemon, the NODE_ENV doesn't seem to have any impact. As for users you should not use root for security reasons. My dev version:

     FROM node:16.14.0-alpine3.15
    
     ENV NODE_ENV=development
    
     # install missing libs and python3
     RUN apk update && apk add -U unzip zip curl && rm -rf 
     /var/cache/apk/* && npm i node-gyp@8.4.1 nodemon@2.0.15 -g
    
     WORKDIR /node
    
     COPY package.json package-lock.json ./
    
    
     RUN mkdir /app && chown -R node:node .
     USER node
    
     RUN npm install && npm cache clean --force
    
     WORKDIR /node/app
    
     COPY --chown=node:node . .
    
    # local development
     CMD ["nodemon", "server.js" ]
    

在生产环境中,我使用Node运行应用程序:

FROM node:16.14.0-alpine

ENV NODE_ENV=production

# install missing libs and python3
RUN apk update && apk add -U unzip zip curl && rm -rf /var/cache/apk/* \
&& npm i node-gyp@8.4.1 -g

WORKDIR /node

COPY package.json package-lock.json ./

RUN mkdir /app && chown -R node:node .
USER node

RUN npm install && npm cache clean --force

WORKDIR /node/app

COPY --chown=node:node . .

CMD ["node", "server.js" ]

我有两个不同版本的docker-compose文件。在docker-compose.dev.yml文件中,我将dockerfile设置为dockerfile.dev:

app:
    depends_on:
      mongodb:
        condition: service_healthy
    build:
      context: .
      dockerfile: Dockerfile.dev
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:5000" ]
      interval: 180s
      timeout: 10s
      retries: 5
    restart: always
    env_file: ./.env
    ports:
      - "5000:5000"
    environment:
       ...
    volumes:
      - /node/app/node_modules

在生产的docker-compose.yml中,有一个指向 Dockerfile 的dockerfile设置。

  1. Nodemon与PM2。在将应用程序docker化之前,我使用过pm2。我无法看到在docker中使用它的任何好处,restart: always 可以处理错误时的重启。你应该使用 restart: unless_stopped,但我更喜欢 always 选项。最初我也在生产环境中使用nodemon,让应用程序反映卷文件变化,但我放弃了这个选择,因为重启效果不佳(它一直等待某些代码变化)。

  2. 用户:你可以在我的示例中看到。我学习了关于docker + nodejs的课程,并推荐设置非root用户,所以我这样做并没有遇到问题。

希望我解释得够清楚并能对你有所帮助。祝你好运。


0
    1. 不管是哪个,都没关系,我更喜欢先有开发细节,然后再用生产细节覆盖。

    2. 我不会将它们提交到我的仓库中,然后在我的dockerfile中运行“npm install”。

  1. 您可以在dockerfile中设置规则,根据构建设置来构建哪一个。

  2. 通常情况下,通过root构建所有内容,并通过root运行主程序。您可以设置其他用户,但对于大多数用途而言,这并不需要,因为docker容器的理念是将每个进程隔离在单独的docker容器中。


1
我可能有点跑题,但根据你的回答,我理解在每个环境中都有两个单独的Docker文件是一种已知的做法?我曾经以为Docker的主要目的之一是拥有一个一致的环境,在任何环境(本地或生产)中都能完全相同地运行。有点像单点故障,只是好的那种方式 :) 我的印象错了吗? - Ben Yitzhaki
@BenYitzhaki 我听说只有一个Docker文件并在组合中进行所有更改是正确的,但我可能是错的。 - Kunok
@Kunok 我同意。自从我提出这个问题(两年前),我一直在使用环境变量和Docker参数来实现这种行为。对我来说效果很好。 - Ben Yitzhaki
@BenYitzhaki 谢谢确认!我刚开始使用它,正在尝试弄清楚它们如何一起工作。您是否有使用环境变量的公共配置示例? - Kunok
@Kunok 抱歉,但我真的没有公开的例子,我认为这也不太可能,因为它取决于您如何部署/构建等(也许我会写一篇关于我如何理解此问题的文章)。在我看来,环境变量应该由 CI(Jenkins/Travis 等)处理。关于环境变量的一个好提示是,在使用 Docker 时可以使用“.env”文件更轻松地处理环境变量 https://docs.docker.com/compose/env-file/ - Ben Yitzhaki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接