Matchu
0c2939dfe4
We used Playwright in the first place to try to work around a Vercel deploy issue, and I'm not sure it really ended up mattering lol :p But yeah, I'm putting the new Puppeteer code through the same prod stress test, and it just doesn't seem to be getting into the same broken state that Playwright was. I'm guessing it's just that Puppeteer has more investment in edge-case handling? (There's also the fact that we're no longer running things as root, which could have been a fucky problem, too?)
331 lines
9.9 KiB
YAML
331 lines
9.9 KiB
YAML
---
|
|
- 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
|
|
|
|
- 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 }}"
|
|
|
|
- name: Create pm2 ecosystem file
|
|
copy:
|
|
content: |
|
|
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",
|
|
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
|
|
|
|
- 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"
|
|
|
|
- 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;
|
|
if ($host = impress-2020-box.openneo.net) {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
server_name impress-2020-box.openneo.net;
|
|
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: 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
|