Deploying a Flask Application¶
To deploy a Flask application means to integrate your Flask
application with Apache, so that someone can use your app with a
normal browser going to http://cs.wellesley.edu/yourapp
or, even
better, https://cs.wellesley.edu/yourapp
(note the use of a secure,
SSL connection because of the HTTPS protocol).
To do this, you have to:
- write a configuration file for use by Apache
- get the system administrator to install that file and restart Apache so that the configuration file is used
Fortunately, I'm the sysadmin for the CS server, so I can help you with the second part.
External Readings. The connection between Apache and a Python-based
external application like our Flask apps is mediated by an API called
WSGI and implemented by the Apache module called modwsgi
. Most of
our work uses a directive called WSGIDaemonProcess
. These readings
are optional, but the links are here if you want to pursue it.
Configuring Apache¶
Apache is a huge, multi-faceted piece of software, and so configuring
it is a huge task. Fortunately, the configuration can be broken up
into separate chunks, each of which has its own file. These files are
loaded when Apache starts (at boot time or when the sysadmin restarts
the daemon). These files are stored in /etc/httpd/conf.d/
.
Here is an excerpt of such a configuration file, namely
wsgi-scott.conf
in that directory:
# home is the home directory
# python-path is a list of directories to add to the path; this adds the home dir
# so that app.wsgi can import app.py and so forth
# inactivity-timeout is the number of seconds to be idle before shutting the process down.
WSGIDaemonProcess scott \
user=anderson \
processes=1 \
threads=2 \
display-name=httpd-scott
home=/home/anderson/wsgi/ \
python-path=/home/anderson/wsgi/
inactivity-timeout=3600
WSGIScriptAlias /scott /home/anderson/wsgi/app.wsgi process-group=scott
A note about the syntax here. Technically, each configuration variable must be on a single line. However, you are allowed to continue on the next line if the last character on the line is a backslash. But be careful: even a single space character after the backslash breaks the continuation and you'll get a syntax error.
Let's break down the parts of that configuration. (All of these are documented in greater detail and precision in the docs linked at top of the reading.)
user
is the user that the app will run as (that is, the username that maps to a given UID). Any read/write permissions on files will depend on that UID. This should be your account or your team account, depending on where the files are.processes
is the number of processes to create (we learned about processes when we learned about threads).threads
is the maximumn number of threads each process will have at one time.display-name
is cosmetic. It determines how the process(es) will show up in a listing of processes running on the server.1home
is the home directory of the application (not necessarily the home directory of the user). I have an example application in thewsgi
sub-folder of my personal account, so that's the home directory here. If you were deploying your beta version, this might be/students/teamacct/beta/
.inactivity-timeout
is the number of seconds of inactivity before the process is restarted. This reclaims memory if your app runs and uses up a bunch of memory and then doesn't run for a while.
All of the WSGIDaemonProcess
configuration information creates a
process group that runs your Flask app. (Like running your
app.py
). In the example above, we named the process group scott
.
Then, the WSGIScriptAlias
maps a URL to your group. This is a URL
that maps to your /
route, and prefixes all your routes. This is
where you come up with a name for your app, which we referred to as
yourapp
at the top of this reading.
Fortunately, you don't have to re-write all your URLs because you used
url_for()
, remember?
So, in the example above, you can get to my app by accessing https://cs.wellesley.edu/scott.
app.wsgi¶
The configuration of WSGIScriptAlias
referred to a file called app.wsgi
:
WSGIScriptAlias /scott /home/anderson/wsgi/app.wsgi process-group=scott
That file lives in your app's directory and imports your app from
app.py
, making it the value of application
, which is the interface
required by WSGI
. So, it's essentially just renaming your global
variable from app
to application
. Having a separate file helps in
a few other ways as well, as we'll see below when we get to
redeploying. Here's the complete app.wsgi
file:
from app import app as application
Next you can proceed to steps about allowing access to your app.
Enabling and Restricting Access¶
By default, Apache doesn't allow any access to directories without
special permission. So, another thing we must do is grant permission
to the directory. In all of the examples below, I'm setting
permissions to the folder that contains my app, namely
/home/anderson/wsgi
. You would change that to the directory that
contains your app, such as /students/teamacct/beta/
or whatever.
At this point, we have some choices to make. If you want to open your app up to the world, that's very easy:
<Directory /home/anderson/wsgi>
Require all granted
</Directory>
On the other hand, you might want to restrict access to just the Wellesley community by allowing access only from on-campus IP addresses (or people using the VPN). The on-campus IP addresses start with 149.130.
<Directory /home/anderson/wsgi>
# wellesley.edu
Require ip 149.130
</Directory>
Actually, that's no longer quite true. Now, most on-campus IP addresses start with 10.128../9, particularly ones on "Wellesley Secure", so we would now use:
<Directory /home/anderson/wsgi>
# wellesley.edu
<RequireAny>
Require ip 149.130
Require ip 10.128/9
</RequireAny>
</Directory>
On the third hand, you might want to further loosen that up to allow
either an on-campus IP or the guest credentials we've used through
this course. The following allows either condition to hold (that's
the RequireAny
).
<Directory /home/anderson/wsgi>
AuthUserFile /var/www/htpasswd-guest
AuthGroupFile /dev/null
# AuthName seems to have no effect on Chrome but otherwise appears
# in the prompt to the user for the htpassword
AuthName "Wellesley Community"
AuthType Basic
<RequireAny>
Require user guest
# wellesley.edu
Require ip 149.130
Require ip 10.128/9
</RequireAny>
</Directory>
The AuthUserFile
is a file on the server that contains usernames and
(encrypted) passwords. See
authUserFile. When
an off-campus visitor visits the page, the browser will ask them for a
username/password, as we've seen ourselves. The
/var/www/htpasswd-guest
file has the credentials we've used
elsewhere in this course. If you want to set other credentials; see me
about that. That file stores username/password pairs. If you're
curious about options for encrypting the password, you can check the
password
formats. One
option is bcrypt.
On the fourth hand, I often allow both of those, plus access from
several validator websites, so the Directory
grows even bigger:
<Directory /home/anderson/wsgi>
AuthUserFile /var/www/htpasswd-guest
AuthGroupFile /dev/null
# AuthName seems to have no effect on Chrome but otherwise appears
# in the prompt to the user for the htpassword
AuthName "Wellesley Community"
AuthType Basic
<RequireAny>
Require user guest
# wellesley.edu
Require ip 149.130
# for the validators
# w3.org
Require ip 128.30.52
# webaim.org
Require ip 67.207.157
</RequireAny>
</Directory>
But these various Directory
settings are just variations on a
theme. If none of these are right for you, consult the Apache
documentation for
access and talk
to me.
Deploying¶
Combine the configuration settings of the WSGIDaemonProcess
and
WSGIScriptAlias
and the <Directory>
permissions into a single
file. Call it something perspicuous like wsgi-myapp.conf
.
If you want to see other examples of that file, you can look in
/etc/httpd/conf.d/
. Once you have such a file, contact the system
administrator (me) and I can copy your file into that directory and
restart Apache, so that it will use that new configuration file.
Then, you can visit your app at https://cs.wellesley.edu/myapp
where
myapp
is the value you specified in your WSGIScriptAlias
configuration variable.
Redeploying¶
Suppose you find and fix a bug in your app, or you add a new feature. How can you test it? How can you ask Apache to load your new code?
First, you can continue development of your app in the usual way. You probably should use git branches, or do your testing in a separate folder from your deployed app. You wouldn't want Apache to accidentally reload a broken version of your app.
But, assuming you've gotten the bug fixed or the new feature working, how can you get Apache to re-load your code? Do you have to ask the sysadmin to restart Apache?
It turns out the answer is no, you can reload your
app
just by indicating that the app.wsgi
has been modified. That will
trigger Apache to reload your code the next time someone visits the URL. You don't even have to modify the app.wsgi
file; it's sufficient to modify the date on the file, which is done by the Unix touch command:
touch app.wsgi
Important note, though: That should be done after debugging and
testing. The reason is that error messages from deployed apps are
written to system log files that you can't read. print()
functions
in your Python code don't work anymore (they don't print to your
terminal, though you could write python code to write to a log
file). In short, debugging a deployed app is difficult at best. So,
you want to have high confidence that it will work before
re-deploying.
Summary¶
- Deploying is important, since it's the goal of writing Flask apps. However, for the purposes of this course, it is entirely optional. You've learned the concepts and techniques, and you have a video demo that you can share with friends, family and potential employers. You have code on GitHub if someone really wants to see what you wrote. Very few CS 304 apps have been deployed. But if you want to, you can.
- The main ideas of deploying are:
- getting your code to run using the version of Python that is compiled into Apache.
- configuring settings like number of processes and threads, home directory, python path and the like
- setting the main "entry point" using
ScriptAlias
- configuring access to your app
- re-deploying as necessary by touching the
app.wsgi
file.