{"id":1457,"date":"2014-06-01T14:49:00","date_gmt":"2014-06-01T21:49:00","guid":{"rendered":"http:\/\/joelinoff.com\/blog\/?p=1457"},"modified":"2014-06-10T14:27:34","modified_gmt":"2014-06-10T21:27:34","slug":"automatically-install-a-webserver-based-on-django-nginx-and-gunicorn-on-a-centos-6-5-vps","status":"publish","type":"post","link":"https:\/\/joelinoff.com\/blog\/?p=1457","title":{"rendered":"Automatically install a webserver based on django, nginx and gunicorn on a CentOS 6.5 VPS"},"content":{"rendered":"<p>This blog presents a bash script that I created to automate the installation of a webserver based on django, nginx and gunicorn on a CentOS 6.5 VPS. The installation installs and configures the necessary system packages, and it installs a number of javascript tools like jquery, jquery-ui, datatables, flot, fancytree and others.<br \/>\n<!--more--><br \/>\nWhen the installation is complete it creates a fully running installation with support for HTTP and HTTPS with a basic django page that show how to use the various javascript tools. It also installs a self signed certificate for HTTPS.<\/p>\n<p>The steps below show how to download and use the script to install a webserver with a sample page that uses a bunch of tools like jquery, bootstrap, datatables, fancytree, flot and jscrypto.<\/p>\n<h2>Step 1: Create a VPS using CentOS 6.5<\/h2>\n<p>You need to have a virtual private server with root access based on CentOS 6.5. I recommend doing this using Amazon EC2, Digital Ocean, Dream Hosts, InMotion, Rackspace or some other VPS provider but you could also do it by setting up a VM on one of your local hosts using tools like VMware Player, VirtualBox, KVM, Xen or Hyper-V.<\/p>\n<p>I use a very basic installation since all of the server tools will be installed by the script.<\/p>\n<p>After it is running make sure that you run &#8220;yum update -y&#8221; to bring all of the packages up to date. You also need to make sure that wget is installed in step 3.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ yum update -y\r\n$ yum install -y wget<\/pre>\n<h2>Step 2: Create the webdev user<\/h2>\n<p>Before running the script you need to create a role account for development. The script assumes that the default role account is webdev. This must be done as root.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ sudo useradd webdev\r\n$ sudo passwd webdev\r\nNew password: webdev*pwd\r\nRetype new password: webdev*pwd\r\npasswd: all authentication tokens update successfully.\r\n<\/pre>\n<div style=\"margin-left: 50px; margin-right: 50px; background-color: #ffffdc; padding: 5px; border: 1px solid yellow\">\n<strong>NOTE:<\/strong> You can use any password you like for the webdev user but if you do not use &#8220;webdev*pwd&#8221; make sure that you modify the WEBUSER_PASSWD variable in the install.sh script before running it.<\/div>\n<div style=\"margin-top: 1.5em;\">\nNow add webdev to the sudoers list.<\/p>\n<div>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" ># add webdev ALL=(ALL) ALL\r\nvisudo<\/pre>\n<h2>Step 3: Download the script and extract it<\/h2>\n<p>The example shows how to download the script using wget but you can also download it here <a href=\"http:\/\/projects.joelinoff.com\/mkwebsite\/centos-6.5-x86_64\/mksite-1.2.tar.bz2\">http:\/\/projects.joelinoff.com\/mkwebsite\/centos-6.5-x86_64\/mksite-1.2.tar.bz2<\/a>.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ su - webdev\r\n$ mkdir work\r\n$ cd work\r\n$ wget http:\/\/projects.joelinoff.com\/mkwebsite\/centos-6.5-x86_64\/mksite-1.2.tar.bz2\r\n$ tar jxf mksite-1.2.tar.bz2\r\n$ ls -1\r\ninstall.sh\r\nmksite-1.2.tar.bz2\r\nsetup\r\n<\/pre>\n<p>It will download the <em>install.sh<\/em> script and the <em>setup<\/em> directory tree in the local directory. The setup directory tree contains a number of tools and data files needed for the website configuration.<\/p>\n<h2>Step 4: Run the installation<\/h2>\n<p>Running the installation is easy. Just execute the script and capture the results in a log for analysis if anything goes wrong.<\/p>\n<div style=\"margin-left: 50px; margin-right: 50px; background-color: #ffffdc; padding: 5px; border: 1px solid yellow\" >\n<strong>NOTE:<\/strong> You may want to change the role account name &#8220;webdev&#8221; and you will definitely want to change the default password &#8220;webdev*pwd&#8221;. That is done by editing the install.sh script and changing the WEBUSER and WEBUSER_PASSWD variables.\n<\/div>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ su - webdev\r\n$ .\/install.sh 2>&1 | tee log\r\n<\/pre>\n<p>When the installation is complete the website will be installed in <code>\/opt\/site<\/code>.<\/p>\n<p>Here are the basic steps that is performs:<\/p>\n<ol>\n<li>verify that all of the necessary system packages are installed<\/li>\n<li>verify that all of the necessary python packages are installed<\/li>\n<li>install all of the javascript and CSS packages<\/li>\n<li>install django<\/li>\n<li>configure gunicorn (with supervisor to control boot time startup)<\/li>\n<li>configure the nginx server (for HTTP and HTTPS)<\/li>\n<li>configure postgresql for the django admin users (fixed pg_hba.conf and postgresql.conf)<\/li>\n<li>configure the django DB interface (for postgresql), mongodb must be handled separately<\/li>\n<li>collect the django static data (this is a django thing)<\/li>\n<li>create the dango app and pre-populate it with some stuff<\/li>\n<li>create the self signed security certificates (to show how it is done)<\/li>\n<\/ol>\n<p>This configuration assumes that you will postgresql for django admin data and MongoDB for the site data based on the idea that there are a small number of admins and a large amount of data.<\/p>\n<h2>Step 5. Test the installation using the Django server<\/h2>\n<p>In one terminal window start the server.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ cd \/opt\/site\/django\/website\r\n$ python django runserver 8888\r\n<\/pre>\n<p>In another window run a browser (in this example firefox).<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ firefox localhost:8888\r\n<\/pre>\n<h2>Step 6. Test the installation as an external server<\/h2>\n<p>To test the installation as an external server, you will need to know your IP address. You can usually find that by looking at the inet address associated with eth0. If eth0 doesn&#8217;t exist or doesn&#8217;t seem to contain a valid IP address then you might be using a bridging scheme. Try running &#8220;ifconfig&#8221; with no arguments and then look through the results for a likely candidate.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ ifconfig eth0\r\n[output snipped]\r\n<\/pre>\n<p>Once you have the IP address run your browser to access that address. In the example below I used 1.2.3.4 which is not real.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ # HTTP\r\n$ firefox http:\/\/1.2.3.4\r\n$ # HTTPS\r\n$ firefox https:\/\/1.2.3.4\r\n<\/pre>\n<p>When it is done the web page will look something like this.<\/p>\n<p><a href=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1.png\"><img loading=\"lazy\" src=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1-1024x724.png\" alt=\"Screenshot-1\" width=\"625\" height=\"441\" class=\"alignnone size-large wp-image-1469\" srcset=\"https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1-1024x724.png 1024w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1-300x212.png 300w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1-624x441.png 624w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screenshot-1.png 1448w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>The Django admin page is available as shown below. You login using the webdev credentials.<\/p>\n<p><a href=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-6.37.31-PM.png\"><img loading=\"lazy\" src=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-6.37.31-PM.png\" alt=\"Screen Shot 2014-06-01 at 6.37.31 PM\" width=\"852\" height=\"686\" class=\"alignnone size-full wp-image-1491\" srcset=\"https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-6.37.31-PM.png 852w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-6.37.31-PM-300x241.png 300w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-6.37.31-PM-624x502.png 624w\" sizes=\"(max-width: 852px) 100vw, 852px\" \/><\/a><\/p>\n<p>And then you get to admin page.<\/p>\n<p><a href=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-7.06.38-PM.png\"><img loading=\"lazy\" src=\"http:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-7.06.38-PM.png\" alt=\"Screen Shot 2014-06-01 at 7.06.38 PM\" width=\"939\" height=\"640\" class=\"alignnone size-full wp-image-1493\" srcset=\"https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-7.06.38-PM.png 939w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-7.06.38-PM-300x204.png 300w, https:\/\/joelinoff.com\/blog\/wp-content\/uploads\/2014\/06\/Screen-Shot-2014-06-01-at-7.06.38-PM-624x425.png 624w\" sizes=\"(max-width: 939px) 100vw, 939px\" \/><\/a><\/p>\n<h2>Step 7. Next steps<\/h2>\n<p>You will want to customize the django app (webapp) so that your pages have relevant information for your site. If you are new to django the easiest way to do that is to login into your VPS as webdev and edit the <code>base__.html<\/code> and <code>index.html<\/code> files in the <code>\/opt\/site\/django\/website\/webapp\/templates\/webapp\/<\/code> directory.<\/p>\n<p>You will also want to make sure that site source files are under some sort of source code control system. I use git for source code control and the github service to make them available to other developers but there are many other options available like RCS, CVS, subversion, perforce and mercurial.<\/p>\n<p>If you want to access the host externally you will have to register a domain name (i.e. example.com or example.io) with one of the common domain registration service providers like HostGator, GoDaddy, NetworkSolutions or Domains.com.<\/p>\n<p>Once the domain is registered the vendor will create a DNS zone file for you which you will have to edit to reference the IP address (normally eth0) of your new VPS. All of the vendors that I have used have tools that allow you to edit the DNS zone file.<\/p>\n<p>You will also need a security certificate for that domain from a vendor like DigiCert to allow users to access your site using HTTPS.<\/p>\n<p>If your server is public facing, then you will probably want to disable root logins (PermitRootLogin no) and change port 22 (Port 22) to something else (e.g. Port 8889) in \/etc\/ssh\/sshd_config to reduce the total intrusion attempts because root login attempts on port 22 are among the most common brute force attacks on the web.<\/p>\n<p>Here is what I had to do to get MongDB running.<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \">$ # Complete the mongodb server installation on CentOS 6.5.\r\n$ sudo yum install -y mongodb mongodb-server\r\n$ sudo service mongod start\r\n$ sudo chkconfig mongod on\r\n$ mongo\r\n> help\r\n> quit()\r\n<\/pre>\n<p>If mongo fails to start:<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \">$ sudo rm \/var\/lib\/mongodb\/mongod.lock\r\n$ sudo service mongod start\r\n<\/pre>\n<h2>Troubleshooting<\/h2>\n<p>If you encounter trouble check the logs or post a question. The most interesting logs are in \/opt\/site\/logs and \/var\/log. I typically use a command like &#8220;<code>find \/var\/log -cmin -5 -type f<\/code>&#8221; to find interesting logs within 5 minutes of a problem event.<\/p>\n<h3>T.1 Firewall block external access<\/h3>\n<p>If you are having trouble accessing the site externally but not locally make sure that your firewall is open for external access if you want to allow that (in some cases that is not desirable for protected development systems).<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true \" >$ sudo iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT\r\n$ sudo iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT\r\n$ sudo service iptables save\r\n$ sudo service iptables restart  # this may not be necessary\r\n<\/pre>\n<h2>Script Revision History<\/h2>\n<div style=\"margin-left: 40px; margin-right: 40px;\">\n<table>\n<tbody>\n<tr>\n<td><strong>NOTE:<\/strong><\/td>\n<td>Fixed a bug in version 1.1 of the script that caused the MongoDB setup to fail.<\/td>\n<\/tr>\n<tr>\n<td><strong>NOTE:<\/strong><\/td>\n<td>Fixed a bug in version 1.0 of the script that caused the gunicorn (supervisord) setup to fail. Fixed another bug where the default password was set improperly.<\/td>\n<\/tr>\n<\/thead>\n<\/table>\n<\/div>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog presents a bash script that I created to automate the installation of a webserver based on django, nginx and gunicorn on a CentOS 6.5 VPS. The installation installs and configures the necessary system packages, and it installs a number of javascript tools like jquery, jquery-ui, datatables, flot, fancytree and others.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[5,16,39],"tags":[],"_links":{"self":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1457"}],"collection":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1457"}],"version-history":[{"count":46,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1457\/revisions"}],"predecessor-version":[{"id":1511,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1457\/revisions\/1511"}],"wp:attachment":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1457"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}