Skip to content

Commit b5fa47e

Browse files
committed
added ansible config for deployment
1 parent b602d3e commit b5fa47e

File tree

12 files changed

+467
-0
lines changed

12 files changed

+467
-0
lines changed

deploy/ansible.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[ssh_connection]
2+
ssh_args = -o StrictHostKeyChecking=no -o ControlMaster=auto -o ControlPersist=30m
3+
pipelining = True
4+
5+
[defaults]
6+
transport = ssh

deploy/hosts.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
all:
2+
hosts:
3+
intbot_setup:
4+
ansible_host: internal.europython.eu
5+
ansible_user: root
6+
nginx_user: nginx_user
7+
app_user: intbot_user
8+
9+
intbot_nginx:
10+
ansible_host: internal.europython.eu
11+
ansible_user: nginx_user
12+
app_user: intbot_user
13+
domain_name: internal.europython.eu
14+
app_port: 4672
15+
letsencrypt_email: "[email protected]"
16+
letsencrypt_rsa_key_size: 4096
17+
18+
intbot_app:
19+
ansible_host: internal.europython.eu
20+
ansible_user: intbot_user
21+
app_port: 4672
22+
repository_url: [email protected]:europython/internal-bot.git

deploy/playbooks/01_setup.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
- name: Deploy nginx and Let's Encrypt SSL certificate
2+
hosts: intbot_setup
3+
become: yes
4+
gather_facts: yes
5+
6+
tasks:
7+
- name: Install Docker dependencies
8+
apt:
9+
name: "{{ package }}"
10+
state: present
11+
update_cache: yes
12+
vars:
13+
package:
14+
- apt-transport-https
15+
- ca-certificates
16+
- curl
17+
- gnupg
18+
- lsb-release
19+
- make
20+
21+
- name: Install Docker
22+
block:
23+
- name: Add Docker GPG key
24+
apt_key:
25+
url: https://download.docker.com/linux/ubuntu/gpg
26+
state: present
27+
28+
- name: Add Docker repository
29+
apt_repository:
30+
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable
31+
state: present
32+
33+
- name: Install Docker
34+
apt:
35+
name: docker-ce
36+
state: present
37+
38+
- name: Combine non-root users to a single list
39+
set_fact:
40+
non_root_user_names: ["{{ nginx_user }}", "{{ app_user }}"]
41+
42+
- name: Create non-root users
43+
block:
44+
- name: Add user
45+
ansible.builtin.user:
46+
name: "{{ username }}"
47+
shell: "/bin/bash"
48+
generate_ssh_key: yes
49+
ssh_key_type: ed25519
50+
ssh_key_comment: "{{ username }}@{{ inventory_hostname }}"
51+
create_home: yes
52+
loop: "{{ non_root_user_names }}"
53+
loop_control:
54+
loop_var: username
55+
56+
- name: Make sure that user has permissions to the their home
57+
ansible.builtin.file:
58+
path: "/home/{{ username }}"
59+
state: directory
60+
owner: "{{ username }}"
61+
group: "{{ username }}"
62+
loop: "{{ non_root_user_names }}"
63+
loop_control:
64+
loop_var: username
65+
66+
- name: Then copy the authorized_keys from root so you can ssh later to the user
67+
copy:
68+
src: "/root/.ssh/authorized_keys"
69+
dest: "/home/{{ username }}/.ssh/authorized_keys"
70+
owner: "{{ username }}"
71+
group: "{{ username }}"
72+
mode: "0600"
73+
remote_src: "yes"
74+
loop: "{{ non_root_user_names }}"
75+
loop_control:
76+
loop_var: username
77+
78+
- name: Add the non root users (both nginx and app) to docker group
79+
user:
80+
name: "{{ username }}"
81+
groups: docker
82+
append: yes
83+
loop: "{{ non_root_user_names }}"
84+
loop_control:
85+
loop_var: username
86+
87+
- name: Read the deploy public key
88+
slurp:
89+
src: "/home/{{ app_user }}/.ssh/id_ed25519.pub"
90+
register: deploy_key
91+
92+
- name: Display the public key
93+
debug:
94+
msg: "For private repositories, make sure to put this key as deploy key on github: {{ deploy_key.content | b64decode }}"
95+

deploy/playbooks/02_nginx.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- name: Make sure that nginx and https work correctly for the app
2+
hosts: intbot_nginx
3+
4+
tasks:
5+
- name: Copy nginx configuration file
6+
ansible.builtin.template:
7+
src: ../templates/nginx/nginx.conf.j2
8+
dest: ./nginx.conf
9+
10+
- name: Create a server Makefile (for nginx) to manage on-server tasks
11+
ansible.builtin.template:
12+
src: ../templates/nginx/Makefile.nginx.j2
13+
dest: ./Makefile
14+
15+
- name: Set up docker-compose.yml on the remote server
16+
ansible.builtin.template:
17+
src: ../templates/nginx/docker-compose.nginx.yml.j2
18+
dest: ./docker-compose.yml
19+
20+
- name: Make sure the directory structure for certs exist
21+
shell: mkdir -p ./data/certbot/conf
22+
23+
- name: Display info at the end
24+
debug:
25+
msg: "Go to /home/{{ ansible_user }} and run make certbot/init-staging; then make certbot/upgrade-to-prod"

deploy/playbooks/03_app.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
- name: Deploy the bot/app/worker on remote host
2+
hosts: intbot_app
3+
4+
tasks:
5+
- name: Clone the repository to a specific version (to a temp location)
6+
git:
7+
repo: "{{ repository_url }}"
8+
dest: /tmp/src
9+
accept_hostkey: yes
10+
version: "{{ app_version }}"
11+
12+
- name: Build with a given commit hash
13+
# This will be stored in local registry, and available as version to docker-compose
14+
# where we can just reference correct version
15+
shell: "cd /tmp/src && make docker/build V={{ app_version }}"
16+
17+
- name: Create a server Makefile to manage app tasks
18+
ansible.builtin.template:
19+
src: ../templates/app/Makefile.app.j2
20+
dest: ./Makefile
21+
22+
- name: Set up docker-compose.yml for the app
23+
ansible.builtin.template:
24+
src: ../templates/app/docker-compose.app.yml.j2
25+
dest: ./docker-compose.yml
26+
27+
- name: Check if the env file exists
28+
ansible.builtin.stat:
29+
path: intbot.env
30+
register: env_file
31+
32+
- name: If env file doesn't exist - copy the example
33+
ansible.builtin.copy:
34+
src: ../templates/app/intbot.env.example
35+
dest: intbot.env.example
36+
when: not env_file.stat.exists
37+
38+
- name: If the env file doesn't exist - fail with error message
39+
ansible.builtin.fail:
40+
msg: "The env file doesn't exist. Please ssh, copy the example and adjust"
41+
when: not env_file.stat.exists
42+
43+
- name: Start docker compose to see if everything is running
44+
shell: "docker compose up -d"
45+
46+
- name: Migrate on prod
47+
shell: "make prod/migrate"
48+
49+
- name: Restart everything and finish
50+
shell: "docker compose up -d"

deploy/templates/app/.env.example

Whitespace-only changes.

deploy/templates/app/Makefile.app.j2

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
echo:
3+
"Dummy target, to not run something accidentally"
4+
5+
prod/migrate:
6+
docker compose run app make in-container/migrate
7+
8+
prod/shell:
9+
docker compose run app make in-container/shell
10+
11+
prod/db_shell:
12+
docker compose run app make in-container/db_shell
13+
14+
prod/manage:
15+
docker compose run app make in-container/manage ARG=$(ARG)
16+
17+
logs:
18+
docker compose logs -f
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
services:
3+
db:
4+
image: postgres:16.4
5+
volumes:
6+
- pgdata:/var/lib/postgresql/data
7+
env_file:
8+
- intbot.env
9+
healthcheck:
10+
test: ["CMD-SHELL", "pg_isready -U intbot_user_prod -d intbot_database_prod"]
11+
interval: 10s
12+
retries: 5
13+
start_period: 30s
14+
timeout: 10s
15+
16+
app:
17+
image: "intbot:{{ app_version }}"
18+
command: "make in-container/gunicorn"
19+
env_file:
20+
- intbot.env
21+
environment:
22+
- APP_VERSION={{ app_version[:8] }}
23+
expose:
24+
- "{{ app_port }}"
25+
networks:
26+
- "shared_with_nginx_network"
27+
- "default"
28+
depends_on:
29+
db:
30+
condition: service_healthy
31+
restart: true
32+
33+
bot:
34+
image: "intbot:{{ app_version }}"
35+
command: "make in-container/bot"
36+
env_file:
37+
- intbot.env
38+
environment:
39+
- APP_VERSION={{ app_version[:8] }}
40+
depends_on:
41+
db:
42+
condition: service_healthy
43+
restart: true
44+
45+
volumes:
46+
pgdata:
47+
48+
49+
networks:
50+
shared_with_nginx_network:
51+
name: shared_with_nginx_network
52+
external: true
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
DJANGO_SETTINGS_MODULE="intbot.settings"
2+
DJANGO_ENV="prod"
3+
4+
# Change this on production
5+
SECRET_KEY="asdf-asdf-asdf-1234-1234-1234"
6+
7+
# Database settings
8+
POSTGRES_DB="intbot_database_prod"
9+
POSTGRES_USER="intbot_user_prod"
10+
POSTGRES_PASSWORD="RandomPasswordPleaseChange"
11+
12+
# Discord
13+
DISCORD_BOT_TOKEN="Token Goes Here"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Set a special docker-compose file for all the operations
2+
COMPOSE := docker compose
3+
4+
CERT_PATH=/etc/letsencrypt/live/{{ domain_name }}
5+
DATA_PATH=./data/certbot
6+
SSL_NGINX_CONF_URL=https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
7+
SSL_DHPARAMS_URL=https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem
8+
9+
echo:
10+
"This is a first dummy target, so you don't accidentally run something :)"
11+
12+
nginx/recreate:
13+
$(COMPOSE) up --force-recreate -d nginx
14+
15+
certbot/renew:
16+
$(COMPOSE) run certbot renew
17+
$(COMPOSE) restart nginx
18+
19+
20+
certbot/list:
21+
$(COMPOSE) run certbot certificates
22+
23+
24+
certbot/init-staging: \
25+
certbot/download-initial-parameters \
26+
certbot/create-dummy-certificate \
27+
nginx/recreate \
28+
certbot/delete-dummy-certificate \
29+
certbot/create-staging-certificate
30+
31+
32+
certbot/download-initial-parameters:
33+
@echo "### Downloading recommended TLS parameters ... "
34+
mkdir -p "$(DATA_PATH)/conf"
35+
curl -s $(SSL_NGINX_CONF_URL) > "$(DATA_PATH)/conf/options-ssl-nginx.conf"
36+
curl -s $(SSL_DHPARAMS_URL) > "$(DATA_PATH)/conf/ssl-dhparams.pem"
37+
38+
39+
certbot/create-dummy-certificate:
40+
@echo "### Creating a dummy certificate for {{ domain_name }}"
41+
mkdir -p "$(DATA_PATH)/conf/live/{{ domain_name }}"
42+
$(COMPOSE) run --rm --entrypoint "\
43+
openssl req -x509 -nodes -newkey rsa:{{ letsencrypt_rsa_key_size }} \
44+
-days 1 \
45+
-keyout '$(CERT_PATH)/privkey.pem' \
46+
-out '$(CERT_PATH)/fullchain.pem' \
47+
-subj '/CN=localhost'" \
48+
certbot
49+
50+
51+
certbot/delete-dummy-certificate:
52+
@echo "### Deleting dummy certificate for {{ domain_name }} ..."
53+
$(COMPOSE) run --rm --entrypoint "\
54+
rm -rf /etc/letsencrypt/live/{{ domain_name }} && \
55+
rm -rf /etc/letsencrypt/archive/{{ domain_name }} && \
56+
rm -rf /etc/letsencrypt/renewal/{{ domain_name }}.conf" \
57+
certbot
58+
59+
60+
certbot/create-staging-certificate:
61+
# First regenerate the certificate
62+
$(COMPOSE) run certbot certonly \
63+
--staging \
64+
--webroot -w /var/www/certbot \
65+
--email "{{ letsencrypt_email }}" \
66+
-d "{{ domain_name }}" \
67+
--rsa-key-size "{{ letsencrypt_rsa_key_size }}" \
68+
--agree-tos \
69+
--force-renewal
70+
# And then restart nginx....
71+
$(COMPOSE) exec nginx nginx -s reload
72+
73+
74+
certbot/force-reissue-PROD-certificate:
75+
# First regenerate the certificate
76+
$(COMPOSE) run certbot certonly \
77+
--force-renewal \
78+
--webroot -w /var/www/certbot \
79+
--email "{{ letsencrypt_email }}" \
80+
-d "{{ domain_name }}" \
81+
--rsa-key-size "{{ letsencrypt_rsa_key_size }}" \
82+
--agree-tos \
83+
--force-renewal
84+
# And then restart nginx....
85+
$(COMPOSE) exec nginx nginx -s reload
86+
87+
88+
certbot/upgrade-to-prod: certbot/force-reissue-PROD-certificate

0 commit comments

Comments
 (0)