---
# System prepended metadata

title: IDVOM TP 2025

---

# IDVOM TP 2025

## Changelog

### 2025-10-27

- Fixed some bugs in catalog and orders, added some README

### 2025-10-29

- Added some information about the probes

## Prereqs

The given files can be found here: https://github.com/fayak/IDVOM

- Docker
- Helm
- k3s

### Make sure your k3s is running

```
$ sudo k3s kubectl get node
NAME       STATUS   ROLES                  AGE   VERSION
titanium   Ready    control-plane,master   11d   v1.33.4+k3s1
```

note: You may have to restart k3s service on reboot of your laptop/vm

note: You may have to add `--disable-network-policy` to k3s CLI arguments (ie `curl -sfL https://get.k3s.io | sh -s - --disable-network-policy`)

## Submission
### Architecture

```
├── catalog
│   ├── app
│   │   ├── db.py
│   │   ├── main.py
│   │   ├── models.py
│   │   └── schemas.py
│   ├── Dockerfile
│   └── pyproject.toml
├── gateway
│   ├── Dockerfile
│   ├── gateway
│   ├── go.mod
│   ├── go.sum
│   └── main.go
├── orders
│   ├── app
│   │   ├── db.py
│   │   ├── main.py
│   │   ├── models.py
│   │   └── schemas.py
│   ├── Dockerfile
│   └── pyproject.toml
└── helm
    ├── ...
    └── templates
        ├── catalog
        │   └── ...
        ├── gateway
        │   └── ...
        ├── orders
        │   └── ...
        └── ...
```

### How to submit ?

The submission will be done by email, to `idvom@zarak.fr`.

The deadline is set to the 11th of January, 23h59.

## First step

Build all images and publish them on docker hub for them to be available in the future steps

Some prebuilt images exist on:
- `zarakprod/catalog`
- `zarakprod/gateway`
- `zarakprod/orders`

*These images were built for AMD64 architecture, and will not work on ARM. Sorry MAC users*

## Second step - The gateway

Create some manifests in the `helm/templates/gateway` directory to deploy the gateway
You'll have to:
- Write a `Deployment` in `deployment.yaml`
- Write a `Service` in `service.yaml` to expose the component internally (ClusterIP)
- Write a `ConfigMap` in `configmap.yaml` to put the configuration's env var to be used by the `Deployment` [if needed]
- Write a `Secret` in `secret.yaml` to put the configurations's secrets env var to be used by the `Deployment` [if needed]

Have a look at the gateway's README for more information on the env variables. You must create some admin user to test your application.

You may find this useful to help you decide on the labels to define : https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/

The Gateway must be exposed to the cluster on the port 8080 while the application listens on port 8000. Use the Service to map the ports.

The Gateway exposes a `/health` endpoint. Use it to define a LivenessProbe.

Confirm that everything works as expected:
```
$ sudo k3s kubectl get pod                  
NAME                      READY   STATUS    RESTARTS   AGE
gateway-5994884b4-dvgxg   1/1     Running   0          7m13s
```

Get the Service's IP+port and make sure it behaves as expected:
```
$ curl $gateway/health         
ok
$ curl $gateway/whoami -u zarak
Enter host password for user 'zarak': *******
zarak
```

_Note about the Secret: It is **not** a good practice to write a Secret this way, and to put the secret value directly in the code. However, doing it in a cleaner way requires to use an operator, and to have external dependencies, etc ... This is not the point of this exercise, so we'll stick with this approach for now_

## Third step - Install some dependency

The next 2 components of the application will require a database. We have 2 options available here:
- Install a database manually
- Use an operator to install and manage it for us

We'll go for the 2nd option in this exercise. The goal of the 3rd step is to install CNPG (**C**loud **N**ative **P**ost**g**reSQL) operator and to use it to create a database for us.

Have a look at https://github.com/cloudnative-pg/charts

To run `helm` commands, you'll have to specify the kubeconfig to use. If you're runing `k3s`, it's located in `/etc/rancher/k3s/k3s.yaml`. You can then use `helm --kubeconfig /etc/rancher/k3s/k3s.yaml`

_You may have a look at the default values with `helm show values cnpg/cloudnative-pg` or by looking at the chart's `values.yaml` from the sources._

Install the operator in the namespace `cnpg-system` using its helm chart.

_If you encounter an error `The CustomResourceDefinition "poolers.postgresql.cnpg.io" is invalid: metadata.annotations: Too long: may not be more than 262144 bytes`, have a look at [this issue](https://github.com/cloudnative-pg/charts/issues/325#issuecomment-2569739488)_

Confirm it's running by checking the operator's pod status.

I also very much recommend you to install the `CNPG` [plugin](https://cloudnative-pg.io/documentation/1.20/kubectl-plugin/) for `kubectl`, to run `kubectl cnpg` subcommands.

## Fourth step - Create a database

Using CNPG's CRD, create a working PostgreSQL database by writing the proper manifests in `helm/templates`. The CNPG operator must create a database in the cluster that will be used by the app.

You need 2 replicas for better availability.

Once done, running `kubectl cnpg status <database name>`
should yield something similar to:
```
Cluster Summary
Name                     <namespace>/<database name>
System ID:               7565628524122476572
PostgreSQL Image:        ghcr.io/cloudnative-pg/postgresql:18.0-system-trixie
Primary instance:        <database name>-1
Primary promotion time:  2025-10-26 20:22:44 +0000 UTC (36s)
Status:                  Cluster in healthy state 
Instances:               2
Ready instances:         2
Size:                    98M
Current Write LSN:       0/4000060 (Timeline: 1 - WAL File: 000000010000000000000004)

Continuous Backup not configured

Streaming Replication status
Replication Slots Enabled
Name  Sent LSN   Write LSN  Flush LSN  Replay LSN  Write Lag  Flush Lag  Replay Lag  State      Sync State  Sync Priority  Replication Slot
----  --------   ---------  ---------  ----------  ---------  ---------  ----------  -----      ----------  -------------  ----------------
<database name>-2  0/4000060  0/4000060  0/4000060  0/4000060   00:00:00   00:00:00   00:00:00    streaming  async       0              active

Instances status
Name  Current LSN  Replication role  Status  QoS         Manager Version  Node
----  -----------  ----------------  ------  ---         ---------------  ----
<database name>-1  0/4000060    Primary           OK      BestEffort  1.27.1           titanium
<database name>-2  0/4000060    Standby (async)   OK      BestEffort  1.27.1           titanium
```

This shows a cluster in a healthy state with 2 replicas.

## Fifth step - The Catalog service

Now is time to deploy the next service: the catalog.

Write its deployment manifests in `helm/templates/catalog`.

The manifests will be somewhat similar to the ones for the gateway, but you'll have to plug the DB in.

To test things, you'll likely want to step up from hitting the `Service` directly: add an `Ingress` to the deployment of the Gateway. This will allow you to test if the gateway is indeed well connected to the Catalog.

The Catalog exposes a `/health` endpoint. Use it to define a LivenessProbe.

You can assert it by running:
```
$ curl 127.1/api/catalog/products -u zarak:$secret --json "$(jo name=computer price_cents=100000)"
{"id":1,"name":"computer","price_cents":100000,"stock":0}
$ curl 127.1/api/catalog/products -u zarak:$secret
[{"id":1,"name":"computer","price_cents":100000,"stock":0}]
```

_note: this is also a good occasion to check that you can use a customly defined account_

## Sixth step - The Orders

Reaching this point, you should have an up-and-working database cluster, a Gateway and a Catalog.

You should have created a product already in your catalog, but now, it's time to deploy the last microservice, the Orders.

Similarly to the previous microservices, deploy the Orders in `helm/templates/orders` and make sure it works:

1. Create a product in your catalog and add some stock: `curl 127.1/api/catalog/products -u zarak:$secret --json "$(jo name=computer price_cents=100000)" && curl 127.1/api/catalog/products/1 -u zarak:$secret -XPUT --json "$(jo stock=10 price_cents=100000 name=computer)"`
2. Check your orders: `curl 127.1/api/orders/orders/me -u zarak:$secret`. It should be empty
3. Place an order: `curl 127.1/api/orders/orders -u zarak:$secret --json "$(jo items="$(jo -a "$(jo product_id=1 qty=1)")")"`
4. Check your orders: `curl 127.1/api/orders/orders/me -u zarak:$secret`. You should see the order placed above

## Seventh step - Helmify it all

You may have not used Helm variables or templating abilities in this project yet. It is now time to change this, and to convert the statically defined manifests into proper Helm templates.

Follow the [helm best practices and convention](https://helm.sh/docs/chart_best_practices/conventions/) and the [helm doc on charts tips and tricks](https://helm.sh/docs/howto/charts_tips_and_tricks/). It will take you few minutes of reading but will get you started and on good tracks

What should be put into the values, and what makes a good Helm Chart ?

Anything that looks like a tunable, a value that may change from a deployment (a release) to another is a good candidate for being into the values.

You can have a look at pre-existing good Helm Charts for suggestions, like `grafana/grafana`, but those are very complex and may take you too much time to analyze.

For this step, I'm only expecting at least these elements to be tunable via the values:
- the image used for the 3 services, the tag and imagePullPolicy
- the number of replicas
- whether or not I want the Ingress to be deployed
- the size for the storage of the database and its number of replicas
- Define some resources limits and requests for the CPU and Memory on each Deployment. Follow [this good practice as well](https://home.robusta.dev/blog/stop-using-cpu-limits)

## BONUS: Eight step - Going further

This step is not mandatory. These are elements to go further if you are interested.

- Define the labels of the pods via templates defined in `_helpers.tpl`
- Define some (anti)affinity on the Deployments
- Define it on the database as well
- Look into defining a SecurityContext
- Create an [HPA](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) and a [PDB](https://kubernetes.io/docs/tasks/run-application/configure-pdb/)
- Deploy a Prometheus Operator and add the possibility to deploy [ServiceMonitor](https://prometheus-operator.dev/docs/api-reference/api/#monitoring.coreos.com/v1.ServiceMonitor) to scrape you app's metrics