Skip to content
Snippets Groups Projects
Commit 1cb103ad authored by marcoemi.poleggi's avatar marcoemi.poleggi
Browse files

WIP: reviewed up to task #6

parent 00c35a80
No related branches found
No related tags found
No related merge requests found
# Lab: Configuration Management - Ansible and AWS
# Lab: Configuration Management - Ansible
Lab template for a CM/deployment exercise with Ansible and AWS.
Lab template for a CM/deployment exercise with Ansible and AWS. Originally
written by Marcel Graf (HEIG-VD).
## Pedagogical objectives ##
......@@ -24,10 +25,10 @@ conventions about the *command line prompt*:
* `ins`: your VM instance
### Task 1: install Ansible ###
### Task #1: install Ansible ###
In this task you will install [Ansible](https://www.ansible.com/) on your
local machine. Please, refer to your [OS
**Goal:** install [Ansible](https://www.ansible.com/) on your local
machine. Please, refer to your [OS
documentation](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
for the proper way to do so.
......@@ -37,62 +38,63 @@ Once done, verify that Ansible is installed correctly by running:
lcl$ ansible --version
```
### Task 2: Create a VM on Amazon Web Services ###
### Task #2: Create a VM on a Cloud of your choice ###
In this task you will create a VM on Amazon Web Services that will be managed
by Ansible.
**Goal:** create a VM that will be managed by Ansible. Chose any Cloud you are
familiar with, then:
1. Import or create an RSA key pair for SSH access to the VM.
1. Switch the AWS console to the N. Virginia region to avoid resource
limitations -- see "Zones" in the "Account attributes" pane.
1. Create a security group/policy that allows incoming SSH, HTTP and HTTPS
traffic from anywhere (0.0.0.0/0).
2. Import or create an RSA key pair in this region and download the private key.
3. It it doesn't exist yet, create a security group named `Lab-Ansible-AWS`
group that allows incoming SSH, HTTP and HTTPS traffic from anywhere
(0.0.0.0/0).
4. Create an EC2 instance with the following characteristics (all other
parameters at default value):
1. Create a VM instance with the following characteristics:
- OS: Ubuntu Server 20.04 LTS
- type: t2.micro
- security group: Lab-Ansible-AWS
- type: the smallest capable of running the above OS. 1 core, 1GB RAM,
10GB virtual disk should be enough.
- security group/policy: the one you created above
- key pair: the one you created above
After launching make sure you can SSH into the VM using your private key
(`<your-private-key>` is a full path):
After launching make sure you can SSH into the VM using your
`<your-private-key>` (must be a full path):
``` shell
lcl$ ssh -i <your-private-key> ubuntu@<VM-DNS-name-or-IP-address>
```
### Task 3: Configure Ansible to connect to the managed VM ###
In this task you will tell Ansible about the machines it shall manage.
### Task #3: Configure Ansible to connect to the managed VM ###
Create a "sandbox" directory on your local machine f.i. `~/ansible/playbooks`,
and create a file called `hosts.yml` which will serve as the inventory file,
and add the following:
**Goal:** intruct Ansible about the machines (hosts) it shall manage.
@@@ RESTART FROM HERE @@@
Create a "sandbox" directory on your local machine f.i. `~/ansible/`, and
create a file called `hosts.yml` which will serve as the *inventory* file
(a.k.a. hostfile; it can also be written in `.ini` style), and add the
following:
``` yaml
testserver ansible_ssh_host=<VM-DNS-name-or-IP-address>
ansible_user=ubuntu
ansible_ssh_private_key_file=<your-private-key>
all:
hosts:
testserver:
ansible_ssh_host: <VM-DNS-name-or-IP-address>
ansible_user: ubuntu
ansible_ssh_private_key_file: <your-private-key>
```
Verify that you can use Ansible to connect to the server:
and check its validity:
ansible testserver -i hosts -m ping
``` shell
ansible-inventory -i ~/ansible/hosts.yml --list
```
You should see output similar to the following:
Verify that you can use Ansible to connect to the testserver:
testserver | SUCCESS => {
"changed": false,
"ping": "pong"
}
``` shell
lcl$ ansible -i ~/ansible/hosts.yml testserver -m ping
```
You should see output similar to the following:
`
testserver | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
......@@ -100,35 +102,264 @@ testserver | SUCCESS => {
"changed": false,
"ping": "pong"
}
`
We can now simplify the configuration of Ansible by using an ansible.cfg file which allows us to set some defaults.
In the playbooks directory create the file ansible.cfg:
Let's simplify the configuration of Ansible by using a user default
configuration file `~/.ansible.cfg` with contents (`.ini` style):
``` ini
[defaults]
inventory = hosts
inventory = ~/ansible/hosts.yml
remote_user = ubuntu
private_key_file = <path to keyfile.pem>
private_key_file = <your-private-key>
host_key_checking = false
deprecation_warnings = false
```
Notice that SSH's host key checking is disabled for convenience, as the
managed VM will get a new IP address each time it is recreated. **For
production systems this is be a security risk!** [See the
doc](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#the-configuration-file)
for the other details.
With the above default settings our inventory file now simplifies to:
``` yaml
all:
hosts:
testserver:
ansible_ssh_host: <VM-DNS-name-or-IP-address>
```
Among the default options we also disable SSH's host key checking. This is convenient when we distroy and recreate the managed server (it will get a new host key every time). In production this may be a security risk.
Now calling ansible is simpler:
We also disable warnings about deprecated features that the 2.x version of Ansible emits.
``` shell
lcl$ ansible testserver -m ping
```
With these default values the hosts inventory file now simplifies to:
The ansible command can be used to run arbitrary commands on the remote
machines. F.i., to execute the uptime command:
testserver ansible_ssh_host=<managed VM's public IP address>
``` shell
lcl$ ansible testserver -m command -a uptime
testserver | CHANGED | rc=0 >>
09:05:10 up 54 min, 2 users, load average: 0.01, 0.00, 0.00
```
We can now run Ansible again and don't need to specify the inventory file any more:
ansible testserver -m ping
### Task #4: Install web application ###
The ansible command can be used to run arbitrary commands on the remote machines. Use the -m command option and add the command in the -a option. For example to execute the uptime command:
**Goal:** configure the managed host to run a simple Web application served by
the nginx server. This necessitates four files:
ansible testserver -m command -a uptime
1. The inventory file `~/ansible/hosts.yml` as written before.
2. A "playbook" that specifies what to configure
`~/ansible/playbooks/web.yml`.
3. The nginx's configuration file `~/ansible/playbooks/files/nginx.conf`.
4. A home page template for our Web site
`~/ansible/playbooks/templates/index.html.j2`.
You should see output similar to this:
To make our playbook more generic, we will use an ansible group called
`webservers` holding our managed server, so that we can then later easily add
more servers which Ansible will configure identically. Modify the hostfile by
adding a definition of the group webservers, this is **left to you as an
exercise**. If you do it correctly, you should get the following inventory
list:
testserver | CHANGED | rc=0 >>
18:56:58 up 25 min, 1 user, load average: 0.00, 0.01, 0.02
``` shell
lcl$ ansible-inventory --list
{
"_meta": {
"hostvars": {
"testserver": {
"ansible_ssh_host": ...
}
}
},
"all": {
"children": [
"ungrouped",
"webservers"
]
},
"webservers": {
"hosts": [
"testserver"
]
}
}
```
You should now be able to ping the webservers group:
``` shell
lcl$ ansible webservers -m ping
```
The output should be the same as before.
Now, create a playbook named `~/ansible/playbooks/web.yml` with the following content:
``` yaml
---
- name: Configure webserver with nginx
hosts: webservers
become: True
tasks:
- name: install nginx
apt: name=nginx update_cache=yes
- name: copy nginx config file
copy: src=files/nginx.conf dest=/etc/nginx/sites-available/default
- name: enable configuration
file: >
dest=/etc/nginx/sites-enabled/default
src=/etc/nginx/sites-available/default
state=link
- name: copy index.html
template: src=templates/index.html.j2 dest=/usr/share/nginx/html/index.html mode=0644
- name: restart nginx
service: name=nginx state=restarted
```
:question: How many tasks are declared in the above playbook?
Then, create the nginx's configuration file
`~/ansible/playbooks/files/nginx.conf` referenced in the playbook references,
which the following content:
``` nginx
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
}
```
As per the aboveconfiguration file, nginx will serve the Web app's homepage
from `index.html`. This will be generated via Ansible's templating engine from
a template, which has to be created as
`~/ansible/playbooks/templates/index.html.j2` and should hold the following:
``` html
<html>
<head>
<title>Welcome to ansible</title> </head>
<body>
<h1>nginx, configured by Ansible</h1>
<p>If you can see this, Ansible successfully installed nginx.</p>
<p>{{ ansible_managed }}</p>
<p>Some facts Ansible gathered about this machine:
<table>
<tr><td>OS family:</td><td>{{ ansible_os_family }}</td></tr>
<tr><td>Distribution:</td><td>{{ ansible_distribution }}</td></tr>
<tr><td>Distribution version:</td><td>{{ ansible_distribution_version }}</td></tr>
</table>
</p>
</body>
</html>
```
Now, run the newly created playbook to install and configure nginx, and to
deploy the Web app on the managed host:
``` shell
lcl$ ansible-playbook ~/ansible/playbooks/web.yml
```
If everything goes well, the last output lines should be like:
PLAY [Configure webserver with nginx] **************************************
TASK [Gathering Facts] *****************************************************
ok: [testserver]
TASK [install nginx] *******************************************************
changed: [testserver]
TASK [copy nginx config file] ***********************************************
changed: [testserver]
TASK [enable configuration] *************************************************
ok: [testserver]
TASK [copy index.html] ******************************************************
changed: [testserver]
TASK [restart nginx] ********************************************************
changed: [testserver]
PLAY RECAP ******************************************************************
testserver : ok=6 changed=4 unreachable=0 failed=0
Point your Web browser to the address of the managed server (mind that we are
not using SSL): `http://<VM-DNS-name-or-IP-address>`. You should see the
homepage showing "nginx, configured by Ansible".
### Task #5: Test Desired State Configuration principles ###
**Goal:** test and verify that Ansible implements the principles of Desired
State Configuration.
According to this principle, before doing anything, Ansible should establish
the current state of the managed server, compare it to the desired state
expressed in the playbook, and then only perform the actions necessary to
bring the current state to the desired state.
In its ouput, Ansible marks tasks where it had to perform some action as
*changed* whereas tasks where the actual state already corresponded to the
desired state as *ok*.
1. Return to the output of the last ansible command that run the `web.yml`
playbook.
1. :question: There is one additional task that was not in the
playbook. Which one?
1. :question: Among the tasks that are in the playbook there is
one task that Ansible marked as *ok*. Which one? Do you have a possible
explanation?
1. Re-run the `web.yml` playbook a second time and compare its output with
the first run's. In principle nothing should have changed.
1. :question: Which tasks are marked as changed? Why?
1. SSH into the managed server. Modify the nginx configuration file
`default` (by the way, what's the deployment path?), for example by
adding a line with a comment. Re-run the playbook.
1. :question: What does Ansible do to the file and what does it show in
its output?
1. Do something more drastic like completely removing the homepage
`index.html` (by the way, what's the deployment path?) and repeat the
previous question.
1. :question: What happened this time?
### Task #6: Adding a handler for nginx restart ###
**Goal:** improve the playbook by restarting nginx only when needed.
The current version of the playbook restarts nginx every time the playbook is
run, irrespective of the managed server's state. This goes indeed a bit too far.
By putting the nginx restart command into a *handler*, instead of a task, its
execution can be made conditional. The rationale is that nginx is restarted
only if one of the tasks that affects nginx's configuration resulted in a
change.
Consult the [Ansible documentation about
handlers](https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html). Modify
the playbook so that the nginx restart becomes a handler and the tasks that
potentially modify its configuration use *notify* to call the handler when
needed.
Copy the modified playbook into the lab report.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment