Server Setup - Introduction [en]
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:
- Digital Ocean for hosting and DNS management
- Ansible for server configuration
- Gitlab CI for continuous integration
- Docker compose for starting and managing Docker containers
- Traefik for automated SSL certificates and reverse proxy
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.
~~T~~he 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
.