Showing posts with label vivid vervet. Show all posts
Showing posts with label vivid vervet. Show all posts

Tuesday, May 12, 2015

Series: How to create your own website based on Docker (Part 4 - Planning Docker container architecture)

Let's design our docker container architecture

This is part 4 of the series: How to create your own website based on Docker.

Docker and Docker Compose are now up and running. So it's about time to let them all play together.

Before we start planning our container architecture, we need to make sure that we understand what we're trying to achieve.
  1. We want to be able to port our apps to any other platform as easy as possible
  2. We want our applications to be as separated as possible (every container should have one purpose)
  3. We want to create more instances of an application container if needed
  4. We don't want crashed applications to crash other applications
So based on this list we need to figure out what components will be needed for the site.

Let's define all components to be "dockerized"

Component 1: nginx reverse proxy

I usually start with nginx as reverse proxy in front of all other services. This allows me to have a single entry point for all requests and to distribute them internally to all containers which should be accessible from the web. This reverse proxy will listen on port 80 and will redirect all requests based on the context root, the subdomain and/or the hostname.

Here's an example:
  • www.project-webdev.com:80 => redirects to my blog which listens on port 8081 internally (the projectwebdev blog will be hosted on another nginx machine).
  • api.project-webdev.com:80 => redirects to my REST API which listens on port 3001 internally (the API will be an ioJS/nodeJS application).
  • Also possible: www.project-webdev.com:80/api => redirects to my REST API which listens on port 3001 internally (the API will be an ioJS/nodeJS application).

As you can see, all requests will go through the nginx reverse proxy and will be redirected internally to the appropriate service.

What's important to mention: The internal ports (e.g. 8081, 3001,...) should not be exposed to public, so it should not be possible to access the blog directly (like www.project-webdev.com:8081).

This would be our first component, right? Wait! Where should our new nginx reverse proxy run on?

We need an operating system... so basically our first component would actually be a container that contains the operating system. For that I'm going to use Ubuntu 14.04 LTS.

So right now our current architecture would look like this:
nginx reverse proxy docker container

Component 2: nginx web server for our website

The next thing we'll need for our web site is the markup for the website. So all we need now is another nginx web service, but this time it will not act as reverse proxy, but as a real web server hosting our files. Since we don't want any port conflicts my basic rule is that 4-digit ports are never exposed to public. (Port 80 and 443 (SSL) are allowed to be accessed from outside).

Since Docker works with container links, we need to add a link to our reverse proxy, so that it can communicate internally with our blog application (nginx web site), which listens on port 8081.


As mentioned before, port 8081 can not be accessed from outside - therefore painted in purple.

You can also see that we've mounted a directory on the docker host into our blog's docker container. We're doing this, because we want to be able to change the website from outside later, without restarting the container.

Technically it would look like the following - I'll go into details later:
In the docker container (our provider's Ubuntu server), we'll create a directory called /opt/docker/projectwebdev/html/ which will be mounted as /var/www/html/ in the container (the directory which nginx uses to load the HTML, CSS and JS files from later). So whenever nginx (the one in our nginx web site container) receives a request from a visitor, it will load the files from our real server (from /opt/docker/projectwebdev/html/) and will provide it to him - I think you've got it, right? It's not that hard.


Component 3: ioJS REST API container

Our website should fetch information from a REST API. Therefore we will need an ioJS application that will provide all data for the website asynchronously. The website will use a call to http://api.project-webdev.com to fetch the contents or any other information needed.

Since this is a new URL, we need to link this container to our nginx as well, so that we can build our redirect to that container internally.

Component 4: mongoDB database container

A REST API does not make sense without any data persistence in the back. Therefore we need to add our mongoDB to that architecture as well. This instance listens on port 3333 and should only be accessible via REST API (and therefore implicitly via our nginx reverse proxy), which is why we need to add a link to our REST API so that it can access the data in the mongoDB.


Additional component: Logs

When running this application stack in the wild later, it's very important to be able to analyse the logs (e.g. using the ELK stack). Since we have several containers, it's does not make to get the logs from each instance separately. So we're creating another volume mount which acts as central storage for all log files of all used containers. This directory can later be used by log file analysis tools, so you can analyse the hell out of your logs. :)

Conclusion

We have now several containers that act as "platform" for a certain purpose. They are all completely encapsulated and are sharing their resources (thanks to Docker).
  1. nginx reverse proxy
    • links:
      • nginx website
      • ioJS REST API
    • volumes:
      • log files (/opt/docker/logs)
  2. nginx web site
    • links:
      • none
    • volumes:
      • web site files (/opt/docker/projectwebdev/html)
      • log files (/opt/docker/logs)
  3. ioJS REST API
    • links:
      • mongoDB database
    • volumes:
      • ioJS application files (/opt/docker/projectwebdev-api/app)
      • log files (/opt/docker/logs)
  4. mongoDB database
    • links:
      • none
    • volumes:
      • mongoDB files (/opt/docker/mongodb/db)
      • log files (/opt/docker/logs)
That's it... that's our "dockerized" architecture for our projectwebdev website based on Docker containers. Let's create our Docker Compose file now... in the next part of this series.

Monday, May 11, 2015

Series: How to create your own website based on Docker (Part 3 - Installing Docker)


It's time to get really started...

This is part 3 of the series: How to create your own website based on Docker.

If you still don't know what Docker is or what it does? Just read the official "What is docker" document!

In this part of the series, we're going to install Docker and Docker Compose - although Docker does not recommend to use Docker Compose for production use, we'll still give it a shot. If you've never heard of Docker Compose, let me tell you in a few words what it does.

Docker Compose is a tool for defining and running complex applications with Docker. With Compose, you define a multi-container application in a single file, then spin your application up in a single command which does everything that needs to be done to get it running. In short: You'll define a YAML file in which you'll specify how the Docker containers must be started and how they are linked together. Docker Compose will then start them for you and will make sure that they are started in the right order. It will also take care of naming these containers baed on your settings. But we'll get into that in one of the next parts.

Let's install Docker & Docker Compose

As I've mentioned before, Docker requires a 64-bit installation regardless of your Ubuntu version. Additionally, your kernel must be 3.10 at minimum. The latest 3.10 minor version or a newer maintained version are also acceptable.

Run the following command, which will download a shell script and will trigger the installation of Docker. When running this command, it will ask you for your password. Just provide the password that you have set for johndoe in part 2 of this series.
# wget -qO- https://get.docker.com/ | sh
To verify that Docker has been installed correctly, just type the following to see the options that you have when running Docker:
# sudo docker --help
Now let's install Docker Compose by typing the following:
# curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
Let's add johndoe to a new group called docker:
# sudo usermod -aG docker johndoe
Now you should be able to run docker containers without using sudo.

In order to start our service automatically when we reboot the system, we need to add it to the default runlevel:

sudo update-rc.d docker defaults
Finally we also need to setup our UFW again, so that it can work with docker, by exposing another port (remember we've done that already with the SSH port):
#sudo ufw allow 2375/tcp
We also need to change some UFW settings to make Docker work correctly:
# sudo vi /etc/default/ufw
Set the DEFAULT_FORWARD_POLICY policy to:
DEFAULT_FORWARD_POLICY="ACCEPT"
Reload UFW to use the new setting.
# sudo ufw reload
Now check the status of the firewall again:
# sudo ufw status
It should now look similar to this:
Status: active
To               Action      From
--               ------      ----
2233/tcp         ALLOW       Anywhere
2375/tcp         ALLOW       Anywhere
Now Docker as well Docker Compose are installed - let's think about a Docker architecture! :)

Series: How to create your own website based on Docker (Part 2 - Setting up Ubuntu for production use)

Setting up Ubuntu as docker host

Let's get started

This is part 2 of the series: How to create your own website based on Docker.

So why do I want to create my website completely based on Docker containers? Well, that's pretty easy, because a) Docker is cool and b) Docker allows me to move my containers to new providers quickly.

Let's imagine, that a new cheap & fast cloud service is brought to the web or my hosting provider gives me a chance to move to a newer/faster hardware, then it would be cool to quickly move my whole page (including all databases, apps and other services that might be running on my "old" machine) to the new appliance.

That's exactly what I need Docker for, because then my "dockerized" apps are completely portable and can run everywhere - on a cloud, on a virtual machine or on my local computer - Linux is pretty much mandatory, though.

If you still don't know what Docker is or what it does? Just read the official "What is docker" document!

Setting up Ubuntu for production use

The big advantage of using Docker is that we don't have to spend that much time creating a production-ready server. This machine will basically only act as platform for all my containers (from now on called "Docker host") and therefore only needs the minimum security configuration. But still - later - in the docker containers - you have to take care of the application security of course - but we'll get to that later.

Although Docker is supported on Ubuntu from 13.10 & up, I'm using the latest greatest Ubuntu distribution: Ubuntu 15.04 (Vivid Vervet). It comes with the latest kernel and is therefore well-prepared for my Docker installation (remember Docker requires a 64-bit installation regardless of your Ubuntu version. Additionally, your kernel must be 3.10 at minimum).

I'm going to install everything on this (small) machine to test its performance and have the opportunity to move to a faster one once everything has been set up: Hetzner vServer VX11 (Dual Core, 2GB of RAM). Let's see how this small machine performs in the real world, running nginx, nodejs and mongodb containers.

Let's start setting up the Ubuntu machine - our new Docker host.

Add a new user to the system


First of all you need make sure that no one (besides you) can access your machine via SSH, so we need to create a new user. Let's say our new user is called "johndoe" - please use your own user name here - but for the sake of simplicity I'll keep using johndoe as our new user in this series.
# adduser johndoe
This command will ask you some questions (including your password) and will then create a new user for you.

Since you want to be able to use sudo later, you need to add root privileges to that user.

Just type the following command and an editor will appear that allows you to add the user to the:
# visudo
Now find the following section: #user privilege specification and add the following line below the root entry:
root ALL=(ALL:ALL) ALL
johndoe ALL=(ALL:ALL) ALL
Now hit Control+O to save then Control+X to exit Nano editor.

Before you exit, you should also change the root password - just to make sure that no one else knows it - just enter the following command and enter your new password twice:
# passwd

Secure your SSH access correctly


Now that you have your user set up, you can log out from your machine (just type exit) and log in with your new user again. Although it's not necessary, I recommend to do so, so that you can test whether your SSH login (with your new user) works as expected.

On a Unix based machine (e.g. Linux or OSX), you would connect to SSH like that:
# ssh johndoe@yourmachine.com
Now that we're logged in as johndoe, we will secure our SSH access. So type the following command to get into the ssh daemon configuration - I will be using vi from now on - but you can also use nano as editor:
# sudo vi /etc/ssh/sshd_config
Now we will change the standard SSH port, so that port sniffers will have a hard time to guess your port - for that please change the following line (in vi just press "i" to change to insert mode):
# What ports, IPs and protocols we listen for
Port 2233
We've now changed the port from 22 to 2233. Please write it down the port number and don't forget the value you have specified here - otherwise you won't be able to login via SSH anymore! This will not prevent hackers from trying to port scan your server, but it will prevent scripts trying to access your machine on the standard SSH port.

Now we'll tell SSH to not allow the root user to login - so we're changing the value of PermitRootLogin to "no"!
AuthenticationLoginGraceTime 120
PermitRootLogin no
StrictMode yes
If you're the only one accessing this machine you can also add the following line to the end of the file:
AllowUsers johndoe
Having changed that, only johndoe can access this machine via SSH now.

Now just his ESC and enter ":wq" to write the changes to the file and reload the SSH daemon:
# sudo service sshd restart
Ok... let's test our new secured SSH service. Just exit from the machine and log in via SSH again, but this time you'll have to specify the port:
 # ssh johndoe@yourmachine.com -p 2233
You can also try to login as root user, but that should not work as we've told the daemon not to allow the root user to login:
  # ssh root@yourmachine.com -p 2233

  Install & enable the Ubuntu firewall

Since we have now set up our SSH access we should set up our firewall now - this will make sure that you can only access the machine via port 2233 (your new SSH port):

Let's install the UFW - Uncomplicated Firewall by typing the following command:
# sudo apt-get install ufw
Once the firewall is installed, check its status:
# sudo ufw status
It will probably tell you that it's not enabled - that's ok for now. So we'll tell it to allow incoming requests to our new port:
# sudo ufw allow 2233/tcp
And we'll also tell it to deny all incoming and allow all outgoing requests by default:
# sudo ufw default deny incoming
# sudo ufw default allow outgoing
Now let's enable the firewall:
# sudo ufw enable
Now check the status of the firewall again:
# sudo ufw status
It should now look similar to this:
Status: active
To               Action      From
--               ------      ----
2233/tcp         ALLOW       Anywhere
That's pretty much it - your Ubuntu machine is now secured from illegal SSH access.

Ok... let's test our new more-secured SSH service. Just exit from the machine and log in via SSH again - again, you'll have to specify the new port:
 # ssh johndoe@yourmachine.com -p 2233
Now you should be logged in and ready to install docker (next part of the series)!