docs: add 100 Days of DevOps challenge notes
This commit is contained in:
161
100 - days of devops/devops-84.md
Normal file
161
100 - days of devops/devops-84.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Assignment
|
||||
|
||||
The Nautilus DevOps team needs to copy data from the jump host to all application servers in Stratos DC using Ansible. Execute the task with the following details:
|
||||
|
||||
|
||||
a. Create an inventory file /home/thor/ansible/inventory on jump_host and add all application servers as managed nodes.
|
||||
|
||||
|
||||
b. Create a playbook /home/thor/ansible/playbook.yml on the jump host to copy the /usr/src/dba/index.html file to all application servers, placing it at /opt/dba.
|
||||
|
||||
|
||||
Note: Validation will run the playbook using the command ansible-playbook -i inventory playbook.yml. Ensure the playbook functions properly without any extra arguments.
|
||||
|
||||
# Solution
|
||||
|
||||
# Ansible Inventory + Playbook — copy `index.html` to all App Servers
|
||||
|
||||
Set up the jump host so `ansible-playbook -i inventory playbook.yml` copies
|
||||
`/usr/src/dba/index.html` to `/opt/dba` on **all three** Stratos DC application servers — with **no
|
||||
extra arguments**.
|
||||
|
||||
> Note: this is an Ansible task, not Kubernetes — no manifests to pipe into `kubectl`. The heredocs
|
||||
> below write the two files.
|
||||
|
||||
## Step 1 — Inventory (all three app servers)
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/inventory <<'EOF'
|
||||
[app_servers]
|
||||
stapp01 ansible_user=tony ansible_ssh_pass=Ir0nM@n ansible_become_pass=Ir0nM@n
|
||||
stapp02 ansible_user=steve ansible_ssh_pass=Am3ric@ ansible_become_pass=Am3ric@
|
||||
stapp03 ansible_user=banner ansible_ssh_pass=BigGr33n ansible_become_pass=BigGr33n
|
||||
|
||||
[app_servers:vars]
|
||||
ansible_connection=ssh
|
||||
EOF
|
||||
```
|
||||
|
||||
## Step 2 — Playbook
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/playbook.yml <<'EOF'
|
||||
---
|
||||
- name: Copy index.html to all application servers
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Ensure /opt/dba exists
|
||||
ansible.builtin.file:
|
||||
path: /opt/dba
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy index.html to /opt/dba
|
||||
ansible.builtin.copy:
|
||||
src: /usr/src/dba/index.html
|
||||
dest: /opt/dba/index.html
|
||||
mode: '0644'
|
||||
EOF
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
### The inventory — all three managed nodes
|
||||
|
||||
Each app server in Stratos DC gets its own line under the `[app_servers]` group, keyed by the
|
||||
**server name from the wiki** (`stapp01`, `stapp02`, `stapp03`). Those names resolve from the jump
|
||||
host, so the inventory name doubles as the connection target — **no `ansible_host` needed**. Confirm
|
||||
with `getent hosts stapp01` if you want certainty.
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `ansible_user` | SSH username, different per server. |
|
||||
| `ansible_ssh_pass` | SSH password (these servers use password auth, not keys). |
|
||||
| `ansible_become_pass` | The **sudo** password, needed because the playbook uses `become: yes`. |
|
||||
| `ansible_connection` | Transport plugin — set once for the whole group via `[app_servers:vars]`. |
|
||||
|
||||
> **Verify every credential against your lab's wiki.** The user/password pairs shown follow the
|
||||
> common Stratos DC pattern, but treat them as values to confirm rather than facts.
|
||||
|
||||
The `[app_servers:vars]` block sets `ansible_connection` once for all members instead of repeating
|
||||
it on every line — the same result, less duplication.
|
||||
|
||||
Because validation runs the bare command (no `-u`, `-k`, `-K`, or `--private-key`), **all**
|
||||
connection *and* escalation details must live in the inventory.
|
||||
|
||||
### Why `become: yes` is required here
|
||||
|
||||
`/opt/dba` is root-owned. The SSH users (`tony`, `steve`, `banner`) can't write there directly, so
|
||||
the play escalates to root with `become: yes`. That in turn means sudo may demand a password —
|
||||
which is why `ansible_become_pass` is set per host in the inventory. Without it, the run would hang
|
||||
or fail with "Missing sudo password," and you can't pass `-K` because no extra arguments are
|
||||
allowed.
|
||||
|
||||
> If your lab's app-server users have **passwordless** sudo, `ansible_become_pass` is harmless and
|
||||
> simply unused. Including it covers both cases.
|
||||
|
||||
### The two tasks
|
||||
|
||||
1. **`ansible.builtin.file` with `state: directory`** — guarantees `/opt/dba` exists before the
|
||||
copy. The `copy` module won't create missing parent directories, so if `/opt/dba` weren't already
|
||||
present the copy would fail with "Destination directory does not exist." This task makes the
|
||||
playbook robust either way, and it's idempotent (reports `ok` when the directory already exists).
|
||||
|
||||
2. **`ansible.builtin.copy`** — transfers the file:
|
||||
- **`src: /usr/src/dba/index.html`** — a path on the **control node** (the jump host). The `copy`
|
||||
module reads from the controller by default; that's exactly what "copy data from the jump host"
|
||||
means here. (Copying between two paths on the *remote* machine would instead need
|
||||
`remote_src: yes` — not the case here.)
|
||||
- **`dest: /opt/dba/index.html`** — the explicit target path. Writing the filename out is clearer
|
||||
than relying on `dest: /opt/dba/` directory-expansion behavior.
|
||||
- **`mode: '0644'`** — quoted so YAML treats it as a string; unquoted octal like `0644` is a
|
||||
classic misparse.
|
||||
|
||||
### Why `hosts: all`
|
||||
|
||||
The inventory contains only the three app servers, so `all` targets exactly them — and it can't
|
||||
break if the group name and the playbook's `hosts:` value ever drift apart. Using
|
||||
`hosts: app_servers` also works given this inventory; `all` is simply the more failure-proof
|
||||
choice.
|
||||
|
||||
### Host key checking
|
||||
|
||||
First-time SSH connections can fail on host-key verification. The safest fix — no extra
|
||||
command-line arguments needed during validation — is an `ansible.cfg` beside the playbook:
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/ansible.cfg <<'EOF'
|
||||
[defaults]
|
||||
host_key_checking = False
|
||||
EOF
|
||||
```
|
||||
|
||||
`sshpass` must also be installed on the jump host for password auth; it usually is, and Ansible's
|
||||
error names it explicitly if not.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
cd /home/thor/ansible
|
||||
|
||||
# All three hosts listed
|
||||
ansible-inventory -i inventory --list
|
||||
|
||||
# Connectivity + credentials across every server at once
|
||||
ansible -i inventory all -m ping
|
||||
|
||||
# The actual validation command
|
||||
ansible-playbook -i inventory playbook.yml
|
||||
|
||||
# Confirm the file landed on all three
|
||||
ansible -i inventory all -b -m command -a "ls -l /opt/dba/index.html"
|
||||
```
|
||||
|
||||
Expected — `ping` returning `SUCCESS`/`pong` for stapp01, stapp02, and stapp03; the playbook
|
||||
finishing with `failed=0` for all three hosts; and the final check listing `/opt/dba/index.html` on
|
||||
each server.
|
||||
|
||||
> `UNREACHABLE` means a hostname didn't resolve or credentials don't match the wiki. "Missing sudo
|
||||
> password" means `ansible_become_pass` is absent or wrong for that host. Permission denied on the
|
||||
> copy means `become: yes` didn't take effect.
|
||||
Reference in New Issue
Block a user