How to pass shell environment variable to Supervisor program?

2k views Asked by At

I have a Django app which runs on Gunicorn, and is managed by SupervisorD which is managed by Ansible.

I want Django to read the DJANGO_SECRET_KEY variable from the environment, since I don't want to store my secret key in a config file or VCS. For that I read the key from the environment in my settings.py:

SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

Looking at Supervisor docs it says:

Note that the subprocess will inherit the environment variables of the shell used to start “supervisord” except for the ones overridden here. See Subprocess Environment.

Here's my supervisor.conf:

[program:gunicorn]
command=/.../.virtualenvs/homepage/bin/gunicorn homepage.wsgi -w 1 --bind localhost:8001 --pid /tmp/gunicorn.pid
directory=/.../http/homepage

When I set the variable and run Gunicorn command from the shell, it starts up just fine:

$ DJANGO_SECRET_KEY=XXX /.../.virtualenvs/homepage/bin/gunicorn homepage.wsgi -w 1 --bind localhost:8001 --pid /tmp/gunicorn.pid

However when I set the variable in the shell and restart the Supervisor service my app fails to start with error about not found variable:

$ DJANGO_SECRET_KEY=XXX supervisorctl restart gunicorn
gunicorn: ERROR (not running)
gunicorn: ERROR (spawn error)

Looking at Supervisor error log:

  File "/.../http/homepage/homepage/settings.py", line 21, in <module>
    SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
  File "/.../.virtualenvs/homepage/lib/python2.7/UserDict.py", line 40, in __getitem__
    raise KeyError(key)
KeyError: 'DJANGO_SECRET_KEY'
[2017-08-27 08:22:09 +0000] [19353] [INFO] Worker exiting (pid: 19353)
[2017-08-27 08:22:09 +0000] [19349] [INFO] Shutting down: Master
[2017-08-27 08:22:09 +0000] [19349] [INFO] Reason: Worker failed to boot.

I have also tried restarting the supervisor service, but same error occurs:

$ DJANGO_SECRET_KEY=XXX systemctl restart supervisor
...
INFO exited: gunicorn (exit status 3; not expected)

My question is how do I make Supervisor to "pass" environment variables to it's child processes?

2

There are 2 answers

2
demonno On

Create executable file similar to this and try to start it manually. i.e create file and copy script below /home/user/start_django.sh

You need to fill in DJANGODIR and make other adjustments according to your case. also, you may need to adjust permissions accordingly.

#!/bin/bash

DJANGODIR=/.../.../..
ENVBIN=/.../.virtualenvs/homepage/bin/bin

# Activate the virtual environment
cd $DJANGODIR
source $ENVBIN/activate

DJANGO_SECRET_KEY=XXX
#define other env variables if you need

# Start your Django
exec gunicorn homepage.wsgi -w 1 --bind localhost:8001 --pid /tmp/gunicorn.pid

If it starts manually then just use this file in your conf.

[program:django_project]
command = /home/user/start_django.sh
user = {your user}
stdout_logfile = /var/log/django.log
redirect_stderr = true
# you can also try to define enviroment variables in this conf
environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8,DJANGO_SECRET_KEY=XXX

references that might be helpful,

0
Babken Vardanyan On

OK figured it out myself. Turns out ansible has feature called Vault, which is used for exactly this kind of jobs - encrypting keys.

Now I added the vaulted secret key to ansible's host_vars, see: Vault: single encrypted variable and Inventory: Splitting out host and group specific data.

I added a task to my ansible playbook to copy the key file from ansible vault to the server:

- name: copy django secret key to server
  copy: content="{{ django_secret_key }}" dest=/.../http/homepage/deploy/django_secret_key.txt mode=0600

And made Django read the secret from that file:

with open(os.path.join(BASE_DIR, 'deploy', 'django_secret_key.txt')) as secret_key_file:
    SECRET_KEY = secret_key_file.read().strip()

If anyone has a simpler/better solution, please post it and I will accept it as the answer.