What is Monica?
Monica is an open-source CRM system. But instead of being intended to store details of your business relationships, its purpose is to store details about your personal relationships in order to foster stronger connections between you and your friends and family.
The first article in this series is a crash course on Kubernetes. There I introduced you to a number of important Kubernetes components, such as pods, deployments, services, secrets, volumes, and ingress objects. We then used Kubernetes to host our own Nextcloud server which you can use to save and share files in the cloud.
In the next article, we used what we learned from the previous article to host a Huginn server on our Kubernetes cluster. We then used Huginn to automatically search for apartments on Craigslist. Plus we learned a few new things such as name-based virtual hosting and how to use Amazon Simple Email Service to send notification emails.
The Purpose of This Article
In this article, I'm going to show you:
- How to host a Monica CRM server on our Kubernetes cluster
- How to enable HTTPS between the clients and the cluster without making any modifications to Nextcloud, Huginn, or Monica
- How to create a Kubernetes secret from a config file instead of from a kubectl command
- Where to safely store secret data, like the config file for a Kubernetes secret, in Git
Source Code (Optional)
If you want to get your hands on the code, click on the Github link below. Be sure to
git checkout the
monica tag to view the finished code. Note that in future posts, I will add additional projects to the repo and making changes that aren't reflected in this post.
Start Up Minikube
We are going to test everything out using Minikube, the single-node development cluster. If you don't already have it installed, go here: https://kubernetes.io/docs/tasks/tools/install-minikube.
Now, start up Minikube:
Configuring the Database Components
Here's the config file for the MySQL database deployment named
replicas field tell us that this deployment makes sure that exactly one database pod is running at all times. In that pod runs a
Mount a Volume onto the DB Container
In order to ensure that the DB data will survive a container crash or a pod eviction, we need to mount a Kubernetes volume onto the directory where MySQL stores the data. We do this because volume storage is usually more durable than container storage.
Here's the code which mounts the volume.
In this case, we declare a volume named
db-storage, and mount it onto the container's
The Persistent Volume Claim
volumes field in the monica-db.yaml file, the
db-storage volume references a PVC named
monica-shared-storage-claim. In this project, this PVC will be shared between the DB and the Monica server. So let's put the PVC's YAML config document in its own file:
monica-shared-storage-claim PVC has 256Mi of storage.
The Service for the Database
Since pods can get replaced at any time, it's a good idea to put a service in front of a set of one or more pod replicas. The service acts as a communications middleman providing a consistent address/endpoint for clients to communicate with.
FYI: Services also provide TCP/UDP loadbalancing between its backing pods, if there's more than one. I just mention this to show you that services are useful in a number of different ways.
Anyway, let's add the service to the monica-db.yaml file.
This service listens on port 3306 for client requests and fowards them on to port 3306 on the
DB Environment Variables
We need to scroll back up to the deployment and add some MySQL environment variables.
env field allows you to declare an array of environment variables. In this case, we're just declaring
MYSQL_DATABASE, i.e. the name of the DB.
envFrom.secretRef field tells the
mysql container to extract the values from the
monica-db-secret object and turn them into environment variables.
Store Database Credentials in a Kubernetes Secret
We need to create the
monica-db-secret. Here's the command to do so:
kubectl create secret generic monica-db-secret \ --from-literal=MYSQL_ROOT_PASSWORD=rootPassw0rd1 \ --from-literal=MYSQL_USER=monica \ --from-literal=MYSQL_PASSWORD=passw0rd1
Now I'm going to show you how to create a config file for the secret that we just created. First you need to output the secret in YAML format, and put it in the
kubectl get secret monica-db-secret -o yaml > monica-db-secret.yaml
Now let's view the file.
This is the YAML-representation of the secret object that we just created. There's a lot of extraneous metadata that we can get rid of. I have crossed those fields out.
Here's the config file after deleting the unnecessary fields and reordering some fields for better legibility.
Note that the
data values are base64 encoded, which is why they look like strings of random characters. Note, they are not encrypted! They are effectively plain-text strings because it's trivial to decode a base64 value.
Let's create the secret using the config file to test things out. But first we must delete the existing
monica-db-secret that we created a minute ago.
kubectl delete secret monica-db-secret
Now we can recreate the secret using the config file.
kubectl apply -f monica-db-secret.yaml
Safely Store Secrets in Git Using Blackbox
Generally speaking, you'll want to save Kubernetes YAML config files in a version control system like Git. But what are you supposed to do about Kubernetes secret config files? After all, they contain things like unencrypted credentials and API keys which should not be saved in your VCS. I like to solve this problem by using Blackbox.
Blackbox is a tool which helps you to encrypt sensitive files before adding them to your Git repository. This prevents credentials and API keys from falling into the wrong hands when they
git clone your repo. (Go here for a more detailed explanation.)
Setting up Blackbox is system-dependent and outside of the scope of this article. I just wanted to let you know that it exists, and that it is a good solution to this problem. Just go to https://github.com/StackExchange/blackbox and follow the installation instructions for your system.
Create the Database Components
We have already created the DB secret. Now let's create the shared PVC
kubectl apply -f monica-shared-pvc.yaml
...and the database deployment and service.
kubectl apply -f monica-db.yaml
Finally, we're going to watch the pods until the pod created by the
monica-db deployment is up and running. We do this by using the
kubectl get pods --watch command.
Note that it may take a minute or two to create the pod if your cluster hasn't yet downloaded and cached the
Configuring the Monica Server Components
Here are the beginnings of the
monica-server deployment. Like usual, we are not expecting gargantuan amount of traffic, so the deployment only creates a single pod. That pod runs a container created from the
Attach a Volume to the Server
In addition to storing things in the database, the Monica server also stores things in
/var/www/monica/storage. Let's mount a volume onto that directory. That way, files in the directory will persist even if the container or pod dies.
The top block mounts the volume, and the bottom block is where we declare the volume. Notice that it's backed by the shared PVC.
Store Non-Sensitive Env Vars in a ConfigMap
We are going to add the following configMap to the file. It contains all of the non-sensitive environment variables that configure the server.
First off, notice that
"3306" are both in quotes because configMap values must be strings, not a boolean or a number.
Second, take a look at
DB_HOST: monica-db. This configures the server to use the database at the address
monica-db, which is the hostname of the DB service defined in the
monica-db.yaml file. Recall that the service acts as a communications intermediary between the database pod and clients such as the Monica server.
Now let's update the server container to reference the configMap.
Store Sensitive Env Vars in a Secret
We need to create a Kubernetes secret named
monica-server-secret to store the
APP_KEY and the server's DB credentials.
kubectl create secret generic monica-server-secret \ --from-literal=APP_KEY=a_random_32-character_stringgggg \ --from-literal=DB_USERNAME=monica \ --from-literal=DB_PASSWORD=passw0rd1
Now let's update the server container to reference the secret.
Add the Service for the Monica Server
Let's add a service which sits in front of the server pod.
Requests sent to
port 80 on the
monica-server service will get forwarded to
port 80 on the server pod.
Create the Monica Server Components
Now we can create the configMap, deployment, and service for the Monica server.
kubectl apply -f monica-server.yaml
These three components will get created almost instantaneously. However, it will probably take a few minutes for the deployment to create the server pod because the cluster needs to download and cache the
So let's watch the pods until the server pod is up and running.
Part 1 Summary
In this article, we went over
- The key Kubernetes components--configMaps, deployments, PVCs, secrets, and services--for our Monica server and MySQL DB
- How to create the config file for a Kubernetes secret
- One way to safely store sensitive data, like the config file for a secret, in Git
In Part 2, I'm going to show you how to use an ingress object to
- Make the Monica server accessible outside of the cluster
- Enable HTTPS between clients and the cluster
Thanks for reading this far. See you in Part 2.