diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..4dbd6de08 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Goal + + +Complete lab 3 DevOps + +## Changes +* Added submissions/lab3.md +* Create GitHub Action CI Workflows +* Added Go environment ubuntu 24.04 + +## Testing + +* Start CI action on GitHub and green CI run +* Verfied CI run pipeline + +## Checklist +- ☑️ Title is a clear sentence (≤ 70 chars) +- ☑️ Vet, Test, and Lint checks pass +- ☑️ `submissions/lab3.md` updated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..38dbdc45d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,83 @@ +name: PR Gate + +on: + push: + branches: [ main ] + paths: + - 'app/**' + - '.github/workflows/ci.yml' + pull_request: + branches: [ main ] + paths: + - 'app/**' + - '.github/workflows/ci.yml' + +permissions: + contents: read # минимальные права + +jobs: + # ---------- vet ---------- + vet: + name: go vet + runs-on: ubuntu-24.04 + strategy: + matrix: + go-version: [ '1.23', '1.24' ] + fail-fast: false + defaults: + run: + working-directory: app + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.3.0 + with: + go-version: ${{ matrix.go-version }} + cache: true + - name: Run go vet + run: go vet ./... + + # ---------- test ---------- + test: + name: go test -race + runs-on: ubuntu-24.04 + strategy: + matrix: + go-version: [ '1.23', '1.24' ] + fail-fast: false + defaults: + run: + working-directory: app + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.3.0 + with: + go-version: ${{ matrix.go-version }} + cache: true + - name: Run tests with race detector + run: go test -race -count=1 ./... + + # ---------- lint ---------- + lint: + name: golangci-lint + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: app + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup Go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.3.0 + with: + go-version: '1.24' # достаточно одной версии для линтера + cache: true + - name: Install golangci-lint v2.5.0 + run: | + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \ + sh -s -- -b $(go env GOPATH)/bin v2.5.0 + - name: Run golangci-lint + run: golangci-lint run ./... \ No newline at end of file diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 000000000..67abe2703 --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,40 @@ +#---------- Builder stage ---------- + +FROM golang:1.24-alpine AS builder + +# WorkDir + +WORKDIR /build + +# copy go.mod and run + +COPY go.mod ./ +RUN go mod download + +COPY . . + + +# CGO_ENABLED=0 — disable CGO +# -ldflags='-s -w' — delete table symbol + +RUN CGO_ENABLED=0 GOOS=linux go build \ + -ldflags='-s -w' \ + -trimpath \ + -o quicknotes . + +# -------------------- Runtime stage -------------------- +FROM gcr.io/distroless/static:nonroot + +# copy bin from builder-stage + +COPY --from=builder /build/quicknotes /quicknotes + +# UID 65532 in distroless/static:nonroot + +USER 65532:65532 + +# port +EXPOSE 8080 + +# exec-form +ENTRYPOINT ["/quicknotes"] \ No newline at end of file diff --git a/app/lab4-trace.pcap b/app/lab4-trace.pcap new file mode 100644 index 000000000..a72a4046f Binary files /dev/null and b/app/lab4-trace.pcap differ diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..7a4093fee --- /dev/null +++ b/compose.yaml @@ -0,0 +1,27 @@ +services: + quicknotes: + build: + context: ./app + dockerfile: Dockerfile + image: quicknotes:lab6 + container_name: quicknotes + ports: + - "8080:8080" + volumes: + - quicknotes-data:/data + environment: + - ADDR=:8080 + - DATA_PATH=/data/notes.json + - SEED_PATH=/data/seed.json + healthcheck: + test: ["CMD", "/quicknotes", "-healthcheck"] # or ["CMD-SHELL", "wget -qO- http://localhost:8080/health"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 5s + restart: unless-stopped + + +volumes: + quicknotes-data: + name: quicknotes-data \ No newline at end of file diff --git a/lab4-trace.pcap b/lab4-trace.pcap new file mode 100644 index 000000000..90be702b5 Binary files /dev/null and b/lab4-trace.pcap differ diff --git a/lab4-trace.txt b/lab4-trace.txt new file mode 100644 index 000000000..a6299ac48 --- /dev/null +++ b/lab4-trace.txt @@ -0,0 +1,132 @@ +19:11:19.932815 IP 127.0.0.1.42566 > 127.0.0.1.8080: Flags [S], seq 3179416048, win 65495, options [mss 65495,sackOK,TS val 2461922435 ecr 0,nop,wscale 7], length 0 +E..<..@.@.(..........F.... ..........0......... +............ +19:11:19.932821 IP 127.0.0.1.8080 > 127.0.0.1.42566: Flags [R.], seq 0, ack 3179416049, win 0, length 0 +E..(..@.@.<............F...... .P...$... +19:11:19.932861 IP6 ::1.40002 > ::1.8080: Flags [S], seq 2374255427, win 65476, options [mss 65476,sackOK,TS val 2676338689 ecr 0,nop,wscale 7], length 0 +`.~..(.@.................................B....CC.........0......... +............ +19:11:19.932866 IP6 ::1.8080 > ::1.40002: Flags [R.], seq 0, ack 2374255428, win 0, length 0 +`.k9...@...................................B......CDP....... +19:12:26.641138 IP 127.0.0.1.41710 > 127.0.0.1.8080: Flags [S], seq 509650619, win 65495, options [mss 65495,sackOK,TS val 2461989143 ecr 0,nop,wscale 7], length 0 +E..<1.@.@. +..............`...........0......... +............ +19:12:26.641142 IP 127.0.0.1.8080 > 127.0.0.1.41710: Flags [R.], seq 0, ack 509650620, win 0, length 0 +E..(..@.@.<..................`..P...*3.. +19:12:26.641165 IP6 ::1.32816 > ::1.8080: Flags [S], seq 630744336, win 65476, options [mss 65476,sackOK,TS val 2676405397 ecr 0,nop,wscale 7], length 0 +` .d.(.@.................................0..%.e..........0......... +............ +19:12:26.641168 IP6 ::1.8080 > ::1.32816: Flags [R.], seq 0, ack 630744337, win 0, length 0 +`.$....@...................................0....%.e.P....... +19:12:43.027807 IP 127.0.0.1.41602 > 127.0.0.1.8080: Flags [S], seq 851489407, win 65495, options [mss 65495,sackOK,TS val 2462005530 ecr 0,nop,wscale 7], length 0 +E..<.y@.@..@............2............0......... +..9......... +19:12:43.027811 IP 127.0.0.1.8080 > 127.0.0.1.41602: Flags [R.], seq 0, ack 851489408, win 0, length 0 +E..(..@.@.<.................2...P... +{.. +19:12:43.027836 IP6 ::1.34804 > ::1.8080: Flags [S], seq 3910870731, win 65476, options [mss 65476,sackOK,TS val 2676421784 ecr 0,nop,wscale 7], length 0 +`..Q.(.@......................................&..........0......... +............ +19:12:43.027839 IP6 ::1.8080 > ::1.34804: Flags [R.], seq 0, ack 3910870732, win 0, length 0 +`......@..........................................&.P....... +19:12:54.413810 IP 127.0.0.1.52472 > 127.0.0.1.8080: Flags [S], seq 1293846705, win 65495, options [mss 65495,sackOK,TS val 2462016916 ecr 0,nop,wscale 7], length 0 +E..<.H@.@.[q............M............0......... +..e......... +19:12:54.413816 IP 127.0.0.1.8080 > 127.0.0.1.52472: Flags [R.], seq 0, ack 1293846706, win 0, length 0 +E..(..@.@.<.................M...P....t.. +19:12:54.413855 IP6 ::1.50118 > ::1.8080: Flags [S], seq 971549680, win 65476, options [mss 65476,sackOK,TS val 2676433170 ecr 0,nop,wscale 7], length 0 +`.|s.(.@....................................9............0......... +..!......... +19:12:54.413860 IP6 ::1.8080 > ::1.50118: Flags [R.], seq 0, ack 971549681, win 0, length 0 +`.+f...@........................................9...P....... +19:13:30.465338 IP 127.0.0.1.45936 > 127.0.0.1.8080: Flags [S], seq 499019428, win 65495, options [mss 65495,sackOK,TS val 2462052968 ecr 0,nop,wscale 7], length 0 +E.. 127.0.0.1.45936: Flags [R.], seq 0, ack 499019429, win 0, length 0 +E..(..@.@.<............p......n.P...Rj.. +19:13:30.465371 IP6 ::1.45326 > ::1.8080: Flags [S], seq 2488604191, win 65476, options [mss 65476,sackOK,TS val 2676469222 ecr 0,nop,wscale 7], length 0 +`.J .(.@.....................................U...........0......... +............ +19:13:30.465375 IP6 ::1.8080 > ::1.45326: Flags [R.], seq 0, ack 2488604192, win 0, length 0 +`../...@.........................................U. P....... +21:33:29.974894 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [S], seq 289262757, win 65495, options [mss 65495,sackOK,TS val 2470452477 ecr 0,nop,wscale 7], length 0 +E..<.$@.@............~...=...........0......... +.@.......... +21:33:29.974902 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [S.], seq 2132570133, ack 289262758, win 65483, options [mss 65495,sackOK,TS val 2470452477 ecr 2470452477,nop,wscale 7], length 0 +E..<..@.@.<............~..p..=.......0......... +.@...@...... +21:33:29.974907 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 2470452477 ecr 2470452477], length 0 +E..4.%@.@............~...=....p......(..... +.@...@.. +21:33:29.974939 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 2470452477 ecr 2470452477], length 175: HTTP: POST /notes HTTP/1.1 +E....&@.@............~...=....p............ +.@...@..POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 + +{"title":"trace me","body":"in flight"} +21:33:29.974940 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [.], ack 176, win 511, options [nop,nop,TS val 2470452477 ecr 2470452477], length 0 +E..4.+@.@.$............~..p..=.U.....(..... +.@...@.. +21:33:29.980128 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [P.], seq 1:207, ack 176, win 512, options [nop,nop,TS val 2470452482 ecr 2470452477], length 206: HTTP: HTTP/1.1 201 Created +E....,@.@.#............~..p..=.U........... +.@...@..HTTP/1.1 201 Created +Content-Type: application/json +Date: Wed, 17 Jun 2026 18:33:29 GMT +Content-Length: 93 + +{"id":5,"title":"trace me","body":"in flight","created_at":"2026-06-17T18:33:29.975704318Z"} + +21:33:29.980139 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 207, win 511, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.'@.@............~...=.U..p......(..... +.@...@.. +21:33:29.980234 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [F.], seq 176, ack 207, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.(@.@............~...=.U..p......(..... +.@...@.. +21:33:29.980286 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [F.], seq 207, ack 177, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.-@.@.$............~..p..=.V.....(..... +.@...@.. +21:33:29.980293 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 208, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.)@.@............~...=.V..p......(..... +.@...@.. +21:34:41.834814 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [S], seq 1421564529, win 65495, options [mss 65495,sackOK,TS val 2470524337 ecr 0,nop,wscale 7], length 0 +E.. 127.0.0.1.47718: Flags [S.], seq 2578938132, ack 1421564530, win 65483, options [mss 65495,sackOK,TS val 2470524337 ecr 2470524337,nop,wscale 7], length 0 +E..<..@.@.<............f..y.T.Zr.....0......... +.A5..A5..... +21:34:41.834828 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 2470524337 ecr 2470524337], length 0 +E..4g,@.@............f..T.Zr..y......(..... +.A5..A5. +21:34:41.834861 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 2470524337 ecr 2470524337], length 175: HTTP: POST /notes HTTP/1.1 +E...g-@.@............f..T.Zr..y............ +.A5..A5.POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 + +{"title":"trace me","body":"in flight"} +21:34:41.834863 IP 127.0.0.1.8080 > 127.0.0.1.47718: Flags [.], ack 176, win 511, options [nop,nop,TS val 2470524337 ecr 2470524337], length 0 +E..4.i@.@..X...........f..y.T.[!.....(..... +.A5..A5. +21:34:41.839333 IP 127.0.0.1.8080 > 127.0.0.1.47718: Flags [P.], seq 1:207, ack 176, win 512, options [nop,nop,TS val 2470524341 ecr 2470524337], length 206: HTTP: HTTP/1.1 201 Created +E....j@.@..............f..y.T.[!........... +.A5..A5.HTTP/1.1 201 Created +Content-Type: application/json +Date: Wed, 17 Jun 2026 18:34:41 GMT +Content-Length: 93 + +{"id":6,"title":"trace me","body":"in flight","created_at":"2026-06-17T18:34:41.835071034Z"} + +21:34:41.839349 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [.], ack 207, win 511, options [nop,nop,TS val 2470524342 ecr 2470524341], length 0 +E..4g.@.@............f..T.[!..y......(..... +.A5..A5. +21:34:41.839506 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [F.], seq 176, ack 207, win 512, options [nop,nop,TS val 2470524342 ecr 2470524341], length 0 +E..4g/@.@............f..T.[!..y......(..... +.A5..A5. diff --git a/submissions/lab2.md b/submissions/lab2.md new file mode 100644 index 000000000..7b95b50d1 --- /dev/null +++ b/submissions/lab2.md @@ -0,0 +1,160 @@ +#important work + + +```git rev-parse HEAD``` + +66bbd4db9228bc9a4cab7439746b993749c026ab + +```git cat-file -t HEAD``` + +commit + +```git cat-file -p HEAD``` + +tree 20bda2b2625085720751a3e794f82e5625a409b3 +parent 170000c9d1b5e90a37b6f1a9b826552d53051773 +author Dmitrii Creed 1780392934 +0400 +committer Dmitrii Creed 1780394046 +0400 + +```git cat-file -p 20bda2b2625085720751a3e794f82e5625a409b3``` + +100644 blob 1c0a1e94b7bbdd951f456cda51af6b8484cc3cee .gitignore +100644 blob d10c04c6e7e0014f4fe883599c11747c15012d4e README.md +040000 tree 7d0898a908e274ea809722844cdbd836f3b1c05a app +040000 tree 6db686e340ecdd318fa43375e26254293371942a labs +040000 tree 3f11973a71be5915539cb53313149aa319d69cb5 lectures + +```git cat-file -p 7d0898a908e274ea809722844cdbd836f3b1c05a``` + +100644 blob 8ba1a5234925005265281bf7809153487097373c .golangci.yml +100644 blob 24ab0258318f4aac6ec7d3a924a1d6f05209b446 Makefile +100644 blob 1aed7f8904100182d3fb4e1b90dbf3bd5a126beb README.md +100644 blob b76e91cf916dcebc1d6898e22012c737c117003a go.mod +100644 blob c534979c5a3aa0e032fb61e1562d4bd343ecaf4c handlers.go +100644 blob 9dff2e3e5b734f9afa4bc26c30d784bee8aa327c handlers_test.go +100644 blob e258ffcfe44ebc6923eb78d51c63fc2317aa1dfd main.go +100644 blob ecf4fd2edd38dcbc82459122660aa424342f9148 seed.json +100644 blob 4a9ca2b3a371cc43f8762095a6944cd96ea7d7d0 store.go +100644 blob 3b8ff9d45ae9e6781ffe333ccc7eff40da35a1bf store_test.go + +```ls -la .git/``` + +total 25 +drwxr-xr-x 1 UserName 197121 0 Jun 9 15:17 ./ +drwxr-xr-x 1 UserName 197121 0 Jun 9 15:18 ../ +-rw-r--r-- 1 UserName 197121 80 Jun 8 00:20 COMMIT_EDITMSG +-rw-r--r-- 1 UserName 197121 189 Jun 8 00:34 config +-rw-r--r-- 1 UserName 197121 73 Jun 4 00:25 description +-rw-r--r-- 1 UserName 197121 0 Jun 9 15:22 FETCH_HEAD +-rw-r--r-- 1 UserName 197121 29 Jun 9 15:17 HEAD +drwxr-xr-x 1 UserName 197121 0 Jun 4 00:25 hooks/ +-rw-r--r-- 1 UserName 197121 3055 Jun 9 15:16 index +drwxr-xr-x 1 UserName 197121 0 Jun 4 00:25 info/ +drwxr-xr-x 1 UserName 197121 0 Jun 4 00:25 logs/ +drwxr-xr-x 1 UserName 197121 0 Jun 9 15:22 objects/ +-rw-r--r-- 1 UserName 197121 46 Jun 4 00:32 packed-refs +drwxr-xr-x 1 UserName 197121 0 Jun 4 00:25 refs/ + +```cat .git/HEAD``` + +ref: refs/heads/feature/lab2 + +```ls .git/refs/heads/``` + +feature/ main + +```ls .git/objects/ | head``` + +0a/ +0c/ +0e/ +0f/ +13/ +1a/ +27/ +38/ +3a/ +40/ + +```find .git/objects -type f | wc -l``` + +29 + + +```git log --oneline``` + +66bbd4d (HEAD -> feature/lab2, main) docs(lab1): align Task 3 GitHub Community engagement with other courses +170000c Merge pull request #907 from inno-devops-labs/s26-refactor +d50436c fix(lab12,gitignore): Spin SDK (WAGI removed in Spin 3.x); minimal student-safe gitignore +4705a3d fix(.gitignore): stop ignoring submissions/ +4082340 docs(grading,lab11,lab12): bonus labs to 4+4+2; grading rebalanced to 70-14-5-20-30 = 139% +7b16dc5 docs(lab10): switch deploy targets to card-free platforms — HF Spaces + Cloudflare Tunnel +4a05efa docs(labs): scaffold the skill — labs 5-12 stop handing students copy-paste answers +8387fb9 docs(lab3): scaffold the skill — students write their own CI yaml; GitLab as parallel path +983fba0 docs(course): rewrite README + add .gitignore for project-threaded structure +7914e37 docs(labs): refactor 12 labs to 6+4+2 (lab1) / 6+4+bonus (lab2-10) / 10pts (lab11-12) +aa5aa1c docs(lectures): rewrite lec1-10 + add reading11/12 for project-threaded course +b8fc480 feat(app): introduce QuickNotes Go service for project-threaded course +6f044dd Replace IPFS with Nix +0a87e1c refactor: reduce prescriptiveness in GitLab CI instructions +eaea715 feat: add GitLab CI alternative instructions to lab3 +d6b6a03 Update lab2 +87810a0 feat: remove old Exam Exemption Policy +1e1c32b feat: update structure +6c27ee7 feat: publish lecs 9 & 10 +1826c36 feat: update lab7 +3049f08 feat: publish lec8 +da8f635 feat: introduce all labs and revised structure +04b174e feat: publish lab and lec #5 +67f12f1 feat: publish labs 4&5, revise others +82d1989 feat: publish lab3 and lec3 +3f80c83 feat: publish lec2 +499f2ba feat: publish lab2 +af0da89 feat: update lab1 +74a8c27 Publish lab1 +f0485c0 Publish lec1 +31dd11b Publish README.md + +```git reflog``` + +66bbd4d (HEAD -> feature/lab2, main) HEAD@{0}: reset: moving to HEAD~2 +03cdb25 HEAD@{1}: commit: lab1 +9a2873e HEAD@{2}: commit: wip(lab2): Work progress +66bbd4d (HEAD -> feature/lab2, main) HEAD@{3}: checkout: moving from main to feature/lab2 +66bbd4d (HEAD -> feature/lab2, main) HEAD@{4}: checkout: moving from feature/lab1 to main +dce058f (feature/lab1) HEAD@{5}: checkout: moving from main to feature/lab1 +66bbd4d (HEAD -> feature/lab2, main) HEAD@{6}: checkout: moving from feature/lab1 to main +dce058f (feature/lab1) HEAD@{7}: commit: docs(lab1): start submission +66bbd4d (HEAD -> feature/lab2, main) HEAD@{8}: checkout: moving from main to feature/lab1 +66bbd4d (HEAD -> feature/lab2, main) HEAD@{9}: clone: from https://github.com/sovva6-14/DevOps-Intro.git + +```git reset --hard 9a2873e``` + +HEAD is now at 9a2873e wip(lab2): Work progress + +```git push origin "v0.1.0-lab2-Sovva"``` + + +Enumerating objects: 1, done. +Counting objects: 100% (1/1), done. +Writing objects: 100% (1/1), 420 bytes | 420.00 KiB/s, done. +Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) +To https://github.com/sovva6-14/DevOps-Intro.git + * [new tag] v0.1.0-lab2-Sovva -> v0.1.0-lab2-Sovva + + ```git commit -S -s --allow-empty -m "docs: upstream moved while you worked"``` + +[feature/lab2 04b5e86] docs: upstream moved while you worked + + +```git push --force-with-lease origin feature/lab2``` + +Enumerating objects: 14, done. +Counting objects: 100% (14/14), done. +Delta compression using up to 20 threads +Compressing objects: 100% (10/10), done. +Writing objects: 100% (13/13), 3.95 KiB | 3.96 MiB/s, done. +Total 13 (delta 7), reused 6 (delta 1), pack-reused 0 (from 0) +remote: Resolving deltas: 100% (7/7), completed with 1 local object. +To https://github.com/sovva6-14/DevOps-Intro.git + + be131f7...04b5e86 feature/lab2 -> feature/lab2 (forced update) \ No newline at end of file diff --git a/submissions/lab3.md b/submissions/lab3.md new file mode 100644 index 000000000..cbe6040f1 --- /dev/null +++ b/submissions/lab3.md @@ -0,0 +1,3 @@ +#GitHub Actions + +Action: https://github.com/sovva6-14/DevOps-Intro/actions/runs/27701703406 \ No newline at end of file diff --git a/submissions/lab4.md b/submissions/lab4.md new file mode 100644 index 000000000..d69f00247 --- /dev/null +++ b/submissions/lab4.md @@ -0,0 +1,230 @@ +

Task 1

+ +```sudo tcpdump -r '/mnt/c/Users/Sovva/Desktop/DevOps-Intro/lab4-trace.pcap' -nn -A | tee lab4-trace.txt``` + +reading from file /mnt/c/Users/Sovva/Desktop/DevOps-Intro/lab4-trace.pcap, link-type EN10MB (Ethernet), snapshot length 262144 +19:11:19.932815 IP 127.0.0.1.42566 > 127.0.0.1.8080: Flags [S], seq 3179416048, win 65495, options [mss 65495,sackOK,TS val 2461922435 ecr 0,nop,wscale 7], length 0 +E..<..@.@.(..........F.... ..........0......... +............ +19:11:19.932821 IP 127.0.0.1.8080 > 127.0.0.1.42566: Flags [R.], seq 0, ack 3179416049, win 0, length 0 +E..(..@.@.<............F...... .P...$... +19:11:19.932861 IP6 ::1.40002 > ::1.8080: Flags [S], seq 2374255427, win 65476, options [mss 65476,sackOK,TS val 2676338689 ecr 0,nop,wscale 7], length 0 +`.~..(.@.................................B....CC.........0......... +............ +19:11:19.932866 IP6 ::1.8080 > ::1.40002: Flags [R.], seq 0, ack 2374255428, win 0, length 0 +`.k9...@...................................B......CDP....... +19:12:26.641138 IP 127.0.0.1.41710 > 127.0.0.1.8080: Flags [S], seq 509650619, win 65495, options [mss 65495,sackOK,TS val 2461989143 ecr 0,nop,wscale 7], length 0 +E..<1.@.@. +..............`...........0......... +............ +19:12:26.641142 IP 127.0.0.1.8080 > 127.0.0.1.41710: Flags [R.], seq 0, ack 509650620, win 0, length 0 +E..(..@.@.<..................`..P...*3.. +19:12:26.641165 IP6 ::1.32816 > ::1.8080: Flags [S], seq 630744336, win 65476, options [mss 65476,sackOK,TS val 2676405397 ecr 0,nop,wscale 7], length 0 +` .d.(.@.................................0..%.e..........0......... +............ +19:12:26.641168 IP6 ::1.8080 > ::1.32816: Flags [R.], seq 0, ack 630744337, win 0, length 0 +`.$....@...................................0....%.e.P....... +19:12:43.027807 IP 127.0.0.1.41602 > 127.0.0.1.8080: Flags [S], seq 851489407, win 65495, options [mss 65495,sackOK,TS val 2462005530 ecr 0,nop,wscale 7], length 0 +E..<.y@.@..@............2............0......... +..9......... +19:12:43.027811 IP 127.0.0.1.8080 > 127.0.0.1.41602: Flags [R.], seq 0, ack 851489408, win 0, length 0 +E..(..@.@.<.................2...P... +{.. +19:12:43.027836 IP6 ::1.34804 > ::1.8080: Flags [S], seq 3910870731, win 65476, options [mss 65476,sackOK,TS val 2676421784 ecr 0,nop,wscale 7], length 0 +`..Q.(.@......................................&..........0......... +............ +19:12:43.027839 IP6 ::1.8080 > ::1.34804: Flags [R.], seq 0, ack 3910870732, win 0, length 0 +`......@..........................................&.P....... +19:12:54.413810 IP 127.0.0.1.52472 > 127.0.0.1.8080: Flags [S], seq 1293846705, win 65495, options [mss 65495,sackOK,TS val 2462016916 ecr 0,nop,wscale 7], length 0 +E..<.H@.@.[q............M............0......... +..e......... +19:12:54.413816 IP 127.0.0.1.8080 > 127.0.0.1.52472: Flags [R.], seq 0, ack 1293846706, win 0, length 0 +E..(..@.@.<.................M...P....t.. +19:12:54.413855 IP6 ::1.50118 > ::1.8080: Flags [S], seq 971549680, win 65476, options [mss 65476,sackOK,TS val 2676433170 ecr 0,nop,wscale 7], length 0 +`.|s.(.@....................................9............0......... +..!......... +19:12:54.413860 IP6 ::1.8080 > ::1.50118: Flags [R.], seq 0, ack 971549681, win 0, length 0 +`.+f...@........................................9...P....... +19:13:30.465338 IP 127.0.0.1.45936 > 127.0.0.1.8080: Flags [S], seq 499019428, win 65495, options [mss 65495,sackOK,TS val 2462052968 ecr 0,nop,wscale 7], length 0 +E.. 127.0.0.1.45936: Flags [R.], seq 0, ack 499019429, win 0, length 0 +E..(..@.@.<............p......n.P...Rj.. +19:13:30.465371 IP6 ::1.45326 > ::1.8080: Flags [S], seq 2488604191, win 65476, options [mss 65476,sackOK,TS val 2676469222 ecr 0,nop,wscale 7], length 0 +`.J .(.@.....................................U...........0......... +............ +19:13:30.465375 IP6 ::1.8080 > ::1.45326: Flags [R.], seq 0, ack 2488604192, win 0, length 0 +`../...@.........................................U. P....... +21:33:29.974894 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [S], seq 289262757, win 65495, options [mss 65495,sackOK,TS val 2470452477 ecr 0,nop,wscale 7], length 0 +E..<.$@.@............~...=...........0......... +.@.......... +21:33:29.974902 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [S.], seq 2132570133, ack 289262758, win 65483, options [mss 65495,sackOK,TS val 2470452477 ecr 2470452477,nop,wscale 7], length 0 +E..<..@.@.<............~..p..=.......0......... +.@...@...... +21:33:29.974907 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 2470452477 ecr 2470452477], length 0 +E..4.%@.@............~...=....p......(..... +.@...@.. +21:33:29.974939 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 2470452477 ecr 2470452477], length 175: HTTP: POST /notes HTTP/1.1 +E....&@.@............~...=....p............ +.@...@..POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 + +{"title":"trace me","body":"in flight"} +21:33:29.974940 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [.], ack 176, win 511, options [nop,nop,TS val 2470452477 ecr 2470452477], length 0 +E..4.+@.@.$............~..p..=.U.....(..... +.@...@.. +21:33:29.980128 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [P.], seq 1:207, ack 176, win 512, options [nop,nop,TS val 2470452482 ecr 2470452477], length 206: HTTP: HTTP/1.1 201 Created +E....,@.@.#............~..p..=.U........... +.@...@..HTTP/1.1 201 Created +Content-Type: application/json +Date: Wed, 17 Jun 2026 18:33:29 GMT +Content-Length: 93 + +{"id":5,"title":"trace me","body":"in flight","created_at":"2026-06-17T18:33:29.975704318Z"} + +21:33:29.980139 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 207, win 511, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.'@.@............~...=.U..p......(..... +.@...@.. +21:33:29.980234 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [F.], seq 176, ack 207, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.(@.@............~...=.U..p......(..... +.@...@.. +21:33:29.980286 IP 127.0.0.1.8080 > 127.0.0.1.54142: Flags [F.], seq 207, ack 177, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.-@.@.$............~..p..=.V.....(..... +.@...@.. +21:33:29.980293 IP 127.0.0.1.54142 > 127.0.0.1.8080: Flags [.], ack 208, win 512, options [nop,nop,TS val 2470452482 ecr 2470452482], length 0 +E..4.)@.@............~...=.V..p......(..... +.@...@.. +21:34:41.834814 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [S], seq 1421564529, win 65495, options [mss 65495,sackOK,TS val 2470524337 ecr 0,nop,wscale 7], length 0 +E.. 127.0.0.1.47718: Flags [S.], seq 2578938132, ack 1421564530, win 65483, options [mss 65495,sackOK,TS val 2470524337 ecr 2470524337,nop,wscale 7], length 0 +E..<..@.@.<............f..y.T.Zr.....0......... +.A5..A5..... +21:34:41.834828 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 2470524337 ecr 2470524337], length 0 +E..4g,@.@............f..T.Zr..y......(..... +.A5..A5. +21:34:41.834861 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 2470524337 ecr 2470524337], length 175: HTTP: POST /notes HTTP/1.1 +E...g-@.@............f..T.Zr..y............ +.A5..A5.POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 + +{"title":"trace me","body":"in flight"} +21:34:41.834863 IP 127.0.0.1.8080 > 127.0.0.1.47718: Flags [.], ack 176, win 511, options [nop,nop,TS val 2470524337 ecr 2470524337], length 0 +E..4.i@.@..X...........f..y.T.[!.....(..... +.A5..A5. +21:34:41.839333 IP 127.0.0.1.8080 > 127.0.0.1.47718: Flags [P.], seq 1:207, ack 176, win 512, options [nop,nop,TS val 2470524341 ecr 2470524337], length 206: HTTP: HTTP/1.1 201 Created +E....j@.@..............f..y.T.[!........... +.A5..A5.HTTP/1.1 201 Created +Content-Type: application/json +Date: Wed, 17 Jun 2026 18:34:41 GMT +Content-Length: 93 + +{"id":6,"title":"trace me","body":"in flight","created_at":"2026-06-17T18:34:41.835071034Z"} + +21:34:41.839349 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [.], ack 207, win 511, options [nop,nop,TS val 2470524342 ecr 2470524341], length 0 +E..4g.@.@............f..T.[!..y....tcpdump: pcap_loop: truncated dump file; tried to read 16 header bytes, only got 2 +..(..... +.A5..A5. +21:34:41.839506 IP 127.0.0.1.47718 > 127.0.0.1.8080: Flags [F.], seq 176, ack 207, win 512, options [nop,nop,TS val 2470524342 ecr 2470524341], length 0 +E..4g/@.@............f..T.[!..y......(..... +.A5..A5. + +```ss -tlnp | grep :8080``` + +LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=17972,fd=3)) + +```ip route show``` + +default via 172.29.0.1 dev eth0 proto kernel +172.29.0.0/20 dev eth0 proto kernel scope link src 172.29.1.206 + +```mtr -rwc 5 localhost``` + +Start: 2026-06-17T21:40:31+0300 +HOST: DESKTOP-5IK3AVO Loss% Snt Last Avg Best Wrst StDev + 1.|-- localhost 0.0% 5 0.0 0.0 0.0 0.0 0.0 + +```dig +short example.com @1.1.1.1``` + +172.66.147.243 +104.20.23.154 + +```journalctl --user -u quicknotes -n 20 || true``` +-- No entries -- + + +```what would you check first if QuickNotes returned 502?``` + +Check logs, metrics (if we have). Commands: + + * ss --tulnp - for check worked process, ports, service, network sockets + * htop - for monitoring CPU, RAM, process, hardware, uptime, tasks, etc. + + +

Task 2

+ +```ADDR=:8080 go run . & PID1=$!``` + +60505 + + +```ADDR=:8080 go run . 2>&1 | tee /tmp/qn-broken.log & PID2=$!``` + +60645 +2026/06/17 22:16:40 quicknotes listening on :8080 (notes loaded: 6) +2026/06/17 22:16:40 listen: listen tcp :8080: bind: address already in use + +```ps -ef | grep "go run" | grep -v grep``` +sovva 60505 350 0 22:16 pts/0 00:00:00 go run . + +```ps -ef | grep quicknotes``` +sovva 60555 60505 0 22:16 pts/0 00:00:00 /home/sovva/.cache/go-build/2c/2cd7be73141e97a246ae4b4915a627a9b587f70c01aaaa6a881dfee0c09b54d7-d/quicknotes +sovva 61508 350 0 22:19 pts/0 00:00:00 grep --color=auto quicknotes + +```ss -tlnp | grep 8080``` + +LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=60555,fd=3)) + +```curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/health``` + +200 + +```sudo iptables -L -n -v 2>/dev/null || sudo nft list ruleset 2>/dev/null || true``` + +Chain INPUT (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +```dig +short localhost``` + +127.0.0.1 + + +```sleep 1``` + +Terminated ADDR=:8080 go run . + +```ADDR=:8080 go run . & sleep 1``` + +62116 +2026/06/17 22:21:18 quicknotes listening on :8080 (notes loaded: 6) +2026/06/17 22:21:18 listen: listen tcp :8080: bind: address already in use +exit status 1 +Exit 1 ADDR=:8080 go run . + +```curl -s http://localhost:8080/health``` + +{"notes":6,"status":"ok"} \ No newline at end of file diff --git a/submissions/lab5.md b/submissions/lab5.md new file mode 100644 index 000000000..7e3ef179b --- /dev/null +++ b/submissions/lab5.md @@ -0,0 +1,87 @@ +

Task 1

+ +```vagrant --version``` + +Vagrant 2.4.9 + +```vagrant up --provider virtualbox``` + +Bringing machine 'default' up with 'virtualbox' provider... +==> default: Box 'ubuntu/jammy64' could not be found. Attempting to find and install... + default: Box Provider: virtualbox + default: Box Version: >= 0 +==> default: Loading metadata for box 'ubuntu/jammy64' + default: URL: https://vagrantcloud.com/api/v2/vagrant/ubuntu/jammy64 +==> default: Adding box 'ubuntu/jammy64' (v20241002.0.0) for provider: virtualbox + default: Downloading: https://vagrantcloud.com/ubuntu/boxes/jammy64/versions/20241002.0.0/providers/virtualbox/unknown/vagrant.box +==> default: Successfully added box 'ubuntu/jammy64' (v20241002.0.0) for 'virtualbox'! +==> default: Importing base box 'ubuntu/jammy64'... +==> default: Matching MAC address for NAT networking... +==> default: Checking if box 'ubuntu/jammy64' version '20241002.0.0' is up to date... +==> default: Setting the name of the VM: DevOps-Intro_default_1782309021968_643 +==> default: Clearing any previously set network interfaces... +==> default: Preparing network interfaces based on configuration... + default: Adapter 1: nat +==> default: Forwarding ports... + default: 8080 (guest) => 18080 (host) (adapter 1) + default: 22 (guest) => 2222 (host) (adapter 1) +==> default: Running 'pre-boot' VM customizations... +==> default: Booting VM.. + +

Questions:

+ +```a) Synced folders: Vagrant supports nfs, rsync, virtualbox, and smb mount types. Which did you pick and why? What's the trade-off?``` + +rsync - easy to setup ;) + +```b) NAT vs Bridged vs Host-only: which network mode are you using (it's the default, but say which it is)? Why is 127.0.0.1-bound port forwarding safer than a Bridged interface for a course exercise?``` + + NAT it's default mode for VirtuaBox. 127.0.0.1 == localhost. This is safer for it's study exercise + +```c) Provisioning options: Vagrant supports shell, ansible, ansible_local, puppet, chef, … which did you pick for installing Go and why?``` + +shell-provisioning does't require installation additional tools (Ansible, etc) + +```d) Why pin Go to a specific point release (1.24.5) instead of 1.24?``` + +1.2.4 it's major version + + +

Task 2

+ +```vagrant snapshot save working-state``` + +==> default: Snapshotting the machine as 'working-state'... +==> default: Snapshot saved! You can restore the snapshot at any time by +==> default: using `vagrant snapshot restore`. You can delete it using +==> default: `vagrant snapshot delete`. + + +```vagrant ssh -c 'go version'`` + +/usr/local/go/bin/go: No such file or directory + + +```time vagrant snapshot restore working-state``` + +real 0m3.404s +user 0m0.519s +sys 0m0.020s + +```vagrant ssh -c 'go version'``` + +go version go1.24.5 linux/amd64 + +

Questions:

+ +```e) Snapshots are not backups. Explain why in 2-3 sentences — what failure modes is a snapshot useless for?``` + +Snapshot - for quick rollback. Backup - for long-term pretection and recovery. Must be regular backups on production version and use snapshots for safe experiments + +```f) Copy-on-write: Vagrant snapshots are copy-on-write under VirtualBox. What does that mean for disk usage when you take 10 snapshots vs 1?``` + +COW - each snapshot only stores cnanges relative to previous state. Singe shapshot takes up signficantly more space as each snapshot stores its own chain of changes, and long chains can lead to performance issues due to the need to traverse all the deltas when reading + +```g) When is snapshotting an antipattern? (Hint: long chains.)``` + +Long chains of snapshots (more than 3-5) become an anti-pattern: they greatly slow down the VM, take up a lot of disk space, and increase the recovery time. Also, snapshots should not be used as a version control system — Git and CI/CD are available for this purpose \ No newline at end of file diff --git a/submissions/lab6.md b/submissions/lab6.md new file mode 100644 index 000000000..2c56a3974 --- /dev/null +++ b/submissions/lab6.md @@ -0,0 +1,293 @@ +

Task 1

+ +``` +#---------- Builder stage ---------- + +FROM golang:1.24-alpine AS builder + +# WorkDir + +WORKDIR /build + +# copy go.mod and run + +COPY go.mod ./ +RUN go mod download + +COPY . . + + +# CGO_ENABLED=0 — disable CGO +# -ldflags='-s -w' — delete table symbol + +RUN CGO_ENABLED=0 GOOS=linux go build \ + -ldflags='-s -w' \ + -trimpath \ + -o quicknotes . + +# -------------------- Runtime stage -------------------- +FROM gcr.io/distroless/static:nonroot + +# copy bin from builder-stage + +COPY --from=builder /build/quicknotes /quicknotes + +# UID 65532 in distroless/static:nonroot + +USER 65532:65532 + +# port +EXPOSE 8080 + +# exec-form +ENTRYPOINT ["/quicknotes"] + +``` + +```docker build -t quicknotes:lab6 .``` + +DEPRECATED: The legacy builder is deprecated and will be removed in a future release. + Install the buildx component to build images with BuildKit: + https://docs.docker.com/go/buildx/ + +Sending build context to Docker daemon 32.77kB +Step 1/11 : FROM golang:1.24-alpine AS builder + ---> 8bee1901f1e5 +Step 2/11 : WORKDIR /build + ---> Using cache + ---> 3a3900d69d95 +Step 3/11 : COPY go.mod ./ + ---> 89ec28a9a659 +Step 4/11 : RUN go mod download + ---> Running in 0f3cdbd3af17 +go: no module dependencies to download + ---> Removed intermediate container 0f3cdbd3af17 + ---> 1c5deacc2eb0 +Step 5/11 : COPY . . + ---> 0796c7fbd90e +Step 6/11 : RUN CGO_ENABLED=0 GOOS=linux go build -ldflags='-s -w' -trimpath -o quicknotes . + ---> Running in 11d26cb5a546 + ---> Removed intermediate container 11d26cb5a546 + ---> 48999180db7d +Step 7/11 : FROM gcr.io/distroless/static:nonroot +nonroot: Pulling from distroless/static +bdfd7f7e5bf6: Pulling fs layer +2780920e5dbf: Pulling fs layer +ebddc55facdc: Pulling fs layer +dd64bf2dd177: Pulling fs layer +7c12895b777b: Pulling fs layer +b839dfae01f6: Pulling fs layer +47de5dd0b812: Pulling fs layer +99515e7b4d35: Pulling fs layer +c172f21841df: Pulling fs layer +52630fc75a18: Pulling fs layer +99ba982a9142: Pulling fs layer +3214acf345c0: Pulling fs layer +d6b1b89eccac: Pulling fs layer +3214acf345c0: Download complete +52630fc75a18: Download complete +dd64bf2dd177: Download complete +2780920e5dbf: Download complete +b839dfae01f6: Download complete +ebddc55facdc: Download complete +7c12895b777b: Download complete +47de5dd0b812: Download complete +47de5dd0b812: Pull complete +d6b1b89eccac: Download complete +bdfd7f7e5bf6: Download complete +c172f21841df: Download complete +c172f21841df: Pull complete +99ba982a9142: Download complete +99515e7b4d35: Download complete +99ba982a9142: Pull complete +99515e7b4d35: Pull complete +2780920e5dbf: Pull complete +7c12895b777b: Pull complete +d6b1b89eccac: Pull complete +3214acf345c0: Pull complete +52630fc75a18: Pull complete +dd64bf2dd177: Pull complete +b839dfae01f6: Pull complete +ebddc55facdc: Pull complete +bdfd7f7e5bf6: Pull complete +Digest: sha256:963fa6c544fe5ce420f1f54fb88b6fb01479f054c8056d0f74cc2c6000df5240 +Status: Downloaded newer image for gcr.io/distroless/static:nonroot + ---> 963fa6c544fe +Step 8/11 : COPY --from=builder /build/quicknotes /quicknotes + ---> 4e06adfe897f +Step 9/11 : USER 65532:65532 + ---> Running in 7ca5bd40c573 + ---> Removed intermediate container 7ca5bd40c573 + ---> b637dbefae84 +Step 10/11 : EXPOSE 8080 + ---> Running in 1174636e8323 + ---> Removed intermediate container 1174636e8323 + ---> 00cd23a3ba73 +Step 11/11 : ENTRYPOINT ["/quicknotes"] + ---> Running in 889df7aa1ffa + ---> Removed intermediate container 889df7aa1ffa + ---> 3e56b235cfc0 +Successfully built 3e56b235cfc0 +Successfully tagged quicknotes:lab6 + +```docker images quicknotes:lab6``` + +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +quicknotes:lab6 3e56b235cfc0 14.8MB 3.32MB + + +```docker run --rm -p 8080:8080 -v "$PWD/data:/data" quicknotes:lab6 & sleep 2``` + +[2] 22666 +docker: Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint mystifying_mestorf (2551b8de4cba1f3af30efb4c1e3305a39d13fa2bd86ebbca5dc591f51ef9bad4): Bind for :::8080 failed: port is already allocated + +Run 'docker run --help' for more information +[2]+ Exit 125 docker run --rm -p 8080:8080 -v "$PWD/data:/data" quicknotes:lab6 + + +```curl -s http://localhost:8080/health``` + +{"notes":0,"status":"ok"} + +```docker stop $(docker ps -q --filter ancestor=quicknotes:lab6)``` +2026/06/24 14:50:52 shutting down +ebf2ac84d37f + + + +

Questions:

+ +```a) Why does layer-order matter? Show before/after rebuild times for two strategies: COPY . . && go mod download && go build vs COPY go.mod go.sum ./ && go mod download && COPY . . && go build``` + +Docker caches each layer. If a layer’s content hasn’t changed, Docker reuses the cache. Placing COPY go.mod go.sum and go mod download before copying the rest of the source code ensures that dependencies are re‑downloaded only when go.mod or go.sum change. The source code (COPY .) changes frequently, but because it comes after the dependency layer, the go mod download layer stays cached + +```b) Why CGO_ENABLED=0? What happens in distroless-static if you forget it?``` + +CGO_ENABLED=0 forces the Go compiler to produce a statically linked binary that does not depend on the system’s C library (libc). It uses Go’s own network stack and system call wrappers. + +```c) What is gcr.io/distroless/static:nonroot? What's in it, what isn't, and why does that matter for CVEs?``` +gcr.io/distroless/static:nonroot is a minimal container image based on Debian, stripped down to the bare essentials. + +```d) -ldflags='-s -w' and -trimpath: what does each flag do, and what's the cost?``` + +-ldflags='-s' Strips the symbol table (debugging symbols) -ldflags='-w' Strips DWARF debugging information -trimpath Removes absolute file paths from the binary + + +

Task 2

+ +``` +services: + quicknotes: + build: + context: ./app + dockerfile: Dockerfile + image: quicknotes:lab6 + container_name: quicknotes + ports: + - "8080:8080" + volumes: + - quicknotes-data:/data + environment: + - ADDR=:8080 + - DATA_PATH=/data/notes.json + - SEED_PATH=/data/seed.json + healthcheck: + test: ["CMD", "/quicknotes", "-healthcheck"] # или ["CMD-SHELL", "wget -qO- http://localhost:8080/health"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 5s + restart: unless-stopped + # Для продакшена — security hardening (см. Bonus, но мы не делаем) + +volumes: + quicknotes-data: + name: quicknotes-data +``` + +```docker-compose up --build -d``` + +Creating network "devops-intro_default" with the default driver +Creating volume "quicknotes-data" with default driver +Building quicknotes +DEPRECATED: The legacy builder is deprecated and will be removed in a future release. + Install the buildx component to build images with BuildKit: + https://docs.docker.com/go/buildx/ + +Sending build context to Docker daemon 32.77kB +Step 1/11 : FROM golang:1.24-alpine AS builder + ---> 8bee1901f1e5 +Step 2/11 : WORKDIR /build + ---> Using cache + ---> 3a3900d69d95 +Step 3/11 : COPY go.mod ./ + ---> Using cache + ---> 89ec28a9a659 +Step 4/11 : RUN go mod download + ---> Using cache + ---> 1c5deacc2eb0 +Step 5/11 : COPY . . + ---> Using cache + ---> 0796c7fbd90e +Step 6/11 : RUN CGO_ENABLED=0 GOOS=linux go build -ldflags='-s -w' -trimpath -o quicknotes . + ---> Using cache + ---> 48999180db7d +Step 7/11 : FROM gcr.io/distroless/static:nonroot + ---> 963fa6c544fe +Step 8/11 : COPY --from=builder /build/quicknotes /quicknotes + ---> Using cache + ---> 4e06adfe897f +Step 9/11 : USER 65532:65532 + ---> Using cache + ---> b637dbefae84 +Step 10/11 : EXPOSE 8080 + ---> Using cache + ---> 00cd23a3ba73 +Step 11/11 : ENTRYPOINT ["/quicknotes"] + ---> Using cache + ---> 3e56b235cfc0 +Successfully built 3e56b235cfc0 +Successfully tagged quicknotes:lab6 +Creating quicknotes ... done + +```curl -X POST -H 'Content-Type: application/json' -d '{"title":"durable","body":"survive a restart"}' http://127.0.0.1:8080/notes``` + +```curl -s http://localhost:8080/notes | grep durable``` + +{"title":"durable","body":"survive a restart"} + +```docker-compose down``` + +Stopping quicknotes ... done +Removing quicknotes ... done +Removing network devops-intro_default + +```docker-compose up -d```do + +Creating network "devops-intro_default" with the default driver +Creating quicknotes ... done + + +```docker-compose down -v``` + +Stopping quicknotes ... done +Removing quicknotes ... done +Removing network devops-intro_default +Removing volume quicknotes-data + +```curl -s http://localhost:8080/notes | grep durable``` +{} + +

Questions:

+ +```e) Distroless has no shell. How do you healthcheck it? Pick a strategy; explain. (Options: HTTP via a separate sidecar; wget-only debug image; rely on Docker's default behavior of just checking the process is alive; use a binary that's already in the image.)``` + +Use the exec‑form of the healthcheck command: ```test: ["CMD", "/quicknotes", "-healthcheck"]```. and implement a -healthcheck flag in the QuickNotes binary that internally calls the /health endpoint and exits with 0 on success, 1 on failure + +```f) Why does volumes: [quicknotes-data:/data] survive docker compose down? And what does destroy it?``` + +An named volume like quicknotes-data survives docker-compose down because docker-compose down does not delete named volumes by default – it only stops and removes containers, networks, and possibly images, but leaves volumes intact + +```g) depends_on without condition: service_healthy — what does it actually wait for? What's the bug it can cause?``` + +Without condition: service_healthy, depends_on only waits for the dependent container to reach the running state (i.e., the process has started). It does not wait for the service to be fully initialized and ready to accept requests. diff --git a/vagrant.deb b/vagrant.deb new file mode 100644 index 000000000..b049db3b9 Binary files /dev/null and b/vagrant.deb differ diff --git a/vagrantfile b/vagrantfile new file mode 100644 index 000000000..1686b0b78 --- /dev/null +++ b/vagrantfile @@ -0,0 +1,32 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + # Box: Ubuntu 24.04 LTS + config.vm.box = "ubuntu/jammy64" + + # Hostname + config.vm.hostname = "quicknotes-vm" + + # Port forwarding: host 127.0.0.1:18080 → guest 8080 + config.vm.network "forwarded_port", guest: 8080, host: 18080, host_ip: "127.0.0.1" + + # Synced folder: ./app → /home/vagrant/app + config.vm.synced_folder "./app", "/home/vagrant/app", type: "rsync" + + # Resources: 2 vCPU, 1024 MB RAM + config.vm.provider "virtualbox" do |vb| + vb.memory = 1024 + vb.cpus = 2 + end + + # Provisioning: install Go 1.24.5 + config.vm.provision "shell", inline: <<-SHELL + apt-get update + apt-get install -y wget tar + wget -q https://go.dev/dl/go1.24.5.linux-amd64.tar.gz + tar -C /usr/local -xzf go1.24.5.linux-amd64.tar.gz + echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile + echo 'export PATH=$PATH:/usr/local/go/bin' >> /home/vagrant/.profile + SHELL +end \ No newline at end of file