impress-2020/deploy/playbooks/setup.yml

351 lines
11 KiB
YAML
Raw Permalink Normal View History

---
- name: Set up the environment for the impress-2020 app
hosts: webserver
vars:
email_address: "emi@matchu.dev" # TODO: Extract this to personal config?
tasks:
- name: Copy authorized SSH keys
copy:
dest: ~/.ssh/authorized_keys
src: ../authorized-ssh-keys.txt
- name: Disable root SSH login
become: yes
lineinfile:
dest: /etc/ssh/sshd_config
regexp: ^#?PermitRootLogin
line: PermitRootLogin no
- name: Disable password-based SSH authentication
become: yes
lineinfile:
dest: /etc/ssh/sshd_config
regexp: ^#?PasswordAuthentication
line: PasswordAuthentication no
- name: Install fail2ban firewall with default settings
become: yes
apt:
update_cache: yes
name: fail2ban
- name: Configure ufw firewall to allow SSH connections on port 22
become: yes
community.general.ufw:
rule: allow
port: "22"
- name: Configure ufw firewall to allow HTTP connections on port 80
become: yes
community.general.ufw:
rule: allow
port: "80"
- name: Configure ufw firewall to allow HTTP connections on port 443
become: yes
community.general.ufw:
rule: allow
port: "443"
- name: Enable ufw firewall with all other ports closed by default
become: yes
community.general.ufw:
state: enabled
policy: deny
- name: Install unattended-upgrades
become: yes
apt:
update_cache: yes
name: unattended-upgrades
- name: Enable unattended-upgrades to auto-upgrade our system
become: yes
copy:
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
dest: /etc/apt/apt.conf.d/20auto-upgrades
- name: Configure unattended-upgrades to auto-reboot our server when necessary
become: yes
lineinfile:
regex: ^(//\s*)?Unattended-Upgrade::Automatic-Reboot ".*";$
line: Unattended-Upgrade::Automatic-Reboot "true";
dest: /etc/apt/apt.conf.d/50unattended-upgrades
- name: Configure unattended-upgrades to delay necessary reboots to 3am
become: yes
lineinfile:
regex: ^(//\s*)?Unattended-Upgrade::Automatic-Reboot-Time ".*";$
line: Unattended-Upgrade::Automatic-Reboot-Time "03:00";
dest: /etc/apt/apt.conf.d/50unattended-upgrades
- name: Configure the system timezone to be US Pacific time
become: yes
community.general.timezone:
name: America/Los_Angeles
- name: Create the app versions folder
become: yes
file:
path: /srv/impress-2020/versions
owner: "{{ ansible_user_id }}"
group: "{{ ansible_user_id }}"
state: directory
- name: Add Nodesource apt key
become: yes
apt_key:
id: 9FD3B784BC1C6FC31A8A0A1C1655A0AB68576280
url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key
- name: Add Node v16 apt repository
become: yes
apt_repository:
repo: deb https://deb.nodesource.com/node_16.x focal main
- name: Install Node v16
become: yes
apt:
update_cache: yes
name: nodejs
- name: Install Yarn
become: yes
npm:
name: yarn
global: yes
- name: Check for a current app version
stat:
path: /srv/impress-2020/current
register: current_app_version
- name: Check whether we already have a placeholder app
stat:
path: /srv/impress-2020/versions/initial-placeholder
register: existing_placeholder_app
when: not current_app_version.stat.exists
- name: Create a placeholder app, to run until we deploy a real version
command:
chdir: /srv/impress-2020/versions
cmd: yarn create next-app initial-placeholder
when: |
not current_app_version.stat.exists and
not existing_placeholder_app.stat.exists
- name: Build the placeholder app
command:
chdir: /srv/impress-2020/versions/initial-placeholder
cmd: yarn build
when: not current_app_version.stat.exists
- name: Set the placeholder app as the current version
file:
src: /srv/impress-2020/versions/initial-placeholder
dest: /srv/impress-2020/current
state: link
when: not current_app_version.stat.exists
2021-11-02 22:49:45 -07:00
- name: Install pm2
become: yes
npm:
name: pm2
global: yes
- name: Create pm2 startup script
# The current user is going to become the pm2 owner of the app server
# process. They'll be able to manage it without `sudo`, including during
# normal deploys, and run `pm2 monit` from their shell to see status.
become: yes
command: "pm2 startup systemd -u {{ ansible_user_id }} --hp /home/{{ ansible_user_id }}"
2021-11-02 22:49:45 -07:00
- name: Create pm2 ecosystem file
copy:
content: |
2021-11-02 22:49:45 -07:00
module.exports = {
apps: [
{
name: "impress-2020",
cwd: "/srv/impress-2020/current",
// Instead of `yarn start`, we specify the `next` binary
// directly, because it helps pm2 monitor our app correctly.
// https://github.com/vercel/next.js/discussions/10675#discussioncomment-34615
script: "./node_modules/.bin/next",
args: "start --port=3000",
2021-11-02 22:49:45 -07:00
instances: "max",
exec_mode: "cluster",
}
]
}
dest: "~/ecosystem.config.js"
# Create a temporary backup file, so we can use it to delete the old
# version of the services. (This is important if e.g. a service is
# removed or renamed, in which case deleting from the *new* config file
# wouldn't include it.)
backup: yes
register: pm2_ecosystem_file
2021-11-02 22:49:45 -07:00
- name: Delete old pm2 services if config file changed
command: "pm2 delete {{ pm2_ecosystem_file.backup_file | quote }}"
when: pm2_ecosystem_file is changed and pm2_ecosystem_file.backup_file is defined
- name: Delete old pm2 config file if it changed
file:
path: "{{ pm2_ecosystem_file.backup_file }}"
state: absent
when: pm2_ecosystem_file is changed and pm2_ecosystem_file.backup_file is defined
- name: Start pm2 services
command: "pm2 start ~/ecosystem.config.js"
2021-11-02 22:49:45 -07:00
- name: Save pm2 startup script
command: pm2 save
- name: Install nginx
become: yes
apt:
update_cache: yes
name: nginx
- name: Install core snap
become: yes
community.general.snap:
name: core
- name: Install certbot as a snap
become: yes
community.general.snap:
name: certbot
classic: yes
- name: Set up certbot
become: yes
command: "certbot certonly --nginx -n --agree-tos --email {{ email_address }} --domains impress-2020-box.openneo.net"
- name: Add impress-2020 config file to nginx
become: yes
copy:
content: |
server {
server_name impress-2020-box.openneo.net;
listen 80;
listen [::]:80;
if ($host = impress-2020-box.openneo.net) {
return 301 https://$host$request_uri;
}
}
server {
server_name impress-2020-box.openneo.net;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/impress-2020-box.openneo.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/impress-2020-box.openneo.net/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_session_cache shared:SSL:10m; # https://superuser.com/q/1484466/14127
# TODO: Serve static files directly, instead of through the proxy
location / {
proxy_pass http://127.0.0.1:3000;
}
}
dest: /etc/nginx/sites-enabled/impress-2020
notify:
- Restart nginx
- name: Create cron log files directory
file:
path: /srv/impress-2020/cron-logs
state: directory
- name: Set up modeling cron jobs
cron:
name: model-needed-items
minute: "*/10" # Every 10 minutes
job: "cd /srv/impress-2020/current && yarn model-needed-items 2>&1 > /srv/impress-2020/cron-logs/model-needed-items.log"
# Idk why the app has been so memory leaky lately, but let's just restart it
# like *constantly* tbh.
- name: Set up cron job to restart the app
cron:
name: restart-impress-2020
minute: "*/8" # Every 8 minutes
job: "pm2 restart impress-2020"
2021-11-02 22:49:45 -07:00
- name: Install dependencies for the npm module node-canvas
become: yes
apt:
update_cache: yes
name:
- build-essential
- libcairo2-dev
- libpango1.0-dev
- libjpeg-dev
- libgif-dev
- librsvg2-dev
- name: Install Playwright system dependencies
# NOTE: I copied the package list from the source list for
# `npx playwright install-deps`, which I couldn't get running in
# Ansible as root, and besides, I prefer manually managing the
# package list over running an npm script as root!
# TODO: We're using Puppeteer now, should this list change in some way?
become: yes
apt:
update_cache: yes
name:
# Tools
- xvfb
- fonts-noto-color-emoji
- ttf-unifont
- libfontconfig
- libfreetype6
- xfonts-cyrillic
- xfonts-scalable
- fonts-liberation
- fonts-ipafont-gothic
- fonts-wqy-zenhei
- fonts-tlwg-loma-otf
- ttf-ubuntu-font-family
# Chromium
- fonts-liberation
- libasound2
- libatk-bridge2.0-0
- libatk1.0-0
- libatspi2.0-0
- libcairo2
- libcups2
- libdbus-1-3
- libdrm2
- libegl1
- libgbm1
- libglib2.0-0
- libgtk-3-0
- libnspr4
- libnss3
- libpango-1.0-0
- libx11-6
- libx11-xcb1
- libxcb1
- libxcomposite1
- libxdamage1
- libxext6
- libxfixes3
- libxrandr2
- libxshmfence1
- name: Enable user namespace cloning for Chromium sandboxing
become: yes
ansible.posix.sysctl:
name: kernel.unprivileged_userns_clone
value: "1"
handlers:
- name: Restart nginx
become: yes
systemd:
name: nginx
state: restarted