docs: add 100 Days of DevOps challenge notes
This commit is contained in:
162
100 - days of devops/devops-89.md
Normal file
162
100 - days of devops/devops-89.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Assignment
|
||||
|
||||
Developers are looking for dependencies to be installed and run on Nautilus app servers in Stratos DC. They have shared some requirements with the DevOps team. Because we are now managing packages installation and services management using Ansible, some playbooks need to be created and tested. As per details mentioned below please complete the task:
|
||||
|
||||
a. On jump host create an Ansible playbook /home/thor/ansible/playbook.yml and configure it to install httpd on all app servers.
|
||||
b. After installation make sure to start and enable httpd service on all app servers.
|
||||
c. The inventory /home/thor/ansible/inventory is already there on jump host.
|
||||
d. Make sure user thor should be able to run the playbook on jump host.
|
||||
|
||||
Note: Validation will try to run playbook using command ansible-playbook -i inventory playbook.yml so please make sure playbook works this way, without passing any extra arguments.
|
||||
|
||||
# Solution
|
||||
|
||||
# Ansible Playbook — install and enable httpd on all App Servers
|
||||
|
||||
Create `/home/thor/ansible/playbook.yml` so `ansible-playbook -i inventory playbook.yml` installs
|
||||
httpd and brings the service up on all app servers — 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
|
||||
|
||||
Requirement (c) confirms the inventory is already in place. **Don't recreate or overwrite it** —
|
||||
just verify its contents:
|
||||
|
||||
```bash
|
||||
cat /home/thor/ansible/inventory
|
||||
```
|
||||
|
||||
Confirm it lists the app servers with their connection variables. If it lacks
|
||||
`ansible_become_pass` and the app-server users need a sudo password, add that per host — the
|
||||
playbook requires privilege escalation and you can't pass `-K` at runtime.
|
||||
|
||||
## Step 1 — Playbook
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/playbook.yml <<'EOF'
|
||||
---
|
||||
- name: Install and enable httpd on all app servers
|
||||
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
|
||||
EOF
|
||||
```
|
||||
|
||||
## Step 2 — Ensure `thor` can run it (requirement d)
|
||||
|
||||
```bash
|
||||
ls -l /home/thor/ansible/
|
||||
```
|
||||
|
||||
Creating the playbook with the heredoc above (as `thor`) already gives correct ownership. If the
|
||||
directory was pre-created by another user and `thor` can't read the files, fix it:
|
||||
|
||||
```bash
|
||||
sudo chown -R thor:thor /home/thor/ansible
|
||||
chmod 644 /home/thor/ansible/playbook.yml
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
### Why `become: yes`
|
||||
|
||||
Both tasks are privileged: installing a package writes to system directories and the RPM database,
|
||||
and managing a systemd service requires root. The SSH users on the app servers are unprivileged, so
|
||||
the play escalates once at the **play level** rather than repeating `become` on each task.
|
||||
|
||||
Because validation runs the bare command with no `-K`, the sudo password must come from
|
||||
`ansible_become_pass` in the existing inventory. "Missing sudo password" at runtime means that
|
||||
variable is absent or wrong — and it's the most common failure on this task, since there's no way to
|
||||
supply it on the command line.
|
||||
|
||||
### Task 1 — install httpd
|
||||
|
||||
`ansible.builtin.yum` with **`state: present`** installs the package if it's missing and does nothing
|
||||
if it's already there. That makes the task **idempotent**: the first run reports `changed`,
|
||||
subsequent runs report `ok`. `state: latest` would instead upgrade on every run, which isn't what
|
||||
"install" asks for.
|
||||
|
||||
These app servers are RHEL/CentOS-family, so `yum` is the correct package module. (On modern
|
||||
RHEL 8+ the `dnf` module is its successor, and Ansible's `yum` module delegates to dnf where
|
||||
appropriate — so `yum` works regardless.)
|
||||
|
||||
### Task 2 — start *and* enable
|
||||
|
||||
Requirement (b) has two distinct halves, and `ansible.builtin.service` covers both:
|
||||
|
||||
| Option | Effect |
|
||||
|--------|--------|
|
||||
| `state: started` | httpd is running **right now**. |
|
||||
| `enabled: yes` | httpd starts automatically **on boot**. |
|
||||
|
||||
These are independent — a service can be running but not enabled (dies on reboot), or enabled but
|
||||
not currently started. "Start and enable" requires both, so both are set.
|
||||
|
||||
### Ordering matters
|
||||
|
||||
The install task must precede the service task: you can't start a service whose unit file doesn't
|
||||
exist yet. Ansible executes tasks top to bottom, so this ordering is a correctness requirement, not
|
||||
just style. Reversing them would fail with "Could not find the requested service httpd."
|
||||
|
||||
### `hosts: all`
|
||||
|
||||
The inventory contains the app servers, so `all` targets exactly them — and it can't break if the
|
||||
inventory's group name and the playbook's `hosts:` value ever drift apart.
|
||||
|
||||
### Requirement (d) — "thor should be able to run the playbook"
|
||||
|
||||
Validation runs as `thor`, so `thor` must be able to **read** the playbook and inventory. Writing the
|
||||
file via heredoc as `thor` satisfies this automatically; Step 2 is a safety check for the case where
|
||||
the directory was pre-created with root ownership, which would fail validation before Ansible even
|
||||
starts.
|
||||
|
||||
### Host key checking
|
||||
|
||||
If a first-time SSH connection trips on host-key verification, add an `ansible.cfg` beside the
|
||||
playbook — safest because it needs no extra command-line arguments:
|
||||
|
||||
```bash
|
||||
cat > /home/thor/ansible/ansible.cfg <<'EOF'
|
||||
[defaults]
|
||||
host_key_checking = False
|
||||
EOF
|
||||
```
|
||||
|
||||
## 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
|
||||
ansible -i inventory all -b -m command -a "rpm -q httpd"
|
||||
|
||||
# Running AND enabled
|
||||
ansible -i inventory all -b -m command -a "systemctl is-active httpd"
|
||||
ansible -i inventory all -b -m command -a "systemctl is-enabled httpd"
|
||||
```
|
||||
|
||||
Expected — `ping` returning `SUCCESS` on every server; the playbook finishing with `failed=0`;
|
||||
`rpm -q httpd` printing an installed version; `is-active` returning `active`; and `is-enabled`
|
||||
returning `enabled`.
|
||||
|
||||
> Check **both** `is-active` and `is-enabled` — passing only one means half the requirement was met.
|
||||
> "Could not find the requested service httpd" ⇒ task order is wrong. "Missing sudo password" ⇒ the
|
||||
> inventory needs `ansible_become_pass`.
|
||||
Reference in New Issue
Block a user