Update 2022: A lot has changed since this was written. Use Docker instead.
In the beginning I used to do a lot of server setup. Every project would require that I do some sort of setup, then do it again for production. As projects became more complex the differences between what I was developing on and what I would eventually host my projects on became greater. So much so that I would end up spending an incredible amount of time duplicating much of the effort I would make getting my development environment up and running.
To that end I decided to make things simpler. Keep my environments close to each other as possible so that I could spend much more time developing cool functionality. To that end I started building I moved development to virtual machines and now if I need a clean slate to start from, I just clone my base VM and start from a point where everything works the way its supposed to.
To that end, I wanted to update my “base” VM and decided it was a good time to write down what exactly I did to it. Now for 24-ish easy steps!
-
Install Parallels, VMWare, or VirtualBox. I personally use Parallels, but I’ve used all three at some point in my career. For me Parallels works really well for my workflow.
-
Download CentOS (Version 6.2 at this time) and install. There are several options.
- Download minimal ISO (this walkthough assumes minimal)
- Download LiveDVD
- Download LiveCD
- Download Netinstall
The minimal is very quick to download and will allow us to only install that which we need. If you want a GUI, download one of the Live options. I don’t really see a benefit to the Netinstall for this walkthrough.
The process for installing a VM using Parallels is pretty straight forward. If you are using the minimal ISO, just go through the process. There is the super-scary step where it formats your hard drive and warns about losing all your data, just plow on through, its sandboxed in your VM container. If you’re installing in VM its not getting rid of anything.
-
Login as root
-
Enable networking.
Throughout this walkthrough I will be using
vi
as my editor. Feel free to use whatever you wish, emacs, joe, kate, etc.-
Disable IPV6 :
vi /etc/systconfig/network
add the line
NETWORKING_IPV6=no
-
Enable DHCP (for now) :
vi /etc/sysconfig/network-scripts/ifcfg-eth0
add the line
BOOTPROTO=dhcp
-
Or if you want to jump straight to static routing :
vi /etc/sysconfig/network-scripts/ifcfg-eth0
add the following lines replacing with their proper values the omitting < and > symbols.
BOOTPROTO=none GATEWAY=<ipaddress> NETMASK=<ipaddress> IPADDR=<ipaddress> USERCTL=false PEERDNS=yes DNS1=<ipaddress> DNS2=<ipaddress>
-
Save the file and restart
/etc/init.d/network restart
-
-
Update currently installed packages :
yum update
At this point you should have a updated vanilla CentOS 6 install.
-
Create admin user that is part of the admin group :
groupadd admin useradd -Gadmin <username>
-
Install
sudo
:yum install sudo
-
Add admin group to the sudoers file
visudo
Add the following line to the end of the file :
%admin ALL=(ALL) ALL
-
I prefer to only use ROOT when absolutely necessary. You can continue to use ROOT for the remainder of this walkthrough if you wish, you will just need to remove the
sudo
command at the beginning of each command that requires elevated permissions. Log out of root and back in as your admin user.(OPTIONAL) At this point, you should also be able to ssh into your VM server. So if you would like to configure public key authentication, this is where I would do it. Then SSH into the server and continue the walkthough.
-
Install development packages :
sudo yum install make automake gcc gcc-c++ man man-pages
-
Install packages specific to the type of development you will be doing. In my case this will be specifically Python/Django related packages as well as web serving technology. Your packages may vary.
sudo yum install python-devel python-setuptools openssl openssl-devel zlib zlib-devel git libjpeg libjpeg-devel freetype freetype-devel lcms lcms-devel python-lcms python-imaging python-imaging-devel libxml2 libxml2-devel
-
Install your webserver, in this case, Nginx. CentOS does not offer the latest version of Nginx in its repositories so you need to add the repository that does, in this case the EPEL repo hosted by Fedora as well as the Nginx repo. You man not need the EPEL repo, but I like having it available. If you are not using CentOS 6.2 the following URL may be different.
sudo rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm sudo rpm -Uvh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm sudo yum install nginx-release-centos.noarch sudo yum install nginx
Start nginx
/etc/init.d/nginx start
-
Configure basic firewall
See what your current firewall configuration looks like.
sudo iptables --line-numbers -n -L
Add a rule to allow port 80 through. In this command I am inserting the rule using -I in position 2.
sudo iptables -I INPUT 2 -m state --state NEW -p tcp --dport 80 -j ACCEPT
Save the rules
sudo service iptables save
Check to see if Nginx is running properly by going to the IP of your server in a web-browser.
-
Install Memcached
sudo yum install libevent-devel memcached sudo service memcached start sudo chkconfig memcached on
-
Install MySQL
sudo yum install mysql mysql-server mysql-devel sudo chkconfig mysqld on sudo service mysqld start
Secure your MySQL installation by setting a root password
and cleaning up the default DBs.sudo /usr/bin/mysql_secure_installation
-
Prepare your Python environment. CentOS 6.2 comes with Python 2.6.6. I could just live with that, but I’ve been using 2.7 fairly extensively and I like to keep everything the same across all my development platforms. I also like to keep the standard Python executable intact so that I don’t accidentally make a change that has a far-reaching effect that I did not foresee and have to debug later. For that reason, I install any updated Python versions side-by-side with the system default. You can install a different version without overriding the “active version” symlink using the
altinstall
command.curl -O http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz tar xvf Python-2.7.2.tgz cd Python-2.7.2 ./configure make # Using "install" here will override your the python symlink # in /usr/local/bin. Depends on what you want your default # version of Python to be. sudo make altinstall
-
Install
pip
,virtualenv
and,virtualenvwrapper
sudo easy_install pip sudo pip install virtualenv sudo pip install virtualenvwrapper
-
Set up virtualenvwrapper. Virtualenvwrapper needs a few settings to work properly. Set these variables in your
~/.bash_profile
replacing the<values>
.export VIRTUALENV_USE_DISTRIBUTE=True export WORKON_HOME=</path> export PROJECT_HOME=</path> source /usr/bin/virtualenvwrapper.sh
Log out then in again, or
source ~/.bash_profile
to have these changes recognized. WORKON_HOME is the directory your virtualenvs will live and PROJECT_HOME is where your Django projects will live. -
At this point you should be able to create a new virtualenv. The
mkvirtualenv
command will create a virtualenv in your WORKON_HOMEmkvirtualenv -p </path> <virtualenv>
It should be activated at this point. If you are coming back to this walkthrough and it is not active, you can simply activate it with the command:
workon <virtualenv>
-
Install Python packages using Pip
If you’re virtualenv is not active you can install you python modules into it anyway using the
-E
flag:pip install <python_module_name>
If it is not active, then you can add
-E <virtualenv>
to the previous command:pip install -E <virtualenv> <python_module_name>
Repeat these steps until you have installed all your requirements. You can also create a requirements file which contains a list of modules you wish to install. A requirements file is a simple text file with one module on each line. Please see the Pip documentation for details on requirement files. Use the
-r
flag to have pip use it.A requirements file might look like this:
# requirements.txt Django==1.3.1 MySQL-python==1.2.3 python-memcached==1.48 uWSGI==1.0.4
Save, then:
pip install -r /path/to/requirements.txt
-
Install the Python Imaging Library. Normally I would just use pip to install this, but since
yum
installs the libraries PIL depends on in a place the installer doesn’t look, some modifications need to be made to the installer.wget http://effbot.org/downloads/Imaging-1.1.7.tar.gz tar xvzf Imaging-1.1.7.tar.gz cd Imaging-1.1.7 vi setup.py
Update the library pointers to know where to look for the libraries we installed earlier. This assumes you are using an x86_64 build. If not, use
/usr/lib
instead. This should start right around line 35.TCL_ROOT = None JPEG_ROOT = "/usr/lib64" ZLIB_ROOT = "/usr/lib64" TIFF_ROOT = "/usr/lib64" FREETYPE_ROOT = "/usr/lib64" LCMS_ROOT = "/usr/lib64"
Save, then build and install
python setup.py build python setup.py install
-
Install and configure uWSGI
pip install uwsgi
Now configure uWSGI
Configure WSGI. Create a file calledwsgi.py
in the root of your Django project and put the following in it. Updating it for your site by replacing the<values>
:SITE_DIR = '</path>' import site site.addsitedir(SITE_DIR) import os import sys sys.path.append(SITE_DIR) os.environ['DJANGO_SETTINGS_MODULE'] = '<your_django_project>.settings' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler()
Create another file in your Project root called
uwsgi_conf.ini
vi uwsgi_conf.ini
Insert the following, changing the
<values>
for your own.[development] uid = www-data socket = /tmp/uwsgi_gasc.sock chmod-socket = 644 pythonpath = /path/to/<your_project> pythonpath = /path/to/ # The directory holding your project virtualenv = /path/to/<virtualenv> master = true processes = 4 env = DJANGO_SETTINGS_MODULE=<your_project>.settings module = django_wsgi logdate = true harakari = 120 optimize = 2 logto = /path/to/<logfile>.log chdir = /path/to/<your_project> # You don't need the following in your ini file, but this is where you can make multiple settings for different deployments. ; [staging] ; [production]
Daemonize uWSGI with Upstart:
sudo touch /etc/init/uwsgi_<your_django_project_>.conf sudo vi /etc/init/uwsgi_<your_django_project_>.conf
Insert the following changing out
<your_django_project>
:# file: /etc/init/uwsgi_<your_django_project>.conf description "uWSGI server for <your_django_project>" start on runlevel [2345] stop on runlevel [!2345] respawn # This is where you stipulate which set of settings in your ini file you will use. # You could specify "production" at the end of this command instead of "development" to use a different configuration setup in your ini file. exec /path/to/<virtualenv>/bin/uwsgi --ini /path/to/uwsgi_conf.ini:development
Add a dedicated system user to run uWSGI:
sudo useradd -M -r --shell /bin/sh --home-dir /opt/www-data www-data
Reload Upstart and start uWSGI:
sudo initctl reload-configuration sudo start uwsgi_<your_django_project>
-
Setup NginX
sudo vi /etc/nginx/uwsgi_params # it may already exist
Put the following in it:
uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;
Then create the nginx conf file for your project:
sudo vi /etc/nginx/conf.d/<your_app_name>.conf
Insert the following:
server{ listen 0.0.0.0:80; server_name www.<your_address>.com <your_address>.com; access_log /path/to/access/log.log; error_log /path/to/error/log.log; location /site_media { # Point this wherever the static files for your django app are stored (i.e. MEDIA_ROOT) alias /opt/apps/<your_app_name>-env/site/media; } location / { uwsgi_pass unix:///tmp/uwsgi_<your_app_name>.sock; include uwsgi_params; } }
Edit
/etc/nginx/nginx.conf
. Change the user fromnginx
towww-data
sudo vi /etc/nginx/nginx.conf
Restart Nginx and make sure it comes back at reboot:
sudo /etc/init.d/nginx restart chkconfig nginx on
-
That’s it! Load your site up in browser and crack a beer.