benjist
benjist

Reputation: 2881

Dockerfile Healthcheck with environment variable

I've a REST Service written in C++ which has an endpoint for localhost:somePort/health. The port is configured in a yaml based config file.

I've created a script which extracts the port from the yaml file. But my problem is to assign the result to the HEALTHCHECK command in my Dockerfile.

So let's say I have a script /app/get_port.sh echoing the actual port used on startup. How do I pass that port to the HEALTHCHECK command? For example to make this work:

HEALTHCHECK --interval=10s --timeout=4s CMD curl -f "http://localhost:$MY_PORT/health" || exit 

Upvotes: 5

Views: 3945

Answers (4)

julie-ng
julie-ng

Reputation: 540

Just use ${PORT} - no workarounds needed

To reference an environment variable in a Dockerfile, just use wrap it inside a ${…} and for then the runtime value is used e.g. for the healthcheck.

You can also set a default like this ${PORT:-80} I don't remember where I saw/read this. But it works :)

So my Dockerfile looks like this

FROM node:lts-alpine
RUN apk add --update curl

ENV NODE_ENV production
ENV HOST '0.0.0.0'
EXPOSE ${PORT:-80}

# Other steps

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost:${PORT}/health || exit 1

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

Upvotes: 4

DudeOfAwesome
DudeOfAwesome

Reputation: 128

I found that you can do this inline with a little bit of parsing.

  redis:
    image: 'docker.io/library/redis'
    command: 'redis-server --requirepass "password"'
    environment:
      REDIS_HOST_PASSWORD: 'password'
    healthcheck:
      test: 'redis-cli -a "$$(env | grep REDIS_HOST_PASSWORD | cut -d = -f2-)" ping'

Upvotes: 0

Matt P
Matt P

Reputation: 11

Mine was a node app, which i needed to pass a base url path into from the docker-compose.yaml file. So did this with a separate healthcheck.js file.

Dockerfile

HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=15s CMD node healthcheck.js > /dev/null || exit 1

healthcheck.js

var http = require("http");

const BASEPATH = process.env.BASEPATH || "/";

var options = {  
    host : "127.0.0.1",
    port: 3000,
    path: BASEPATH,
    timeout : 2000
};

var request = http.request(options, (res) => {  
    console.log(`STATUS: ${res.statusCode}`);
    if (res.statusCode == 200) {
        process.exit(0);
    }
    else if (res.statusCode == 301) {
        process.exit(0);
    }
    else if (res.statusCode == 302) {
        process.exit(0);
    }
    else {
        process.exit(1);
    }
});

request.on('error', function(err) {  
    console.log('ERROR',err);
    process.exit(1);
});

request.end();  

docker-compose.yaml

  posterr:
    image: petersem/posterr:dev
    container_name: posterr
    restart: unless-stopped
    ports:
      - "9876:3000"
    volumes:
      - /c/docker/posterr/randomthemes:/randomthemes
      - /c/docker/posterr/config:/usr/src/app/config
    environment:
      - TZ=Australia/Brisbane
      - BASEPATH=/mypath

Upvotes: 1

benjist
benjist

Reputation: 2881

The workaround was for me to use a subshell within the curl command:

RUN echo "Healthcheck port: $(/app/get_port.sh)\n"
HEALTHCHECK --interval=10s --timeout=4s CMD curl -f "http://localhost:$(/app/get_port.sh)/health" || exit 1

Though I wish Docker would get advanced options for handling ENV.

Upvotes: 1

Related Questions