|
| 1 | +# Ansible Elixir Playbooks |
| 2 | + |
| 3 | +This project has ansible playbooks for: |
| 4 | + |
| 5 | +* Elixr Build Server - it has installed Erlang, Elixir and nodejs. Basically, all what is required to compile the Phoenix Framework application. |
| 6 | +* Phoenix Website - it is a playbook to provision server with installed PostgreSQL and configured nginx and Let's Encrypt for SSL. There is no Erlang/Elixir on this server because we will deploy there only compiled Phoenix application. |
| 7 | + |
| 8 | +You can learn more about the project from this blog post (TODO: add here url). |
| 9 | + |
| 10 | +## Requirements |
| 11 | + |
| 12 | +### Control machine (your computer) |
| 13 | + |
| 14 | +* Install [Ansible](https://www.ansible.com/) |
| 15 | + |
| 16 | +* Download roles: |
| 17 | + |
| 18 | + ```shell |
| 19 | + $ ansible-galaxy install -r requirements.yml |
| 20 | + ``` |
| 21 | + |
| 22 | +* Generate `vault_pass.txt` file into this repository. You need it to be able to encrypt/decrypt secrets. |
| 23 | + |
| 24 | + :warning: For security reasons, the `vault_pass.txt` file should not be committed into the repository. It's ignored in `.gitignore`. |
| 25 | + |
| 26 | + ```shell |
| 27 | + $ openssl rand -base64 256 > vault_pass.txt |
| 28 | + ``` |
| 29 | + |
| 30 | +* Generate your DB password and put output to `apps/phoenix-website/host_vars/phoenix-website.lunarlogic.io` |
| 31 | + |
| 32 | + ```shell |
| 33 | + $ ansible-vault encrypt_string --name db_password "YOUR_DB_PASSWORD" |
| 34 | + ``` |
| 35 | + |
| 36 | +### Target machine (server) |
| 37 | + |
| 38 | +* Server with [Ubuntu](https://www.ubuntu.com/) 16.04 LTS. |
| 39 | + |
| 40 | +## Public keys |
| 41 | + |
| 42 | +We keep our public keys in [public_keys/] directory. This set of keys is uploaded to the server during each provisioning |
| 43 | +and overwrites the list of authorized keys, so proper people have access to the server. It is important to keep |
| 44 | +the list of keys up to date. |
| 45 | + |
| 46 | +If you don't know how to generate a key for yourself, |
| 47 | +[read this article](https://help.github.com/articles/connecting-to-github-with-ssh/). |
| 48 | + |
| 49 | +## App deployement |
| 50 | + |
| 51 | +### CircleCI deployment |
| 52 | + |
| 53 | +If you want to deploy app from CI to the staging/production host then you must generate RSA keys for CircleCI. |
| 54 | + |
| 55 | +```shell |
| 56 | +$ ssh-keygen -t rsa -b 4096 -N "" -C "circle_ci" -f ./apps/elixir-build-server/circle_ci |
| 57 | +$ ssh-keygen -t rsa -b 4096 -N "" -C "circle_ci" -f ./apps/phoenix-website/circle_ci |
| 58 | +``` |
| 59 | + |
| 60 | +Add `circle_ci.pub` public key to your app playbook for the role `user`: |
| 61 | + |
| 62 | +``` |
| 63 | +- role: user/0.0.1 |
| 64 | + username: phoenix |
| 65 | + authorized_key_paths: |
| 66 | + - ../../public_keys/*.pub |
| 67 | + - ./circle_ci.pub # add this line |
| 68 | +``` |
| 69 | + |
| 70 | +Go to CircleCI and find your project, open settings and find `SSH Permissions`. Click `Add an SSH key` button and paste there private key `apps/YOUR_APP_NAME/circle_ci`. |
| 71 | + |
| 72 | +Now you can remove private key `apps/YOUR_APP_NAME/circle_ci` from local machine. It should not be commited into repo! |
| 73 | + |
| 74 | +Commit into repo only public key `apps/YOUR_APP_NAME/circle_ci.pub`. |
| 75 | + |
| 76 | +You can always generate a new fresh keys if you need it hence no reason to backup private key. You already added it to CircleCI. |
| 77 | + |
| 78 | +## Run playbooks |
| 79 | + |
| 80 | +__Warning:__ This command will provision all servers listed in inventory file for particular app `apps/app_name`. |
| 81 | + |
| 82 | +```shell |
| 83 | +$ ./play apps/app_name |
| 84 | +``` |
| 85 | + |
| 86 | +If you want to provision only specific machine do (it's useful if your app is deployed to multiple servers like staging and production): |
| 87 | + |
| 88 | +```shell |
| 89 | +# Warning: There must be comma and the end of the hosts list! |
| 90 | +$ ansible-playbook -i 'example-staging.lunarlogic.io,' apps/app_name/playbook.yml |
| 91 | +``` |
| 92 | + |
| 93 | +## Provisioning logs |
| 94 | + |
| 95 | +You can check when and with what git commit the host was provisioned in log file: `/var/log/provision.log` (stored on the target machine). |
| 96 | + |
| 97 | +## System users |
| 98 | + |
| 99 | +There are 3 types of users on the server: |
| 100 | + |
| 101 | +* `root` - for provisioning |
| 102 | +* `admin` - user has the sudo access |
| 103 | +* `app_name_user` - for instance `phoenix` user for Phoenix Website application. The user has no sudo access. The application is running under this user. |
| 104 | + |
| 105 | +## Add playbook for new app |
| 106 | + |
| 107 | +* create new app directory in the `app` directory |
| 108 | +* in this new directory create `playbook.yml` and `inventory` files |
| 109 | +* in the `inventory` file put host names to provision (see [Ansible docs](http://docs.ansible.com/ansible/intro_inventory.html)) |
| 110 | +* implement `playbook.yml` |
| 111 | + |
| 112 | +## Secrets |
| 113 | + |
| 114 | +We store secrets in encrypted version using [Vault]. If you are adding new secrets, make sure you commit them to the repository in the encrypted form. |
| 115 | + |
| 116 | +* Encrypting single values (that can be placed inside a "clear text" YAML file, using the `!vault` tag): |
| 117 | + |
| 118 | + ```shell |
| 119 | + $ ansible-vault encrypt_string --name pass_to_some_service "secret" # stdout encrypted string |
| 120 | + ``` |
| 121 | + |
| 122 | +* Encrypting whole YAML files: |
| 123 | + |
| 124 | + ```shell |
| 125 | + $ ansible-vault encrypt secret.yml # encrypt unencrypted file |
| 126 | + $ ansible-vault edit secret.yml # edit encrypted file |
| 127 | + $ ansible-vault decrypt secret.yml # decrypt encrypted file |
| 128 | + ``` |
| 129 | + |
| 130 | +## Roles |
| 131 | + |
| 132 | +### Role versioning |
| 133 | + |
| 134 | +We use roles versioning the simplest possible way, we just add version subdirectories under every role directory. |
| 135 | + |
| 136 | +```shell |
| 137 | +roles/role-name/role-version/ # e.g. roles/webserver/0.3.2/ |
| 138 | +``` |
| 139 | + |
| 140 | +To create a new version just copy an existing one, bump the role version and modify it. |
| 141 | +Please, respect [Semantic Versioning 2.0.0]. |
| 142 | + |
| 143 | +### Community developed roles |
| 144 | + |
| 145 | +Include the roles in [requirements.yml] and download them using the following command: |
| 146 | + |
| 147 | +```shell |
| 148 | +$ ansible-galaxy install -r requirements.yml |
| 149 | +``` |
| 150 | + |
| 151 | +### SSL with Let's Encrypt |
| 152 | + |
| 153 | +You can use `lets_encrypt` role to generate free SSL certificate thanks to https://letsencrypt.org |
| 154 | + |
| 155 | +#### Rate Limits |
| 156 | + |
| 157 | +The main limit is Certificates per Registered Domain (20 per week). |
| 158 | + |
| 159 | +https://letsencrypt.org/docs/rate-limits/ |
| 160 | + |
| 161 | +If you are testing Let's Encrypt then use `staging` environment with higher limits! |
| 162 | + |
| 163 | +``` |
| 164 | +- role: lets_encrypt/0.0.1 |
| 165 | + app_name: myapp |
| 166 | + lets_encrypt_contact_email: [email protected] |
| 167 | + lets_encrypt_environment: staging # you can change it to production once ready |
| 168 | +``` |
| 169 | + |
| 170 | +#### If you want to change main domain for your certificate |
| 171 | + |
| 172 | +If you want to change main domain for your certificate then you need to generate a new certificate. |
| 173 | + |
| 174 | +Here is example file for [Phoenix Website project with multiple domains](apps/phoenix-website/host_vars/phoenix-website.lunarlogic.io). |
| 175 | + |
| 176 | +In order to generate a new certificate please remove first the old files generated by `lets_encrypt` role on the server: |
| 177 | + |
| 178 | +```shell |
| 179 | +$ rm -rf /etc/letsencrypt/accounts/* |
| 180 | +$ rm -rf /etc/letsencrypt/archive/* |
| 181 | +$ rm -rf /etc/letsencrypt/csr/* |
| 182 | +$ rm -rf /etc/letsencrypt/keys/* |
| 183 | +$ rm -rf /etc/letsencrypt/live/* |
| 184 | +$ rm -rf /etc/letsencrypt/renewal/* |
| 185 | + |
| 186 | +# remove the snippents that load SSL certificate |
| 187 | +$ rm -rf /etc/nginx/snippets/project_name |
| 188 | +``` |
| 189 | + |
| 190 | +Ensure the nginx is running. It's required so Let's Encrypt can do request to our domain. |
| 191 | +Provision server again. |
| 192 | + |
| 193 | +Note: If you would like to add a new subdomain to domain list then you can just provision server and a new subdomain will be added to the certificate. You need to generate certificate from scrach only if you change the main domain (the first domain on the list of domains). |
| 194 | + |
| 195 | + |
| 196 | +[Vault]: http://docs.ansible.com/ansible/playbooks_vault.html |
| 197 | +[public_keys/]: public_keys/ |
| 198 | +[requirements.yml]: requirements.yml |
| 199 | +[Semantic Versioning 2.0.0]: http://semver.org/ |
0 commit comments