🚀 Deploying The Backend
We'll deploy the app piece by piece, and at first we'll deploy & configure things in a sub-optimal way. This is in order to explore the Kubernetes concepts and show their purpose. Then we'll iterate and improve towards the final architecture.
We have four "microservices" we need to deploy, and due to dependencies between them we'll start with the PostgreSQL database then the API and then move onto the frontend.
From here we will be creating and editing files, so it's worth creating a project folder locally (or even a git repo) in order to work from if you haven't done so already.
🍃 Deploying PostgreSQL
We'll apply configurations to Kubernetes using kubectl
and YAML manifest files, and we'll be doing this a lot
throughout the workshop. These files will describe the objects we want to create, modify and delete in the cluster.
If you want to take this workshop slowly and treat it as more of a hack, you can research and build the required YAML yourself, you can use Kubernetes docs (link below) and the following hints:
📚 Kubernetes Docs: Deployments
- Deployment should be used with a single replica.
- The image to be run is
__ACR_NAME__.azurecr.io/nanomon-postgres:latest
. Where__ACR_NAME__
should be replaced with the name of your ACR resource. - The port 5432 should be exposed from the container.
- Do not worry about persistence or using a Service at this point.
- Set
POSTGRES_DB
andPOSTGRES_USER
environmental vars to the container setting both to the value "nanomon" - Set
POSTGRES_PASSWORD
environmental var, with the value "notVerySecret123!". Yes a plain text password, we will fix this later!
Why are we not using the official PostgreSQL image? Well really we should, but initializing database schema would require concepts like config maps & volume mounts which we're not ready for yet. The nanomon-postgres image has been specifically built with the database initialization baked in, so we can keep things simple for now.
On placeholders in the YAML, throughout this workshop you will see placeholders in the YAML files, these in double underscores e.g.
__SOMETHING__
these always need to be replaced with real values. This double underscore convention was chosen to avoid confusion with other syntax and does not upset any YAML parsers or linters.
Alternatively you can use the YAML below to paste into postgres-deployment.yaml
don't worry this isn't cheating, in
the real world everyone is too busy to write Kubernetes manifests from scratch 😉
Click here for the PostgreSQL deployment YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: __ACR_NAME__.azurecr.io/nanomon-postgres:latest
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: "nanomon"
- name: POSTGRES_USER
value: "nanomon"
- name: POSTGRES_PASSWORD
value: "notVerySecret123!"
Then apply the manifest with:
kubectl apply -f postgres-deployment.yaml
If successful you will see deployment.apps/postgres created
, this will have created one Deployment and one Pod.
You can check the status of your cluster with a few commands:
kubectl get deployment
- List the deployments, you should see 1/1 in ready status.kubectl get pod
- List the pods, you should see one prefixedpostgres-
with a status of Running.kubectl describe deploy postgres
- Examine and get details of the deployment.kubectl describe pod {podname}
- Examine the pod, you will need to get the name from theget pod
command.kubectl get all
- List everything; all pods, deployments etc.
Get used to these commands you will use them a LOT when working with Kubernetes.
For the next part we'll need the IP address of the pod that was just deployed, you can get this by running
kubectl get pod -o wide
or the command below:
kubectl describe pod --selector app=postgres | grep ^IP:
🗃️ Deploying The API
Next we'll deploy the first custom part of our app, the backend API, and like the DB we'll deploy it from an image hosted in our private registry.
- The image needs to be
__ACR_NAME__.azurecr.io/nanomon-api:latest
where__ACR_NAME__
should be replaced in the YAML with your real value, i.e. the name of your ACR resource. - Set the number of replicas to 2.
- The port exposed from the container should be 8000.
- An environmental variable called
POSTGRES_DSN
should be passed to the container, with the connection string to connect to the database, which will behost=__POSTGRES_POD_IP__ port=5432 user=nanomon dbname=nanomon sslmode=disable
where__POSTGRES_POD_IP__
should be replaced in the YAML with the pod IP address you just queried. - A second environmental variable called
POSTGRES_PASSWORD
should be passed to the container, with the valuenotVerySecret123!
. - Label the pods with
app: nanomon-api
.
Again you can try building the Deployment yourself or use the provided YAML to create a api-deployment.yaml
file
Click here for the API deployment YAML
kind: Deployment
apiVersion: apps/v1
metadata:
name: nanomon-api
spec:
replicas: 2
selector:
matchLabels:
app: nanomon-api
template:
metadata:
labels:
app: nanomon-api
spec:
containers:
- name: api-container
image: __ACR_NAME__.azurecr.io/nanomon-api:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
env:
- name: POSTGRES_DSN
value: "host=__POSTGRES_POD_IP__ port=5432 user=nanomon dbname=nanomon sslmode=disable"
- name: POSTGRES_PASSWORD
value: "notVerySecret123!"
💥 Notice: We have the password in plain text within the connection string! This clearly is a very bad practice, we will fix this at a later stage when we introduce Kubernetes Secrets.
Make the changes described above, remember to make the edits, you can not use this YAML as is!, and then run:
kubectl apply -f api-deployment.yaml
Check the status as before with kubectl
and it's worth checking the logs with kubectl logs {podname}
to see the
output from the app as it starts up.
This time we've set the number of replicas to two, if you run kubectl get pods -o wide
you will see which Nodes the
Pods have been scheduled (assigned) to. You might see each Pod has been scheduled to different Nodes, but this is
not guaranteed. Pod scheduling and placement is a fairly complex topic, for now we can move on.
It's also worth mention the Pod names, they are prefixed with the name of the Deployment, followed by a hash, folowed by a random string. Pod names are nearly always auto-generated like this, and are not something you should rely on or try to set yourself.
⏩ Accessing the API (The quick & dirty way)
Now it would be nice to access and call this API, to check it's working. But the IP address of the Pods are private and only accessible from within the cluster. In the next section we'll fix that, but for now there's a short-cut we can use.
Kubernetes provides a useful way to "tunnel" network traffic into the cluster through the control plane, this is done
with the kubectl port-forward
command.
Pick the name of either one of the two api
Pods, and run:
kubectl port-forward {pod_name} 8000:8000
And then accessing the following URL http://localhost:8000/api/info either in your
browser or with curl
we should see a JSON response with some status and debug information from the API.
curl -s http://localhost:8000/api/status | json_pp
Clearly this isn't a good way to expose your apps long term, but can be extremely useful when debugging and triaging issues.
When done, cancel & close the port-forwarding with ctrl-c
🖼️ Cluster & Architecture Diagram
The resources deployed into the cluster & in Azure at this stage can be visualized as follows
Yes, it's looking a little empty at this stage, don't worry it'll become a lot more complex soon!