docs: add 100 Days of DevOps challenge notes
This commit is contained in:
172
100 - days of devops/devops-88.md
Normal file
172
100 - days of devops/devops-88.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Assignment
|
||||
|
||||
The Nautilus DevOps team wants to install and set up a simple httpd web server on all app servers in Stratos DC. Additionally, they want to deploy a sample web page for now using Ansible only. Therefore, write the required playbook to complete this task. Find more details about the task below.
|
||||
|
||||
We already have an inventory file under /home/thor/ansible directory on jump host. Create a playbook.yml under /home/thor/ansible directory on jump host itself.
|
||||
Using the playbook, install httpd web server on all app servers. Additionally, make sure its service should up and running.
|
||||
Using blockinfile Ansible module add some content in /var/www/html/index.html file. Below is the content:
|
||||
|
||||
Welcome to XfusionCorp!
|
||||
This is Nautilus sample file, created using Ansible!
|
||||
Please do not modify this file manually!
|
||||
The /var/www/html/index.html file's user and group owner should be apache on all app servers.
|
||||
The /var/www/html/index.html file's permissions should be 0777 on all app servers.
|
||||
|
||||
|
||||
Note:
|
||||
i. Validation will try to run the playbook using command ansible-playbook -i inventory playbook.yml so please make sure the playbook works this way without passing any extra arguments.
|
||||
ii. Do not use any custom or empty marker for blockinfile module.
|
||||
|
||||
# Solution
|
||||
|
||||
# Ansible Playbook — install httpd + deploy sample page on all App Servers
|
||||
|
||||
Create `/home/thor/ansible/playbook.yml` so `ansible-playbook -i inventory playbook.yml` installs
|
||||
httpd, starts it, and writes a sample `index.html` with specific ownership and permissions — with
|
||||
**no extra arguments**.
|
||||
|
||||
> Note: this is an Ansible task, not Kubernetes — no manifests to pipe into `kubectl`. The heredoc
|
||||
> below writes the playbook.
|
||||
|
||||
## Step 0 — The inventory already exists; leave it alone
|
||||
|
||||
The task states an inventory is already present. **Do not recreate or overwrite it** — just confirm
|
||||
what's in it:
|
||||
|
||||
```bash
|
||||
cat /home/thor/ansible/inventory
|
||||
```
|
||||
|
||||
Check that it lists the app servers with their connection variables. If it lacks
|
||||
`ansible_become_pass` and the app-server users require a sudo password, add that per host — the
|
||||
playbook needs privilege escalation and you can't pass `-K` at runtime.
|
||||
|
||||
## Step 1 — Playbook
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/playbook.yml <<'EOF'
|
||||
---
|
||||
- name: Install httpd and deploy sample web page
|
||||
hosts: all
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Install httpd package
|
||||
ansible.builtin.yum:
|
||||
name: httpd
|
||||
state: present
|
||||
|
||||
- name: Start and enable httpd service
|
||||
ansible.builtin.service:
|
||||
name: httpd
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Add sample content to index.html
|
||||
ansible.builtin.blockinfile:
|
||||
path: /var/www/html/index.html
|
||||
create: yes
|
||||
block: |
|
||||
Welcome to XfusionCorp!
|
||||
This is Nautilus sample file, created using Ansible!
|
||||
Please do not modify this file manually!
|
||||
owner: apache
|
||||
group: apache
|
||||
mode: '0777'
|
||||
EOF
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
### Why `become: yes`
|
||||
|
||||
Every task here is privileged: installing a package writes to system directories and the RPM
|
||||
database, managing a systemd service requires root, and `/var/www/html/` is root-owned. The SSH
|
||||
users are unprivileged, so the whole play escalates with `become: yes` at the play level rather than
|
||||
repeating it per task.
|
||||
|
||||
Since validation runs the bare command with no `-K`, the sudo password must come from
|
||||
`ansible_become_pass` in the **existing** inventory. If the play fails with "Missing sudo password,"
|
||||
that variable is what's missing.
|
||||
|
||||
### Task 1 — install httpd
|
||||
|
||||
`ansible.builtin.yum` with **`state: present`** installs the package if absent and does nothing
|
||||
otherwise, making it idempotent (`changed` on first run, `ok` after). `state: latest` would upgrade
|
||||
on every run, which isn't what's asked.
|
||||
|
||||
### Task 2 — service up and running
|
||||
|
||||
`ansible.builtin.service` handles both halves of the requirement:
|
||||
|
||||
- **`state: started`** — ensures httpd is running **right now**.
|
||||
- **`enabled: yes`** — ensures it starts automatically **on boot**.
|
||||
|
||||
These are independent settings; "up and running" is satisfied by `started`, and `enabled` makes it
|
||||
durable across reboots. Including both is the standard, complete answer.
|
||||
|
||||
### Task 3 — `blockinfile` with default markers
|
||||
|
||||
`blockinfile` inserts (or updates) a **block** of text delimited by marker comments, so the block can
|
||||
be managed idempotently on re-runs — Ansible finds its own markers and replaces the content between
|
||||
them rather than appending duplicates.
|
||||
|
||||
- **No `marker:` parameter is specified**, per note (ii). Omitting it uses the default,
|
||||
`# {mark} ANSIBLE MANAGED BLOCK`, which produces `# BEGIN ANSIBLE MANAGED BLOCK` and
|
||||
`# END ANSIBLE MANAGED BLOCK` around your text. Supplying a custom or empty marker would violate
|
||||
the requirement — and an empty marker would break idempotency, since Ansible could no longer locate
|
||||
its block.
|
||||
|
||||
- **`create: yes`** — creates `/var/www/html/index.html` if it doesn't exist. A fresh httpd install
|
||||
typically has no `index.html`, and without this flag `blockinfile` would fail with "Destination
|
||||
does not exist."
|
||||
|
||||
- **`block: |`** — the literal block scalar preserves the text **exactly**, including line breaks.
|
||||
Note the double space in `This is Nautilus sample file` — that's reproduced verbatim from the
|
||||
requirement, since validation may compare the content character for character.
|
||||
|
||||
- **`owner: apache` / `group: apache`** — the required ownership. `apache` is the user httpd runs as
|
||||
on RHEL/CentOS-family systems, and the account exists only **after** the httpd package is
|
||||
installed — which is why the install task must come first. Reordering would fail with "chown
|
||||
failed: failed to look up user apache."
|
||||
|
||||
- **`mode: '0777'`** — quoted so YAML parses it as a string. Unquoted octal (`0777`) is a classic
|
||||
misparse that silently yields the wrong permissions.
|
||||
|
||||
### Ordering matters
|
||||
|
||||
The three tasks form a dependency chain: **install** creates the `apache` user and
|
||||
`/var/www/html/`; **service** brings httpd up; **blockinfile** then writes into a directory that
|
||||
exists and assigns ownership to a user that exists. Ansible runs tasks top to bottom, so this order
|
||||
is the correctness requirement, not just style.
|
||||
|
||||
### `hosts: all`
|
||||
|
||||
The inventory holds the app servers, so `all` targets exactly them and can't break if the group name
|
||||
and the playbook's `hosts:` value drift apart.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
cd /home/thor/ansible
|
||||
|
||||
# Connectivity first
|
||||
ansible -i inventory all -m ping
|
||||
|
||||
# The actual validation command
|
||||
ansible-playbook -i inventory playbook.yml
|
||||
|
||||
# httpd installed and running
|
||||
ansible -i inventory all -b -m command -a "systemctl is-active httpd"
|
||||
|
||||
# File content, ownership, and permissions
|
||||
ansible -i inventory all -b -m command -a "ls -l /var/www/html/index.html"
|
||||
ansible -i inventory all -b -m command -a "cat /var/www/html/index.html"
|
||||
```
|
||||
|
||||
Expected — the playbook finishing with `failed=0`; `systemctl is-active httpd` returning `active`
|
||||
on each server; `ls -l` showing `-rwxrwxrwx` with `apache apache`; and `cat` showing the three lines
|
||||
wrapped in `# BEGIN ANSIBLE MANAGED BLOCK` / `# END ANSIBLE MANAGED BLOCK`.
|
||||
|
||||
> Those BEGIN/END marker lines are **expected and correct** — they're the default markers required by
|
||||
> note (ii). A "failed to look up user apache" error means the blockinfile task ran before httpd was
|
||||
> installed. "Missing sudo password" means the inventory needs `ansible_become_pass`.
|
||||
Reference in New Issue
Block a user