0

I have a simple express.js server that, for some operations, needs to spawn some docker containers. I want to dockerize this NodeJs server using a Dockerfile, so that it will spawn docker containers inside a docker container. My Dockerfile currently looks like this:

FROM docker:23.0.6-dind-alpine3.18


RUN apk update
RUN apk add --no-cache nodejs 
RUN apk add --no-cache npm
RUN apk add --no-cache iptables bash
RUN apk add --no-cache --upgrade bash

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install --force --verbose


COPY . .


RUN npm run completeBuild


EXPOSE 4000

CMD ["npm","start"]

I build it with:

docker build -t trydocker .

And I run it with:

docker run --privileged -it -p 4000:4000 -p 2375:2375 -p 2376:2376 trydocker 

The server process gets spawned correctly and I can access my express server via localhost:4000. But when I try to call docker pull from inside the container I get an error that says that my docker daemon is not running.

Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running?

Instead, if I remove the last directive CMD ["npm","start"] from the Dockerfile, the docker daemon starts correctly, but my server is not online!

Is there a way to have both running?

I have looked at some related issues, like this one, but it did not help.

  • I have also tried to use as a command `CMD ["/bin/bash", "-c", "dockerd & npm start"]`, but it did not work... – NicolaLovo Aug 07 '23 at 15:01
  • While [this answer](https://stackoverflow.com/a/48242863) mostly talks about why not to do it, the [first] documentation page it links does have examples on how to do this with a custom script or (what I'd use) a [process manager](https://docs.docker.com/config/containers/multi-service_container/#use-a-process-manager) like [Supervisord](https://github.com/Supervisor/supervisor). – RickN Aug 07 '23 at 15:19
  • I presume it's fine if these spawned containers run on the same host as the container that's doing the spawning? docker _from_ docker as opposed to docker _in_ docker? If so, then you have to mount the host's docker socket into the container that will do the spawning. I'd explain that in full, but it's called _sibling containers_ (you can search). Also, does https://stackoverflow.com/questions/27879713/is-it-ok-to-run-docker-from-inside-docker explain it well enough already? – Wyck Aug 07 '23 at 15:20

1 Answers1

0

A Docker container only runs one process. Your image runs the Node application (its CMD runs npm start) but because of this it does not run the nested Docker daemon.

If you really need Docker-in-Docker (do you?) then you need to launch the nested Docker daemon as a sibling container. This Docker Compose setup could do it, for example:

version: '3.8'
services:
  docker:
    image: docker:24-dind
    privileged: true
    environment:
      DOCKER_TLS_CERTDIR: /certs
    volumes:
      - docker-certs-ca:/certs/ca
      - docker-certs-client:/certs/client
      - docker-data:/var/lib/docker
  app:
    build: .
    environment:
      DOCKER_HOST: tcp://docker:2376
      DOCKER_TLS_CERTDIR: /certs
    volumes:
      - docker-certs-client:/certs/client
volumes:
  docker-certs-ca:
  docker-certs-client:
  docker-data:

The Docker Hub docker image page has some more details on the settings.

Your application image itself doesn't embed the Docker daemon and so it can use a more normal node image base

FROM node:lts
# exactly the last half of your existing Dockerfile
WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install --force --verbose

COPY . .
RUN npm run completeBuild

EXPOSE 4000
CMD ["npm","start"]

Using Docker-in-Docker is usually discouraged, since there are many complexities around which Docker daemon you're actually using and the DinD container must run in privileged mode. With this same Dockerfile, you could remove the docker container and associated volumes, and instead bind-mount the host's /var/lib/docker.sock socket file into the container on the same path, which would allow you to launch containers on the host's Docker daemon.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • Thank you for you answer, I tried your setup but it did not work. My NodeJs app needs to access docker to spawn some containers and with this setup if, for example I say in my nodeJs server `exec("docker pull python:lastest")`, i get the error "/bin/sh: 1: docker: not found". I don't know if I missed something – NicolaLovo Aug 09 '23 at 07:25
  • You should use a Docker SDK like [dockerode](https://github.com/apocas/dockerode); that would be listed in your `package.json` file like any other library dependency. If you really need the `docker` CLI tool you'd need to install it, probably using `RUN apt-get install`. This will be in the same package but is logically separate from the Docker daemon, and you don't need to start the daemon since it will be in a separate container (or on the host, if you bind-mount its socket). – David Maze Aug 09 '23 at 09:35
  • Used dockerode as you suggested: `const docker = new Docker({}); docker.pull("wetambara/wtpythonimage:0.0.0.DEV",...`. But get a new error from dockerode: `http: TLS handshake error from 172.23.0.3:44772: EOF Error: unable to verify the first certificate` `code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'` – NicolaLovo Aug 09 '23 at 12:37