Adrian Mouat

Adrian Mouat

Why Use Fig for Docker Automation?

January 6, 2015 by Adrian Mouat

If you've been using Docker for a little while, but you've not tried out Fig yet, this blog is for you. Like me, you've probably either become used to dealing with long and unwieldy Docker commands using dozens of arguments, or you've come up with a bunch of shell scripts to start your containers. At its core, Fig is just a simple bit of automation and abstraction to help with this stuff.

It's easiest to explain by showing an example. We'll create a simple Python Flask app that shows a timestamp for every time it has been requested. The Python code isn't really relevant, so feel free to skip it, but if you'd like to follow along, create a file app.py in a new directory with the following contents:


from flask import Flask
from redis import StrictRedis
from datetime import datetime
 
app = Flask(__name__)
redis = StrictRedis(host='redis', port=6379)
 
@app.route('/')
def home():
    redis.lpush('times', datetime.now().strftime('%H:%M:%S'))
    return 'This page was requested at: {}n'.format(
        [t.decode('utf-8') for t in redis.lrange('times', 0, -1)])
 
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

And the following Dockerfile:


FROM python:3.4
 
RUN mkdir /code
COPY app.py /code/app.py
WORKDIR /code
RUN pip install flask redis
CMD ['python', 'app.py']

Now the bit we're interested in. We can build and run this application with the following Docker commands:

  
docker build -t fig_ex .
(out) ...snip...
docker run -d --name redis redis
(out) 68fece140431f4ad67fbd9fbaa43253785b4c3cb6ceeda1b1eb7de2eee22615c
docker run -d -p 5000:5000 --link redis:redis fig_ex
(out) cb7588cd15ade0ec09e005ea64aaa8753befa2d47d9a8e331a711137fdc59bc8
curl localhost:5000
(out) This page was requested at: ['13:18:39']
curl localhost:5000
(out) This page was requested at: ['13:18:40', '13:18:39']
curl localhost:5000
(out) This page was requested at: ['13:18:41', '13:18:40', '13:18:39']

Or we can use Fig to do much the same. Create a fig.yml file in the same directory with the following contents:


figex:
  build: .
  ports:
    - '5000:5000';
  links:
    - redis
 
redis:
  image: redis

And run fig up:

  
fig up
(out) Creating figcode_redis_1...
(out) Creating figcode_figex_1...
(out) Attaching to figcode_redis_1, figcode_figex_1
(out) redis_1 | [1] 06 Jan 10:27:12.745 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
(out) ...snip...
(out) redis_1 | [1] 06 Jan 10:27:12.749 * The server is now ready to accept connections on port 6379
(out) figex_1 |  * Running on http://0.0.0.0:5000/
(out) figex_1 |  * Restarting with reloader

Fig will build the images (if necessary), launch the containers in the correct order and attach to them. Output from the containers is displayed prefixed with the container name (which by default is a concatenation of the directory name and image name). We can test out the containers in a new terminal:

  
curl localhost:5000
(out) This page was requested at: ['13:24:27']
curl localhost:5000
(out) This page was requested at: ['13:24:28', '13:24:27']
curl localhost:5000
(out) This page was requested at: ['13:24:29', '13:24:28', '13:24:27']

Great, it works just the same, just with a lot less to remember -- 3 Docker commands with multiple arguments have been reduced to two words. Essentially all that has happened is we've moved all the annoying to remember config flags out to the fig.yml file.

To stop the containers, just hit ctrl-c. You can use fig rm to remove them completely. Most of the time, you won't want the output from the containers, so you can use fig up -d to start Fig in detached mode. You then need to use fig stop to stop the containers.

There's really not a lot more to Fig, most commands map one for one to their docker run equivalents. There are some things you should be aware of though:

  • There is currently no syntax checking of the YAML file. This means if you make a typo, such as forgetting a - character, you can end up with very confusing errors.
  • Fig has confusing behaviour with regard to volumes. When fig up executes, it will attempt to mount any previous volumes using --volumes-from. This leads to problems when the volumes statement in a fig.yml is changed, as it will often conflict with the previous volumes — generally the solution is just to make sure you always remove previous containers with fig rm. Part of the problem here is that Docker itself needs more tools for dealing with volumes.
  • Fig is largely designed to be used in development, but I've also found it very useful in testing and it could potentially be used in small scale deployments.

Finally, it's worth pointing out that docker compose is in the works, which will be a successor to Fig. I would still recommend using Fig now though, as docker compose will re-use the existing Fig code and is very likely to have similar syntax and commands. Also, it's so quick to get going with Fig that you are likely to pay back your time investment almost immediately.

Add a comment