Skip to content

Commit ac3243a

Browse files
committed
basic deployment setup
1 parent 6b5cf10 commit ac3243a

File tree

10 files changed

+314
-0
lines changed

10 files changed

+314
-0
lines changed

deploy/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

deploy/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
run-playbook:
2+
ansible-playbook -i hosts.ini playbook.yml --extra-vars "app_version=$(V)"
3+
4+
deploy/deps/init:
5+
pip install pip-tools
6+
7+
deploy/deps/compile:
8+
pip-compile
9+
10+
deploy/deps/install:
11+
pip-sync

deploy/docker-compose.yml.j2

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
version: '3'
2+
3+
services:
4+
app:
5+
build: .
6+
env_file:
7+
- env/config
8+
volumes:
9+
- ./data/static:/srv/data/public
10+
11+
nginx:
12+
image: nginx:latest
13+
ports:
14+
- "80:80"
15+
- "443:443"
16+
volumes:
17+
- ./nginx.conf:/etc/nginx/nginx.conf
18+
- ./data/certbot/conf:/etc/letsencrypt
19+
- ./data/certbot/www:/var/www/certbot
20+
- ./data/static:/usr/share/static
21+
depends_on:
22+
- app
23+
24+
certbot:
25+
image: certbot/certbot
26+
volumes:
27+
- ./data/certbot/conf:/etc/letsencrypt
28+
- ./data/certbot/www:/var/www/certbot

deploy/env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PRETALX_TOKEN=pretalx_token

deploy/hosts.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[hetzner]
2+
49.13.23.199 ansible_user=root domain_name=programapi24.europython.eu ansible_ssh_common_args='-o StrictHostKeyChecking=no'

deploy/init-letsencrypt.sh.j2

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/bin/bash
2+
3+
if ! [ -x "$(command -v docker)" ]; then
4+
echo 'Error: docker is not installed.' >&2
5+
exit 1
6+
fi
7+
8+
domains=({{ domain_name }} www.{{ domain_name }})
9+
rsa_key_size=4096
10+
data_path="./data/certbot"
11+
email="czepiel.artur@gmail.com" # Adding a valid address is strongly recommended
12+
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
13+
14+
if [ -d "$data_path" ]; then
15+
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
16+
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
17+
exit
18+
fi
19+
fi
20+
21+
22+
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
23+
echo "### Downloading recommended TLS parameters ..."
24+
mkdir -p "$data_path/conf"
25+
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
26+
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
27+
echo
28+
fi
29+
30+
echo "### Creating dummy certificate for $domains ..."
31+
path="/etc/letsencrypt/live/$domains"
32+
mkdir -p "$data_path/conf/live/$domains"
33+
docker compose run --rm --entrypoint "\
34+
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
35+
-keyout '$path/privkey.pem' \
36+
-out '$path/fullchain.pem' \
37+
-subj '/CN=localhost'" certbot
38+
echo
39+
40+
41+
echo "### Starting nginx ..."
42+
docker compose up --force-recreate -d nginx
43+
echo
44+
45+
echo "### Deleting dummy certificate for $domains ..."
46+
docker compose run --rm --entrypoint "\
47+
rm -Rf /etc/letsencrypt/live/$domains && \
48+
rm -Rf /etc/letsencrypt/archive/$domains && \
49+
rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
50+
echo
51+
52+
53+
echo "### Requesting Let's Encrypt certificate for $domains ..."
54+
#Join $domains to -d args
55+
domain_args=""
56+
for domain in "${domains[@]}"; do
57+
domain_args="$domain_args -d $domain"
58+
done
59+
60+
# Select appropriate email arg
61+
case "$email" in
62+
"") email_arg="--register-unsafely-without-email" ;;
63+
*) email_arg="--email $email" ;;
64+
esac
65+
66+
# Enable staging mode if needed
67+
if [ $staging != "0" ]; then staging_arg="--staging"; fi
68+
69+
docker compose run --rm --entrypoint "\
70+
certbot certonly --webroot -w /var/www/certbot \
71+
$staging_arg \
72+
$email_arg \
73+
$domain_args \
74+
--rsa-key-size $rsa_key_size \
75+
--agree-tos \
76+
--force-renewal" certbot
77+
echo
78+
79+
echo "### Reloading nginx ..."
80+
docker compose exec nginx nginx -s reload

deploy/nginx.conf.j2

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
user nginx;
2+
worker_processes 1;
3+
4+
error_log /var/log/nginx/error.log warn;
5+
pid /var/run/nginx.pid;
6+
7+
8+
events {
9+
worker_connections 1024;
10+
}
11+
12+
http {
13+
include /etc/nginx/mime.types;
14+
default_type application/octet-stream;
15+
16+
map $remote_addr $remote_addr_anon {
17+
~(?P<ip>\d+\.\d+\.\d+)\. $ip.0;
18+
~(?P<ip>[^:]+:[^:]+): $ip::;
19+
# IP addresses to not anonymize (such as your server)
20+
127.0.0.1 $remote_addr;
21+
::1 $remote_addr;
22+
#w.x.y.z $remote_addr;
23+
#a:b:c:d::e:f $remote_addr;
24+
default 0.0.0.0;
25+
}
26+
27+
log_format anonymized '$remote_addr_anon - $remote_user [$time_local] '
28+
'"$request" $status $body_bytes_sent '
29+
'"$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
30+
31+
access_log /var/log/nginx/access.log anonymized;
32+
33+
keepalive_timeout 65;
34+
sendfile on;
35+
36+
37+
# To drop all the connections that don't use the valid host name
38+
server {
39+
listen 80 default_server;
40+
listen [::]:80 default_server;
41+
42+
listen 443 ssl http2 default_server;
43+
listen [::]:443 ssl http2 default_server;
44+
45+
ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem;
46+
ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem;
47+
48+
include /etc/letsencrypt/options-ssl-nginx.conf;
49+
50+
server_name _;
51+
52+
return 444;
53+
}
54+
55+
server {
56+
listen 80;
57+
server_name {{ domain_name }} www.{{ domain_name }};
58+
59+
location /.well-known/acme-challenge/ {
60+
root /var/www/certbot;
61+
}
62+
63+
location / {
64+
return 301 https://$server_name$request_uri;
65+
}
66+
}
67+
68+
server {
69+
listen 443 ssl;
70+
server_name {{ domain_name }} www.{{ domain_name }};
71+
72+
ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem;
73+
ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem;
74+
75+
include /etc/letsencrypt/options-ssl-nginx.conf;
76+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
77+
78+
location /2024 {
79+
alias /usr/share/static/2024;
80+
}
81+
}
82+
}

deploy/playbook.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
- name: Deploy programapi generator with Nginx and Let's Encrypt SSL certificate
2+
hosts: hetzner
3+
become: yes
4+
gather_facts: yes
5+
6+
vars:
7+
repository_url: https://github.com/EuroPython/programapi.git
8+
9+
tasks:
10+
- name: Install Docker dependencies
11+
apt:
12+
name: "{{ packages }}"
13+
state: present
14+
update_cache: yes
15+
vars:
16+
packages:
17+
- apt-transport-https
18+
- ca-certificates
19+
- curl
20+
- gnupg
21+
- lsb-release
22+
23+
- name: Install Docker
24+
block:
25+
- name: Add Docker GPG key
26+
apt_key:
27+
url: https://download.docker.com/linux/ubuntu/gpg
28+
state: present
29+
30+
- name: Add Docker repository
31+
apt_repository:
32+
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable
33+
state: present
34+
35+
- name: Install Docker
36+
apt:
37+
name: docker-ce
38+
state: present
39+
40+
- name: Add current user to docker group
41+
user:
42+
name: "{{ ansible_user }}"
43+
groups: docker
44+
append: yes
45+
changed_when: false
46+
47+
- name: Clone repository to specific version (to a temporary location)
48+
git:
49+
repo: "{{ repository_url }}"
50+
dest: /tmp/repo
51+
version: "{{ app_version }}"
52+
53+
- name: Copy latest src from repository clone to /srv
54+
command: cp -R /tmp/repo/src /srv
55+
56+
- name: Copy Dockerfile
57+
command: cp /tmp/repo/Dockerfile /srv
58+
59+
- name: Copy requirements.txt
60+
command: cp /tmp/repo/requirements.txt /srv
61+
62+
- name: Copy Makefile
63+
command: cp /tmp/repo/Makefile /srv
64+
65+
- name: Copy docker-compose.yml to the remote server
66+
ansible.builtin.template:
67+
src: ./docker-compose.yml.j2
68+
dest: /srv/docker-compose.yml
69+
70+
- name: Copy Nginx configuration file
71+
ansible.builtin.template:
72+
src: ./nginx.conf.j2
73+
dest: /srv/nginx.conf
74+
75+
- name: Copy Init-Letsencrypt
76+
ansible.builtin.template:
77+
src: ./init-letsencrypt.sh.j2
78+
dest: /srv/init-letsencrypt.sh
79+
80+
#- name: Start Docker compose
81+
# shell: "cd /srv && docker compose up -d"
82+
# docker compose up -d --build --force-recreate

deploy/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ansible

deploy/requirements.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.11
3+
# by the following command:
4+
#
5+
# pip-compile
6+
#
7+
ansible==9.5.1
8+
# via -r requirements.in
9+
ansible-core==2.16.6
10+
# via ansible
11+
cffi==1.16.0
12+
# via cryptography
13+
cryptography==42.0.7
14+
# via ansible-core
15+
jinja2==3.1.4
16+
# via ansible-core
17+
markupsafe==2.1.5
18+
# via jinja2
19+
packaging==24.0
20+
# via ansible-core
21+
pycparser==2.22
22+
# via cffi
23+
pyyaml==6.0.1
24+
# via ansible-core
25+
resolvelib==1.0.1
26+
# via ansible-core

0 commit comments

Comments
 (0)