-
-
Notifications
You must be signed in to change notification settings - Fork 167
Running a production WeBWorK server using Docker
It is possible to run a reasonable production WeBWorK server using Docker. It was already possible to do this using WeBWorK 2.14, but the 2.15 release will include a docker-compose.yml file and several sample configuration files to make setting up a production system much easier.
At present, this includes an R server, a MariaDB server (MariaDB 10.4 for WW 2.15), and a WeBWorK server running Apache. (In principle, it would also be possible to run a Docker system connecting to an external mysql-derived database server.)
In the future, the Docker system may be extended to also allow using lighthttpd to reduce the workload on Apache.
Configuration in the docker-compose.yml file and files it may mount to a running container:
-
Before first starting your Docker based system you should change the default sample SQL passwords. Once the Maria DB data volume is created, you would need to modify those values by hand via direct mysql commands.
-
MYSQL_ROOT_PASSWORDis the root password for mysql, and appears only once in the file.- This is still set in
docker-compose.yml.
- This is still set in
- The
WEBWORK_DB_PASSWORDandWEBWORK_DB_USERare now set via the.envfile and not via thedocker-compose.ymlfile. -
MYSQL_PASSWORDis the password used by the WeBWorK server to access thewebworkdatabase and the same value needs to also be set forWEBWORK_DB_PASSWORD:in theenvironment:section further down in the file.- These values are now both set via the
.envfile and not via thedocker-compose.ymlfile.
- These values are now both set via the
-
- Consider where to store
docker-compose.yml:- If you are using a shared
webwork2directory on shared storage to operate several VMs each running WeBWorK via Docker, you will want to store yourdocker-compose.ymlper VM in a special per-VM directory, and have each one point to the central location ofwebwork2using the settings in thebuild:section of the file. Sample lines are included. - Using a special directory to store
docker-compose.ymlalso allows you to use modifications to the file to keep different Docker images of webwork available in parallel. This is helpful when you upgrade the Docker image (to get core OS upgrades, etc.) or build a different Docker image of WW for any other reason. Some comments on this are given below in the section on updating the Docker image.
- If you are using a shared
- It is possible to mount
webwork2/and/orpg/from outside the standard image.- Those options are intended for people doing development work, but is also useful is you need a customized version of the code.
- Small customizations could be better made by mounting just specific modified files from outside the image.
- By keeping locally customized files outside the main
webwork2tree and mounting them to the image using specific lines indocker-compose.ymlyou can easily get such local versions into upgraded WW2 Docker images.
- By keeping locally customized files outside the main
- The location from which the
coursesdirectory is mounted should be set.- This can be set using the value of
COURSES_DIRECTORY_ON_HOSTin.envor directly set by making suitable changes todocker-compose.yml. - The current default setting for Docker use is to store it in
../ww-docker-data/coursesnamely, that aww-docker-datadirectory will be in parallel to thewebwork2directory. - For production servers, a more appropriate location should be used.
- This can be set using the value of
- Several other directories and files should be mounted from a persistent location outside of the docker container.:
- the WeBWorK log file directory
webwork2/logs/, - the Apache logs directory
/var/log/apache2(possibly relocated by Apache config) - the WeBWorK
webwork2/htdocs/tmpdirectory, htdocs/my_site_info.txt- (optional) the OPL
- local WeBWorK configuration files:
conf/localOverrides.confconf/site.confconf/authen_LTI.conf
- the WeBWorK log file directory
- Local Apache configuration / default files should be created and mounted from appropriate locations:
-
var/www/html/index.html- Set your server URL for the
Refreshline and the<a href=...">setting.
- Set your server URL for the
-
/var/www/html/.htaccess- To be used if you are running with SSL and want to redirect all non-SSL traffic to SSL.
- Set your server URL for the
Redirectto https line.
-
/etc/apache2/sites-available/000-default.conf- Set
ServerName,ServerAdmin, andServerAliasin the lastVirtualHostblock. - Set the
Redirect permanentif redirecting all traffic to SSL.
- Set
-
/etc/apache2/apache2.conf- Set
ServerName,ServerAdmin
- Set
-
/etc/apache2/mods-enabled/mpm_prefork.conf- Set suitable values for the amount of RAM available. See:
- http://webwork.maa.org/wiki/Installation_Manual_for_2.12_on_Ubuntu_16.04#Configuring_Apache%EF%BB%BF
- http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3904
- http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3827
- http://webwork.maa.org/moodle/mod/forum/discuss.php?d=3928
- http://webwork.maa.org/moodle/mod/forum/discuss.php?d=4331
- http://hirebenjam.in/tag/webwork/
- Set suitable values for the amount of RAM available. See:
- for SSL:
- You need to provide your own SSL key, CA-signed SSL certificate, and probably a CA "chain file" and mount them or a directory containing all 3 files to the location you set in the configuration files.
- The sample files assume it/thet will be mounted to
/etc/ssl/local /etc/apache2/mods-available/ssl.conf-
/etc/apache2/sites-available/default-ssl.conf- Adjust the in-container location of the SSLCertificateFile/KeyFile/ChainFail files in both
VirtualHostblocks. - Set
ServerName,ServerAdmin, andServerAliasin the secondVirtualHostblock.
- Adjust the in-container location of the SSLCertificateFile/KeyFile/ChainFail files in both
-
- Set the
hostname: - Adjust the
ports:for production vs. personal PC use. See the setting in.envand check if it should be changed/used. - As needed modify the environment variables set in the
.envfile:- The SQL database password and user:
WEBWORK_DB_PASSWORDandWEBWORK_DB_USER. -
COURSES_DIRECTORY_ON_HOSTwhich is used to provide a value to mount (by default) to/opt/webwork/coursesin the default version ofdocker-compose.yml. -
WEBWORK2_HTTP_PORT_ON_HOSTsets the host port HTTP number used to which connects to the server.- By default is is
8080as suitable for using Docker on a development machine. - For production use, it could be changed to
80, but it would be simpler to just switch which lines are commented/uncommented in theports:section ofdocker-compose.yml.
- By default is is
- The SQL database password and user:
- Set/adjust the environment variables section
environment:-
very important
WEBWORK_DB_PASSWORD:-
now set via
.envso both settings have a single source. - If either is manually set then the same password must be used here and in MariaDB section (not the mysql root password).
-
now set via
-
SSL: 1to turn on SSL -
PAPERSIZE: sizeto change the default system paper-size (defaults toletteranda4or something else may be desired) -
ADD_LOCALES:can be used to set which locales which will be generated and available in the running container. -
ADD_PACKAGES:can be used to have additional Ubuntu packages (exvim) installed in the running container. (Such packages additions are not persistent and will be reinstalled each container start-up.) -
SYSTEM_TIMEZONE:can set the server timezone of the running container. (The default isUTC.) -
WEBWORK_ROOT_URL:can set the URL, and should be used in particular if you are using SSL. -
WEBWORK_SMTP_SERVER:,WEBWORK_SMTP_SENDER:sets these environment variables used by the running container. -
WEBWORK_TIMEZONE:sets the timezone WeBWorK uses by default.
-
very important
Warning: I have found that the default setting of max_connections for the MariaDB container is too low for production use. As a result, I copied /etc/mysql/my.cnf out of a running container, and edit the value for max_connections and now mount the modified file into the MariaDB container using docker-compose.yml.
Symptoms of trouble: lines reporting error instantiating DB driver WeBWorK::DB::Driver::SQL in the Apache error.log file, and/or reports from students about error pages where that is reported.
There are quite a few discussions in the forums about the need to use a relatively large value of max_connections:
- https://webwork.maa.org/moodle/mod/forum/discuss.php?d=1399
- https://webwork.maa.org/moodle/mod/forum/discuss.php?d=1511
- https://webwork.maa.org/moodle/mod/forum/discuss.php?d=2590
- https://webwork.maa.org/moodle/mod/forum/discuss.php?d=2927
It is recommended to backup the courses data and the SQL database data.
See: http://webwork.maa.org/wiki/Backup_and_Disaster_Recovery
Since the courses data is mounted from outside the Docker container, regular procedures can be used to back that data up.
However, the SQL database data is stored in a named storage volume.
Here is one possible approach using a modified method:
- Create an external "database backup" directory and the local file to be mounted to the container.
- Edit
docker-compose.ymlto mount the directory and files.- the external "database backup" directory
-
/root/.my.cnffrom the relevant file with the correct password.- Make sure to fix the value of the password in the file.
-
/root/ww-mysqldumpfrom a file containing the file below, modified as necessary, and made executable.
- Restart the container:
docker-compose down
docker-compose up -d
- Test it once from the command line using something like the line below.
The line below assumes that you are running under "webwork2/"
so that precedes the
app_1in the container name, and mounted the script to the location given.
docker container exec -i webwork2_app_1 /root/ww-mysqldump
- Set up a cron job (for a user in the
dockergroup) by editing your crontab usingcrontab -eand adding a line like
30 1 * * * /usr/bin/docker container exec -i webwork2_app_1 /root/ww-mysqldump
- Set up some backup of the dump files on a different server.
Sample for /root/.my.cnf:
[client]
user=webworkWrite
password=passwordRW
host=db
port=3306
default-character-set=utf8mb4
Change the password to match what you set.
Sample for /root/ww-mysqldump:
#!/bin/bash
HOME=/root
# Some versions of mysqldump need "--column-statistics=0" below and others do not.
/usr/bin/mysqldump --column-statistics=0 --opt webwork | /bin/gzip -c > /database_backup/webwork.sql.gz
#/usr/bin/mysqldump --opt webwork | /bin/gzip -c > /database_backup/webwork.sql.gz
It seems that with mysqldump from Ubuntu 20.04 the extra --column-statistics=0 switch is needed when the actual database is on Maria DB 10.4.
Change the backup path to match what you set.
Sample mount lines to add to docker-compose.yml:
# For mysql backup
- "/your_path_to/root-my.cnf:/root/.my.cnf"
- "/your_path_to/ww-mysqldump:/root/ww-mysqldump"
- "/your_path_to/database_backup:/database_backup"
Change the paths to match where things are on your system.
The Docker image for webwork is built on top of an ubuntu system image and with many additional packages installed.
Just as a real server needs to be maintained by updating packages, so does the WeBWorK image.
One benefit of using Docker is that by renaming the image, you can keep the prior image (for emergency rollback) when you create a new image.
Below are my notes on the process I used to do this.
The lines below assume that WW is still using the noted versions of ubuntu and mariadb. Adjust as necessary:
docker image ls
docker pull ubuntu:18.04
docker pull alpine:latest
docker pull alpine/git
docker pull mariadb:10.4
docker image ls
Old and unneeded images can be removed using docker image rm hash.
Assuming you want to use the current version of WW (and in particular of the Dockerfile) with any updates:
-
First change into the location where you have your
webwork2directory (the one used when you built the images). -
Check if you have any local modifications to
Dockerfileand if so save a copy for comparison.- If you have the local version of
Dockerfilestored outside the mainwebwork2tree - look at that file, and keep a backup copy if relevant.
- If you have the local version of
-
Save a copy of your local version of
docker-compose.yml(from wherever you keep it).- You may need to
git checkout -- docker-compose.ymlbefore you can rungit pullbelow if you store your locally modified file in the mainwebwork2tree. - Keeping the different versions of
docker-compose.ymlwhich refer to different image builds in separate directories will make switching between which image to run easier. - When doing so, you should set the
context:in thebuild:section to point to path to where your main (Git controlled)webwork2directory is located. - If you need a custom
Dockerfileit can be specified usingdockerfile:in thebuild:section, and that allows keeping the locally modified file outside the Git controlledwebwork2tree. -
Note: You will need to run the
docker-composecommands in the directory where you have the relevant version ofdocker-compose.yml.
- You may need to
- Use
git branchto check that you are on the expected branch (ex.master). - If necessary change branch using
git checkout branchname. - Fetch and pull updates
git fetch origin
git pull
git status
- Merge in any local modifications to
Dockerfileas necessary.- If such modifications are needed, I recommend storing the
Dockerfileelsewhere and using thedockerfile:setting in thebuild:section ofdocker-compose.yml.
- If such modifications are needed, I recommend storing the
- Make sure you are in the location with the correct version of
docker-compose.yml. - Run
docker-compose build - Pay attention to the end of the output. If all went well there should be a message
Successfully builtwith the new image hash ID, and possibly followed by oneSuccessfully taggedwith the image name you are using. - Check what
docker image lsshows. - Most likely there will be some temporary images from the build process which can be removed using
docker image rm. - You probably will want to save the prior webwork and ubuntu images until you are sure that the new image is working properly.
- Change into the location of the old
docker-compose.ymlfile. docker-compose down- Change into the location of the new
docker-compose.ymlfile. docker-compose up -d- Check that your container is working as expected.
When you are certain you do not need the older Docker images, you can remove them.
Use docker image ls to see what images there are and docker image rm hash to remove unneeded images.
Warning: Do not rush to remove your old images until you are certain that you will not want to roll-back to the prior image.