|
| 1 | +# Lab 5 Submission |
| 2 | + |
| 3 | +## Task 1: Vagrant Up + Run QuickNotes Inside |
| 4 | + |
| 5 | +### Vagrantfile |
| 6 | + |
| 7 | +The `Vagrantfile` at the repo root: |
| 8 | + |
| 9 | +- uses the `bento/ubuntu-24.04` box (Ubuntu 24.04 LTS) |
| 10 | +- sets the hostname to `quicknotes-vm` |
| 11 | +- forwards `127.0.0.1:18080` -> guest `:8080` |
| 12 | +- syncs host `./app` -> guest `/home/vagrant/app` |
| 13 | +- caps the VM at 2 vCPU / 1024 MB RAM |
| 14 | +- installs Go 1.24.5 via an idempotent shell provisioner |
| 15 | + |
| 16 | +See [Vagrantfile](../Vagrantfile). |
| 17 | + |
| 18 | +### `vagrant up` β first 10 lines |
| 19 | + |
| 20 | +```text |
| 21 | +Bringing machine 'default' up with 'virtualbox' provider... |
| 22 | +==> default: Box 'bento/ubuntu-24.04' could not be found. Attempting to find and install... |
| 23 | + default: Box Provider: virtualbox |
| 24 | + default: Box Version: >= 0 |
| 25 | +==> default: Loading metadata for box 'bento/ubuntu-24.04' |
| 26 | + default: URL: https://vagrantcloud.com/api/v2/vagrant/bento/ubuntu-24.04 |
| 27 | +==> default: Adding box 'bento/ubuntu-24.04' (v202510.26.0) for provider: virtualbox (amd64) |
| 28 | + default: Downloading: https://vagrantcloud.com/bento/boxes/ubuntu-24.04/versions/202510.26.0/providers/virtualbox/amd64/vagrant.box |
| 29 | +==> default: Successfully added box 'bento/ubuntu-24.04' (v202510.26.0) for 'virtualbox (amd64)'! |
| 30 | +==> default: Importing base box 'bento/ubuntu-24.04'... |
| 31 | +``` |
| 32 | + |
| 33 | +Tail of provisioning confirms the Go install: |
| 34 | + |
| 35 | +```text |
| 36 | +==> default: Mounting shared folders... |
| 37 | + default: D:/Desktop/DevOps-Intro/app => /home/vagrant/app |
| 38 | +==> default: Running provisioner: shell... |
| 39 | + default: Installing Go 1.24.5... |
| 40 | + default: go version go1.24.5 linux/amd64 |
| 41 | +``` |
| 42 | + |
| 43 | +> **Environment notes:** |
| 44 | +> |
| 45 | +> - Vagrant 2.4.9 crashes with `Log level must be in 0..8` when fetching a box. Fix: set `VAGRANT_CLOUD_LOG=error` before `vagrant up`. |
| 46 | +> - Host had Hyper-V active (WSL2 + Docker). VirtualBox 7.2 ran the VM anyway, so no need to disable it. |
| 47 | +
|
| 48 | +### Verify |
| 49 | + |
| 50 | +**Inside the VM:** |
| 51 | + |
| 52 | +```bash |
| 53 | +$ vagrant ssh -c 'go version' |
| 54 | +go version go1.24.5 linux/amd64 |
| 55 | + |
| 56 | +$ vagrant ssh -c 'cd /home/vagrant/app && go build -o /tmp/qn . \ |
| 57 | + && (DATA_PATH=/tmp/notes.json setsid /tmp/qn >/tmp/qn.log 2>&1 </dev/null &) \ |
| 58 | + && sleep 3 && curl -s http://localhost:8080/health' |
| 59 | +{"notes":4,"status":"ok"} |
| 60 | + |
| 61 | +$ vagrant ssh -c 'ss -ltnp | grep 8080' |
| 62 | +LISTEN 0 4096 *:8080 *:* users:(("qn",pid=3099,fd=3)) |
| 63 | +$ vagrant ssh -c 'cat /tmp/qn.log' |
| 64 | +2026/06/23 19:51:34 quicknotes listening on :8080 (notes loaded: 4) |
| 65 | +``` |
| 66 | + |
| 67 | +**From the host (via the `127.0.0.1:18080` port forward):** |
| 68 | + |
| 69 | +```text |
| 70 | +$ curl -s http://localhost:18080/health |
| 71 | +{"notes":4,"status":"ok"} |
| 72 | +
|
| 73 | +$ curl -s http://localhost:18080/notes |
| 74 | +[{"id":1,"title":"Welcome to QuickNotes",...}, |
| 75 | + {"id":2,"title":"Read app/main.go first",...}, |
| 76 | + {"id":3,"title":"DevOps mantra",...}, |
| 77 | + {"id":4,"title":"Endpoint cheat-sheet",...}] |
| 78 | +``` |
| 79 | + |
| 80 | +### 1.2 Design questions |
| 81 | + |
| 82 | +**a) Synced folder type β which and why? Trade-off?** |
| 83 | + |
| 84 | +I used the default **VirtualBox shared folder** β no extra host tooling, and a |
| 85 | +live two-way mount so host edits show up in the guest instantly. Trade-off: |
| 86 | +it's slow for heavy small-file I/O, where `rsync` (one-way copy) or `nfs` |
| 87 | +(faster, but needs a daemon and a Linux/macOS host) would do better. For one |
| 88 | +small Go service, simplicity wins. |
| 89 | + |
| 90 | +**b) NAT vs Bridged vs Host-only; why is loopback-bound forwarding safer?** |
| 91 | + |
| 92 | +I use Vagrant's default **NAT** with a forwarded port. NAT hides the guest from |
| 93 | +the LAN unless a port is explicitly forwarded, and I bound the forward to |
| 94 | +`127.0.0.1`, so `:18080` is reachable only from the host. A **Bridged** |
| 95 | +interface would give the VM its own LAN IP, exposing the unauthenticated |
| 96 | +service to everyone on the network β needless attack surface for a lab. |
| 97 | + |
| 98 | +**c) Provisioner β which and why?** |
| 99 | + |
| 100 | +The **`shell`** provisioner. Installing one Go tarball is a few imperative |
| 101 | +steps, and an inline script handles that with no extra dependencies. |
| 102 | +`ansible`/`puppet`/`chef` pay off across many hosts; for a single VM with one |
| 103 | +tool to install, they're overkill. |
| 104 | + |
| 105 | +**d) Why pin `1.24.5` instead of `1.24`?** |
| 106 | + |
| 107 | +`go.dev/dl/` only serves full point releases (`go1.24.5...tar.gz`), not a bare |
| 108 | +`1.24`. Pinning the exact patch keeps every `vagrant up` reproducible β same |
| 109 | +compiler today or in six months. Floating on `1.24` would silently pull |
| 110 | +whatever the latest patch is, reintroducing version drift. |
| 111 | + |
| 112 | +--- |
| 113 | + |
| 114 | +## Task 2 β Snapshots: Save, Break, Restore |
| 115 | + |
| 116 | +### Commands: save -> break -> verify -> restore -> verify |
| 117 | + |
| 118 | +```bash |
| 119 | +$ vagrant snapshot save quicknotes-clean-go124 |
| 120 | +==> default: Snapshotting the machine as 'quicknotes-clean-go124'... |
| 121 | +==> default: Snapshot saved! ... |
| 122 | + |
| 123 | +$ vagrant ssh -c 'sudo rm -rf /usr/local/go /usr/local/bin/go \ |
| 124 | + /usr/local/bin/gofmt /etc/profile.d/go.sh' |
| 125 | + |
| 126 | +$ vagrant ssh -c 'go version' |
| 127 | +bash: line 1: /usr/local/bin/go: No such file or directory (exit 127) |
| 128 | + |
| 129 | +$ time vagrant snapshot restore quicknotes-clean-go124 |
| 130 | + |
| 131 | +$ vagrant ssh -c 'go version' |
| 132 | +go version go1.24.5 linux/amd64 |
| 133 | +$ curl -s http://localhost:18080/health |
| 134 | +{"notes":4,"status":"ok"} |
| 135 | +``` |
| 136 | + |
| 137 | +The snapshot captured the **live** machine (RAM included), so the restore |
| 138 | +brought the QuickNotes process back with the same PID `3099`, still listening |
| 139 | +on `:8080`. |
| 140 | + |
| 141 | +### 6. Restore timing |
| 142 | + |
| 143 | +```text |
| 144 | +real 0m21.592s |
| 145 | +user 0m0.015s |
| 146 | +sys 0m0.000s |
| 147 | +``` |
| 148 | + |
| 149 | +### 2.2 Design questions |
| 150 | + |
| 151 | +**e) Snapshots are not backups β why?** |
| 152 | + |
| 153 | +A snapshot lives on the **same disk and host** as the VM, so it's useless when |
| 154 | +that disk dies, the laptop is lost/stolen, or run `vagrant destroy` β it |
| 155 | +goes with the original. It also can't undo corruption that predates it: if data was already bad (or encrypted by ransomware) when snapshotted, restoring just brings the bad state back. A backup is a separate, off-host, retained copy |
| 156 | + |
| 157 | +**f) Copy-on-write β 10 snapshots vs 1?** |
| 158 | + |
| 159 | +Copy-on-write means a snapshot doesn't copy the disk. The current disk is |
| 160 | +frozen as a read-only base and new writes go to a delta file, so each snapshot |
| 161 | +costs only the blocks that changed since the last one. Ten snapshots β base + |
| 162 | +ten small deltas, usually far less than 10Γ the disk. The cost is on reads, |
| 163 | +which must walk the whole delta chain |
| 164 | + |
| 165 | +**g) When is snapshotting an antipattern?** |
| 166 | + |
| 167 | +When the chain gets **long**. Each snapshot adds a delta layer, so long chains |
| 168 | +mean slower reads, unbounded disk growth, and fragility β losing one delta |
| 169 | +breaks everything after it. Snapshots should be short-lived (take β test β |
| 170 | +restore-or-delete), not long-term history or a backup substitute. Hoarding VM |
| 171 | +state as a "pet" instead of rebuilding from code ("cattle") is the antipattern |
0 commit comments