Files
kodekloud-engineer/100 - days of devops/devops-88.md

172 lines
7.3 KiB
Markdown

# 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`.