diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..5bc8f90db --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Goal + + +## Changes + + +## Testing + + +--- + +### Checklist +- [ ] Clear, descriptive PR title +- [ ] Documentation/README updated (if needed) +- [ ] No secrets or large temporary files committed diff --git a/labs/lab11/app/Dockerfile b/labs/lab11/app/Dockerfile new file mode 100644 index 000000000..e73b624c4 --- /dev/null +++ b/labs/lab11/app/Dockerfile @@ -0,0 +1,4 @@ +FROM golang:1.22 +WORKDIR /app +COPY main.go . +RUN go build -o app main.go \ No newline at end of file diff --git a/labs/lab11/app/Dockerfile.traditional b/labs/lab11/app/Dockerfile.traditional new file mode 100644 index 000000000..ba24343c2 --- /dev/null +++ b/labs/lab11/app/Dockerfile.traditional @@ -0,0 +1,3 @@ +FROM scratch +COPY lab11-app-binary /app +ENTRYPOINT ["/app"] diff --git a/labs/lab11/app/default.nix b/labs/lab11/app/default.nix new file mode 100644 index 000000000..656806969 --- /dev/null +++ b/labs/lab11/app/default.nix @@ -0,0 +1,19 @@ +{ pkgs ? import { } }: + +pkgs.buildGoModule rec { + pname = "lab11-app"; + version = "0.1.0"; + + src = ./.; + + vendorHash = null; + + ldflags = [ "-s" "-w" ]; + + meta = with pkgs.lib; { + description = "Lab 11 Go sample (stdlib only)"; + license = licenses.mit; + # Matches $out/bin name from go.mod module path ("lab11/app" → "app"), not pname. + mainProgram = "app"; + }; +} diff --git a/labs/lab11/app/docker.nix b/labs/lab11/app/docker.nix new file mode 100644 index 000000000..76510ea82 --- /dev/null +++ b/labs/lab11/app/docker.nix @@ -0,0 +1,23 @@ +{ pkgs ? import { } }: + +let + lab11-app = import ./default.nix { inherit pkgs; }; +in + +pkgs.dockerTools.buildLayeredImage { + name = "lab11-app"; + tag = "latest"; + + contents = [ + lab11-app + ]; + + config = { + # Exact store path to the Go binary from the Task 1 derivation (reproducible). + Cmd = [ + (pkgs.lib.getExe lab11-app) + ]; + # Do not set created = "now"; it fixes image metadata to the build wall-clock time + # and breaks bitwise reproducibility of the artifact. + }; +} diff --git a/labs/lab11/app/go.mod b/labs/lab11/app/go.mod new file mode 100644 index 000000000..7516d0e85 --- /dev/null +++ b/labs/lab11/app/go.mod @@ -0,0 +1,3 @@ +module lab11/app + +go 1.22 diff --git a/labs/lab11/app/lab11-app-binary b/labs/lab11/app/lab11-app-binary new file mode 100755 index 000000000..59c129095 Binary files /dev/null and b/labs/lab11/app/lab11-app-binary differ diff --git a/labs/lab11/app/main.go b/labs/lab11/app/main.go new file mode 100644 index 000000000..c0ded1f3e --- /dev/null +++ b/labs/lab11/app/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Printf("Built with Nix at compile time\n") + fmt.Printf("Running at: %s\n", time.Now().Format(time.RFC3339)) +} \ No newline at end of file diff --git a/labs/lab11/app/result b/labs/lab11/app/result new file mode 120000 index 000000000..394f0d23c --- /dev/null +++ b/labs/lab11/app/result @@ -0,0 +1 @@ +/nix/store/i0wjfriarcn59rz32yll9yjx75qkr9y3-lab11-app.tar.gz \ No newline at end of file diff --git a/labs/submission11.md b/labs/submission11.md new file mode 100644 index 000000000..1df0d6d8c --- /dev/null +++ b/labs/submission11.md @@ -0,0 +1,121 @@ +# Solution + +## Task 1 + +Installation proof: + +```bash +(hw) lexandrinnn_t@63906:~/inno/devops/DevOps-Intro$ nix --version +nix (Determinate Nix 3.19.0) 2.34.6 +(hw) lexandrinnn_t@63906:~/inno/devops/DevOps-Intro$ nix run nixpkgs#hello +Hello, world! +``` + +Explained `default.nix`: + +```nix +# the file is a function whose default argument is “the Nixpkgs set” (all standard packages and builders). +{ pkgs ? import { } }: + +# Use Nix’s Go-module builder +pkgs.buildGoModule rec { + pname = "lab11-app"; + version = "0.1.0"; + + src = ./.; # Build from this directory + + vendorHash = null; # No third-party Go modules are vendored + + ldflags = [ "-s" "-w" ]; # Pass linker flags to Go: strip symbol table / debug info and DWARF debug info + + # Metadata Nix tools can show + meta = with pkgs.lib; { + description = "Lab 11 Go sample (stdlib only)"; + license = licenses.mit; + mainProgram = pname; + }; +} + +``` + +Paths (different): +```bash +(hw) lexandrinnn_t@63906:~/inno/devops/DevOps-Intro/labs/lab11/app$ readlink result +/nix/store/rkw3l70g3kn4sigsq2rk9k2wk5w361yn-lab11-app-0.1.0 +(hw) lexandrinnn_t@63906:~/inno/devops/DevOps-Intro/labs/lab11/app$ rm result +nix-build +readlink result +... +/nix/store/rkw3l70g3kn4sigsq2rk9k2wk5w361yn-lab11-app-0.1.0 +``` + +Hash of the binary: `b92df2b454189f31dd5263b4ca784078564e3365af725ab95a9d3ab3d4c433cd` + +**Docker** is not reproducible due to base image moves, cache/provenance metadata changes, and built artifacts can differ unlike a **fixed-input Nix derivation** where the store path is fully determined by hashed inputs. + +**Nix store path:** `/nix/store/-` + +## Task 2 + +Explained `docker.nix`: +```nix +# The file is a Nix function. The argument pkgs defaults to your nixpkgs package set (all builders and packages). +{ pkgs ? import { } }: + +# Build the same Go app as in Task 1 +let + lab11-app = import ./default.nix { inherit pkgs; }; +in + +# Uses Nix dockerTools to build an OCI Docker image without a Dockerfile +pkgs.dockerTools.buildLayeredImage { + # Sets the image reference + name = "lab11-app"; + tag = "latest"; + + # Lists which Nix packages are copied into the image + contents = [ + lab11-app + ]; + + config = { + # Exact store path to the Go binary from the Task 1 derivation (reproducible). + Cmd = [ + (pkgs.lib.getExe lab11-app) + ]; + }; +} +``` + +Sizes comparison: + +```bash +lab11-app:latest 34fc1efab212 11.6MB 4.61MB +traditional-app:latest 669e14e8bc4d 2.41MB 756kB +``` +Docker history for Nix-built image: +```bash +(hw) lexandrinnn_t@63906:~/inno/devops/DevOps-Intro/labs/lab11/app$ docker history lab11-app:latest +IMAGE CREATED CREATED BY SIZE COMMENT +34fc1efab212 N/A 12.3kB store paths: ['/nix/store/jmssxczi7vzsj0b9rzfr3h0yzqj94l9a-lab11-app-customisation-layer'] + N/A 1.68MB store paths: ['/nix/store/ibjwr62mimglzrk55z3qbqfzai1hxfin-lab11-app-0.1.0'] + N/A 5.33MB store paths: ['/nix/store/cxjmhdbpy3bk12jc6lwpmcvlas76a7zm-tzdata-2026a'] +``` + +Docker history for traditional-app: +```bash +IMAGE CREATED CREATED BY SIZE COMMENT +669e14e8bc4d 4 minutes ago ENTRYPOINT ["/app"] 0B buildkit.dockerfile.v0 + 4 minutes ago COPY lab11-app-binary /app # buildkit 1.66MB buildkit.dockerfile.v0 +``` + +So in this lab, the scratch + copied static binary image is smaller, not the Nix one. The history explains why. + +**Layer structure** +- Nix: tzdata (~5.33 MB in history) → app (~1.68 MB) → customisation (image config/metadata, ~12 kB). +- Traditional: Single COPY of lab11-app-binary (~1.66 MB), ENTRYPOINT, plus BuildKit’s zero-size instruction rows. + +Nix-built images are more reproducible mainly because: +- Inputs are fixed +- Stable metadata +- Layers = content-addressed paths, so layers correspond to immutable, named-by-hash artifacts \ No newline at end of file