Server Setup - Introduction [en]

2019-02-01 | 2019-02-11

The objective of this series of blog posts is to explain the current setup that I ended up with, since the majority of tutorials and posts I found on the web focused on parts of this type of setup and not the full picture. Although this series will focus on Django applications, and a specific setup that works for me, I hope it can help someone else that's doing something similar with different technologies.

The following stack was used:

Index

  • Part 1: setting up the remote server
  • Part 2: start traefik on the remote server
  • Part 3: Deploy using Gitlab CI

Part 1: Setting up the remote server

For this section, I wrote some config files that I keep on a Gitlab repo. You can check them out at https://gitlab.com/afk_mcz/srv although they are probably not going to stay in sync with this post, so you can check the following commit for version at the time of writing this.

Digital Ocean

The general idea with this setup is that is really simple to extend, improve and maintain low costs.

The firs step is to create a new DO droplet, I prefer using the $5 USD droplet so if I need more, I can start a new one and only get charged for the amount of resources I actually use. The easier way to kickstart development is to choose the Docker One-Click App as a starting point. If you decide to go for another route, the main requirements for the remote server are being able to connect via SSH and have docker and docker-compose installed and configured.

Be sure to add your SSH key to the droplet when you are creating it as it will be necessary for ansible to connect to the server.

Ansible

If you are not familiar with Ansible, I recommend reading the Ansible documentation, once it is installed, the first thing is to configure the hosts file. First I like to place my hosts file at ~/.config/ansible/hosts and for that I need to configure Ansible to pick it up from there so we need to add this line in your config file.

# ~/.asnible.cfg

[defaults]

inventoru = ~/.config/ansible/hosts

After that, create the hosts file if it doesn't exist

# ~/.config/ansible/hosts

[webservers] # This is the group of hosts that we will configure.
1.1.1.1 # replace it with your droplet's IP adress.

Once ansible is configured you can check everything is working as expected pinging the remote server using the following command: ansible all -m ping -u root a and you should get something similar to this.

DO_IP_ADRES | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Now we need to tell ansible what do we want on our new server. This can be done using the ansible-playbook command which accepts a YAML config file defining a series of tasks that will be run on the remote server via SSH. So create a file named init-web-server.yml and write the following:

# init-web-server.yml
---
- hosts: webservers
  gather_facts: false
  remote_user: root
  tasks:
    - name: Bootstrap a host without python installed
      raw: apt install -y python

The first thing we need to do is install python on the remote server, as the DO docker app doesn't have it by default and ansible needs it to function, so we specify don't need to check the status of the server and run a raw command to install python; we only need to run this playbook once with the following command ansible-playbook init-web-server.yml

If you don't get any errors then you can continue writing the next playbook, web-server.yml.

- hosts: webservers
  remote_user: root
  tasks:
    - name: Install a list of packages
      apt:
        name: "{{ packages }}"
        update_cache: true
      vars:
        packages:
          - python
          - python-pip
          - vim
          - zsh
          - tree
    - name: install python deps
      pip:
        name: docker-compose
    - name: Create a deploy user
      user:
        name: deploy
        generate_ssh_key: true
        # export DEPLOY_PASSWORD=$(mkpasswd --method=sha-512)
        password: "{{ lookup('env','DEPLOY_PASSWORD') }}"
        groups:
          - docker
          - sudo
        state: present
        shell: /bin/zsh
    - name: Add ssh key
      authorized_key:
        user: deploy
        state: present
        manage_dir: true
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
    - name: Add deploy key
      authorized_key:
        user: deploy
        state: present
        manage_dir: true
        key: "{{ lookup('file', '~/.ssh/deploy.pub') }}"

Install a list of packages

The first task on web-server.yml playbook is to install a list of packages that will make development easier. The majority of them are optional so you can skip them.

The only important ones are python-pip and docker-compose so make sure you have them.

Deploy user

Next we need a new user that will be in charge of all deployment tasks. It needs to be on the docker and sudo groups and to set the password, ansible will look the environment variable DEPLOY_PASSWORD , to generate a hashed password. You can run the following command export DEPLOY_PASSWORD=$(mkpasswd --method=sha-512) if you are on linux, if this doesn't work for you, you can check out other ways to do it on the Ansible FAQ, just make sure the env variable is set up correctly, you can confirm this running echo $DEPLOY_PASSWORD from the terminal.

SSH keys

On the ansible playbook we tell ansible to look for two files on our local computer, id_rsa.pub and [deploy.pub](http://deploy.pub) you could only pass one, but for being able to deploy using Gitlab CI, you need a SSH key without a passphrase, and I normally do have one on my default SSH key, so just make sure you add the user on at least one SSH key without passphrase.

Now run the following command ansible-playbook web-server.yml and make sure everything worked by connecting to the remote server using SSH ssh deploy@DO_IP_ADDRESS.

Iconoellugar