Define the Environment
bunnyshell.yaml
- The Environment Definition
As mentioned before, the Bunnyshell Books repository contains:
- a Helm Chart to deploy the backend, located in
helm/backend
- a plain Kubernetes manifest to deploy the frontend, located in
manifests/frontend
- no means to deploy the database
We will now create the bunnyshell.yaml
which will put everything together. Let's take it one component at a time, and in the end, we'll look at the complete definition.
For the purpose of this guide, it is important you go through components in order, as the explanations build on what was previously pointed out.
The Environment Variables
There are a few variables which are needed to link Components together, so they need to be injected in more than one Component. We will define them as Environment Variables, so they will be available in all Components.
environmentVariables:
BACKEND_URL: 'https://api-{{ env.base_domain }}'
FRONTEND_URL: 'https://frontend-{{ env.base_domain }}'
POSTGRES_DB: bunny_books
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_USER: postgres
The database / Helm
The database is a Postgres, so we will use the Bitnami Chart to deploy it.
Because this chart does not expose the number of replicas for the master as a Chart value, we will also need
kubectl
to scale the replicas down to 0 when stopping, and back to 1 when starting.Therefore, we will use the image
dtzar/helm-kubectl
for the runner.
Deploy
The purpose is to deploy
this component, and by using a Helm Chart, we ultimately need to perform an install. As usual, helm upgrade --install
is the preferred way to achieve this.
We will end up running:
helm upgrade --install --namespace {{ env.k8s.namespace }} \
--post-renderer /bns/helpers/helm/add_labels/kustomize \
-f my_values.yaml \
postgres bitnami/postgresql --version 11.9.11
So, let's brake down the install command:
Namespace
We will install the Helm Chart into the namespace which is created by default by Bunnyshell for the current Environment.
You can install Charts into other namespaces as well, but you will need to handle the namespaces yourself.
Example: my-custom-ns-{{ env.unique }}
would produce an unique namespace, making it suitable to be deployed in any number of Environments, without causing conflicts.
Post-renderer
You can notice a post-renderer being involved, meaning /bns/helpers/helm/add_labels/kustomize
. Its role is to add a set of labels on the Kubernetes resources, so Bunnyshell can know which Component created which resources.
If you remove the
--post-renderer
from thehelm upgrade --install
command, Bunnyshell will not be able to display the created Kubernetes resources, and therefore, you will not be able to see the container logs & events in Bunnyshell.
Custom values file
Before running the Chart install, we need to generate the custom values.yaml
, named my_values.yaml
by us, but it can be an arbitrary name, as long as it's referenced into the install/upgrade command as well.
Complete Component definition
The rest of the arguments for helm upgrade --install
are standard, specifying the release name, chart to be used and version, so we're not going to dive deeper here.
The complete Component definition is listed below:
components:
...
- kind: Helm
name: postgres
runnerImage: 'dtzar/helm-kubectl:3.8.2'
deploy:
- |
cat << EOF > my_values.yaml
global:
storageClass: bns-network-sc
auth:
postgresPassword: {{ env.vars.POSTGRES_PASSWORD }}
database: {{ env.vars.POSTGRES_DB }}
EOF
- 'helm repo add bitnami https://charts.bitnami.com/bitnami'
- 'helm upgrade --install --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize -f my_values.yaml postgres bitnami/postgresql --version 11.9.11'
- |
POSTGRES_HOST="postgres-postgresql.{{ env.k8s.namespace }}.svc.cluster.local"
destroy:
- 'helm uninstall postgres --namespace {{ env.k8s.namespace }}'
start:
- 'kubectl scale --replicas=1 --namespace {{ env.k8s.namespace }} statefulset/postgres-postgresql-11.9.11'
stop:
- 'kubectl scale --replicas=0 --namespace {{ env.k8s.namespace }} statefulset/postgres-postgresql-11.9.11'
exportVariables:
- POSTGRES_HOST
Start/stop
In this case, the consequence of starting/stopping the database is to create/eliminate the Pods associated with its StatefulSet. As the number of master replicas is not configurable (which is understandable), we will use kubectl
to perform the change.
Destroy
A simple helm uninstall
is performed.
Exporting variables for other Components
A thing worth mentioning here is the mechanism through which the Postgres host is exported from within the database Component, and will be used in the backend Component.
Sure, it's a deterministic value, so there was no real need to have it exported, we could have just constructed it in the backend - but we decided to do so in order to present you this mechanism also.
Notice the exportVariables
property of the Component: it will capture the environment variables with the names specified as list, in this case only POSTGRES_HOST
. To make this work, POSTGRES_HOST
is defined during the deploy
step. This value will be captured and used in subsequent workflows (start/stop/destroy).
The Components are ran according to the
dependsOn
attribute when deploying or starting the Environment, and in parallel when stopping or destroying.
You can find out more in the dedicated Environment Workflows section.
The backend / Helm
Build
The backend also needs an image to be built, so we will use a DockerImage
Component as well.
The properties - we hope - are pretty self-explanatory, but if you have any doubts or concerns, do not hesitate to check out the DockerImage Component page.
Deploy
The deploy
is very similar to the Database deploy, but that is somewhat expected, since they're both Helm Charts.
Complete Component definition
What was added here are the git*
attributes, specifying the git repository, branch and path where the scripts will be ran. Basically, the repo will be cloned and mounted within your runner container, so you can leverage files from it, such as the Helm Chart and other possible scripts.
components:
...
- kind: DockerImage
name: backend-image
context: /backend
dockerfile: Dockerfile
target: prod
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /backend
- kind: Helm
name: api
runnerImage: 'dtzar/helm-kubectl:3.8.2'
deploy:
- |
cat << EOF > my_values.yaml
serviceImage: {{ components.backend-image.image }}
replicas: 1
ingress:
className: bns-nginx
host: api-{{ env.base_domain }}
postgres:
host: '{{ components.postgres.exported.POSTGRES_HOST }}'
db: '{{ env.vars.POSTGRES_DB }}'
user: '{{ env.vars.POSTGRES_USER }}'
password: '{{ env.vars.POSTGRES_PASSWORD }}'
frontendUrl: '{{ env.vars.FRONTEND_URL }}'
EOF
- 'helm upgrade --install --namespace {{ env.k8s.namespace }} --dependency-update --post-renderer /bns/helpers/helm/add_labels/kustomize -f my_values.yaml api-{{ env.unique }} ./helm/backend'
destroy:
- 'helm uninstall api-{{ env.unique }} --namespace {{ env.k8s.namespace }}'
start:
- 'helm upgrade --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize --reuse-values --set replicas=1 api-{{ env.unique }} ./helm/backend'
stop:
- 'helm upgrade --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize --reuse-values --set replicas=0 api-{{ env.unique }} ./helm/backend'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /helm/backend
Start/stop
As the number of replicas is exposed in this case in the values.yaml
file, we can use a helm upgrade ... --reuse-values --set replicas=1
to start, and a similar, with "0" command to stop the Component.
Destroy
Similarly, a simple helm uninstall
is performed.
The frontend / KubernetesManifest
For the sake of completeness, we also wanted to demonstrate a Component deployed with plain Kubernetes manifests.
Build
It also has a build, represented through a DockerImage
Component.
Deploy
Similarly, the deploy
consists of scripts to be ran, except that this time there's more "manual" work involved, since we need to adapt a fixed string to our dynamic nature for Environments.
To keep it simple, we decided to perform some rudimentary replacements with sed
, just to illustrate the principle behind. It would be wise for you to use something that also can assert the number of replacements made.
Complete Component definition
components:
...
- kind: DockerImage
name: frontend-image
context: /frontend
dockerfile: Dockerfile
target: prod
args:
REACT_APP_BASE_API: '{{ env.vars.BACKEND_URL }}'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /frontend
- kind: KubernetesManifest
name: frontend
runnerImage: 'alpine/k8s:1.22.15'
deploy:
- 'cd manifests/frontend'
- 'kustomize create --autodetect --recursive --labels=app.kubernetes.io/instance-frontend:bns,app.kubernetes.io/part-of:env-{{ env.unique }} --namespace {{ env.k8s.namespace }}'
- 'kustomize edit set image needsimage={{ components.frontend-image.image }}'
- 'sed -i "s/frontend.example.com/frontend-{{ env.base_domain }}/g" ingress.yaml'
- 'sed -i "s/ingressClassName: nginx/ingressClassName: bns-nginx/g" ingress.yaml'
- 'kubectl apply -k .'
destroy:
- 'cd manifests/frontend'
- 'kustomize create --autodetect --recursive --namespace {{ env.k8s.namespace }}'
- 'kubectl delete -k .'
start:
- 'kubectl scale --replicas=1 --namespace {{ env.k8s.namespace }} deployment/frontend'
stop:
- 'kubectl scale --replicas=0 --namespace {{ env.k8s.namespace }} deployment/frontend'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /manifests/frontend
Start/stop
Similarly to the database, start/stop leverages kubectl
and setting the number of replicas on the Kubernetes Deployment.
Destroy
A simple kubectl delete
is performed, on the same manifests used when deploying.
Complete Environment definition
The complete
bunnyshell.yaml
definition can also be found in in the Git repo.
kind: Environment
name: Demo Books
type: primary
environmentVariables:
BACKEND_URL: 'https://api-{{ env.base_domain }}'
FRONTEND_URL: 'https://frontend-{{ env.base_domain }}'
POSTGRES_DB: bunny_books
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_USER: postgres
components:
- kind: DockerImage
name: frontend-image
context: /frontend
dockerfile: Dockerfile
target: prod
args:
REACT_APP_BASE_API: '{{ env.vars.BACKEND_URL }}'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /frontend
- kind: DockerImage
name: backend-image
context: /backend
dockerfile: Dockerfile
target: prod
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /backend
- kind: Helm
name: postgres
runnerImage: 'dtzar/helm-kubectl:3.8.2'
deploy:
- |
cat << EOF > my_values.yaml
global:
storageClass: bns-network-sc
auth:
postgresPassword: {{ env.vars.POSTGRES_PASSWORD }}
database: {{ env.vars.POSTGRES_DB }}
EOF
- 'helm repo add bitnami https://charts.bitnami.com/bitnami'
- 'helm upgrade --install --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize -f my_values.yaml postgres bitnami/postgresql --version 11.9.11'
- |
POSTGRES_HOST="postgres-postgresql.{{ env.k8s.namespace }}.svc.cluster.local"
destroy:
- 'helm uninstall postgres --namespace {{ env.k8s.namespace }}'
start:
- 'kubectl scale --replicas=1 --namespace {{ env.k8s.namespace }} statefulset/postgres-postgresql-11.9.11'
stop:
- 'kubectl scale --replicas=0 --namespace {{ env.k8s.namespace }} statefulset/postgres-postgresql-11.9.11'
exportVariables:
- POSTGRES_HOST
- kind: Helm
name: api
runnerImage: 'dtzar/helm-kubectl:3.8.2'
deploy:
- |
cat << EOF > my_values.yaml
serviceImage: {{ components.backend-image.image }}
replicas: 1
ingress:
className: bns-nginx
host: api-{{ env.base_domain }}
postgres:
host: '{{ components.postgres.exported.POSTGRES_HOST }}'
db: '{{ env.vars.POSTGRES_DB }}'
user: '{{ env.vars.POSTGRES_USER }}'
password: '{{ env.vars.POSTGRES_PASSWORD }}'
frontendUrl: '{{ env.vars.FRONTEND_URL }}'
EOF
- 'helm upgrade --install --namespace {{ env.k8s.namespace }} --dependency-update --post-renderer /bns/helpers/helm/add_labels/kustomize -f my_values.yaml api-{{ env.unique }} ./helm/backend'
destroy:
- 'helm uninstall api-{{ env.unique }} --namespace {{ env.k8s.namespace }}'
start:
- 'helm upgrade --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize --reuse-values --set replicas=1 api-{{ env.unique }} ./helm/backend'
stop:
- 'helm upgrade --namespace {{ env.k8s.namespace }} --post-renderer /bns/helpers/helm/add_labels/kustomize --reuse-values --set replicas=0 api-{{ env.unique }} ./helm/backend'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /helm/backend
- kind: KubernetesManifest
name: frontend
runnerImage: 'alpine/k8s:1.22.15'
deploy:
- 'cd manifests/frontend'
- 'kustomize create --autodetect --recursive --labels=app.kubernetes.io/instance-frontend:bns,app.kubernetes.io/part-of:env-{{ env.unique }} --namespace {{ env.k8s.namespace }}'
- 'kustomize edit set image needsimage={{ components.frontend-image.image }}'
- 'sed -i "s/frontend.example.com/frontend-{{ env.base_domain }}/g" ingress.yaml'
- 'sed -i "s/ingressClassName: nginx/ingressClassName: bns-nginx/g" ingress.yaml'
- 'kubectl apply -k .'
destroy:
- 'cd manifests/frontend'
- 'kustomize create --autodetect --recursive --namespace {{ env.k8s.namespace }}'
- 'kubectl delete -k .'
start:
- 'kubectl scale --replicas=1 --namespace {{ env.k8s.namespace }} deployment/frontend'
stop:
- 'kubectl scale --replicas=0 --namespace {{ env.k8s.namespace }} deployment/frontend'
gitRepo: 'git://github.com/bunnyshell/demo-books.git'
gitBranch: master
gitApplicationPath: /manifests/frontend
Updated about 1 year ago