Dockerfile LABEL from executable version - docker

I am trying to label my docker image with my app version from within my Dockerfile using the LABEL command.
I can write the version into a file with the following:
RUN /opt/app/foo --version > /var/app-version
/var/app-version would now contain a plaintext file with the application version inside, ready for use in my label.
Now I need a way to place this into a docker variable/arg such that I can do something like the following:
LABEL application_version=$APP_VERSION
Or perhaps there is another way to achieve the same?

You're not going to be able set a label using content generated dynamically from something installed inside the container. You can set a label dynamically from your host environment using a build argument, like this:
FROM docker.io/alpine:latest
ARG application_version=1
LABEL application_version=${application_version}
If I build the container like this:
docker build -t myimage .
I will have:
$ docker inspect image myimage | jq '.[0].Config.Labels'
{
"application_version": "1"
}
Whereas if I build the container like this:
docker build --build-arg application_version=3 -t myimage .
I will have:
$ docker inspect image myimage | jq '.[0].Config.Labels'
{
"application_version": "3"
}
If you use the same build argument value to select the version of the
software you install in your container, you can ensure that the
installed version and the label match.

Related

How to set base image in Dockerfile based on an external (config) file?

I'd like to set the base image in my Dockerfile based on a configuration file.
Let's say my config file is called config.json and its content is:
{
"repo" : "ubuntu",
"tag" : "latest"
}
I'd like my Dockerfile to look something like:
ARG BASE_IMAGE_REPO=<parse repo from config.json>
ARG BASE_IMAGE_TAG=<parse tag from config.json>
FROM ${BASE_IMAGE_REPO}:${BASE_IMAGE_TAG}
...
But I know it can't be done that way.
** edit **
one of my requirements is that we can still build the docker image using the basic docker build . -t foo:bar command.
Is there an elegant way to achieve my goal?
Thanks.
From the docker file itself, there is no way of doing this.
An option you could try (for development) is docker-compose
version: '3'
services:
development:
build:
context: .
args:
BASE_IMAGE_REPO: ubuntu
BASE_IMAGE_TAG: latest
Or you could use a script to do this combined with docker build.
Something like this, if you use JQ to parse the config.json file
docker build --build-arg BASE_IMAGE_REPO =$(jq .repo config.json) --build-arg BASE_IMAGE_TAG=$(jq .tag config.json) -t example .
On the other hand, you are setting defaults that can be overwritten during build anyway (see above). So I personally also don't see why you should not be using a hard-coded default.
ARG BASE_IMAGE_REPO=ubuntu
ARG BASE_IMAGE_TAG=latest
FROM ${BASE_IMAGE_REPO}:${BASE_IMAGE_TAG}

How to refer another label in Dockerfile?

Is there a way to refer a label (in current Dockerfile or inherited labels) in a Docker file? For example lets say I want to have a Dockerfile like below
FROM nginx
LABEL firstlabel="first label"
LABEL secondlabel="I want to use value of ${firstlabel}"
where I want to use the value of label "firstlabel" in the secondlabel. Is that possible? I tried using the ${} variable but it is not working.
You can use build arguments and environment variables to do this kind of thing.
You should be aware though that there are two phases for every container i. e. building and running. Labels defined in a Dockerfile are associated to an image so are generally set at build time although you can set/overwrite labels on docker run using --label <label-name>=<label-value> option. So passing a value for an environment variable that is used within a label to docker run will not update that label as the label is set on build time.
Consider the following Dockerfile:
FROM alpine
# build argument of name buildDate
ARG buildDate
# environment variables for author name
ENV AUTHOR_FNAME=John \
AUTHOR_LNAME=McClain
# we can use both in our labels
LABEL testlabel="Author: ${AUTHOR_FNAME} ${AUTHOR_LNAME}"\
buildDate=${buildDate}
Now build the container and provide the build arguments:
docker build -t test/test --build-arg buildDate=$(date +'%Y-%m-%d') .
Run the container:
docker run --name test -it --rm test/test sh
No open up another Terminal session and use the following command to inspect the labels:
docker inspect test --format='{{json .Config.Labels}}'
Result:
{"buildDate":"2022-04-28","testlabel":"Author: John McClain"}
You can now type exit into to sh to leave the container and it will automatically be removed.

Environment variable not set in container image after PUSH and PULL

I would like to see if we can find a way to persist env variables in images AFTER they are pulled from private docker registry. I have done the following steps
content of .env file
APP_DB_CACHE_UPDATE = 3600
content of docker-compose file
services:
app:
build: .
ports:
- "9000:9000"
environment:
- APP_DB_CACHE_UPDATE=${APP_DB_CACHE_UPDATE}
command to build and check container and its env vars
$ APP_DB_CACHE_UPDATE=3602 docker-compose up
$ docker exec -it <container_id> printenv
APP_DB_CACHE_UPDATE=3602
then I
push the image to private docker registry
I remove image from local machine
pull image from docker registry
$ docker tag app-name_app-name:latest localhost:5000/app-name_app-name:latest
$ docker push localhost:5000/app-name_app-name:latest
$ docker rmi localhost:5000/app-name_app-name:latest
$ docker pull localhost:5000/app-name_app-name:latest
and now I check the enviorment after running this image, i am unable to see APP_DB_CACHE_UPDATE env variable
docker exec -it <new container image> printenv
Docker compose does not build images with env vars set through "environment" or "env_file". It builds images first, then provides the environment variables to the container runtime. See environment specification:
If your service specifies a build option, variables defined in
environment are not automatically visible during the build.
Because of this, your image does not know about the environment variable specified in docker-compose file.
To set environment variables during/before the build, which will be persisted in the container after the image is built, you need to specify them in you build environment. You can do this in Dockerfile using ENV, for example:
ENV APP_DB_CACHE_UPDATE="3600"
See Dockerfile ENV specification.
The environment variables set using ENV will persist when a container
is run from the resulting image. You can view the values using docker
inspect, and change them using docker run --env =
Then tell compose to use your Dockerfile with:
build:
context: .
dockerfile: Dockerfile

Is there a way to automatically "Rebase" an image in Docker?

I have a docker-compose script that brings up a service
version : '2.0'
services:
orig-db:
image: web-url:{image_tag}
custom-db:
image: local_image: latest
Where image used in custom DB is the the result of bringing up a container with orig-db, performing some basic bash commands, and doing a docker commit. I want the custom-db image to always be the original image + these commands, even if the original image is updated. Is there a way to "rebase" off the original image?
You can think of a Dockerfile as a simple form of a "rebase".
# Content of subdir/Dockerfile
FROM orig_image:latest
RUN some.sh
RUN basic.sh
RUN bash_commands.sh
When you build an image based on this file, it will always run the bash commands on top of the base image. Inside the compose file you can use the build property to instruct docker-compose to build the image instead of using a pre-made image.
version : '2.0'
services:
orig-db:
image: web-url:{image_tag}
custom-db:
build: somedir
If the base image changes, you need to tell docker-compose to rebuild the custom-db image again, running the bash commands again on top of the updated original image.
docker-compose up -d --build custom-db

Docker Always force to use a cached image

I'm using docker compose to build my application using docker.
Version of docker-compose is 2.2
I have all the containers running well at the moment where one of the container has nginx running.
I need to change some configuration on this container.
The way I need to do (because of special scenario) is, to update the config inside the container.
Then I commit the container to build a new image.
docker commit <container> <image-name>
Now I have new image with tag latest.
What I want is to use this image when I run, docker-compose down && docker-compose up --build next time.
docker-compose down && docker-compose up --build -d
With --build option, docker-compose will go through the steps in Dockerfile and run those and all my changes will be reverted.
Question:
Is there anyway that I can tell docker-compose to use the newly created image as cache and ignore Dockerfile for this one container?
Solution Tried:
I have tried with docker-compose-override and using option cache-from and it's not working.
docker-compose.override.yml
container:
build:
cache_from:
- new-image:latest
Thanks in advance.
I don't understand why you would want to build an image from docker-compose even though you have already built it by docker-commit.
Now I have new image with tag latest.
What I want is to use this image when I run, docker-compose down && docker-compose up
If you have already built image, skip the build phase in docker-compose. Just specify which image should be used like so:
container:
image: new-image:latest
container_name: "Foo bar"
.....(other options)
Image
Specify the image to start the container from. Can either be a
repository/tag or a partial image ID.
image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd
If the image does
not exist, Compose attempts to pull it, unless you have also specified
build, in which case it builds it using the specified options and tags
it with the specified tag.
If you have any other images that you build from inside docker-compose run:
docker-compose build && docker-compose up
If not simple docker-compose up will suffice.

Resources