ℹ️ This project is a part of GitOps workflow example using Flux2 which includes Kubernetes manifests for Redis and NGINX Ingress Controller as well as handles Continuous Delivery.
A simple example REST API Application using Node.js + Express.js + Redis running in Kubernetes.
In a nutshell the application is used as a proxy for fetching most starred GitHub repositories.
A simplified usage flow of the data fetching and Redis cache usage:
flowchart LR;
A[User] --> G["/"];
G --> B{Check Redis};
B -->|Found| C[Return data];
B -->|Not found| D[Fetch from GitHub API];
D --> E[Save to Redis];
E --> F[Return data];
The application serves the following endpoints:
| Method | Path | Description |
|---|---|---|
| GET | / | Returns 30 most starred GitHub repositories as JSON |
| GET | /readyz | Readiness status used by Kubernetes readiness probe |
| GET | /livez | Liveness status used by Kubernetes liveness probe |
- Optimized Dockerfile using multi-stage builds
- SHA256 digest pinned Docker images with automated update using Renovate
- Automated vulnerability scan of the Docker image and npm dependencies using Trivy
- YAML validation using yamllint
- JavaScript validation using ESLint
- Graceful shutdown and health checks (readiness and liveness) using Terminus
- Structured JSON logging using Pino
- Kubernetes configuration customization using Kustomize
- Network traffic flow control using Network Policies
- In order to keep the Docker image size optimal a multi-stage builds is used
- The npm dependencies (without
devDependencies) are installed in abuildstage - Only the nessecary things (
node_modulesand the application code) are copied from thebuildstate toreleasestage in order to have minimum amount of layers - Only the layers from the
releasestage are pushed when the Docker image is build
SHA256 digest pinning is used to achieve reliable and reproducable builds. Using digest as the image's primary identifier instead of using a tag makes sure that specific version of the image is used.
In order to receive Docker image and npm dependency updates Renovate is used to create a pull request when:
- Newer digest from chainguard/node is available on Docker Hub
MinororPatchupdate of a npm dependency is available
In order to regularly scan Docker image and npm dependencies for vulnerabilities a scheduled job is used to build the Docker image and scan it's content using Trivy.
In order to gracefully shutdown the node process it must:
- Receive Termination Signals e.g.
SIGTERMandSIGINT - Be able to handle the Signals
To achieve this:
- The Docker image
CMDinstructions uses exec form["node", "src/index.js"]which does not invoke a command shell/bin/sh -c, node, src/index.js - Instead it just replaces the command shell with the command to be executed
node src/index.js. This makes sure that thenodeprocess is spawned as PID 1. - Terminus is used to handle the
SIGTERMandSIGINTSignals
Kustomize configuration is based on Directory Structure Based Layout in order to be able to use multiple environments with different configuration.
📁 k8s
├── 📁 base
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── kustomization.yaml
│ ├── netpol-egress.yaml
│ ├── netpol-ingress.yaml
│ ├── pdb.yaml
│ └── service.yaml
└── 📁 staging
├── hpa-patch.yaml
├── kustomization.yaml
├── namespace.yaml
└── pdb-patch.yamlStart the redis container:
docker run -d --rm --name redis -p 6379:6379 redis:7-alpineStart the nodemon tool which automatically restarts the node application when file changes in the directory are detected:
npm run dev