diff options
Diffstat (limited to 'docs/deployment.md')
| -rw-r--r-- | docs/deployment.md | 147 | 
1 files changed, 147 insertions, 0 deletions
| diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 00000000..841e08c7 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,147 @@ +# Deployment +The default Dockerfile should do a good job at running a solid web server that +automatically adjusts its worker count based on traffic. This is managed by +uWSGI. You need to configure the `DATABASE_URL` and `SECRET_KEY` variables. If +you want to deploy to a different host than the default, configure the +`ALLOWED_HOSTS` variable. + +## Static file hosting +You can either collect the static files in the container and use uWSGI to host +them, or put them on your host and manage them through a web server running on +the host like nginx. + +## Database migrations +To bring the schema up-to-date, first stop an existing database container, then +start a container that just runs the migrations and exits, and then starts the +main container off the new container again. + +## Ansible task +An example Ansible task to deploy the site is shown below, it should read fairly +humanly and give you a rough idea of steps needed to deploy the site. + +```yml +--- +- name: ensure the `{{ pysite_pg_username }}` postgres user exists +  become: yes +  become_user: postgres +  postgresql_user: +    name: "{{ pysite_pg_username }}" +    password: "{{ pysite_pg_password }}" +  when: pysite_pg_host == 'localhost' + +- name: ensure the `{{ pysite_pg_database }}` postgres database exists +  become: yes +  become_user: postgres +  postgresql_db: +    name: "{{ pysite_pg_database }}" +    owner: "{{ pysite_pg_username }}" +  when: pysite_pg_host == 'localhost' + +- name: ensure the `{{ pysite_hub_repository }}` image is up-to-date +  become: yes +  docker_image: +    name: "{{ pysite_hub_repository }}" +    force: yes + +- name: ensure the nginx HTTP vhosts are up-to-date +  become: yes +  template: +    src: "nginx/{{ item.key }}.http.conf.j2" +    dest: "/etc/nginx/sites-available/{{ item.value }}.http.conf" +  with_dict: "{{ pysite_domains }}" +  notify: reload nginx + +- name: ensure the nginx HTTPS vhosts are up-to-date +  become: yes +  template: +    src: "nginx/{{ item.key }}.https.conf.j2" +    dest: "/etc/nginx/sites-available/{{ item.value }}.https.conf" +  with_dict: "{{ pysite_domains }}" +  notify: reload nginx + +- name: ensure the nginx HTTP vhosts are symlinked to `/etc/nginx/sites-enabled` +  become: yes +  file: +    src: /etc/nginx/sites-available/{{ item.value }}.http.conf +    dest: /etc/nginx/sites-enabled/{{ item.value }}.http.conf +    state: link +  with_dict: "{{ pysite_domains }}" +  notify: reload nginx + +- name: ensure we have HTTPS certificates +  include_role: +    name: thefinn93.letsencrypt +  vars: +    letsencrypt_cert_domains: "{{ pysite_domains | dict2items | map(attribute='value') | list }}" +    letsencrypt_email: "[email protected]" +    letsencrypt_renewal_command_args: '--renew-hook "systemctl restart nginx"' +    letsencrypt_webroot_path: /var/www/_letsencrypt + +- name: ensure the nginx HTTPS vhosts are symlinked to `/etc/nginx/sites-enabled` +  become: yes +  file: +    src: /etc/nginx/sites-available/{{ item.value }}.https.conf +    dest: /etc/nginx/sites-enabled/{{ item.value }}.https.conf +    state: link +  with_dict: "{{ pysite_domains }}" +  notify: reload nginx + +- name: ensure the web container is absent +  become: yes +  docker_container: +    name: pysite +    state: absent + +- name: ensure the `{{ pysite_static_file_dir }}` directory exists +  become: yes +  file: +    path: "{{ pysite_static_file_dir }}" +    state: directory +    owner: root +    group: root + +- name: collect static files +  become: yes +  docker_container: +    image: "{{ pysite_hub_repository }}" +    name: pysite-static-file-writer +    command: python manage.py collectstatic --noinput +    detach: no +    cleanup: yes +    network_mode: host +    env: +      DATABASE_URL: "{{ pysite_pg_database_url }}" +      SECRET_KEY: "me-dont-need-no-secret-key" +      STATIC_ROOT: "/html" +    volumes: +      - "/var/www/pythondiscord.com:/html" + +- name: ensure the database schema is up-to-date +  become: yes +  docker_container: +    image: "{{ pysite_hub_repository }}" +    name: pysite-migrator +    detach: no +    cleanup: yes +    command: python manage.py migrate +    network_mode: host +    env: +      DATABASE_URL: "postgres://{{ pysite_pg_username }}:{{ pysite_pg_password }}@{{ pysite_pg_host }}/{{ pysite_pg_database }}" +      SECRET_KEY: "me-dont-need-no-secret-key" + +- name: ensure the website container is started +  become: yes +  docker_container: +    image: "{{ pysite_hub_repository }}" +    name: pysite +    network_mode: host +    restart: yes +    restart_policy: unless-stopped +    ports: +      - "127.0.0.1:4000:4000" +    env: +      ALLOWED_HOSTS: "{{ pysite_domains | dict2items | map(attribute='value') | join(',') }}" +      DATABASE_URL: "postgres://{{ pysite_pg_username }}:{{ pysite_pg_password }}@{{ pysite_pg_host }}/{{ pysite_pg_database }}" +      PARENT_HOST: pysite.example.com +      SECRET_KEY: "{{ pysite_secret_key }}" +``` | 
