1

I developed locally using Docker and cookiecutter-django.

Cookiecutter-django creates several Dockerfiles within the "compose" directory.

I am now trying to push the project to Heroku.
However, heroku container:push web will return No images to push.

If I try the same command within a subdirectory of the compose directory, it will eventually break--probably due to an attempt at only pushing partial dockerfiles.

How do I get this up and running on Heroku taking advantage of Heroku containers?

This article from Heroku says I can push multiple images using by renaming the Dockerfiles, but I'm not sure how to figure out which of these cookiecutter-generated Dockerfiles are of which process type.

docker images will return:

REPOSITORY                                    TAG                 IMAGE ID            CREATED             SIZE
<none>                                        <none>              16e570f295a7        10 minutes ago      318MB
luup_celeryworker                           latest              bd7ff7e5eb10        2 hours ago         531MB
luup_django                                 latest              bd7ff7e5eb10        2 hours ago         531MB
luup_celerybeat                             latest              bd7ff7e5eb10        2 hours ago         531MB
luup_postgres                               latest              e50eb7b8a704        2 hours ago         287MB
registry.heroku.com/fierce-forest-57627/web   latest              27690b3e49d4        16 hours ago        766MB
python                                        3.6-alpine          c3a4a35c9244        22 hours ago        90MB

This is the Dockerfile made by cookiecutter. It is the DF under compose/production/django. There are other DFs--for caddy, postgres, as well as DFs for local.

FROM python:3.6-alpine

ENV PYTHONUNBUFFERED 1

RUN apk update \
  # psycopg2 dependencies
  && apk add --virtual build-deps gcc python3-dev musl-dev \
  && apk add postgresql-dev \
  # Pillow dependencies
  && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
  # CFFI dependencies
  && apk add libffi-dev openssl-dev py-cffi

RUN addgroup -S django \
    && adduser -S -G django django

# Requirements have to be pulled and installed here, otherwise caching won't work
COPY ./requirements /requirements
RUN pip install --no-cache-dir -r /requirements/production.txt \
    && rm -rf /requirements

COPY ./compose/production/django/gunicorn.sh /gunicorn.sh
RUN sed -i 's/\r//' /gunicorn.sh
RUN chmod +x /gunicorn.sh
RUN chown django /gunicorn.sh

COPY ./compose/production/django/entrypoint.sh /entrypoint.sh
RUN sed -i 's/\r//' /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN chown django /entrypoint.sh

COPY ./compose/production/django/celery/worker/start.sh /start-celeryworker.sh
RUN sed -i 's/\r//' /start-celeryworker.sh
RUN chmod +x /start-celeryworker.sh

COPY ./compose/production/django/celery/beat/start.sh /start-celerybeat.sh
RUN sed -i 's/\r//' /start-celerybeat.sh
RUN chmod +x /start-celerybeat.sh

COPY . /app

RUN chown -R django /app

USER django

WORKDIR /app

ENTRYPOINT ["/entrypoint.sh"]

Please let me know if you need any more information

Edit--adding tree:

note: I've renamed the Dockerfiles to Dockerfile.processtype as instructed by Heroku here--Although I'm no longer pushing multiple images.. As you can tell in the tree, I've also moved a copy of Dockerfile.django and Dockerfile.local to the root of the project, as that's where it needs to be in order to Heroku container:push processtype --recursive.

├── Dockerfile.django
├── Dockerfile.local
├── LICENSE
├── Procfile
├── README.rst
├── compose
│   ├── local
│   │   └── django
│   │       ├── celery
│   │       │   ├── beat
│   │       │   │   └── start.sh
│   │       │   └── worker
│   │       │       └── start.sh
│   │       └── start.sh
│   └── production
│       ├── caddy
│       │   ├── Caddyfile
│       │   └── Dockerfile.caddy
│       ├── django
│       │   ├── Dockerfile.django
│       │   ├── celery
│       │   │   ├── beat
│       │   │   │   └── start.sh
│       │   │   └── worker
│       │   │       └── start.sh
│       │   ├── entrypoint.sh
│       │   └── gunicorn.sh
│       └── postgres
│           ├── Dockerfile.postgres
│           └── maintenance
│               ├── _sourced
│               │   ├── constants.sh
│               │   ├── countdown.sh
│               │   ├── messages.sh
│               │   └── yes_no.sh
│               ├── backup
│               ├── backups
│               └── restore
├── config
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-35.pyc
│   │   ├── __init__.cpython-36.pyc
│   │   ├── urls.cpython-36.pyc
│   │   └── wsgi.cpython-36.pyc
│   ├── settings
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-35.pyc
│   │   │   ├── __init__.cpython-36.pyc
│   │   │   ├── base.cpython-35.pyc
│   │   │   ├── base.cpython-36.pyc
│   │   │   ├── local.cpython-35.pyc
│   │   │   ├── local.cpython-36.pyc
│   │   │   └── production.cpython-36.pyc
│   │   ├── base.py
│   │   ├── local.py
│   │   ├── production.py
│   │   └── test.py
│   ├── urls.py
│   └── wsgi.py
├── docs
│   ├── Makefile
│   ├── __init__.py
│   ├── conf.py
│   ├── deploy.rst
│   ├── docker_ec2.rst
│   ├── index.rst
│   ├── install.rst
│   └── make.bat
├── heroku.yml
├── local.yml
├── locale
│   └── README.rst
├── lurnup
│   ├── __init__.py
│   ├── __pycache__
│   │   └── __init__.cpython-36.pyc
│   ├── contrib
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   └── __init__.cpython-36.pyc
│   │   └── sites
│   │       ├── __init__.py
│   │       ├── __pycache__
│   │       │   └── __init__.cpython-36.pyc
│   │       └── migrations
│   │           ├── 0001_initial.py
│   │           ├── 0002_alter_domain_unique.py
│   │           ├── 0003_set_site_domain_and_name.py
│   │           ├── __init__.py
│   │           └── __pycache__
│   │               ├── 0001_initial.cpython-36.pyc
│   │               ├── 0002_alter_domain_unique.cpython-36.pyc
│   │               ├── 0003_set_site_domain_and_name.cpython-36.pyc
│   │               └── __init__.cpython-36.pyc
│   ├── static
│   │   ├── css
│   │   │   └── project.css
│   │   ├── fonts
│   │   ├── images
│   │   │   └── favicon.ico
│   │   ├── js
│   │   │   └── project.js
│   │   └── sass
│   │       ├── custom_bootstrap_vars.scss
│   │       └── project.scss
│   ├── taskapp
│   │   ├── __init__.py
│   │   ├── __pycache__
│   │   │   ├── __init__.cpython-36.pyc
│   │   │   └── celery.cpython-36.pyc
│   │   └── celery.py
│   ├── templates
│   │   ├── 403_csrf.html
│   │   ├── 404.html
│   │   ├── 500.html
│   │   ├── account
│   │   │   ├── account_inactive.html
│   │   │   ├── base.html
│   │   │   ├── email.html
│   │   │   ├── email_confirm.html
│   │   │   ├── login.html
│   │   │   ├── logout.html
│   │   │   ├── password_change.html
│   │   │   ├── password_reset.html
│   │   │   ├── password_reset_done.html
│   │   │   ├── password_reset_from_key.html
│   │   │   ├── password_reset_from_key_done.html
│   │   │   ├── password_set.html
│   │   │   ├── signup.html
│   │   │   ├── signup_closed.html
│   │   │   ├── verification_sent.html
│   │   │   └── verified_email_required.html
│   │   ├── base.html
│   │   ├── pages
│   │   │   ├── about.html
│   │   │   └── home.html
│   │   └── users
│   │       ├── user_detail.html
│   │       ├── user_form.html
│   │       └── user_list.html
│   └── users
│       ├── __init__.py
│       ├── __pycache__
│       │   ├── __init__.cpython-36.pyc
│       │   ├── adapters.cpython-36.pyc
│       │   ├── admin.cpython-36.pyc
│       │   ├── apps.cpython-36.pyc
│       │   ├── models.cpython-36.pyc
│       │   ├── urls.cpython-36.pyc
│       │   └── views.cpython-36.pyc
│       ├── adapters.py
│       ├── admin.py
│       ├── apps.py
│       ├── migrations
│       │   ├── 0001_initial.py
│       │   ├── __init__.py
│       │   └── __pycache__
│       │       ├── 0001_initial.cpython-36.pyc
│       │       └── __init__.cpython-36.pyc
│       ├── models.py
│       ├── tests
│       │   ├── __init__.py
│       │   ├── factories.py
│       │   ├── test_admin.py
│       │   ├── test_models.py
│       │   ├── test_urls.py
│       │   └── test_views.py
│       ├── urls.py
│       └── views.py
├── manage.py
├── merge_production_dotenvs_in_dotenv.py
├── production.yml
├── pytest.ini
├── requirements
│   ├── base.txt
│   ├── local.txt
│   └── production.txt
Jay Jung
  • 1,805
  • 3
  • 23
  • 46

1 Answers1

0

Cookiecutter-django provides a standard docker compose configuration that you can push on several hosting services with full Docker support.

However, Heroku supports Docker with a few restrictions. For instance, the port for the WSGI server is hardcoded to 5000, where Heroku requires to use the environment variable $PORT.

These are NOT accounted for in cookiecutter-django, so you will have to change a few things:

  • I'd start with the images in compose/production/django and ignore the DB (prvided via add-on) and Caddy (see here instead).
  • Change the Gunicorn port from 5000 to $PORT
  • The code to get DATABASE_URL in entrypoint.sh should probably not be needed.
  • Last but not least: create a heroku.yml file to point at the DF you want to use.
  • With docker-compose, you can re-use the same Dockerfile and change the entrypoint, but I don't think it's feasible with Heroku. You might need to create a duplicate the web Dockerfile and change it for your Celery dynos.

I've tried Docker on Heroku, but not with cookiecutter-django' setup. The Heroku way is not standard enough IMO, it's a bit of a middle ground between pure Heroku and bare Docker.

Hope that helps!

Bruno A.
  • 1,765
  • 16
  • 17
  • I'm ignoring Caddy, but holding off on the SSL cert instructs. since it seems they want me to upgrade to a payable dyno--which I'll consider if there's any sign I can even get this working. Finally, I've tried several ways to build the heroku.yml file, but receive errors re: `invalid Compose File because: Unsuppoarted config option for build: 'docker'`. For now, I've moved the cookiecutter Dockerfiles to my root and am `heroku container:push dockerfile --recursive`'ing them instead. It gets the container up on Heroku, but again, I'm stopped atm by the Postgres issue.. – Jay Jung Mar 25 '18 at 06:49