Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33ebb4af89 | |||
| 8e99fe7614 | |||
| bd723dbd05 | |||
| 55d7345854 | |||
| 87ce0ec6ee | |||
| 53da6fe547 | |||
| 6adc52830f | |||
| e6176999c1 | |||
| ec03bcf778 |
Vendored
+34
@@ -121,6 +121,40 @@ jobs:
|
||||
mv fleetdm-stack-*.tgz .tmp/
|
||||
ls -la .tmp/
|
||||
|
||||
- name: Mirror FleetDM image to Gitea registry
|
||||
run: |
|
||||
CRANE_VER="v0.20.3"
|
||||
curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VER}/go-containerregistry_Linux_x86_64.tar.gz" \
|
||||
| tar -xz -C /usr/local/bin crane
|
||||
|
||||
APP_VER=$(grep '^appVersion:' fleetdm-stack/Chart.yaml | awk '{print $2}' | tr -d '"')
|
||||
CHART_TAG="${{ steps.version.outputs.new_tag }}"
|
||||
SRC="docker.io/fleetdm/fleet:v${APP_VER}"
|
||||
OWNER=$(echo "${{ gitea.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
DST="git.produktor.io/${OWNER}/flamingo-tech-test"
|
||||
|
||||
crane auth login git.produktor.io -u "${{ gitea.actor }}" -p "${{ secrets.REPO_TOKEN }}"
|
||||
crane copy "${SRC}" "${DST}:${APP_VER}"
|
||||
crane tag "${DST}:${APP_VER}" "${CHART_TAG}"
|
||||
crane tag "${DST}:${APP_VER}" "latest"
|
||||
echo "Mirrored ${SRC} → ${DST}:{${APP_VER},${CHART_TAG},latest}"
|
||||
|
||||
- name: Trivy vulnerability scan
|
||||
run: |
|
||||
TRIVY_VER="0.58.2"
|
||||
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VER}/trivy_${TRIVY_VER}_Linux-64bit.tar.gz" \
|
||||
| tar -xz -C /usr/local/bin trivy
|
||||
|
||||
APP_VER=$(grep '^appVersion:' fleetdm-stack/Chart.yaml | awk '{print $2}' | tr -d '"')
|
||||
OWNER=$(echo "${{ gitea.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
IMAGE="git.produktor.io/${OWNER}/flamingo-tech-test:${APP_VER}"
|
||||
|
||||
export TRIVY_USERNAME="${{ gitea.actor }}"
|
||||
export TRIVY_PASSWORD="${{ secrets.REPO_TOKEN }}"
|
||||
|
||||
echo "Scanning ${IMAGE} for vulnerabilities..."
|
||||
trivy image --severity HIGH,CRITICAL --exit-code 0 "${IMAGE}"
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git config user.name "Gitea Actions"
|
||||
|
||||
Vendored
+1
@@ -5,3 +5,4 @@ fleetdm-stack/charts/*.tgz
|
||||
.helm/
|
||||
*.log
|
||||
/*-flamingo
|
||||
.idea
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# FleetDM Stack — Flamingo DevOps Assignment
|
||||
# 🦩 FleetDM Stack
|
||||
|
||||
Helm chart deploying **FleetDM Server** with **MySQL** and **Redis** to Kubernetes. Suitable for local development (Kind/Minikube) and adaptable for production.
|
||||
|
||||
@@ -67,7 +67,7 @@ Fleet setup wizard will guide you through initial configuration.
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
## Teardown
|
||||
|
||||
@@ -144,6 +144,25 @@ For production, configure proper TLS and a stable DNS name for agents.
|
||||
1. **Basic CI pipeline** — Gitea Actions lint on push, release on tag (see [.github/workflows/release.yaml](.github/workflows/release.yaml))
|
||||
2. **Exposed Fleet UI** — `make port-forward` on port 8585 (+ ingress `fleet.localhost`)
|
||||
3. **`fleet prepare db`** — Handled by `autoApplySQLMigrations: true` in the Fleet Helm chart
|
||||
4. **Container image mirroring** — CI mirrors the upstream `fleetdm/fleet` image to the [Gitea OCI registry](https://git.produktor.io/eSlider/-/packages) using `crane` (daemonless)
|
||||
5. **Trivy vulnerability scan** — Each release scans the mirrored image for HIGH/CRITICAL CVEs
|
||||
|
||||
## Container Registry
|
||||
|
||||
The CI pipeline mirrors the FleetDM image to the Gitea container registry on every release.
|
||||
|
||||
```bash
|
||||
# Pull the latest mirrored image
|
||||
docker pull git.produktor.io/eslider/flamingo-tech-test:latest
|
||||
|
||||
# Pull a specific app version
|
||||
docker pull git.produktor.io/eslider/flamingo-tech-test:4.80.1
|
||||
|
||||
# Pull by chart release tag
|
||||
docker pull git.produktor.io/eslider/flamingo-tech-test:v0.0.10
|
||||
```
|
||||
|
||||
Browse all available tags at [git.produktor.io/eSlider/-/packages/container/flamingo-tech-test/latest](https://git.produktor.io/eSlider/-/packages/container/flamingo-tech-test/latest).
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
||||
@@ -85,4 +85,4 @@ Company Inc. is a small startup developing a web application and planning to dep
|
||||
|
||||
We value your time and want to gain a clear understanding of how you approach DevOps tasks in a clean, thoughtful, and structured manner.
|
||||
|
||||
**Good luck, and happy deploying\! 🦩**
|
||||
**Good luck, and happy deploying! 🦩**
|
||||
|
||||
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
@@ -125,13 +125,55 @@ flowchart LR
|
||||
|
||||
**Cost impact:** Near-zero — both slots share the same node pool; the idle slot consumes minimal resources until traffic is switched. Argo Rollouts automates the full lifecycle within ArgoCD.
|
||||
|
||||
### 4.5 Containerisation and CI/CD
|
||||
### 4.5 Containerisation Strategy
|
||||
|
||||
#### Image Building Process
|
||||
|
||||
Each service (Flask backend, React frontend) has its own **multi-stage Dockerfile**:
|
||||
|
||||
1. **Build stage** — installs dependencies and compiles artefacts in a full SDK image (e.g. `python:3.12`, `node:20`).
|
||||
2. **Runtime stage** — copies only the built artefacts into a minimal base image (e.g. `python:3.12-slim`, `nginx:alpine`). This cuts image size by 60–80% and removes build tools from the attack surface.
|
||||
3. **Non-root user** — the runtime stage runs as a dedicated unprivileged user (`appuser`), never as root.
|
||||
4. **Reproducible builds** — dependency lock files (`requirements.txt` / `package-lock.json`) are copied and installed before application code to maximise Docker layer caching.
|
||||
|
||||
**Tagging convention:** images are tagged with the **git SHA** for traceability and a `latest` alias for convenience. Semantic version tags (e.g. `v1.3.0`) are added on release.
|
||||
|
||||
#### Container Registry Management
|
||||
|
||||
All container images are stored in **GCP Artifact Registry** in the `company-inc-shared` project:
|
||||
|
||||
- **Single source of truth** — one registry serves both staging and production via cross-project IAM pull permissions.
|
||||
- **Vulnerability scanning** — Artifact Registry's built-in scanning is enabled; CI fails if critical CVEs are detected.
|
||||
- **Image retention policy** — keep the latest 10 tagged images per service; automatically garbage-collect untagged manifests older than 30 days.
|
||||
- **Access control** — CI service account has `roles/artifactregistry.writer`; GKE node service accounts have `roles/artifactregistry.reader`. No human push access.
|
||||
|
||||
*For self-hosted Git platforms (e.g. Gitea), the built-in OCI container registry can serve the same role at zero additional cost. In the practical part of this project, this is demonstrated: the CI pipeline mirrors the upstream FleetDM image to the Gitea OCI registry using `crane` (a daemonless image tool), then scans it with **Trivy** for HIGH/CRITICAL CVEs before publishing the release.*
|
||||
|
||||
#### Deployment Pipelines (CI/CD Integration)
|
||||
|
||||
The pipeline follows a **GitOps** model with clear separation between CI and CD:
|
||||
|
||||
| Phase | Tool | What happens |
|
||||
|-------|------|-------------|
|
||||
| **Lint & Test** | Gitea / GitHub Actions | Unit tests, linting, Helm lint on every push |
|
||||
| **Build & Push** | Gitea / GitHub Actions | `docker build` → tag with git SHA → push to registry |
|
||||
| **Security Scan** | Trivy (in CI) | Scan image for OS and library CVEs; block on critical findings |
|
||||
| **Manifest Update** | CI job | Update image tag in the GitOps manifests repo (or Helm values) |
|
||||
| **Sync & Deploy** | ArgoCD | Detects manifest drift → triggers blue-green rollout via Argo Rollouts |
|
||||
| **Promotion** | Argo Rollouts | Automated analysis (metrics, health checks) → promote or rollback |
|
||||
|
||||
**Key properties:**
|
||||
- **CI never touches the cluster directly** — it only builds images and updates manifests. ArgoCD is the sole deployer.
|
||||
- **Rollback is instant** — revert the manifest repo to the previous commit; ArgoCD syncs automatically.
|
||||
- **Audit trail** — every deployment maps to a git commit in the manifests repo.
|
||||
|
||||
### 4.6 CI/CD Summary
|
||||
|
||||
| Aspect | Approach |
|
||||
|-------|----------|
|
||||
| **Image build** | Dockerfile per service; multi-stage builds; non-root user |
|
||||
| **Registry** | Artifact Registry in `company-inc-shared` |
|
||||
| **CI** | GitHub/Gitea Actions — build, test, security scan |
|
||||
| **Image build** | Multi-stage Dockerfile; layer caching; non-root; git-SHA tags |
|
||||
| **Registry** | Artifact Registry in `company-inc-shared` (or Gitea built-in OCI registry) |
|
||||
| **CI** | Gitea / GitHub Actions — lint, test, build, scan, push |
|
||||
| **CD** | ArgoCD + Argo Rollouts — GitOps with blue-green strategy |
|
||||
| **Secrets** | External Secrets Operator + GCP Secret Manager |
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ flowchart TB
|
||||
Blue[Blue — new release<br/>smoke tests]
|
||||
end
|
||||
subgraph Workloads
|
||||
API[Backend — Python / Flask<br/>HPA · 2–3 replicas]
|
||||
API[Backend — Python / Flask<br/>HPA · 2-3 replicas]
|
||||
SPA[Frontend — React SPA<br/>Nginx]
|
||||
end
|
||||
Redis[Redis — Memorystore<br/>Session / Cache]
|
||||
@@ -94,10 +94,13 @@ flowchart LR
|
||||
flowchart LR
|
||||
Dev[Developer] -->|push| Repo[Git Repo]
|
||||
Repo -->|webhook| CI[CI Pipeline<br/>lint · test · build]
|
||||
CI -->|push image| Registry[Artifact Registry]
|
||||
CI -->|crane copy / docker push| Registry[Container Registry<br/>Artifact Registry / Gitea OCI]
|
||||
Registry -->|scan image| Trivy[Trivy<br/>HIGH + CRITICAL CVEs]
|
||||
Trivy -->|pass| CI2[Publish Release]
|
||||
CI -->|update manifests| GitOps[GitOps Repo]
|
||||
GitOps -->|sync| Argo[ArgoCD]
|
||||
Argo -->|blue-green deploy| GKE[GKE Cluster]
|
||||
GKE -->|pull image| Registry
|
||||
```
|
||||
|
||||
## Network Security Layers
|
||||
|
||||
Reference in New Issue
Block a user