ℹ️ 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 abuild
stage - Only the nessecary things (
node_modules
and the application code) are copied from thebuild
state torelease
stage in order to have minimum amount of layers - Only the layers from the
release
stage 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
Minor
orPatch
update 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.
SIGTERM
andSIGINT
- Be able to handle the Signals
To achieve this:
- The Docker image
CMD
instructions 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 thenode
process is spawned as PID 1. - Terminus is used to handle the
SIGTERM
andSIGINT
Signals
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.yaml
Start the redis container:
docker run -d --rm --name redis -p 6379:6379 redis:7-alpine
Start the nodemon tool which automatically restarts the node application when file changes in the directory are detected:
npm run dev