Skip to content

Commit 16b97d8

Browse files
authored
Merge pull request #37 from QuLogic/ansible-setup
Add an Ansible playbook for creating a droplet
2 parents 06f901b + e272779 commit 16b97d8

File tree

3 files changed

+168
-45
lines changed

3 files changed

+168
-45
lines changed

README.md

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ prerequisites:
3838

3939
* Create a DigitalOcean API token, and pass it to the inventory generator by
4040
setting the `DO_API_TOKEN` environment variable.
41+
* If you are creating a new droplet, and want to configure DNS as well, then
42+
create a CloudFlare API token, and pass it to the Ansible playbook by setting
43+
the `CLOUDFLARE_TOKEN` environment variable.
4144
* Set the vault decryption password of the Ansible vaulted file with our
4245
secrets. This may be done by setting the `ANSIBLE_VAULT_PASSWORD_FILE`
4346
environment variable to point to a file containing the password.
@@ -99,9 +102,11 @@ Naming
99102
We follow a simplified version of the naming scheme on [this blog
100103
post](https://mnx.io/blog/a-proper-server-naming-scheme/):
101104

102-
* Servers are named `<prefix>.matplotlib.org` in A records.
103-
* Servers get a functional CNAME alias (e.g., `web01.matplotlib.org`).
104-
* matplotlib.org is a CNAME to the functional CNAME of a server.
105+
* Servers are named `<prefix>.matplotlib.org` in A records, pointing to the
106+
IPv4 address of the droplet.
107+
* Servers get a functional CNAME alias (e.g., `web01.matplotlib.org`) pointing
108+
to the hostname `<prefix>.matplotlib.org`.
109+
* matplotlib.org is a CNAME alias of the functional CNAME of a server.
105110

106111
We use [planets in our Solar System](https://namingschemes.com/Solar_System)
107112
for the name prefix. When creating a new server, pick the next one in the list.
@@ -113,51 +118,34 @@ The summary of the initial setup is:
113118

114119
1. Create the droplet with monitoring and relevant SSH keys.
115120
2. Assign new droplet to the matplotlib.org project and the Web firewall.
116-
3. Grab the SSH host fingerprints.
117-
4. Reboot.
121+
3. Add DNS entries pointing to the server on CloudFlare.
122+
4. Grab the SSH host fingerprints.
123+
5. Reboot.
118124

119-
We currently use a simple $10 droplet from DigitalOcean. You can create one
120-
from the control panel, or using the `doctl` utility. Be sure to enable
121-
monitoring, and add the `website` tag and relevant SSH keys to the droplet. An
122-
example of using `doctl` is the following:
125+
We currently use a simple $12 droplet from DigitalOcean. You can create one
126+
from the control panel, or using the `create.yml` Ansible playbook:
123127

124128
```
125-
doctl compute droplet create \
126-
--image fedora-35-x64 \
127-
--region tor1 \
128-
--size s-1vcpu-2gb \
129-
--ssh-keys <key-id>,<key-id> \
130-
--tag-name website \
131-
--enable-monitoring \
132-
venus.matplotlib.org
129+
ansible-playbook create.yml
133130
```
134131

135-
Note, you will have to use `doctl compute ssh-key list` to get the IDs of the
136-
relevant SSH keys saved on DigitalOcean, and substitute them above. Save the ID
137-
of the new droplet from the output, e.g., in:
132+
This playbook will prompt you for 3 settings:
138133

139-
```
140-
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
141-
294098687 mpl.org 2048 1 50 tor1 Fedora 35 x64 new website monitoring,droplet_agent
142-
```
143-
144-
the droplet ID is 294098687.
134+
1. The host name of the droplet, which should follow the naming convention
135+
above.
136+
2. The functional CNAME alias of the droplet.
137+
3. The names of SSH keys to add to the droplet.
145138

146-
147-
You should also assign the new droplet to the `matplotlib.org` project and the
148-
`Web` firewall:
139+
You may also pass these directly to Ansible as:
149140

150141
```
151-
doctl projects list
152-
# Get ID of the matplotlib.org project from the output.
153-
doctl projects resources assign <project-id> --resource=do:droplet:<droplet-id>
154-
155-
156-
doctl compute firewall list
157-
# Get ID of the Web firewall from the output.
158-
doctl compute firewall add-droplets <firewall-id> --droplet-ids <droplet-id>
142+
ansible-playbook create.yml --extra-vars "host=pluto functional=web99 ssh_keys='a b c'"
159143
```
160144

145+
The playbook will create the server, as well as add DNS records on CloudFlare.
146+
Note, you must set `DO_API_TOKEN` and `CLOUDFLARE_TOKEN` in the environment to
147+
access these services.
148+
161149
Then, to ensure you are connecting to the expected server, you should grab the
162150
SSH host keys via the DigitalOcean Droplet Console:
163151

@@ -181,14 +169,6 @@ Finally, you should reboot the droplet. This is due to a bug in cloud-init on
181169
DigitalOcean, which generates a new machine ID after startup, causing system
182170
logs to be seem invisible.
183171

184-
DNS setup
185-
---------
186-
187-
1. Add an A record for `<prefix>.matplotlib.org` to the IPv4 address of the new
188-
droplet.
189-
2. Add a CNAME record for `webNN.matplotlib.org` pointing to the given
190-
`<prefix.matplotlib.org>`.
191-
192172
Running Ansible
193173
---------------
194174

collections/requirements.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
collections:
33
- name: ansible.posix
44
- name: community.general
5+
version: ">=2.0.0"
56
- name: community.digitalocean

create.yml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
- hosts: localhost
3+
tasks:
4+
- name: Gather information about DigitalOcean droplets
5+
community.digitalocean.digital_ocean_droplet_info:
6+
register: do_droplets
7+
- name: Gather information about DigitalOcean SSH keys
8+
community.digitalocean.digital_ocean_sshkey_info:
9+
register: do_ssh_keys
10+
11+
- name: Print info on existing droplets
12+
ansible.builtin.debug:
13+
msg: >-
14+
{{ item.name }}:
15+
{{ item.networks.v4 | map(attribute='ip_address') | join(',') }}
16+
loop: "{{ do_droplets.data }}"
17+
loop_control:
18+
label: "{{ item.id }}"
19+
20+
- name: "Enter name for new droplet (subdomain only)"
21+
ansible.builtin.pause:
22+
register: input_name
23+
when: host is not defined
24+
25+
- name: "Enter functional name for new droplet (webNN)"
26+
ansible.builtin.pause:
27+
register: input_functional
28+
when: functional is not defined
29+
30+
- name: Print available SSH public keys
31+
ansible.builtin.debug:
32+
msg: "{{ item.name}} {{ item.fingerprint }}"
33+
loop: "{{ do_ssh_keys.data }}"
34+
loop_control:
35+
label: "{{ item.id }}"
36+
37+
- name: "Enter SSH key names for new droplet (space separated)"
38+
ansible.builtin.pause:
39+
register: input_ssh_keys
40+
when: ssh_keys is not defined
41+
42+
- name: Set droplet facts
43+
ansible.builtin.set_fact:
44+
host: >-
45+
{{
46+
(host if host is defined else input_name.user_input) |
47+
trim
48+
}}
49+
functional: >-
50+
{{
51+
(functional if functional is defined else input_functional.user_input) |
52+
trim
53+
}}
54+
ssh_fingerprints: >-
55+
{{
56+
do_ssh_keys.data |
57+
selectattr(
58+
'name',
59+
'in',
60+
(ssh_keys if ssh_keys is defined
61+
else input_ssh_keys.user_input) | split) |
62+
map(attribute='fingerprint')
63+
}}
64+
65+
- name: Verify droplet configuration
66+
ansible.builtin.assert:
67+
that:
68+
- host in valid_planets
69+
# Must not be an existing name.
70+
- >-
71+
do_droplets.data |
72+
selectattr('name', 'equalto', '{{ host }}.matplotlib.org') |
73+
count == 0
74+
# TODO: Also check that functional name doesn't already exist.
75+
- functional is regex('^web[0-9][0-9]$')
76+
# At least 1 key, and same number as requested.
77+
- ssh_fingerprints | length >= 1
78+
- >-
79+
ssh_fingerprints | length == (
80+
ssh_keys if ssh_keys is defined
81+
else input_ssh_keys.user_input) | split | length
82+
83+
- name: Print configuration
84+
ansible.builtin.debug:
85+
msg: "Creating droplet '{{ host }}' with SSH keys {{ ssh_fingerprints }}"
86+
87+
- name: Please verify the above configuration
88+
ansible.builtin.pause:
89+
90+
- name: Create droplet on DigitalOcean
91+
community.digitalocean.digital_ocean_droplet:
92+
state: present
93+
name: "{{ host }}.matplotlib.org"
94+
firewall:
95+
- Web
96+
image: fedora-39-x64
97+
monitoring: true
98+
project: matplotlib.org
99+
region: tor1
100+
size: s-1vcpu-2gb
101+
ssh_keys: "{{ ssh_fingerprints }}"
102+
tags:
103+
- website
104+
unique_name: true
105+
register: new_droplet
106+
107+
- name: Setup DNS for droplet on CloudFlare
108+
community.general.cloudflare_dns:
109+
state: present
110+
proxied: true
111+
record: "{{ host }}"
112+
type: A
113+
value: >-
114+
{{
115+
new_droplet.data.droplet.networks.v4 |
116+
selectattr('type', 'equalto', 'public') |
117+
map(attribute='ip_address') |
118+
first
119+
}}
120+
zone: matplotlib.org
121+
122+
- name: Setup functional DNS for droplet on CloudFlare
123+
community.general.cloudflare_dns:
124+
state: present
125+
proxied: true
126+
record: "{{ functional }}"
127+
type: CNAME
128+
value: "{{ host }}.matplotlib.org"
129+
zone: matplotlib.org
130+
131+
vars:
132+
# We currently name servers based on planets in the Solar System.
133+
valid_planets:
134+
- mercury
135+
- venus
136+
- earth
137+
- mars
138+
- jupiter
139+
- saturn
140+
- uranus
141+
- neptune
142+
- pluto

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy