Install Cairo and CairoSVG on an Alpine Docker Image
Posted on Jun 25, 2018. Updated on Jun 25, 2018
I just wanted to convert SVG to PNG files with Python and the library CairoSVG. That was no problem on my Ubuntu system. But running the SVG converter script within a lightweight Alpine Docker container turned out to be problematic. Figuring out which libraries have to be installed up front took me some time. That’s why I like to share my findings here. Hopefully, it’ll save your time.
TL;DR
Dockerfile:
FROM python:3.6.4-alpine3.7
RUN apk add --no-cache \
build-base cairo-dev cairo cairo-tools \
# pillow dependencies
jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev
RUN pip install "flask==1.0.1" "CairoSVG==2.1.3"
COPY circle.svg /
COPY svg-converter-service.py /
CMD python3 /svg-converter-service.py
svg-converter-service.py:
import cairosvg
from flask import Flask, Response
app = Flask(__name__)
@app.route('/image')
def convert_image():
png_data = cairosvg.svg2png(url="circle.svg", parent_width=300, parent_height=300)
return Response(png_data, mimetype='image/png')
if __name__ == '__main__':
app.run(debug=True, port=5000, host='0.0.0.0')
You can see the converted PNG by opening http://localhost:5000/image
in the browser.
Source Code
The complete source code can be found on GitHub in the project cairosvg-on-alpine.
The Journey
I started my journey with a quite plain Python & Alpine Docker Image without any apk
command:
FROM python:3.6.4-alpine3.7
RUN pip install "flask==1.0.1" "CairoSVG==2.1.3"
COPY circle.svg /
COPY svg-converter-service.py /
CMD python3 /svg-converter-service.py
That leaded me directly to the first error when pip tries to install cairocffi
:
Collecting cairocffi (from CairoSVG==2.1.3)
...
Running setup.py (path:/tmp/pip-build-7l3xzt_6/cairocffi/setup.py) egg_info for package cairocffi
Running command python setup.py egg_info
No working compiler found, or bogus compiler options passed to
the compiler from Python's standard "distutils" module. See
the error messages above. Likely, the problem is not related
to CFFI but generic to the setup.py of any Python package that
tries to compile C code. (Hints: on OS/X 10.8, for errors about
-mno-fused-madd see http://stackoverflow.com/questions/22313407/
Otherwise, see https://wiki.python.org/moin/CompLangPython or
the IRC channel #python on irc.freenode.net.)
...
unable to execute 'gcc': No such file or directory
The GNU Compiler Collection aka gcc
is missing. We can fix that by installing build-base
via apk
.
RUN apk add --no-cache build-base
Still, pip can’t install cairocffi
:
Running setup.py (path:/tmp/pip-build-3ryf8sen/cairocffi/setup.py) egg_info for package cairocffi
...
to the PKG_CONFIG_PATH environment variable
Package 'libffi', required by 'virtual:world', not found
c/_cffi_backend.c:15:17: fatal error: ffi.h: No such file or directory
#include <ffi.h>
^
compilation terminated.
Traceback (most recent call last):
File "/usr/local/lib/python3.6/distutils/unixccompiler.py", line 118, in _compile
extra_postargs)
File "/usr/local/lib/python3.6/distutils/ccompiler.py", line 909, in spawn
spawn(cmd, dry_run=self.dry_run)
File "/usr/local/lib/python3.6/distutils/spawn.py", line 36, in spawn
_spawn_posix(cmd, search_path, dry_run=dry_run)
File "/usr/local/lib/python3.6/distutils/spawn.py", line 159, in _spawn_posix
% (cmd, exit_status))
distutils.errors.DistutilsExecError: command 'gcc' failed with exit status 1
Now, we need to know that CairoSVG is a Python wrapper around the C library Cairo. So we need to install Cairo additionally on our Docker image:
RUN apk add --no-cache build-base cairo-dev cairo cairo-tools
Now we get the following error:
Running setup.py bdist_wheel for pillow: started
Running setup.py bdist_wheel for pillow: finished with status 'error'
...
The headers or library files could not be found for jpeg,
a required dependency when compiling Pillow from source.
Please see the install instructions at:
https://pillow.readthedocs.io/en/latest/installation.html
Traceback (most recent call last):
File "/tmp/pip-build-g2zllesg/pillow/setup.py", line 794, in <module>
zip_safe=not (debug_build() or PLATFORM_MINGW), )
File "/usr/local/lib/python3.6/site-packages/setuptools/__init__.py", line 129, in setup
return distutils.core.setup(**attrs)
File "/usr/local/lib/python3.6/distutils/core.py", line 148, in setup
dist.run_commands()
File "/usr/local/lib/python3.6/distutils/dist.py", line 955, in run_commands
self.run_command(cmd)
File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
cmd_obj.run()
File "/usr/local/lib/python3.6/site-packages/wheel/bdist_wheel.py", line 204, in run
self.run_command('build')
File "/usr/local/lib/python3.6/distutils/cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
cmd_obj.run()
File "/usr/local/lib/python3.6/distutils/command/build.py", line 135, in run
self.run_command(cmd_name)
File "/usr/local/lib/python3.6/distutils/cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "/usr/local/lib/python3.6/distutils/dist.py", line 974, in run_command
cmd_obj.run()
File "/usr/local/lib/python3.6/distutils/command/build_ext.py", line 339, in run
self.build_extensions()
File "/tmp/pip-build-g2zllesg/pillow/setup.py", line 582, in build_extensions
raise RequiredDependencyException(f)
__main__.RequiredDependencyException: jpeg
Pillow is a Python Imaging library and a dependency of CairoSVG. Pillow, in turn, requires that some libraries are installed on the system. This can be done by adding some dependencies to our apk
command (I looked them up in the Alpine Docker Image in the Pillow GitHub Repo):
RUN apk add --no-cache \
build-base cairo-dev cairo cairo-tools \
# pillow dependencies
jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev
Yes! We finally made it! Our Alpine Docker container with CairoSVG and Cairo is up and running. Opening http://localhost:5000/image
in the browser will show the converted PNG.
Extra: Local Development (under Ubuntu)
During development, we don’t want to rebuild the Alpine Docker image after every change in the Python script. Fortunately, it’s easy to execute the script locally because, on Ubuntu, we don’t have to install all these native C libraries (tested with Ubuntu 17.04).
I like to use pipenv for local development. We only need a Pipfile
containing our dependencies flask and CairoSVG:
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
flask = "==0.12.2"
CairoSVG = "==2.1.3"
[dev-packages]
[requires]
python_version = "3.6"
Start the Python script:
# pip install pipenv
pipenv install
pipenv shell
cd src
python svg-converter-service.py
All files can be found on GitHub.
This worked for me the last time. However, I somehow used to get the error ModuleNotFoundError: No module named 'PIL'
:
Traceback (most recent call last):
File "svg-converter-service.py", line 3, in <module>
import cairosvg
File "/home/pha/.local/share/virtualenvs/cairosvg-on-alpine--SwTTCnJ/lib/python3.6/site-packages/cairosvg/__init__.py", line 29, in <module>
from . import surface
File "/home/pha/.local/share/virtualenvs/cairosvg-on-alpine--SwTTCnJ/lib/python3.6/site-packages/cairosvg/surface.py", line 34, in <module>
from .image import image
File "/home/pha/.local/share/virtualenvs/cairosvg-on-alpine--SwTTCnJ/lib/python3.6/site-packages/cairosvg/image.py", line 25, in <module>
from PIL import Image
ModuleNotFoundError: No module named 'PIL'
Most likely, this means that Pillow couldn’t be installed by pip because the mentioned native C libraries are missing. Try sudo apt-get install python3-dev python3-setuptools libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
.