Search code examples
node.jsdockerdocker-composedockerfiledocker-swarm

Unable to get docker swarm mode working with nodejs


I'm trying to get a basic nodeJs and postgres system up and running but for some reason the node container keeps receiving a SIGTERM signal and shutting down only to be started back up due to the restart policy and then shutdown again. The cycle goes on and on.

What am I missing here? I ran the same code in non-swarm mode and it worked fine, the container was healthy and stayed up. One more thing I did notice during swarm mode is that inspite of asking docker to keep 1 replica, the docker stack services service_name always returns 0/1 replicas

Posting my dockerfile and docker-compose.yml file here

# BASE stage
FROM node:14-alpine as base
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY package.json ./
COPY yarn.lock ./
RUN yarn install --frozen-lockfile --prod

FROM node:14-alpine
ENV NODE_ENV development
ENV VERSION V1
WORKDIR /usr/src/app
RUN apk --no-cache add curl
COPY src src/
# Other copy commands
COPY --from=base /usr/src/app/node_modules /usr/src/app/node_modules

# check every 5s to ensure this service returns HTTP 200
HEALTHCHECK --interval=5s --timeout=3s --start-period=10s --retries=3 \ 
    CMD curl -fs http://localhost/health

ENTRYPOINT [ "node", "src/index.js" ] 
version: "3.7"
services:
  api:
    image: demo/hobby:v1
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 5
        window: 120s
      rollback_config:
        parallelism: 1
        delay: 20s
        order: start-first
      update_config:
        parallelism: 1
        delay: 1s
        failure_action: rollback
        order: start-first
    env_file:
      - ./.env
    ports:
      - target: 9200
        published: 80
        mode: host
    networks:
      - verse

  postgres:
    image: "postgres:12.3-alpine"
    container_name: "test-db-dev"
    networks:
      - verse
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_USER=${DB_USER}
    expose:
      - "5432"
    ports:
      - "5432:5432"
    restart: "unless-stopped"

networks:
  verse:
    driver: overlay
    external: false

Solution

  • I assume that your container is being killed every 30 seconds. If this is true, then this is the cause:

    The HEALTHCHECK is trying to curl http://localhost/health (defaults to port 80).
    Although you expose the app to the host on port 80, the HEALTHCHECK is performed from the container perspective, where no service is listening on that port.

    Assuming that your node app is listening on port 9200 and that a GET performed on /health returns status 200, the Dockerfile should be built like this:

    [...]
    
    HEALTHCHECK --interval=5s --timeout=3s --start-period=10s --retries=3 \ 
       CMD curl -fs http://localhost:9200/health
    
    [...]