Kubernetes Init Container Pattern

Overview

Design Patterns serve as reusable and replicable solutions for common challenges in software and architectural design. They advocate for the development of highly cohesive and loosely coupled applications. In today’s era of microservices, these patterns extend beyond traditional software design and find application in infrastructure and deployment.

This article explores one such design pattern—the Kubernetes Init Container Pattern.

Goal

During development, developers may make assumptions about the presence of certain files or properties at specific paths for the application to function properly. For instance, database credentials might be stored in a property file. However, keeping these files as part of the project may not be ideal for reasons such as security. The challenge arises when deploying microservices in the cloud—these crucial configuration files, like DB credentials, must be injected into the container before it starts.

The goal is to ensure a secure and flexible way to inject property files at runtime for multiple microservices, promoting a generic and reusable solution.

  • Our application requires DB credentials to connect to the DB.
  • DB credentials are kept safe in AWS secrets manager.
  • Developers while developing the application in their local, they keep the application properties in their local and able to develop and test just fine.
  • However, when we deploy the application in the cloud, these DB credentials should be part of the container for the app to start.
  • We need a safe way to inject this property file into the container at run time before the app starts.
  • There are N microservices and each microservice requires this property file. We need to find a generic, reusable solution

We can easily argue that why can we not include that feature / add some piece of code in the application itself that is – to get those files programmatically from the AWS secrets manager. It can be done, but we need to update all the N microservices. Also, we might want to add the AWS specific dependencies in our application during local development. There is a much better and cleaner way to accomplish this goal using Kubernetes Init Container Pattern.

Init Container

Init Container is a docker container, an application, which runs before the main application starts. Kubernetes guarantees that main application does not even start unless the init container successfully exits.

Init container should be

  • short living container.
  • should have all the application preparation logic.
  • should exit successfully. (It it fails, main app will NOT start)

Init container and main application container shares the disk space. So init container can prepare the required files and keep them ready for the app server to use it when it starts.

AWS Secrets Manager

  • I set up some credentials in the AWS secrets manager as shown below.

  • Give a name for the secrets

  • Save the credentials. Make the note of the ARN which you might need to update the IAM role for the EC2 instance to access the secrets manager.

Kubernetes Init Container Pattern

  • The main purpose of the init container in our case is to access the secrets manager using the aws-cli and store the credentials in a location.
  • We create a separate docker file and image for this purpose. This docker image in local cannot pull those secrets as we would not have access. But it would work just fine in the Kubernetes cluster as the EC2 instance would have access to the secrets manager.

provider.sh

aws secretsmanager --region ca-central-1 get-secret-value --secret-id vinsguru/prod | jq -r .SecretString > app.properties
mkdir /secret
mv app.properties /secret/app.properties

Dockerfile:

FROM amazon/aws-cli

# install jq
RUN yum install jq -y

ADD provider.sh provider.sh

ENTRYPOINT sh provider.sh
  • I create this above docker file and create an docker image with name vinsdocker/secrets-provider
  • This would be acting as the init-container.

Pod

  • We create our pod/deployment file as shown here.
apiVersion: v1
kind: Pod
metadata:
  name: vinsguru-init
  labels:
    app: vinsguru
spec:
  initContainers:
    - name: secrets-provider
      image: vinsdocker/secrets-provider
      volumeMounts:
        - name: secret
          mountPath: /secret
  containers:
    - name: microservice
      image: busybox
      command: [ 'sh', '-c', 'echo The app is running! && sleep 3600' ]
      volumeMounts:
        - name: secret
          mountPath: /app
  volumes:
    - name: secret
      emptyDir: {}
  • When we create a pod using the above file, first an init container will run, pull the credentials and keep it in /secret/app.properties.
  • The same volume is shared with our microservice & we can access the file as /app/app.properties.
  • Once the pod is running, run the below command
kubectl exec vinsguru-init -- cat app/app.properties

#prints below lines
api.username=prod-username
api.password=prod-password

Conclusion

By implementing the Kubernetes Init Container Pattern, I was able to successfully inject a property file from the Secrets Manager into my application docker container. Init containers provide a secure means of preparing the main app and setting up the required environment for seamless execution. While our example focused on accessing the Secrets Manager, this approach can be used to various tasks, such as interacting with external URLs, downloading files from S3, or GitHub. This approach ensures that the main application remains focused on its functionality without worrying about intricate setup procedures.

Happy Learning 🙂

 

 

Share This:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.