├── LICENSE ├── README.md ├── common_funcs.sh ├── create_django_project_run_env.sh ├── deploy_django_project.sh ├── install_os_prereq.sh ├── supervisord └── supervisord.conf /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016, Hari Mahadevan(何瑞理) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/harikvpy) 2 | 3 | # deploy-django 4 | A bash script to deploy a django project for production sites. The script sets 5 | up the environment under which the django project can be run safely. The 6 | environment will have the following characterstics: 7 | 8 | * Designed to be run using Gunicorn/WSGI and as an app under NGINX. 9 | * Each site will be run in a dedicated automation user account sandbox. 10 | * A new PostgreSQL database with the same name as the project specified 11 | in command line that can be used by Django. 12 | * Python virtualenv setup with basic packages such as pip and django installed. 13 | * Supervisor, the python based daemon control process installed and 14 | configured with the necessary conf file to control the WSGI process. 15 | * An script to autostart supervisor and control it like other Ubuntu services. 16 | 17 | # Usage 18 | The primary end-user facing components of the package are the two scripts:- 19 | 20 | * `install_os_prereq.sh` 21 | * `deploy_django_project.sh` 22 | 23 | The names ought to be self explanatory. 24 | 25 | In any case, the first script ensures that the necessary packages are installed 26 | in the OS. Run this first when deploying a django project to a fresh Ubuntu 27 | image. 28 | 29 | The second script creates the automation account, virtual environment, 30 | PostgreSQL DB & and sets up the assortment of scripts that make up the website 31 | deployment. 32 | 33 | `install_os_prereq.sh` needs to be run only once per machine, typically after 34 | a fresh OS image installation. Thereafter `deploy_django_project.sh` can be run 35 | as many times as necessary, once for each domain that you want to support on 36 | the same machine (using HTTP virtual hosts). 37 | 38 | > The old command `create_django_project_run_env.sh` is essentially a wrapper 39 | > that calls the above two scripts in sequence. Unless you have a specific 40 | > reason (I can't think of any), it's best that you don't use this script at 41 | > all. Instead use the new scripts `install_os_prereq.sh` and 42 | > `deploy_django_project.sh`. 43 | 44 | Use the commands as follows: 45 | ``` 46 | install_os_prereq.sh [] 47 | deploy_django_project.sh [] 48 | ``` 49 | where: 50 | 51 | `` is the name of the parent project you use to refer to the 52 | solution. This should be a single word without space or other special charactes. 53 | A new user account with this name will be created under group `webapps` and 54 | the django project will be served from this account's home folder 55 | `/webapps/_project`. If group `webapps` does not exist, it will 56 | be created. 57 | 58 | `` is the domain name where the website is to be to be deployed. 59 | Specify this without the `www` prefix. Appropriate NGINX configuration files 60 | will be generated to direct requets to both `` and 61 | `www.` to the django app. 62 | 63 | `` is either `2` or `3`, which is the python version that you 64 | wish to use for the project. In most cases this can be omitted as it defaults 65 | to `3`. 66 | 67 | For example, for deploying the domain example.com, use the following command: 68 | 69 | ``` 70 | $ install_os_prereq.sh 71 | $ deploy_django_project.sh example example.com 72 | ``` 73 | Note that in both the above commands the default Python version of `3` is used. 74 | 75 | This will create a new user account `example` under group `webapps` with home 76 | folder set to `/webapps/example_project/`. Under this folder, it will create a 77 | python virtual environment and a gunicorn startup script, that will be auto 78 | started using the Supervisor process control system. Ngix configuration will be 79 | updated such that requests to domain example.com (and wwww.example.com) will be 80 | proxied to this gunicon WSGI instance. A placeholder Django app, aptly named 81 | `` will also be created under the home folder which will serve as the 82 | the WSGI endpoint. 83 | 84 | # Background 85 | A production Django app is deployed using the WSGI proxy mechanism which 86 | serves requests proxied from an HTTP server such as NGINX or Apache. Here the 87 | HTTP server does little more than forwarding the incoming requests to the 88 | configured WSGI backend application server and forward the received response 89 | from the appserver to the client. The HTTP server will be configured such 90 | that for specific domain name(s), a specific WSGI app server instance will be 91 | used. For Django apps, the recommended HTTP server and WSGI app servers are 92 | NGINX and Gunicorn respectively. 93 | 94 | Since deploying a production web app involves multiple components and each of 95 | them with their own configuration, it's imperative that all these varied steps 96 | are either documented or captured in the form of script files such that these 97 | can be replicated across multiple server instances. Even for the most simple 98 | web application that uses a single server instance, a production site requires 99 | a staging server where the code needs to be deployed and tested for runtime 100 | and deployment issues before launching it live. And for sites expecting medium 101 | to heavy traffic and load, the code would have be deployed on multiple servers 102 | with the HTTP server deployed as a load balancer or a dedicated load balancer 103 | distributing the HTTP request load equally across multiple application servers. 104 | Therefore the need for consistent configuration of multiple servers cannot be 105 | overemphasized. 106 | 107 | An even better solution would be standardizing the deployment characteristics 108 | of a Django web application such that multiple Django applications, serving 109 | multiple sites, all are deployed in a certain fixed configuration. This would 110 | make troubleshooting and subsequent fixing of any deployment issues easy as 111 | all sites retain the same characteristics. 112 | 113 | This script is an attempt to achieve this deployment standardization for all 114 | Django apps. 115 | 116 | # Assumptions 117 | 118 | * Script is written to work with Ubuntu Linux 14.04, though it should work fine 119 | on Ubuntu versions >= 12.04. Script does install the prerequisite Linux 120 | packages if they are not installed and therefore a vanilla OS installation 121 | is all that is necessary. 122 | * PostgreSQL is used as the database backend. PostgreSQL is considered the best 123 | DB backend for Django apps and provides more sophisticated RDBMS features than 124 | MySQL. 125 | * As already mentioned HTTP server is provided by NGNIX and WSGI is served by 126 | Gunicorn. NGINX and Gunicorn are considered the best match for Django apps. 127 | * Supervisor process control system is managed to Gunicorn appserver processes. 128 | Using supervisor provides automatic restart of the Gunicorn appserver daemon, 129 | should it crash for some reason, making the overall deployment that much more 130 | robust. 131 | * The app server is served using python runtime from a dedicated virtual 132 | environment and therefore the system-wide python distribution is not touched. 133 | 134 | # Details 135 | The sequence of steps taken by the script can be summed up as: 136 | ## OS Packages 137 | As part of the installation, the following OS packages are installed: 138 | * git 139 | * build-essential 140 | * nginx 141 | * postgresql 142 | * libpq-dev 143 | * python-dev|python3-dev 144 | * python-pip|python3-pip 145 | 146 | Note that the package `python-pip` & `python3-pip` is a python package, though 147 | it is installed from the OS package distribution mechanism. 148 | 149 | After successful completion of the above, necessary global python packages 150 | are installed. These are: 151 | * virtualenv 152 | * supervisor 153 | These are installed using Python Package Installer, which itself installed in 154 | the previous step(through `python-pip`). 155 | 156 | ## User/Group 157 | A dedicated user account is created for the app server. This helps isolate 158 | the app server's run environment from other normal user accounts which are 159 | typically used to login to the machine to do management tasks. This is an 160 | automation account and disabled for interactive login. This user account name 161 | defaults the `` argument of the command line and is made a member of 162 | group `webapps`. If this group does not exist, it will be created. 163 | 164 | Home folder for this account will be set to `/webapps/_project`. 165 | 166 | ## Runtime environment 167 | Post user/group creation, the runtime evironment for the app is created. First 168 | the python virtual environment is created. This is created in the home folder 169 | of the dedicated automation user account. Therefore `python` and related 170 | binaries will be installed to `~/bin` folder and python packages will be 171 | installed at `~/local/lib/python2.x` or `~/local/lib/python3.x`. 172 | 173 | The following packages will be installed to the dedicated python virtual 174 | environment just created: 175 | 176 | * django 177 | * psycopg2 178 | * gunicorn 179 | * setproctitle 180 | 181 | ## Runtime scripts 182 | The script then generates two bash scripts that will be used to start the app 183 | server. The script is split into two files such that it can be used for 184 | interactive shell for manual interaction with the Django app server. These 185 | scripts are: 186 | * prepare_env.sh 187 | * gunicorn_start.sh 188 | 189 | The former is an environment script and is to be sourced from the interactive 190 | shell whereas the latter is the master script that is used to the start Gunicorn 191 | app server (Supervisor will be configured to start the app server through this 192 | script). 193 | 194 | So if manual interaction with the production site is desired, one may sudo into 195 | the `` user account and source it as: 196 | 197 | ``` 198 | $ sudo -u -i 199 | $ source ./prepare_env.sh 200 | ``` 201 | 202 | ## Database 203 | A PostgreSQL database, with the same name as `` will be created for 204 | use by the Django app. A dedicated PostgreSQL role with the same name will 205 | also be created. This role is configured with a random password and this 206 | password is stored in `~/.django_db_password`. 207 | 208 | ## Nginx Configuration 209 | A Nginx conf file for the requested domain will be created in 210 | `/etc/nginx/sites-available` and it will be enabled by creating the necessary 211 | soft link in `/etc/nginx/sites-enabled`. This configuration will be setup such 212 | all requests to the domain (specified in the command line argument) will be 213 | proxied to the Gunicorn server started through the script created earlier. 214 | 215 | ## Setup supervisor 216 | Supervisor installation does not create its own configuration file, 217 | `/etc/supervisord.conf`. Also Supervisor installation does not create the 218 | necessary `init.d` script to start it automatically and manage it interactively. 219 | This script addresses both by creating the necessary files for this -- 220 | `/etc/supervisord.conf` and `/etc/init.d/supervisord`, a script to manage it 221 | using the Ubuntu standard `service {start|stop}` commands. 222 | 223 | The script will also create `/etc/supervisor//conf` file which will 224 | contain the configuration for the Gunicorn app server. 225 | 226 | ## Placeholder Django app 227 | A placeholder Django app will be created in 228 | `/webapps/_project/`. This placeholder app can be replaced 229 | with the real production code. 230 | 231 | ## Other Details 232 | ### Django secret key 233 | Django applications generated using its admin command, will put the secret key 234 | in the generated settings file. Since the Django source is shared between 235 | different developers and since the code is typically stored in an online 236 | repository, this key gets shared across different machines and usere. Therefore 237 | production sites should not use this embedded key and instead should use its 238 | own key. To automate this the script will generate a random string which is 239 | stored in `~/.django_secret_key`. 240 | 241 | ### Environment variables 242 | Both the secret key and the database role password (stored in 243 | `~/.django_secret_key` and `~/.django_db_password`) is made available to 244 | the Django app through the environment variables `SECRET_KEY` and 245 | `DB_PASSWORD` respectively. These variables will be set through the script 246 | `~/prepare_env.sh`. 247 | 248 | Additionally, the settings file to be used with the Django app is set 249 | through the environment variable `DJANGO_SETTINGS_MODULE`. 250 | 251 | Providing these three environment variables, allows the Django app to use 252 | different values for these three Django app parameters in production 253 | environment than what is used in development. 254 | 255 | # Notes 256 | The script, `~/gunicorn_start.sh` is the only connection between the Gunicorn 257 | configuration and the Django app server. So as long as running this script 258 | results in a valid app server instance, the rest of the system will remain 259 | of the expected integrity. So if the Django app being deployed requires 260 | different configuration, any requisite changes can be made from this file. 261 | 262 | `~/prepare_env.sh` is sourced from this file to setup the environment. So this 263 | file is also available for further tweaking to suit the deployed app's 264 | runtime environment. 265 | -------------------------------------------------------------------------------- /common_funcs.sh: -------------------------------------------------------------------------------- 1 | # To be sourced from the other modules 2 | 3 | # Exits the script with a message and exit code 1 4 | function error_exit 5 | { 6 | echo "$1" 1>&2 7 | exit 1 8 | } 9 | 10 | # check if we're being run as root 11 | function check_root 12 | { 13 | if [ "$EUID" -ne 0 ]; then 14 | echo "Please run as root" 15 | exit 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /create_django_project_run_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Usage: 4 | # $ create_django_project_run_env 5 | 6 | source ./common_funcs.sh 7 | 8 | check_root 9 | 10 | # conventional values that we'll use throughout the script 11 | APPNAME=$1 12 | DOMAINNAME=$2 13 | PYTHON_VERSION=$3 14 | 15 | # check appname was supplied as argument 16 | if [ "$APPNAME" == "" ] || [ "$DOMAINNAME" == "" ]; then 17 | echo "Usage:" 18 | echo " $ create_django_project_run_env [python-version]" 19 | echo 20 | echo " Python version is 2 or 3 and defaults to 3 if not specified. Subversion" 21 | echo " of Python will be determined during runtime. The required Python version" 22 | echo " has to be installed and available globally." 23 | echo 24 | exit 1 25 | fi 26 | 27 | # Default python version to 3. OS has to have it installed. 28 | if [ "$PYTHON_VERSION" == "" ]; then 29 | PYTHON_VERSION=3 30 | fi 31 | 32 | if [ "$PYTHON_VERSION" != "3" -a "$PYTHON_VERSION" != "2" ]; then 33 | error_exit "Invalid Python version specified. Acceptable values are 2 or 3 (default)" 34 | fi 35 | 36 | ./install_os_prereq.sh $PYTHON_VERSION 37 | error_exit "Error setting up OS prerequisites." 38 | 39 | ./deploy_django_project.sh $APPNAME $DOMAINNAME $PYTHON_VERSION 40 | -------------------------------------------------------------------------------- /deploy_django_project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Usage: 4 | # $ create_django_project_run_env 5 | 6 | source ./common_funcs.sh 7 | 8 | check_root 9 | 10 | # conventional values that we'll use throughout the script 11 | APPNAME=$1 12 | DOMAINNAME=$2 13 | PYTHON_VERSION=$3 14 | 15 | # check appname was supplied as argument 16 | if [ "$APPNAME" == "" ] || [ "$DOMAINNAME" == "" ]; then 17 | echo "Usage:" 18 | echo " $ create_django_project_run_env [python-version]" 19 | echo 20 | echo " Python version is 2 or 3 and defaults to 3 if not specified. Subversion" 21 | echo " of Python will be determined during runtime. The required Python version" 22 | echo " has to be installed and available globally." 23 | echo 24 | exit 1 25 | fi 26 | 27 | # Default python version to 3. OS has to have it installed. 28 | if [ "$PYTHON_VERSION" == "" ]; then 29 | PYTHON_VERSION=3 30 | fi 31 | 32 | if [ "$PYTHON_VERSION" != "3" -a "$PYTHON_VERSION" != "2" ]; then 33 | error_exit "Invalid Python version specified. Acceptable values are 2 or 3 (default)" 34 | fi 35 | 36 | GROUPNAME=webapps 37 | # app folder name under /webapps/_project 38 | APPFOLDER=$1_project 39 | APPFOLDERPATH=/$GROUPNAME/$APPFOLDER 40 | 41 | # Determine requested Python version & subversion 42 | if [ "$PYTHON_VERSION" == "3" ]; then 43 | PYTHON_VERSION_STR=`python3 -c 'import sys; ver = "{0}.{1}".format(sys.version_info[:][0], sys.version_info[:][1]); print(ver)'` 44 | else 45 | PYTHON_VERSION_STR=`python -c 'import sys; ver = "{0}.{1}".format(sys.version_info[:][0], sys.version_info[:][1]); print ver'` 46 | fi 47 | 48 | # Verify required python version is installed 49 | echo "Python version: $PYTHON_VERSION_STR" 50 | 51 | # ################################################################### 52 | # Create the app folder 53 | # ################################################################### 54 | echo "Creating app folder '$APPFOLDERPATH'..." 55 | mkdir -p /$GROUPNAME/$APPFOLDER || error_exit "Could not create app folder" 56 | 57 | # test the group 'webapps' exists, and if it doesn't create it 58 | getent group $GROUPNAME 59 | if [ $? -ne 0 ]; then 60 | echo "Creating group '$GROUPNAME' for automation accounts..." 61 | groupadd --system $GROUPNAME || error_exit "Could not create group 'webapps'" 62 | fi 63 | 64 | # create the app user account, same name as the appname 65 | grep "$APPNAME:" /etc/passwd 66 | if [ $? -ne 0 ]; then 67 | echo "Creating automation user account '$APPNAME'..." 68 | useradd --system --gid $GROUPNAME --shell /bin/bash --home $APPFOLDERPATH $APPNAME || error_exit "Could not create automation user account '$APPNAME'" 69 | fi 70 | 71 | # change ownership of the app folder to the newly created user account 72 | echo "Setting ownership of $APPFOLDERPATH and its descendents to $APPNAME:$GROUPNAME..." 73 | chown -R $APPNAME:$GROUPNAME $APPFOLDERPATH || error_exit "Error setting ownership" 74 | # give group execution rights in the folder; 75 | # TODO: is this necessary? why? 76 | chmod g+x $APPFOLDERPATH || error_exit "Error setting group execute flag" 77 | 78 | # install python virtualenv in the APPFOLDER 79 | echo "Creating environment setup for django app..." 80 | if [ "$PYTHON_VERSION" == "3" ]; then 81 | su -l $APPNAME << 'EOF' 82 | pwd 83 | echo "Setting up python virtualenv..." 84 | virtualenv -p python3 . || error_exit "Error installing Python 3 virtual environment to app folder" 85 | 86 | EOF 87 | else 88 | su -l $APPNAME << 'EOF' 89 | pwd 90 | echo "Setting up python virtualenv..." 91 | virtualenv . || error_exit "Error installing Python 2 virtual environment to app folder" 92 | 93 | EOF 94 | fi 95 | 96 | # ################################################################### 97 | # In the new app specific virtual environment: 98 | # 1. Upgrade pip 99 | # 2. Install django in it. 100 | # 3. Create following folders:- 101 | # static -- Django static files (to be collected here) 102 | # media -- Django media files 103 | # logs -- nginx, gunicorn & supervisord logs 104 | # nginx -- nginx configuration for this domain 105 | # ssl -- SSL certificates for the domain(NA if LetsEncrypt is used) 106 | # ################################################################### 107 | su -l $APPNAME << 'EOF' 108 | source ./bin/activate 109 | # upgrade pip 110 | pip install --upgrade pip || error_exist "Error upgrading pip to the latest version" 111 | # install prerequisite python packages for a django app using pip 112 | echo "Installing base python packages for the app..." 113 | # Standard django packages which will be installed. If any of these fail, script will abort 114 | DJANGO_PKGS=('django' 'psycopg2' 'gunicorn' 'setproctitle') 115 | for dpkg in "${DJANGO_PKGS[@]}" 116 | do 117 | echo "Installing $dpkg..." 118 | pip install $dpkg || error_exit "Error installing $dpkg" 119 | done 120 | # create the default folders where we store django app's resources 121 | echo "Creating static file folders..." 122 | mkdir logs nginx run static media || error_exit "Error creating static folders" 123 | # Create the UNIX socket file for WSGI interface 124 | echo "Creating WSGI interface UNIX socket file..." 125 | python -c "import socket as s; sock = s.socket(s.AF_UNIX); sock.bind('./run/gunicorn.sock')" 126 | EOF 127 | 128 | # ################################################################### 129 | # Generate Django production secret key 130 | # ################################################################### 131 | echo "Generating Django secret key..." 132 | DJANGO_SECRET_KEY=`openssl rand -base64 48` 133 | if [ $? -ne 0 ]; then 134 | error_exit "Error creating secret key." 135 | fi 136 | echo $DJANGO_SECRET_KEY > $APPFOLDERPATH/.django_secret_key 137 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/.django_secret_key 138 | 139 | # ################################################################### 140 | # Generate DB password 141 | # ################################################################### 142 | echo "Creating secure password for database role..." 143 | DBPASSWORD=`openssl rand -base64 32` 144 | if [ $? -ne 0 ]; then 145 | error_exit "Error creating secure password for database role." 146 | fi 147 | echo $DBPASSWORD > $APPFOLDERPATH/.django_db_password 148 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/.django_db_password 149 | 150 | # ################################################################### 151 | # Create the script that will init the virtual environment. This 152 | # script will be called from the gunicorn start script created next. 153 | # ################################################################### 154 | echo "Creating virtual environment setup script..." 155 | cat > /tmp/prepare_env.sh << EOF 156 | DJANGODIR=$APPFOLDERPATH/$APPNAME # Django project directory 157 | 158 | export DJANGO_SETTINGS_MODULE=$APPNAME.settings.production # settings file for the app 159 | export PYTHONPATH=\$DJANGODIR:\$PYTHONPATH 160 | export SECRET_KEY=`cat $APPFOLDERPATH/.django_secret_key` 161 | export DB_PASSWORD=`cat $APPFOLDERPATH/.django_db_password` 162 | 163 | cd $APPFOLDERPATH 164 | source ./bin/activate 165 | EOF 166 | mv /tmp/prepare_env.sh $APPFOLDERPATH 167 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/prepare_env.sh 168 | 169 | # ################################################################### 170 | # Create gunicorn start script which will be spawned and managed 171 | # using supervisord. 172 | # ################################################################### 173 | echo "Creating gunicorn startup script..." 174 | cat > /tmp/gunicorn_start.sh << EOF 175 | #!/bin/bash 176 | # Makes the following assumptions: 177 | # 178 | # 1. All applications are located in a subfolder within /webapps 179 | # 2. Each app gets a dedicated subfolder under /webapps. This will 180 | # be referred to as the app folder. 181 | # 3. The group account 'webapps' exists and each app is to be executed 182 | # under the user account . 183 | # 4. The app folder and all its recursive contents are owned by 184 | # :webapps. 185 | # 5. The django app is stored under /webapps// folder. 186 | # 187 | 188 | cd $APPFOLDERPATH 189 | source ./prepare_env.sh 190 | 191 | SOCKFILE=$APPFOLDERPATH/run/gunicorn.sock # we will communicte using this unix socket 192 | USER=$APPNAME # the user to run as 193 | GROUP=$GROUPNAME # the group to run as 194 | NUM_WORKERS=3 # how many worker processes should Gunicorn spawn 195 | DJANGO_WSGI_MODULE=$APPNAME.wsgi # WSGI module name 196 | 197 | echo "Starting $APPNAME as \`whoami\`" 198 | 199 | # Create the run directory if it doesn't exist 200 | RUNDIR=\$(dirname \$SOCKFILE) 201 | test -d \$RUNDIR || mkdir -p \$RUNDIR 202 | 203 | # Start your Django Unicorn 204 | # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon) 205 | exec ./bin/gunicorn \${DJANGO_WSGI_MODULE}:application \ 206 | --name $APPNAME \ 207 | --workers \$NUM_WORKERS \ 208 | --user=\$USER --group=\$GROUP \ 209 | --bind=unix:\$SOCKFILE \ 210 | --log-level=debug \ 211 | --log-file=- 212 | EOF 213 | 214 | # Move the script to app folder 215 | mv /tmp/gunicorn_start.sh $APPFOLDERPATH 216 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/gunicorn_start.sh 217 | chmod u+x $APPFOLDERPATH/gunicorn_start.sh 218 | 219 | # ################################################################### 220 | # Create the PostgreSQL database and associated role for the app 221 | # Database and role name would be the same as the argument 222 | # ################################################################### 223 | echo "Creating PostgreSQL role '$APPNAME'..." 224 | su postgres -c "createuser -S -D -R -w $APPNAME" 225 | echo "Changing password of database role..." 226 | su postgres -c "psql -c \"ALTER USER $APPNAME WITH PASSWORD '$DBPASSWORD';\"" 227 | echo "Creating PostgreSQL database '$APPNAME'..." 228 | su postgres -c "createdb --owner $APPNAME $APPNAME" 229 | 230 | # ################################################################### 231 | # Create nginx template in $APPFOLDERPATH/nginx 232 | # ################################################################### 233 | mkdir -p $APPFOLDERPATH/nginx 234 | APPSERVERNAME=$APPNAME 235 | APPSERVERNAME+=_gunicorn 236 | cat > $APPFOLDERPATH/nginx/$APPNAME.conf << EOF 237 | upstream $APPSERVERNAME { 238 | server unix:$APPFOLDERPATH/run/gunicorn.sock fail_timeout=0; 239 | } 240 | server { 241 | listen 80; 242 | server_name $DOMAINNAME; 243 | 244 | client_max_body_size 5M; 245 | keepalive_timeout 5; 246 | underscores_in_headers on; 247 | 248 | access_log $APPFOLDERPATH/logs/nginx-access.log; 249 | error_log $APPFOLDERPATH/logs/nginx-error.log; 250 | 251 | location /media { 252 | alias $APPFOLDERPATH/media; 253 | } 254 | location /static { 255 | alias $APPFOLDERPATH/static; 256 | } 257 | location /static/admin { 258 | alias $APPFOLDERPATH/lib/python$PYTHON_VERSION_STR/site-packages/django/contrib/admin/static/admin/; 259 | } 260 | # This would redirect http site access to HTTPS. Uncomment to enable 261 | #location / { 262 | # rewrite ^ https://\$http_host\$request_uri? permanent; 263 | #} 264 | # To make the site pure HTTPS, comment the following section while 265 | # uncommenting the above section. Also uncoment the HTTPS section 266 | location / { 267 | proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; 268 | proxy_set_header Host \$http_host; 269 | proxy_redirect off; 270 | proxy_pass http://$APPSERVERNAME; 271 | } 272 | } 273 | 274 | # Uncomment this if you want to enable HTTPS access. Also, remember to install 275 | # the site certificate, either purcahased or generated. 276 | #server { 277 | # listen 443 default ssl; 278 | # server_name $DOMAINNAME; 279 | # 280 | # client_max_body_size 5M; 281 | # keepalive_timeout 5; 282 | # 283 | # ssl_certificate /etc/nginx/ssl/cert_chain.crt; 284 | # ssl_certificate_key $APPFOLDERPATH/ssl/$DOMAINNAME.key; 285 | # 286 | # access_log $APPFOLDERPATH/logs/nginx-access.log; 287 | # error_log $APPFOLDERPATH/logs/nginx-error.log; 288 | # 289 | # location /media { 290 | # alias $APPFOLDERPATH/media; 291 | # } 292 | # location /static { 293 | # alias $APPFOLDERPATH/static; 294 | # } 295 | # location /static/admin { 296 | # alias $APPFOLDERPATH/lib/python$PYTHON_VERSION_STR/site-packages/django/contrib/admin/static/admin/; 297 | # } 298 | # location / { 299 | # proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; 300 | # proxy_set_header Host \$http_host; 301 | # proxy_set_header X-Forwarded-Proto \$scheme; 302 | # proxy_redirect off; 303 | # proxy_pass http://$APPSERVERNAME; 304 | # } 305 | #} 306 | EOF 307 | 308 | cat > $APPFOLDERPATH/nginx/index.html << EOF 309 | 310 | 311 | 312 | 313 | 314 | $DOMAINNAME - UNDER MAINTENANCE 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 333 | 334 | 335 |
336 |
337 |
338 |
339 | 340 |
341 |

Sorry, we're down for maintenance.

342 |

We'll be back shortly!

343 |
344 |
345 |
346 |
347 | ©2018 SmallPearl LLC. 348 |
349 | 350 | 351 | EOF 352 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/nginx/index.html 353 | 354 | # nginx site maintenance configuration 355 | cat > $APPFOLDERPATH/nginx/$APPNAME.maintenance.conf << EOF 356 | server { 357 | listen 80; 358 | server_name $DOMAINNAME; 359 | 360 | root $APPFOLDERPATH/nginx; 361 | index index.html; 362 | 363 | client_max_body_size 5M; 364 | keepalive_timeout 5; 365 | underscores_in_headers on; 366 | 367 | access_log $APPFOLDERPATH/logs/nginx-access.log; 368 | error_log $APPFOLDERPATH/logs/nginx-error.log; 369 | 370 | location / { 371 | try_files \$uri \$uri/ =404; 372 | } 373 | } 374 | EOF 375 | 376 | # make a symbolic link to the nginx conf file in sites-enabled 377 | ln -sf $APPFOLDERPATH/nginx/$APPNAME.conf /etc/nginx/sites-enabled/$APPNAME 378 | 379 | # ################################################################### 380 | # Setup supervisor 381 | # ################################################################### 382 | 383 | # Copy supervisord.conf if it does not exist 384 | if [ ! -f /etc/supervisord.conf ]; then 385 | cp ./supervisord.conf /etc || error_exit "Error copying supervisord.conf" 386 | fi 387 | 388 | # Create the supervisor application conf file 389 | mkdir -p /etc/supervisor 390 | cat > /etc/supervisor/$APPNAME.conf << EOF 391 | [program:$APPNAME] 392 | command = $APPFOLDERPATH/gunicorn_start.sh 393 | user = $APPNAME 394 | stdout_logfile = $APPFOLDERPATH/logs/gunicorn_supervisor.log 395 | redirect_stderr = true 396 | EOF 397 | 398 | SUPERVISORD_ACTION='reload' 399 | # Create supervisord init.d script that can be controlled with service 400 | if [ ! -f /etc/init.d/supervisord ]; then 401 | echo "Setting up supervisor to autostart during bootup..." 402 | cp ./supervisord /etc/init.d || error_exit "Error copying /etc/init.d/supervisord" 403 | # enable execute flag on the script 404 | chmod +x /etc/init.d/supervisord || error_exit "Error setting execute flag on supervisord" 405 | # create the entries in runlevel folders to autostart supervisord 406 | update-rc.d supervisord defaults || error_exit "Error configuring supervisord to autostart" 407 | SUPERVISORD_ACTION='start' 408 | fi 409 | 410 | # Now create a quasi django project that can be run using a GUnicorn script 411 | echo "Installing quasi django project..." 412 | su -l $APPNAME << EOF 413 | source ./bin/activate 414 | django-admin.py startproject $APPNAME 415 | 416 | # Change Django's default settings.py to use app/settings/{base.py|dev.py|production.py} 417 | mv $APPNAME/$APPNAME/settings.py $APPNAME/$APPNAME/base.py 418 | mkdir $APPNAME/$APPNAME/settings 419 | mv $APPNAME/$APPNAME/base.py $APPNAME/$APPNAME/settings 420 | 421 | EOF 422 | 423 | echo "Changing quasi django project settings to production.py..." 424 | cat > $APPFOLDERPATH/$APPNAME/$APPNAME/settings/production.py << EOF 425 | from .base import * 426 | 427 | def get_env_variable(var): 428 | '''Return the environment variable value or raise error''' 429 | try: 430 | return os.environ[var] 431 | except KeyError: 432 | error_msg = "Set the {} environment variable".format(var) 433 | raise ImproperlyConfigured(error_msg) 434 | 435 | DEBUG = False 436 | 437 | # Note that this is a wildcard specification. So it matches 438 | # smallpearl.com as well as www.smallpearl.com 439 | ALLOWED_HOSTS = ['.$DOMAINNAME'] 440 | 441 | # CSRF middleware token & session cookie will only be transmitted over HTTPS 442 | CSRF_COOKIE_SECURE = True 443 | SESSION_COOKIE_SECURE = True 444 | 445 | # Get secret hash key from environment variable (set by ./prepre_env.sh) 446 | SECRET_KEY = get_env_variable('SECRET_KEY') 447 | 448 | # Get production DB password is from environment variable 449 | DATABASES = { 450 | 'default': { 451 | 'ENGINE': 'django.db.backends.postgresql', 452 | 'NAME': '$APPNAME', 453 | 'USER': '$APPNAME', 454 | 'PASSWORD': get_env_variable('DB_PASSWORD'), 455 | 'HOST': 'localhost', 456 | 'PORT': '', 457 | } 458 | } 459 | 460 | # This setting corresponds to NGINX server configuration, which adds this 461 | # to the request headers that is proxied to gunicorn app server. 462 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 463 | 464 | EOF 465 | chown $APPNAME:$GROUPNAME $APPFOLDERPATH/$APPNAME/$APPNAME/settings/production.py 466 | 467 | # ################################################################### 468 | # Reload/start supervisord and nginx 469 | # ################################################################### 470 | # Start/reload the supervisord daemon 471 | service supervisord status > /dev/null 472 | if [ $? -eq 0 ]; then 473 | # Service is running, restart it 474 | service supervisord restart || error_exit "Error restarting supervisord" 475 | else 476 | # Service is not running, probably it's been installed first. Start it 477 | service supervisord start || error_exit "Error starting supervisord" 478 | fi 479 | 480 | # Reload nginx so that requests to domain are redirected to the gunicorn process 481 | nginx -s reload || error_exit "Error reloading nginx. Check configuration files" 482 | 483 | echo "Done!" 484 | -------------------------------------------------------------------------------- /install_os_prereq.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./common_funcs.sh 4 | 5 | check_root 6 | 7 | PYTHON_VERSION=$1 8 | 9 | # check appname was supplied as argument 10 | if [ "$PYTHON_VERSION" == "" ]; then 11 | echo "Usage:" 12 | echo " $ install_os_prereq.sh [python-version]" 13 | echo 14 | echo " Python version is 2 or 3 and defaults to 3 if not specified. Subversion" 15 | echo " of Python will be determined during runtime. The required Python version" 16 | echo " has to be installed and available globally." 17 | echo 18 | exit 1 19 | fi 20 | 21 | # Default python version to 3. OS has to have it installed. 22 | if [ "$PYTHON_VERSION" == "" ]; then 23 | PYTHON_VERSION=3 24 | fi 25 | 26 | if [ "$PYTHON_VERSION" != "3" -a "$PYTHON_VERSION" != "2" ]; then 27 | error_exit "Invalid Python version specified. Acceptable values are 2 or 3 (default)" 28 | fi 29 | 30 | # Prerequisite standard packages. If any of these are missing, 31 | # script will attempt to install it. If installation fails, it will abort. 32 | if [ "$PYTHON_VERSION" == "3" ]; then 33 | PIP="pip3" 34 | LINUX_PREREQ=('git' 'build-essential' 'python3-dev' 'python3-pip' 'nginx' 'postgresql' 'libpq-dev' ) 35 | else 36 | PIP="pip" 37 | LINUX_PREREQ=('git' 'build-essential' 'python-dev' 'python-pip' 'nginx' 'postgresql' 'libpq-dev') 38 | fi 39 | PYTHON_PREREQ=('virtualenv' 'supervisor') 40 | 41 | # Test prerequisites 42 | echo "Checking if required packages are installed..." 43 | declare -a MISSING 44 | for pkg in "${LINUX_PREREQ[@]}" 45 | do 46 | echo "Installing '$pkg'..." 47 | apt-get -y install $pkg 48 | if [ $? -ne 0 ]; then 49 | echo "Error installing system package '$pkg'" 50 | exit 1 51 | fi 52 | done 53 | 54 | for ppkg in "${PYTHON_PREREQ[@]}" 55 | do 56 | echo "Installing Python package '$ppkg'..." 57 | $PIP install $ppkg 58 | if [ $? -ne 0 ]; then 59 | echo "Error installing python package '$ppkg'" 60 | exit 1 61 | fi 62 | done 63 | 64 | if [ ${#MISSING[@]} -ne 0 ]; then 65 | echo "Following required packages are missing, please install them first." 66 | echo ${MISSING[*]} 67 | exit 1 68 | fi 69 | 70 | echo "All required packages have been installed!" 71 | 72 | -------------------------------------------------------------------------------- /supervisord: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: supervisord 4 | # Required-Start: $remote_fs 5 | # Required-Stop: $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Example initscript 9 | # Description: This file should be used to construct scripts to be 10 | # placed in /etc/init.d. 11 | ### END INIT INFO 12 | 13 | # Author: Dan MacKinlay 14 | # Based on instructions by Bertrand Mathieu 15 | # http://zebert.blogspot.com/2009/05/installing-django-solr-varnish-and.html 16 | 17 | # Do NOT "set -e" 18 | 19 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 20 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 21 | DESC="A process control system" 22 | NAME=supervisord 23 | DAEMON=/usr/local/bin/supervisord 24 | DAEMON_ARGS="-- -c /etc/supervisord.conf" 25 | PIDFILE=/tmp/$NAME.pid 26 | SCRIPTNAME=/etc/init.d/$NAME 27 | 28 | # Exit if the package is not installed 29 | [ -x "$DAEMON" ] || exit 0 30 | 31 | # Read configuration variable file if it is present 32 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME 33 | 34 | # Load the VERBOSE setting and other rcS variables 35 | . /lib/init/vars.sh 36 | 37 | # Define LSB log_* functions. 38 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 39 | . /lib/lsb/init-functions 40 | 41 | # 42 | # Function that starts the daemon/service 43 | # 44 | do_start() 45 | { 46 | # Return 47 | # 0 if daemon has been started 48 | # 1 if daemon was already running 49 | # 2 if daemon could not be started 50 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ 51 | || return 1 52 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \ 53 | $DAEMON_ARGS \ 54 | || return 2 55 | # Add code here, if necessary, that waits for the process to be ready 56 | # to handle requests from services started subsequently which depend 57 | # on this one. As a last resort, sleep for some time. 58 | } 59 | 60 | # 61 | # Function that stops the daemon/service 62 | # 63 | do_stop() 64 | { 65 | # Return 66 | # 0 if daemon has been stopped 67 | # 1 if daemon was already stopped 68 | # 2 if daemon could not be stopped 69 | # other if a failure occurred 70 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME 71 | RETVAL="$?" 72 | [ "$RETVAL" = 2 ] && return 2 73 | # Wait for children to finish too if this is a daemon that forks 74 | # and if the daemon is only ever run from this initscript. 75 | # If the above conditions are not satisfied then add some other code 76 | # that waits for the process to drop all resources that could be 77 | # needed by services started subsequently. A last resort is to 78 | # sleep for some time. 79 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 80 | [ "$?" = 2 ] && return 2 81 | # Many daemons don't delete their pidfiles when they exit. 82 | rm -f $PIDFILE 83 | return "$RETVAL" 84 | } 85 | 86 | # 87 | # Function that sends a SIGHUP to the daemon/service 88 | # 89 | do_reload() { 90 | # 91 | # If the daemon can reload its configuration without 92 | # restarting (for example, when it is sent a SIGHUP), 93 | # then implement that here. 94 | # 95 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME 96 | return 0 97 | } 98 | 99 | case "$1" in 100 | start) 101 | log_daemon_msg "Starting $NAME.." 102 | do_start 103 | case "$?" in 104 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 105 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 106 | esac 107 | ;; 108 | stop) 109 | log_daemon_msg "Stopping $NAME..." 110 | do_stop 111 | case "$?" in 112 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 113 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 114 | esac 115 | ;; 116 | #reload|force-reload) 117 | # 118 | # If do_reload() is not implemented then leave this commented out 119 | # and leave 'force-reload' as an alias for 'restart'. 120 | # 121 | #log_daemon_msg "Reloading $NAME" 122 | #do_reload 123 | #log_end_msg $? 124 | #;; 125 | restart|force-reload) 126 | # 127 | # If the "reload" option is implemented then remove the 128 | # 'force-reload' alias 129 | # 130 | log_daemon_msg "Stopping $NAME..." 131 | do_stop 132 | case "$?" in 133 | 0|1) 134 | log_daemon_msg "Starting $NAME..." 135 | do_start 136 | case "$?" in 137 | 0) log_end_msg 0 ;; 138 | 1) log_end_msg 1 ;; # Old process is still running 139 | *) log_end_msg 1 ;; # Failed to start 140 | esac 141 | ;; 142 | *) 143 | # Failed to stop 144 | log_end_msg 1 145 | ;; 146 | esac 147 | ;; 148 | status) 149 | status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? 150 | ;; 151 | 152 | *) 153 | echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2 154 | exit 3 155 | ;; 156 | esac 157 | 158 | : 159 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | ; Sample supervisor config file. 2 | ; 3 | ; For more information on the config file, please see: 4 | ; http://supervisord.org/configuration.html 5 | ; 6 | ; Notes: 7 | ; - Shell expansion ("~" or "$HOME") is not supported. Environment 8 | ; variables can be expanded using this syntax: "%(ENV_HOME)s". 9 | ; - Comments must have a leading space: "a=b ;comment" not "a=b;comment". 10 | 11 | [unix_http_server] 12 | file=/tmp/supervisor.sock ; (the path to the socket file) 13 | ;chmod=0700 ; socket file mode (default 0700) 14 | ;chown=nobody:nogroup ; socket file uid:gid owner 15 | ;username=user ; (default is no username (open server)) 16 | ;password=123 ; (default is no password (open server)) 17 | 18 | ;[inet_http_server] ; inet (TCP) server disabled by default 19 | ;port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) 20 | ;username=user ; (default is no username (open server)) 21 | ;password=123 ; (default is no password (open server)) 22 | 23 | [supervisord] 24 | logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) 25 | logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) 26 | logfile_backups=10 ; (num of main logfile rotation backups;default 10) 27 | loglevel=info ; (log level;default info; others: debug,warn,trace) 28 | pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) 29 | nodaemon=false ; (start in foreground if true;default false) 30 | minfds=1024 ; (min. avail startup file descriptors;default 1024) 31 | minprocs=200 ; (min. avail process descriptors;default 200) 32 | ;umask=022 ; (process file creation umask;default 022) 33 | ;user=chrism ; (default is current user, required if root) 34 | ;identifier=supervisor ; (supervisord identifier, default is 'supervisor') 35 | ;directory=/tmp ; (default is not to cd during start) 36 | ;nocleanup=true ; (don't clean up tempfiles at start;default false) 37 | ;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP) 38 | ;environment=KEY="value" ; (key value pairs to add to environment) 39 | ;strip_ansi=false ; (strip ansi escape codes in logs; def. false) 40 | 41 | ; the below section must remain in the config file for RPC 42 | ; (supervisorctl/web interface) to work, additional interfaces may be 43 | ; added by defining them in separate rpcinterface: sections 44 | [rpcinterface:supervisor] 45 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 46 | 47 | [supervisorctl] 48 | serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket 49 | ;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket 50 | ;username=chris ; should be same as http_username if set 51 | ;password=123 ; should be same as http_password if set 52 | ;prompt=mysupervisor ; cmd line prompt (default "supervisor") 53 | ;history_file=~/.sc_history ; use readline history if available 54 | 55 | ; The below sample program section shows all possible program subsection values, 56 | ; create one or more 'real' program: sections to be able to control them under 57 | ; supervisor. 58 | 59 | ;[program:theprogramname] 60 | ;command=/bin/cat ; the program (relative uses PATH, can take args) 61 | ;process_name=%(program_name)s ; process_name expr (default %(program_name)s) 62 | ;numprocs=1 ; number of processes copies to start (def 1) 63 | ;directory=/tmp ; directory to cwd to before exec (def no cwd) 64 | ;umask=022 ; umask for process (default None) 65 | ;priority=999 ; the relative start priority (default 999) 66 | ;autostart=true ; start at supervisord start (default: true) 67 | ;startsecs=1 ; # of secs prog must stay up to be running (def. 1) 68 | ;startretries=3 ; max # of serial start failures when starting (default 3) 69 | ;autorestart=unexpected ; when to restart if exited after running (def: unexpected) 70 | ;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2) 71 | ;stopsignal=QUIT ; signal used to kill process (default TERM) 72 | ;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10) 73 | ;stopasgroup=false ; send stop signal to the UNIX process group (default false) 74 | ;killasgroup=false ; SIGKILL the UNIX process group (def false) 75 | ;user=chrism ; setuid to this UNIX account to run the program 76 | ;redirect_stderr=true ; redirect proc stderr to stdout (default false) 77 | ;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO 78 | ;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 79 | ;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10) 80 | ;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) 81 | ;stdout_events_enabled=false ; emit events on stdout writes (default false) 82 | ;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO 83 | ;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 84 | ;stderr_logfile_backups=10 ; # of stderr logfile backups (default 10) 85 | ;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) 86 | ;stderr_events_enabled=false ; emit events on stderr writes (default false) 87 | ;environment=A="1",B="2" ; process environment additions (def no adds) 88 | ;serverurl=AUTO ; override serverurl computation (childutils) 89 | 90 | ; The below sample eventlistener section shows all possible 91 | ; eventlistener subsection values, create one or more 'real' 92 | ; eventlistener: sections to be able to handle event notifications 93 | ; sent by supervisor. 94 | 95 | ;[eventlistener:theeventlistenername] 96 | ;command=/bin/eventlistener ; the program (relative uses PATH, can take args) 97 | ;process_name=%(program_name)s ; process_name expr (default %(program_name)s) 98 | ;numprocs=1 ; number of processes copies to start (def 1) 99 | ;events=EVENT ; event notif. types to subscribe to (req'd) 100 | ;buffer_size=10 ; event buffer queue size (default 10) 101 | ;directory=/tmp ; directory to cwd to before exec (def no cwd) 102 | ;umask=022 ; umask for process (default None) 103 | ;priority=-1 ; the relative start priority (default -1) 104 | ;autostart=true ; start at supervisord start (default: true) 105 | ;startsecs=1 ; # of secs prog must stay up to be running (def. 1) 106 | ;startretries=3 ; max # of serial start failures when starting (default 3) 107 | ;autorestart=unexpected ; autorestart if exited after running (def: unexpected) 108 | ;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2) 109 | ;stopsignal=QUIT ; signal used to kill process (default TERM) 110 | ;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10) 111 | ;stopasgroup=false ; send stop signal to the UNIX process group (default false) 112 | ;killasgroup=false ; SIGKILL the UNIX process group (def false) 113 | ;user=chrism ; setuid to this UNIX account to run the program 114 | ;redirect_stderr=false ; redirect_stderr=true is not allowed for eventlisteners 115 | ;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO 116 | ;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 117 | ;stdout_logfile_backups=10 ; # of stdout logfile backups (default 10) 118 | ;stdout_events_enabled=false ; emit events on stdout writes (default false) 119 | ;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO 120 | ;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 121 | ;stderr_logfile_backups=10 ; # of stderr logfile backups (default 10) 122 | ;stderr_events_enabled=false ; emit events on stderr writes (default false) 123 | ;environment=A="1",B="2" ; process environment additions 124 | ;serverurl=AUTO ; override serverurl computation (childutils) 125 | 126 | ; The below sample group section shows all possible group values, 127 | ; create one or more 'real' group: sections to create "heterogeneous" 128 | ; process groups. 129 | 130 | ;[group:thegroupname] 131 | ;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions 132 | ;priority=999 ; the relative start priority (default 999) 133 | 134 | ; The [include] section can just contain the "files" setting. This 135 | ; setting can list multiple files (separated by whitespace or 136 | ; newlines). It can also contain wildcards. The filenames are 137 | ; interpreted as relative to this file. Included files *cannot* 138 | ; include files themselves. 139 | 140 | [include] 141 | files = /etc/supervisor/*.conf 142 | --------------------------------------------------------------------------------