May 1, 2017 · dsp2017 docker tools

Better Docker experience with Dockerize

I've had some problems with my Docker images and docker-compose configuration. Let's analyze following docker-compose.yml file:

version: '2'  
services:  
  database:
    image: postgres

  application:
    image: registry.example.com/our-application
    command: node
    ports:
      - 80:80

Do you see anything wrong here? Probably not, because file is perfectly OK. But there's a problem!

When you run docker-compose up, docker starts all containers at the same time. And there's a race - application is trying to connect to database, which may be still initializing. Typical reaction to not available database is crash. And because it's race, you probably won't see this everytime. Intermediate solution is to add restart: always to our application config, to retry until DB start responding. Can we do better? Sure.

Dockerize

I found a great, little tool to help with such problems. It's called Dockerize and can be found on Github.

Firsly, we need to add in to our application Dockerfile (copied from README):

ENV DOCKERIZE_VERSION v0.4.0  
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \  
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

Next, let's modify our docker-compose.yml file:

services:  
  ...
  application:
    image: registry.example.com/our-application
    command: dockerize -wait tcp://database:5432 node
  ...

No more race! Dockerize delay start of our command until database is available. Not just started - it's waiting for connections. That's a difference between Dockerize and depends_on.

In it's core, Dockerize is a wrapper. dockerize our_normal_command just calls our command. But optionally, we can add parameters to delay execution, perform file templating or redirect output from files to STDOUT/STDERR. Very common and useful operations in a Docker world.

Examples:

# redirect files to stdout and stderr
dockerize \  
  -stdout info.log \
  -stdout perf.log \
  ...

# wait for 2 services with 10s timeout
dockerize \  
  -wait tcp://db:5432 \
  -wait http://web:80 \
  -timeout 10s \
  ...

# template option
dockerize \  
  -template nginx.tmpl:nginx.conf \
  ...

Template example (it's GO templating syntax):

http {  
 server_name {{ .Env.HOST }};
 port {{ .Env.PORT }};
 location / {
  proxy_pass http://{{ .Env.UPSTREAM }};
 }
}

Conclusion

I'm using Dockerize for long time now, and it really made my life easier. If you have similar problems, I encourage you to give it a try.

Comments powered by Disqus