I recently built my first PC (view parts list here) that I wanted to setup as a playground to work on different IT related projects. One of the projects I had planned for the PC was to turn it into a web server for the home network. I am happy to say that I had completed the task and my Python web app is fully operational although it did not come without its roadblocks. With that said, I want to share the journey that I had this past couple days covering topics such as setting up the hypervisor, configuring the OS and web servers to get everything to play nice with each other.

The hypervisor that I went with was one that I have been very familiar with, Oracle’s VirtualBox. This application is solid and works well with the GNS3 application. It can be used on any operating system and best of all it is free. While setting up the PC I did think about using Microsoft’s Hyper-V but I could not get it to run on top of Windows 10 because the version I had (courtesy of FIU and Azure for Education) needed to be run on top of the Windows Server operating system.

Installing and configuring the CentOS VM

Once VirtualBox was installed, I downloaded CentOS to be used as the main operating system. I decided on this particular Linux distribution because I had worked on it during my unix administration class at FIU. Embarrassingly, during the installation process I did overlook a major detail. Before I ran the machine I would point to the iso file from the optical drive setting within the machine and after each reboot it would start the install process again. I was stuck in a loop until I figured out it was because I failed to change the boot order. That problem could have also be avoided by just pointing to the file without linking to it through the optical drive but I moved on.

The final configuration change that I needed to make was to change the network adapter settings of the machine. By default, the VM has one adapter enabled and is set to NAT meaning that it gets internet access through the host machine but cannot be accessed by the outside network. This setting was not ideal as I needed the server to be on the home network to serve the web apps. With a bit of research I changed the setting to bridged adapter which allows the VM to use the network interface that the host machine has physically available. In my case it was the WiFi adapter but it also recognized the two Ethernet ports that are built into the motherboard which I may use later on when my setup changes.

Configuring the Server

When it came to setting up a server there were some things that I remembered from class but most of what I had learned had to be relearned. Luckily I held onto the notes that I had taken and had them stored on a hard drive for moments like this. However the extent of my personal project went beyond what we did in the classroom so there were still some things I needed to look up.

When it came down to picking a web server I had decided to go with Nginx because it can serve as a reverse proxy and forward requests to the server that will handle the Python Application. I knew I also had to configure some settings within the firewall daemon to allow for network access. This was done by creating a zone that had unique tcp enabled ports opened not the generic http port. I then tried to start up Nginx on the machine but it would fail. I checked the status of the service and it had said that permission was denied. At first I was flummoxed, but in a sort of eureka moment I remembered I had faced a similar problem when I was in class.

The problem was with Selinux which is another security measure built into the CentOS system. The solution I had used back in class was to disable it completely but I still wanted it to be able to provide an extra level of security on the machine. Instead, I changed the permission to be permissive of http connections. With that change I was able to access the default Nginx web page from a different computer on the home network. A great step forward to reaching my goal but the next step proved to be a bit more painful.

Surface Laptop Available in Four Colors

The Python Web Application

Python is a language that I am completely comfortable writing in and became a big fan of using it to build web apps when I learned how to do it at FIU. In order to use Python for web apps I used it with a framework called Flask and was able to run it on the local host on my iMac. On the new CentOS machine however, I had to make sure that all the packages were installed to make sure the application would run. That included:

  • Mysql-server
  • pip3 (installer for Python Modules)
  • Flask (Python web framework)
  • MySQL-Connector-Python (Module to connect and communicate to MySQL server)
  • Virtualenv (creates an environment for the app to run in)

Once those packages were installed I then had to transfer the Python application files to the VM. Since the VM was already on the local network, I simply used the scp command on Terminal to move the files from one machine to the other.

The benefit of flask is that you are able to run the website on the local host machine to be able to fine tune the overall look and performance but it is not meant to be used in a production environment. It even tells you so when you run it on your machine and recommends using WSGI. WSGI is a server that easily recognizes and serves Python applications either on its own or behind a full web server. WSGI plays nice with Nginx which is another reason why I chose that particular web server. However, I had never used WSGI and was in for some rough seas.

WSGI, Nginx, and Systemd Configuration

How WSGI works in a nutshell is that it handles requests from the Nginx server and sends them back so that Nginx may fulfill the web page request out to the client. Pretty simple. Since it is meant to work in tandem with Nginx I had to configure the nginx.conf file to add a server and give the details such as which IP address and port it was using as well as some WSGI specific coding. Within the WSGI parameters I have it set up to forward requests that are coming in from the main socket to a Unix socket.

Starting with the installation process there are a few more packages that need to be installed to get WSGI to integrate gracefully with the Python application. Those packages are:

  • epel-release
  • python-devel
  • gcc

As mentioned previously the Nginx server can pass requests through the Unix socket that was described in the configuration file. With this type of socket, you can go ahead and take the next step to enable uWSGI when the machine boots up. This configuration in particular was headache to figure out. The general idea on how to accomplish this is to create a wsgi.service file for Systemd to be able to spin up when you enable the service. This service file is located in the /etc/systemd/system folder. The only downside to this setup is that you’ll have to continue to add more service files and other configuration files when you want to add more than one app. For now this set up will suffice, but for those looking to ramp things up I will suggest looking into using Emperor to manage your apps. 

Connecting WSGI to the Python App

The last hurdle that I needed to get over was to link WSGI to my python application. Th way to accomplish this was to create an uWSGI initialization file within the folder of the application. Within the .ini file you will list out other configuration information for when WSGI starts up such as the number of processes that you want to run or if you would like stats to display in a certain area. The most important configuration piece is the lines of code that tell WSGI what to call and where to call it from. 

For instance, if you were to run WSGI via command line you will need to point to the file that starts up your application. Fortunately since the .ini file is located within the application’s folder you can just put the name of the file (i.e myapp.py) rather than the full file path. However with this type of configuration you will also have to use the “callable” keyword within the initialization file. This keyword tells WSGI what that root file is called when not dealing with the default name of application. With Flask, I have learned and continue to implement the “app” name for the root file and therefore that is what I had set as the callable variable.

Final Word

Even though I have the apps up and running (at least when the PC is on) there is still some work that I want to get done. The most urgent would be to setup DNS on my machine so that the apps can be accessed by a custom web name rather than an IP address and port number. In the mean time however I have distracted myself by trying to debug my apps and make them a bit more functional for other users.

All in all, the experience was laborious and at times frustrating but I have to say it feels good to be able to put my skills to the test and actually create something of value. Thanks everyone for reading, I’m happy to share my journey with you. 

Links to resources that I used: