3

I have a code in Python Flask where I generate pdf files using an HTML template. The code works just fine when I run it alone, but when I try to run it inside a Docker container, as soon as I call the endpoint that generates the report the docker crashes and resets. It just stays loading then it returns an error (in Postman which I'm using to test).

The code for the PDF is as follows:

def create_report(download_uuid):
    transactions = get_transaction_for_report(download_uuid)
    config = pdfkit.configuration(wkhtmltopdf=environ.get('WKHTMLTOPDF'))

    file_obj = io.BytesIO()
    with zipfile.ZipFile(file_obj, 'w') as zip_file:
        for transaction in transactions:
            html = render_template("report.html", transaction=transaction)
            pdf = pdfkit.from_string(html, False, configuration=config)
            data = zipfile.ZipInfo('{}.pdf'.format(transaction['control_number']))
            data.compress_type = zipfile.ZIP_DEFLATED
            zip_file.writestr(data, pdf)
    file_obj.seek(0)
    return send_file(file_obj, attachment_filename="forms.zip", as_attachment=True)

It is returning a zip file, but inside the zip file are pdf files. Furthermore, if I remove the pdf generating part, the zip file returns just fine. This is my Dockerfile:

FROM madnight/docker-alpine-wkhtmltopdf as wkhtmltopdf_image
FROM python:3.9-alpine

RUN adduser -D custom

WORKDIR /home/Project

COPY requirements.txt requirements.txt
RUN python -m venv venv
RUN venv/bin/pip install --upgrade pip
RUN apk add make automake gcc g++ subversion python3-dev jpeg-dev zlib-dev libffi-dev musl-dev openssl-dev freetype freetype-dev ttf-freefont libxrender qt5-qtbase-dev
RUN venv/bin/pip install -r requirements.txt
RUN venv/bin/pip install gunicorn

COPY Project Project
COPY boot.sh app.py .env run.py create_database.py config.py ./
COPY templates templates
COPY static static
COPY --from=wkhtmltopdf_image /bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
RUN chmod +x boot.sh

ENV FLASK_APP app.py

USER root
RUN chown -R custom ./
USER custom

EXPOSE 9001
ENTRYPOINT ["./boot.sh"]

I should say that this is the last iteration of many, MANY attempts to try to get this to work. Essentially, I've tried getting wkhtmltox by curl, I've tried putting wkhtmltopdf in different directories. So far nothing has worked. I don't know what I'm missing. This is basically what I need to fix in order to finish this project so any help at all will be immensely appreciated.

EDIT: docker-compose.yml

version: '2'
services:
  app:
    build: .
    networks:
      - custom
    ports:
      - "9001:9001"
    volumes:
      - "./static:/home/EventismedEquipmentAPI/static"
    external_links:
      - eventismed-equipment:db
networks:
  custom:
    external: true
plasmy
  • 99
  • 3
  • 9
  • 32

1 Answers1

7

Let's fix this.

I've managed to run wkhtmltopdf isolated on a docker container.

Dockerfile:

# https://stackoverflow.com/a/62737156/152016
# Create image based on the official openjdk 8-jre-alpine image from the dockerhub
FROM openjdk:8-jre-alpine

# Install wkhtmltopdf
# https://stackoverflow.com/a/56925361/152016
RUN apk add --no-cache wkhtmltopdf ttf-dejavu

ENTRYPOINT ["sh"]

docker-compose.yml:

version: '3.8'

services:
  wkhtmltopdf:
    image: wkhtmltopdf
    container_name: wkhtmltopdf
    build:
      dockerfile: Dockerfile
      context: .

Then:

docker-compose build
docker run -ti --rm -v /tmp:/tmp wkhtmltopdf

Inside the container:

$ cd /tmp
$ wkhtmltopdf https://www.google.com test.pdf

Then you will see the pdf on your mac at /tmp/test.pdf

First let me know if this works.

Niloct
  • 9,491
  • 3
  • 44
  • 57
  • Yes, this worked perfectly :) But is there any way I can integrate this with my project? – plasmy Apr 28 '21 at 18:30
  • Your python docker image is running on alpine too. So you can just get rid of the first `FROM` and the `COPY --from=wkhtmltopdf`, and add the 2 packages `wkhtmltopdf ttf-dejavu` on your original `RUN apk add`, change entrypoint to `ENTRYPOINT ["sh"]` to test, then run the `docker run` command I told you, and generate another pdf inside the new image to test isolated pdf creation. – Niloct Apr 28 '21 at 18:52
  • So I tried this and I got the following: `QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-eventismed' Loading page (1/2) Error compiling builtin: ] 30% Fatal error compiling builtin function 'apply': Segmentation fault` I also have a docker-compose file I use, I'll edit it to the main question – plasmy Apr 28 '21 at 19:11
  • Also I should add, I tried changing the `ENTRYPOINT` like you said, but the project was not running for some reason so I had to change it to how I have it. – plasmy Apr 28 '21 at 19:14
  • Read about `ENTRYPOINT`: https://docs.docker.com/engine/reference/builder/#entrypoint basically it's the command your container runs on start. I changed it to `sh` so when you do a docker run, it will start a shell inside the container and you can repeat the last commands to see if wkhtmltopdf is running ok. Then you change entrypoint back to `./boot.sh`. – Niloct Apr 28 '21 at 20:25
  • Good morning @plasmy. Let me know if you don't understand the last comment. – Niloct Apr 29 '21 at 13:49
  • 2
    Good morning @Niloct. First, I would like to thank you for everything you've helped me with. I stayed up until late last night, and I managed to fix it. The problem was never WKHTML, it was the html file I was using. For some reason, any reference I had to static images or files was causing the process to crash. I was using the standard way in jinja2, by using the `url_for`. I had to change that up and everything worked like a charm. I can't believe it took me so long to figure this out. But I am very grateful for your help and for following up. – plasmy Apr 29 '21 at 15:33
  • Congratulations @plasmy! You can add details in a response, and mark it as an answer here. If you feel good about it. – Niloct Apr 29 '21 at 16:11
  • 1
    I do. I am very grateful to you @Niloct. Very rarely do I find people as helpful as you in this website. Hopefully we can coincide some other time. You seem like someone I could learn a lot from. – plasmy Apr 29 '21 at 18:33
  • You're very polite, thanks! Actually I don't think I've expressed myself, I meant you could add an answer yourself and mark that as the accepted answer, not my try. You can mark me here or shout me at my e-mail anytime :) – Niloct Apr 29 '21 at 18:38