docs: add 100 Days of DevOps challenge notes
This commit is contained in:
200
100 - days of devops/devops-91.md
Normal file
200
100 - days of devops/devops-91.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Assignment
|
||||
|
||||
The Nautilus DevOps team want to install and set up a simple httpd web server on all app servers in Stratos DC. They also want to deploy a sample web page using Ansible. Therefore, write the required playbook to complete this task as per details mentioned below.
|
||||
|
||||
|
||||
We already have an inventory file under /home/thor/ansible directory on jump host. Write a playbook playbook.yml under /home/thor/ansible directory on jump host itself. Using the playbook perform below given tasks:
|
||||
|
||||
|
||||
Install httpd web server on all app servers, and make sure its service is up and running.
|
||||
|
||||
|
||||
Create a file /var/www/html/index.html with content:
|
||||
|
||||
|
||||
This is a Nautilus sample file, created using Ansible!
|
||||
|
||||
|
||||
Using lineinfile Ansible module add some more content in /var/www/html/index.html file. Below is the content:
|
||||
|
||||
Welcome to Nautilus Group!
|
||||
|
||||
|
||||
Also make sure this new line is added at the top of the file.
|
||||
|
||||
|
||||
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 0655 on all app servers.
|
||||
|
||||
|
||||
Note: 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.
|
||||
|
||||
# Solution
|
||||
|
||||
# Ansible Playbook — httpd + sample page with `lineinfile`
|
||||
|
||||
Create `/home/thor/ansible/playbook.yml` so `ansible-playbook -i inventory playbook.yml` installs
|
||||
httpd, starts it, and builds an `index.html` with a line prepended at the top — 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
|
||||
|
||||
```bash
|
||||
cat /home/thor/ansible/inventory
|
||||
```
|
||||
|
||||
Confirm it lists the app servers with their connection variables, and that
|
||||
`ansible_become_pass` is present — the play needs root and you can't pass `-K`.
|
||||
|
||||
## 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: Create index.html with sample content
|
||||
ansible.builtin.copy:
|
||||
dest: /var/www/html/index.html
|
||||
content: |
|
||||
This is a Nautilus sample file, created using Ansible!
|
||||
owner: apache
|
||||
group: apache
|
||||
mode: '0655'
|
||||
|
||||
- name: Add welcome line at the top of index.html
|
||||
ansible.builtin.lineinfile:
|
||||
path: /var/www/html/index.html
|
||||
line: 'Welcome to Nautilus Group!'
|
||||
insertbefore: BOF
|
||||
owner: apache
|
||||
group: apache
|
||||
mode: '0655'
|
||||
EOF
|
||||
```
|
||||
|
||||
Resulting file content:
|
||||
|
||||
```
|
||||
Welcome to Nautilus Group!
|
||||
This is a Nautilus sample file, created using Ansible!
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
### Task order is a hard dependency
|
||||
|
||||
The four tasks must run in this sequence:
|
||||
|
||||
1. **Install httpd** — this creates the `apache` user *and* the `/var/www/html/` directory. Both are
|
||||
needed by later tasks.
|
||||
2. **Start/enable the service** — httpd must exist before it can be managed.
|
||||
3. **Create the file** — the directory now exists and `apache` is a valid owner.
|
||||
4. **Prepend the line** — the file must exist before `lineinfile` can modify it.
|
||||
|
||||
Reordering breaks things concretely: a `copy` before the install fails with "Destination directory
|
||||
does not exist" or "failed to look up user apache"; a `lineinfile` before the `copy` has nothing to
|
||||
insert into.
|
||||
|
||||
### Task 1 & 2 — install and run
|
||||
|
||||
- **`yum` with `state: present`** — installs if missing, no-ops otherwise (idempotent: `changed`
|
||||
first run, `ok` after). `state: latest` would upgrade on every run, which isn't what's asked.
|
||||
- **`service` with `state: started` *and* `enabled: yes`** — two independent settings.
|
||||
`started` = running now; `enabled` = starts on boot. "Up and running" needs `started`; `enabled`
|
||||
makes it survive reboots. Both are standard for this requirement.
|
||||
|
||||
### Task 3 — `copy` with inline `content`
|
||||
|
||||
Using `copy` with the **`content:`** parameter writes a literal string to `dest` — no source file on
|
||||
the controller needed. The `|` block scalar preserves the text exactly and appends a trailing
|
||||
newline, so the next task's line lands cleanly above it.
|
||||
|
||||
(`copy` also accepts `src:` for pushing an existing file; `content:` is the right choice when the
|
||||
text is defined inline, as here.)
|
||||
|
||||
### Task 4 — `lineinfile` with `insertbefore: BOF`
|
||||
|
||||
This is the crux of requirement 3. `lineinfile` ensures a **single line** is present in a file:
|
||||
|
||||
- **`line: 'Welcome to Nautilus Group!'`** — the exact text to guarantee.
|
||||
- **`insertbefore: BOF`** — `BOF` is a special value meaning **Beginning Of File**. It places the
|
||||
line as the very first line, which is precisely "added at the top." Without it, `lineinfile`
|
||||
appends to the end by default — the file would still contain both lines, but in the wrong order,
|
||||
and validation would fail.
|
||||
|
||||
(The mirror value is `insertafter: EOF` for the end of the file.)
|
||||
|
||||
`lineinfile` is also **idempotent**: on re-runs it finds the line already present and reports `ok`
|
||||
rather than inserting a duplicate.
|
||||
|
||||
### `copy` vs `lineinfile` — why both
|
||||
|
||||
They do different jobs. `copy` establishes the file's whole content from scratch (requirement 2);
|
||||
`lineinfile` surgically inserts one line into an existing file (requirement 3). The task explicitly
|
||||
asks for `lineinfile` for the second piece, so this two-step approach matches the requirements
|
||||
directly rather than just writing both lines in one `copy`.
|
||||
|
||||
### Ownership and permissions
|
||||
|
||||
- **`owner: apache` / `group: apache`** — the `apache` account exists only *after* the httpd package
|
||||
is installed, which is why task 1 must come first.
|
||||
- **`mode: '0655'`** — **quoted**. Unquoted octal like `0655` is a classic YAML misparse that
|
||||
silently produces the wrong permissions. Note this is `0655`, not the more familiar `0644` or
|
||||
`0755` — copy it exactly.
|
||||
|
||||
Both are set on the `copy` **and** the `lineinfile` task. Setting them on the final task is what
|
||||
guarantees the end state, since `lineinfile` rewrites the file; specifying them on both makes the
|
||||
result deterministic regardless of which task last touched the file.
|
||||
|
||||
### `become: yes`
|
||||
|
||||
Installing packages, managing systemd services, writing under root-owned `/var/www/html/`, and
|
||||
running `chown` all require root. The play escalates once at play level, with the sudo password
|
||||
coming from `ansible_become_pass` in the inventory.
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
cd /home/thor/ansible
|
||||
|
||||
# Connectivity
|
||||
ansible -i inventory all -m ping
|
||||
|
||||
# The actual validation command
|
||||
ansible-playbook -i inventory playbook.yml
|
||||
|
||||
# Service 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"
|
||||
|
||||
# Content order, ownership, permissions
|
||||
ansible -i inventory all -b -m command -a "cat /var/www/html/index.html"
|
||||
ansible -i inventory all -b -m command -a "ls -l /var/www/html/index.html"
|
||||
```
|
||||
|
||||
Expected — playbook `failed=0`; `is-active` → `active` and `is-enabled` → `enabled`; `cat` showing
|
||||
**`Welcome to Nautilus Group!` on the first line** followed by the sample-file line; and `ls -l`
|
||||
showing `-rw-r-xr-x` (0655) with `apache apache`.
|
||||
|
||||
> The line **order** is the thing to check most carefully — if the welcome line appears at the
|
||||
> bottom, `insertbefore: BOF` is missing or misspelled. "failed to look up user apache" ⇒ a file task
|
||||
> ran before the httpd install.
|
||||
Reference in New Issue
Block a user