Using the Kubernetes Secrets API
This is the third part in my series on building and deploying a Rails app using Docker containers and Kubernetes. Here is the first part and the second part.
To follow along, you’ll need to complete the steps in the “Project Set Up” section of the previous blog post.
What are secrets?
The previous version of the ToDo app has some issues. One of the biggest is that the username and password for the database are included in plain text in several places. This isn’t a good idea for real applications. The Kubernetes Secret object was designed to handle securely sharing sensitive information between containers.
There are a couple ways to access secret objects. For this tutorial, I’m going to have the secret object mounted as a set of files on a volume the container can access. If you are used to accessing secrets in environment variables this may feel a little awkward at first but it quickly becomes familiar.
Creating the Secrets File
I have three secrets to store: the database username, the database password, and the Rails secret key. Secrets need to be base64 encoded and it is easy to do the encoding in Ruby.
Once I have the base64 encoded values I create a file for the secrets object. The yaml file to create a set of secrets looks very similar to the file that creates a pod or service.
To create the secret I use kubectl create -f
just like I do with other Kubernetes objects.
Modifying the Database Pod
The next step is to make the database container use the secrets. The official Postgres image doesn’t work with secrets as is so I need to make a Dockerfile to extend it. I put this Dockerfile in a separate pg
directory.
The last line of that Dockerfile contains three separate commands and is a bit hard to read. Here are the commands with some added whitespace:
The first line reads the password from /etc/secrets/password
and puts it in the POSTGRES_PASSWORD
environment variable. The second line does the same thing for the POSTGRES_USER
environment variable. The last line runs the entrypoint script that is part of the Postgres image and starts up Postgres. Now I need to build this image and store it in the Google Container Registry.
Now I need to modify the database pod to use the new image and access the secrets object. Here’s the old version:
And here’s the new version:
The first change is to remove the env:
section. That information will come in via secrets now. After that I add a volume for the secrets (lines 9 - 12) and a volume mount (line 16 - 19). The last change is updating the image from the official Postgres image: postgres
to the image I just built: gcr.io/my_project_id/pg:v1
.
I create the database pod and database service using kubectl create -f
.
Now the database pods are using secrets to get the Postgres password and username.
Modifying the Rails Pods
Modifying the Rails pods to use secrets is also pretty straightforward. All the setup for the Rails pods is in init.sh
so I just add lines to create environment variables from my three secrets. Here is the new version of init.sh
:
I also need to modify config/database.yml
(the Rails database config file) to use the environment variables created in init.sh
.
Because I changed the Dockerfile, I need to rebuild the Docker image for the Rails container. I’m tagging this version of the image as v2. Once the image is built, I push it up to the Google Container Registry.
Just like with the database I need to modify the pods to use the new image and to access the secrets. The Rails pods are created by the web replication controller so I make my changes to web-controller.yml
To start up the Rails pods and web service I follow the same steps as I did in the previous Kubernetes blog post.
The final step is opening up the firewall. This step doesn’t change at all:
In the next post in this series, I’ll show how to make your database pod(s) more fault tolerant with a persistent disk.