The mkwebsite package allows you to install multiple completely self contained production quality websites on a single server for development and debugging without root privileges. I have used it for web development on linux and Mac OS X. The sites generated can also be used in production.
I developed it because I wanted to be able to completely remove my development sites without leaving any system wide packages around that I didn’t need and because I wanted to compare security settings side by side for sites that were otherwise identical.
It is very easy to use, simply download the package, extract it and run the associated install script for each website that you want to create. It takes care of all the system administration details (like database and server configuration) so that you can concentrate on development.
Each website contains a web server (nginx), a database (postgres), a web framework (django), a web server gateway interface (uwsgi) to connect the framework to the server, caching using memcache and a host of client side tools for web development such as jquery, jquery-ui, cryptojs, tooltipters, bootstrap, datatables, dynatree and flot.
In addition each website has HTTP and HTTPS infrastructure with self signed certificates to get you started. Because HTTP and HTTPS ports (80, 443) are on privileged ports by default, the installation process will choose non-privileged ports for those services. Like almost all aspects of the package, these are fully customizable during installation.
And finally media and static files are handled by the server in the the django directory tree as the media and static sub-directories, respectively. You can, of course, customize the server to serve them from other hosts if you wish.
This package mkwebsite system incorporates the following open source packages and would not be possible without them. Please give them your support.
Package | Version | Type | URL |
---|---|---|---|
bootstrap | 3.0.3 | client | http://getbootstrap.com/ |
cryptojs | 3.1.2 | client | http://code.google.com/p/crypto-js/ |
datatables | 1.9.4 | client | http://datatables.net/ |
django | 1.6.2 | server | https://www.djangoproject.com/ |
dynatree | 1.2.5 | client | http://code.google.com/p/dynatree/ |
flot | 0.8.2 | client | http://www.flotcharts.org/ |
jquery | 2.0.3 | client | http://jquery.com/ |
jquery-ui | 1.10.3 | client | http://jqueryui.com/ |
libevent | 2.0.21 | server | http://libevent.org/ |
memcached | 1.4.17 | server | http://memcached.org/ |
nginx | 1.4.4 | server | http://nginx.org/en/ |
pcre | 8.34 | server | http://www.pcre.org/ |
postgresql | 9.3.2 | server | http://www.postgresql.org/ |
tooltipster | n/a | client | http://iamceege.github.io/tooltipster/ |
uwsgi | 1.2.6 | server | https://uwsgi-docs.readthedocs.org/en/latest/ |
The mkwebsite package is available as tar or zip files from the following links.
TYPE | CHECKSUM | INSTALLATION |
---|---|---|
tar.bz2 | 20780 | $ wget http://projects.joelinoff.com/mkwebsite/mkwebsite.tar.bz2 |
tar.gz | 62790 | $ wget http://projects.joelinoff.com/mkwebsite/mkwebsite.tar.gz |
zip | 13983 | $ wget http://projects.joelinoff.com/mkwebsite/mkwebsite.zip |
The table above shows how to run the install script for each different download type. As you can see, the package will be extracted to a local directory. At the time of this writing the version is 0.16 so the local install directory will be mkwebsite-0.16. The version number will change when new versions are released but the basic structure will be the same.
EXAMPLE: Download and Install
The example below shows how to install the tool.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Joes-MacBook-Air:webdev jlinoff$ # Installation Example Joes-MacBook-Air:webdev jlinoff$ mkdir ~/webdev Joes-MacBook-Air:webdev jlinoff$ cd ~/webdev Joes-MacBook-Air:webdev jlinoff$ wget http://projects.joelinoff.com/mkwebsite/mkwebsite.tar.bz2 --2014-02-22 12:47:13-- http://projects.joelinoff.com/mkwebsite/mkwebsite.tar.bz2 Resolving projects.joelinoff.com (projects.joelinoff.com)... 205.134.253.188 Connecting to projects.joelinoff.com (projects.joelinoff.com)|205.134.253.188|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 28299258 (27M) [application/x-tar] Saving to: ‘mkwebsite.tar.bz2’ 100%[===========================================================>] 28,299,258 1.01MB/s in 28s 2014-02-22 12:47:42 (986 KB/s) - ‘mkwebsite.tar.bz2’ saved [28299258/28299258] Joes-MacBook-Air:webdev jlinoff$ tar jxf mkwebsite.tar.bz2 Joes-MacBook-Air:webdev jlinoff$ ls -l total 55272 drwxr-xr-x 8 jlinoff staff 272 Feb 22 11:56 mkwebsite-0.16/ -rw-r--r-- 1 jlinoff staff 28299258 Feb 22 12:12 mkwebsite.tar.bz2 Joes-MacBook-Air:webdev jlinoff$ |
EXAMPLE: Create a Website
Once the tool has been downloaded and extracted you can create a website by running the install.sh script in the mkwebsite-<version> directory.
Before starting this process you will want to choose a site name and an app name. I typically use a company name (like acme_inc) or a group name (like data_mining) for the site and the app name (inventory). The app name is really not that important because you easily add new apps in Django using the startapp command.
If you plan to install multiple websites, make sure that you use different ports for HTTP, HTTPS and the database to avoid creating hard to debug errors. Unfortunately this cannot be automated because the installations are local for easy removal and because other sites may not be running at the time of the install so the port use cannot be detected.
You will see that I used the “-p” switch to force the install script to prompt for each option. I did that because the install script remembers all of your previous entries so that you can re-use them. You always want to use this option if you are planning to install multiple sites. If you are only planning to install one then it doesn’t matter.
This example shows the long, interactive form. You can also run the whole thing using command line options and no prompts.
NOTE: You will not be able to install a web site if you do not have a development environment available that contains make, gcc, python and other tools used to build the infrastructure. Once the site is built you can migrate it to another server that does not contain those tools.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
Joes-MacBook-Air:webdev jlinoff$ # Create website example: Acme Inc. Joes-MacBook-Air:webdev jlinoff$ cd ~/webdev Joes-MacBook-Air:webdev jlinoff$ ./mkwebsite-0.16/install.sh -p # **************************************************************** # This is the installer for mkwebsite-0.16. # # Would you like to see the help before proceeding? # # Please enter yes or no. Help <yes>? USAGE install.sh [OPTIONS] DESCRIPTION This script will install a web site in a completely self contained directory tree on this host that does not require administration privileges to run. It also creates a customized script that you can use later for other installations. You can remove this installation at any time by simply stopping the site using the <site>/rtf/bin/sitemgr.py tool. There are number of command line options available. They are described below. If you do not specify them then you will be prompted for them with defaults. OPTIONS -a <name>, -app <name> The app name. The default is "myapp". -c <file>, --cfg <file> The cfg file. This is where the install options are read from and save to. The default is install.cfg. --db-port <port> The database port to use. The default is 9082. -h, --help This help message. --http-port <port> The HTTP port to use. The default is 9080. --https-port <port> The HTTPS port to use. The default is 9081. -p, --prompt Force all prompts. This is useful when a configuration file is present. --ports <HTTP> <HTTPS> <DB> Specify all of the ports at once separated by commas in order HTTP, HTTPS, DB. -r <dir>, --root <dir> The root directory for the site. The default is the current directory. -s <name>, --site <name> The site name. The full path to the site will be: <root>/<site>. The default is "mywebsite". --start Start the site automatically after installation. -v, --verbose Increase the level of verbosity. EXAMPLES 1. Run with prompts: $ ./mkwebsite-0.16/install.sh 2. Dry run with prompts: $ ./mkwebsite-0.16/install.sh -n 3. Run with the configuration file, force prompts: $ # Assumes that a previous run was done. $ ./mkwebsite-0.16/install.sh -p 4. Run with some command line options: $ ./mkwebsite-0.16/install.sh --ports 10080 10081 10082 -a introduction # **************************************************************** # Please enter the root directory for the installation. # # The default is the current directory: /Users/jlinoff/webdev # All relative paths are relative to it. Root directory </Users/jlinoff/webdev>? # **************************************************************** # Please enter the site name. # # This is a subdirectory under the root directory that contains # this site. With this approach you can have multiple sites in # the same directory tree, for example: # # /opt/websites/production # /opt/websites/development # # Where /opt/websites is the root directory with two sites: # production and development. Site name <mywebsite>? acme_inc # **************************************************************** # Please enter your app name. # # This is the name of the initial app name that is set up for you in the # Django directory structure. You can change it later, you can delete it # and you can additional apps so the name isn't that important. App name <myapp>? inventory # **************************************************************** # Please enter the HTTP port value. # # This is the port that you use to access the website in your # browser. If you want to use port 80 you will have to either # install the website as root (bad idea for security reasons) # or update your firewall to forward traffic between port 80 # and this port. HTTP port <9080>? # **************************************************************** # Please enter the HTTPS port value. # # This is the port that you use to access the website in your # browser. If you want to use port 443 you will have to either # install the website as root (bad idea for security reasons) # or update your firewall to forward traffic between port 443 # and this port. HTTPS port <9081>? # **************************************************************** # Please enter the database port value. # # This is the port that you use to access your database server. Database port <9082>? Configuration Options: App Name : "inventory" Configuration : "install.cfg" Database Port : 9082 HTTP Port : 9080 HTTPS Port : 9081 Log File : "install.log" Root Dir : "/Users/jlinoff/webdev" Site Name : "acme_inc" Start : no Website Dir : "/Users/jlinoff/webdev/acme_inc" Verbosity : 0 Version : 0.16 # **************************************************************** # Are the above configuration options correct? # # Please enter yes if they are or no if they are not. Correct <yes>? <output snipped> |
At this point the configuration is done and the install script calls the mkwebsite.py script which is the tool that does all of the heavy lifting. The full installation usually takes between 10 and 20 minutes depending on your host and internet connection speed.
It generates a lot of output which is captured in a log file in the acme_inc/rtf/log/install.log file. At the end you will see a message that looks something like this that tells you how to manage the web server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
<output snipped> . . 2446 static files copied to '/Users/jlinoff/webdev/acme_inc/django/acme_inc/static'. 2014-02-22 13:08:54,191 INFO mkwebsite.py:00143 cmd-end:1684: 4.5 seconds 2014-02-22 13:08:54,191 INFO mkwebsite.py:02808 TODO: install examples 2014-02-22 13:08:54,191 INFO mkwebsite.py:01703 install mgr /Users/jlinoff/webdev/acme_inc/rtf/bin/acme_inc-mgr.py 2014-02-22 13:08:54,192 INFO mkwebsite.py:02811 context final state Website Bootstrap v0.16 The installation completed on 2014-02-22 13:08:54.193311. The following 16 packages were installated: 1 bootstrap-3.0.3-dist.zip 2 CryptoJS%20v3.1.2.zip 3 DataTables-1.9.4.zip 4 flot-0.8.2.zip 5 jquery-2.0.3.js 6 jquery-2.0.3.min.js 7 jquery-2.0.3.min.map 8 jquery-ui-1.10.3.zip 9 jquery-ui-themes-1.10.3.zip 10 jquery.dynatree-1.2.5-all.zip 11 libevent-2.0.21-stable.tar.gz 12 memcached-1.4.17.tar.gz 13 nginx-1.4.4.tar.gz 14 pcre-8.34.tar.bz2 15 postgresql-9.3.2.tar.bz2 16 tooltipster-master.zip You can start the django test server using: $ # URL: http://localhost:9080 $ # URL: http://localhost:9080/inventory $ # URL: http://localhost:9080/admin $ acme_inc/rtf/bin/acme_inc-mgr.py stop $ acme_inc/rtf/bin/acme_inc-mgr.py dbstart $ /Users/jlinoff/webdev/acme_inc/venv/bin/activate $ cd django $ python manage.py runserver 0.0.0.0:9080 Or using uwsgi as follows to verify the stack: web client <--> uswgi <--> django $ # URL: http://localhost:9080 $ # URL: http://localhost:9080/inventory $ # URL: http://localhost:9080/admin $ acme_inc/rtf/bin/acme_inc-mgr.py stop $ acme_inc/rtf/bin/acme_inc-mgr.py dbstart $ /Users/jlinoff/webdev/acme_inc/venv/bin/activate $ cd django $ uwsgi --http :9080 --module website.wsgi Django admin login: Username: bigbob Password: bigbob DB admin login: $ acme_inc/rtf/bin/acme_inc-mgr.py stop $ acme_inc/rtf/bin/acme_inc-mgr.py dbstart $ ../acme_inc/rtf/db/bin -U jlinoff acme_inc Used ports: Database 9082 HTTP 9080 HTTPS 9081 Here are the control commands that are available. $ acme_inc/rtf/bin/acme_inc-mgr.py help Help. $ acme_inc/rtf/bin/acme_inc-mgr.py collectstatic Collect static files for django. $ acme_inc/rtf/bin/acme_inc-mgr.py syncdb Sync the database for django. $ acme_inc/rtf/bin/acme_inc-mgr.py dbstart Start the DB server. $ acme_inc/rtf/bin/acme_inc-mgr.py dbstatus Status of the DB server. $ acme_inc/rtf/bin/acme_inc-mgr.py dbstop Stop the DB server. $ acme_inc/rtf/bin/acme_inc-mgr.py restart Reload the DB. $ acme_inc/rtf/bin/acme_inc-mgr.py reload Restart the uwsgi, nginx, memcached and DB servers. $ acme_inc/rtf/bin/acme_inc-mgr.py start Start the uwsgi, nginx, memcached and DB servers. $ acme_inc/rtf/bin/acme_inc-mgr.py status Status of the uwsgi, nginx, memcached and DB servers. $ acme_inc/rtf/bin/acme_inc-mgr.py stop Stop the uwsgi, nginx, memcached and DB servers. $ acme_inc/rtf/bin/acme_inc-mgr.py test Test the environment. To start using your new website follow these steps: 1. Start the all of the web services: nginx, uwsgi and postgresql using the management tool that was generated for your site. These services run locally from the website directory and will not conflict with system wide services that might be running as long as the ports are different. $ acme_inc/rtf/bin/acme_inc-mgr.py start 2. Navigate to the website: $ firefox http://localhost:9080 $ firefox http://localhost:9080/inventory $ firefox https://localhost:9081 $ firefox https://localhost:9081/inventory Self signed certificates were automatically created for HTTPS: CRT: acme_inc/rtf/nginx/conf/acme_inc.crt KEY: acme_inc/rtf/nginx/conf/acme_inc.key 3. The django app is located in acme_inc/django/acme_inc/inventory. The templates and static subdirectories are also located in that directory. You can cd there to edit the app contents including the urls. 4. The django project manage.py tool is in acme_inc/django/acme_inc. To use it you must set your virtualenv like this: $ acme_inc/rtf/bin/acme_inc-mgr.py start $ . acme_inc/venv/bin/activate $ cd acme_inc/django/acme_inc $ python manage.py -h $ python manage.py collectstatic --noinput $ python manage.py syncdb --noinput $ python manage.py startapp mynewapp # create another app 5. The django site settings.py and urls.py files are in acme_inc/django/acme_inc/acme_inc. 6. You can access the database using psql like this: $ acme_inc/rtf/bin/acme_inc-mgr.py dbstart $ ../acme_inc/rtf/db/bin -U jlinoff acme_inc 7. You can install additional python packages like this: $ . acme_inc/venv/bin/activate (venv) $ pip install boto twisted (venv) $ deactivate 8. This information is captured in: acme_inc/rtf/log/install.log. 2014-02-22 13:08:54,194 INFO mkwebsite.py:02819 writing install info to acme_inc/rtf/log/install.log 2014-02-22 13:08:54,197 INFO mkwebsite.py:02835 elapsed time 409.7 410.11 real 252.82 user 93.34 sys Joes-MacBook-Air:webdev jlinoff$ |
As you can see, the installer created a custom website manager tool was created called “acme_inc/rtf/bin/acme_inc-mgr.py” that provides a number of commands for managing the server.
There are commands for starting the server, stopping the server, determining the status of the server, for collecting static data for django, for database management and so on. Although each of these can be run independently using tools like psql (for the database) or in your own custom python script, having them in a single manager tool is very convenient.
With all of this in place you can start the webserver as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Joes-MacBook-Air:webdev jlinoff$ ./acme_inc/rtf/bin/acme_inc-mgr.py start # ================================================================ # start # ================================================================ start db cmd: pg_ctl -D /Users/jlinoff/webdev/acme_inc/rtf/db/dbdata -l /Users/jlinoff/webdev/acme_inc/rtf/log/postgresql.log start server starting exit_code = 0 start uwsgi cmd: uwsgi -d /Users/jlinoff/webdev/acme_inc/rtf/log/uwsgi.log --ini /Users/jlinoff/webdev/acme_inc/django/acme_inc/uwsgi.ini [uWSGI] getting INI configuration from /Users/jlinoff/webdev/acme_inc/django/acme_inc/uwsgi.ini exit_code = 0 start nginx cmd: nginx exit_code = 0 start memcached cmd: memcached -d -s /Users/jlinoff/webdev/acme_inc/rtf/log/memcached.sock -P /Users/jlinoff/webdev/acme_inc/rtf/log/memcached.pid exit_code = 0 |
You can check the webserver status like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Joes-MacBook-Air:webdev jlinoff$ ./acme_inc/rtf/bin/acme_inc-mgr.py status # ================================================================ # status # ================================================================ ports db 9082 open http 9080 open https 9081 open status db cmd: pg_ctl -D /Users/jlinoff/webdev/acme_inc/rtf/db/dbdata -l /Users/jlinoff/webdev/acme_inc/rtf/log/postgresql.log status pg_ctl: server is running (PID: 54347) /Users/jlinoff/webdev/acme_inc/rtf/db/bin/postgres "-D" "/Users/jlinoff/webdev/acme_inc/rtf/db/dbdata" exit_code = 0 status uwsgi running (pid=54350) status nginx running (pid=54358) status memcached running (pid=54361) |
To stop the web server simply use the “stop” command.
Now that that the webserver is running you can access it using your favorite browser at http://localhost:9080 or https://localhost:9081.
Here is a screen shot showing what this site looks like in firefox.
Now you can customize it using python and the django infrastructure.
Delete a Website
Deleting a website is trivial. Simply stop it using the manager in <website>/rtf/bin and then delete the directory tree using rm.
Production Considerations
Because the website is completely self contained, it is easy to copy it around for use in production systems. Just keep the following considerations in mind.
You do not need to install development tools like make or gcc on the production website because you are only using the results of the build/installation.
Directories are hard coded during the installation so the location on the production site must be the same as the location on the development site. I typically use a location like “/opt/website
” as the root.
To install on the production site, all you need to do it copy over the entire website directory tree or use the update/get option of a distributed version control system like git. In both cases the src sub directory tree can be ignored.
One final consideration is that you should use the same user on both systems. Although that is not required it makes things simpler.
Directory Structure
The website directory is a combination of the root directory and the website name. For the example above the website directory is “/Users/jlinoff/webdev/acme_inc
” where “/Users/jlinoff/webdev
” is the root.
Here is a list of interesting sub directories under the website installation directory.
DIRECTORY | DESCRIPTION |
---|---|
django/<website> |
The django directory tree. This is where the django management tools, apps, static and media directories are located. The initial app directory is in django/<website>/<app> .The main website directory is in django/<website>/<website> .The media directory is in django/<website>/media .The static directory is in django/<website>/static .
|
rtf |
The release to field directory. This is where the production versions of the database (postgres), server (nginx), gateway interface (uwsgi), the logs and the management tools are located. |
src | The directory tree that contains the source code for the installed packages. |
venv |
The python virtual environment for the website. You must call <website>/venv/bin/activate before doing any django work.
|
Conclusion
As you can see from this brief introduction, mkwebsite is a powerful tool for helping you develop, maintain and test websites. It also useful for learning how the infrastructure of a website works.
I hope that you find it as useful as I have.
Enjoy!
Thanks !
I tried it, and it worked great. Now that I have the infrastructure in place, all I need is some content !
dt
Excellent! Thank you for trying it.