33

For clarification, when I say base image, I mean the parent image that has all the common configurations, so that the children based on it don't need to download the dependencies individually.

From my understanding, docker-compose.yml files are the run-time configurations, while Dockerfiles are the build-time configurations. However, there is a build option using docker-compose, and I was wondering how I could use this to build a base image.

As of right now, I use a shellscript that runs other shellscripts. One builds all my images, from a base image that it also creates. The other runs them as containers with the necessary configurations. However, the base image is never ran as a container.

Currently, the shellscript I hope to change into a docker-compose file, looks like so:

echo "Creating docker network net1"
docker network create net1

echo "Running api as a container with port 5000 exposed on net1"
docker run --name api_cntr --net net1 -d -p 5000:5000 api_img

echo "Running redis service with port 6379 exposed on net1"
docker run --name message_service --net net1 -p 6379:6379 -d redis

echo "Running celery worker on net1"
docker run --name celery_worker1 --net net1 -d celery_worker_img

echo "Running flower HUD on net1 with port 5555 exposed"
docker run --name flower_hud --net net1 -d -p 5555:5555 flower_hud_img

The shellscript that makes the images, is as follows:

echo "Building Base Image"
docker build -t base ../base-image

echo "Building api image from Dockerfile"
docker build -t api_img  ../api

echo "Building celery worker image"
docker build -t celery_worker_img ../celery-worker

echo "Building celery worker HUD"
docker build -t flower_hud_img ../flower-hud

My questions comes down to one thing, can I create this Base image without ever running it in a container with docker-compose. (All the Dockerfiles start with FROM base:latest other than the base itself). I'm looking to make it as easy as possible for other people, so that they only have to run a single command.

EDIT: I am using version 3, and acording to the docs, build: is ignored, and docker-compose only accepts pre-built images.

Will Parzybok
  • 2,764
  • 4
  • 12
  • 18

6 Answers6

14

Yes, kind of. Use it like this:

version: '2'

services:

    wls-admin:
        container_name: wls-admin
        image: weblogic-domain
        build:
            context: wls-admin
            args:
                - ADMIN_PORT=${WLS_ADMIN_PORT}
                - CLUSTER_NAME=${WLS_CLUSTER_NAME}
                - PRODUCTION_MODE=dev
        networks:
            - wls-network

image clause here makes docker-compose build generate docker image named weblogic-domain for this service. This image can be re-used by other services' Dockerfiles, even in the same build process.

Kanedias
  • 386
  • 5
  • 7
  • 6
    Yes, but the `docker-compose up` will try to run the container from it and I would like to avoid it because it's only the base image for the other services. – Mišo Nov 15 '19 at 11:36
  • 2
    @Mišo yes, docker-compose [doesn't support this](https://github.com/docker/compose/issues/1896) as of now. The current solution, apparently, is to use multiple yaml files, where you do `build` on first and `up` on the other. – Kanedias Nov 16 '19 at 12:43
  • 5
    You could set the CMD in the base Dockerfile to just be "echo". This way, your base image does start but it quickly exits. Not ideal, but it works. – zsteinkamp Dec 23 '19 at 07:03
11

Doing a bit more research based on @amiasato 's anser, it looks as if there is a replicated key, which you can set to 0 like so:

version: "3"
services:
  base-image:
    build:
      context: .
      dockerfile: Dockerfile-base
    deploy:
      mode: replicated
      replicas: 0

See https://docs.docker.com/compose/compose-file/compose-file-v3/#replicas

Sebastian Wagner
  • 2,308
  • 2
  • 25
  • 32
  • 1
    According to the docs, "replicated" mode is the default, so "mode: replicated" line can be omitted. – vvzh Dec 20 '22 at 06:33
5

Instead of running docker-compose, you can implement a script, witch builds image with specific tag docker build ... -t your_tag, then runs docker-compose. In children dockerfiles you can use FROM your_tag.

4

Just a minor addition to Kanedias' answer. If you choose to follow his approach (which was my choice), you can avoid instantiating a container for the base image with the --scale flag from the docker-compose up command:

docker-compose up --scale wls-admin=0

From the up command documentation:

    --scale SERVICE=NUM        Scale SERVICE to NUM instances. Overrides the
                               `scale` setting in the Compose file if present.

One important thing to note is that the scale setting in the docker-compose.yml was removed in v3, so there is actually nothing to override in v3.

amiasato
  • 914
  • 6
  • 14
3

As per the documentation the build option of a service takes a directory as an argument which contains the famous Dockerfile. There is no way to build a base image and then the actual image of the service.

Docker is a environment in which your application runs. When you are creating a base image, it should have things which are not going to change often. Then you need to build baseiamge once and upload to your repository and use FROM baseimage:latest in the Dockerfile.

For example, if you are building a python application you can create it from python and install requirements:

FROM python:3.6
COPY requirements.txt .
RUN pip install -r requirements.txt

here, python:3.6 is the base image which is not going to change often and thus you need not build it every time you are running docker compose commands.

vedarthk
  • 1,253
  • 1
  • 14
  • 34
  • Would it then be best to build the base image first, then in a separate command do the docker-compose up, and have a shellscript run both of these if I am going for the one-command-does-all? – Will Parzybok Jul 18 '17 at 18:05
  • 1
    @WillParzybok That seems a better idea and suitable to your purpose. – Ayushya Jul 18 '17 at 18:07
  • That's what I do. Jenkins builds my base images and puts it in a private image repo. If you tag them properly with version number you always have control. It's a fairly straight forward process to deal with. – Grimmy Jul 18 '17 at 18:14
2

From the shellscript that makes the images, we can see that you have different dockerfiles in different directories. You can use that to create a docker-compose.yml file. The build settings are used to tell docker that how should it build the image.

You can use those dockerfiles in your compose file in this manner:

version: '3'
services:

  api_cntr:
    image: api_img
    build:
      context: ./api
    container_name:api_cntr
    ports:
      - 5000:5000

Here, I have assumed that your docker-compose.yml file is placed in a folder which also contains a directory called base-image. And base-image has a dockerfile which is used to build the image.

This can be structure of one of your service. In similar manner, you can create other services also. And while usig docker-compose you will not need to specify a network for each, because all services declared within a docker-compose.yml file are part of an isolated network.

Ayushya
  • 9,599
  • 6
  • 41
  • 57
  • Have I misunderstood the question? – Ayushya Jul 18 '17 at 17:57
  • Somewhat, and that is probably my fault for poor wording. The base-image directory contains a Dockerfile that creates an image called ```base```. All the other containers, have their own directory with their own Dockerfile that references this base in their ```FROM```statement. In other words, the base itself is never ran as a container, and is instead a pre-built base for all the other images. – Will Parzybok Jul 18 '17 at 17:59
  • @ayushya your compose file will use `base-image` directory are context to build the image and tag it as `api_img`. What @WillParzybok want is to build base image whenever he is bringing up any service in the compose file. – vedarthk Jul 18 '17 at 18:00
  • @vedarthk My fault, I was building api and don't know how I gave context of base image. – Ayushya Jul 18 '17 at 18:03