Tag: kubernetes

Five Minute Tip: Leveraging configmaps and secrets for environmental variables

Five Minute Tip: Leveraging configmaps and secrets for environmental variables

For me, I’ve have been working with Kubernetes for a while, and I notice that there are only a few issues that stick out when looking at deployment.yaml. That looking deployment YAML files can be pretty lengthy. For example:

cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: wordpress-nginx
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: wordpress-nginx
  strategy:
    type: RollingUpdate
  template:
    metadata:
      annotations:
      labels:
        app: wordpress-nginx
    spec:
      affinity:
        podAntiAffinity:                                 
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:          
              matchLabels:
                app: wordpress-nginx
      containers:
      - image: gcr.io/bearmoo-cloud-net/wordpress-nginx
        imagePullPolicy: Always
        name: wordpress-nginx
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
            scheme: HTTP
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 5
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 20m
            memory: 256Mi
        securityContext:
          allowPrivilegeEscalation: false
          privileged: false
          procMount: Default
          readOnlyRootFilesystem: false
          runAsNonRoot: false
        env:
        - name: PASSWORD
          value: randompassword
        - name: HOSTNAME
          value: www.bearmoo.net
        - name: IPADDRESS
          value: "192.168.0.0/24"
        - name: RUNAS
          value apache

I know that there are times where I want to edit one environmental variable quickly without changing the whole file. Besides, there is another issue as well; The PASSWORD is in plain text! Having to deal with logging programs like Splunk, I know that there is a potential for the passwords to be in the logs, and that’s not good at all. But, I think it can be improved using configmaps and secrets. I can update the deployment to point to another location to import the environmental variables by creating a configmap which is pretty straight forward:

cat sample-configmaps.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wordpress-nginx-configmap
  labels:
    app: wordpress-nginx
data:
  PASSWORD: randompassword 
  IntIpAddress: "192.168.0.0/24"
  HOSTNAME: www.bearmoo.net
  RUNAS: apache

Under data it’s structure is:

<VARIABLENAME>: <VALUE>

That’s pretty simple, right?

Let’s use kubectl to apply the new configmap to the cluster:

kubectl apply -f sameple-configmaps.yaml

Now, time to check if the newly created configmap exist inside of the cluster:

kubectl get configmaps sample-configmaps -o yaml
apiVersion: v1
data:
  PASSWORD: randompassword 
  IntIpAddress: "192.168.0.0/24"
  HOSTNAME: www.bearmoo.net
  RUNAS: apache
kind: ConfigMap
metadata:
  annotations:
  creationTimestamp: "2020-04-29T18:11:23Z"
  labels:
    app: wordpress-nginx
  name: sample-configmaps
  resourceVersion: "10612459"
  selfLink: /api/v1/namespaces/default/configmaps/sample-configmaps
  uid: 31077265-cbb3-4574-a61c-8a3ba8cce759

Yay! it exists! Now time to update the deployment file to point to the configmap.

cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: wordpress-nginx
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: wordpress-nginx
  strategy:
    type: RollingUpdate
  template:
    metadata:
      annotations:
      labels:
        app: wordpress-nginx
    spec:
      affinity:
        podAntiAffinity:                                 
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:          
              matchLabels:
                app: wordpress-nginx
      containers:
      - image: gcr.io/bearmoo-cloud-net/wordpress-nginx
        imagePullPolicy: Always
        name: wordpress-nginx
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
            scheme: HTTP
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 5
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 20m
            memory: 256Mi
        securityContext:
          allowPrivilegeEscalation: false
          privileged: false
          procMount: Default
          readOnlyRootFilesystem: false
          runAsNonRoot: false
        envFrom:
        - configMapRef:
            name: sample-configmaps

Now, instead of a long list of environmental variables in deployment, where pointing to a configmap to import the variables from:

        envFrom:
        - configMapRef:
            name: sample-configmaps

Hrmm. What about Our PASSWORD? Ahh, yes, like creating a configmap, will create a secret file.


apiVersion: v1
data:
  PASSWORD: randompassword
kind: Secret
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: sample-secrets
type: Opaque

There are a few differences; the first one is “kind” statement. Where in kind where telling Kubernetes that this is a secret. All so, the value for PASSWORD needs to be encoded. Though this command, our values can be encoded:


echo -n | base64 && echo ""
cmFuZG9tcGFzc3dvcmQ=

Now that I got the output from the last command, it’s time to update the secret file to this:

apiVersion: v1
data:
  PASSWORD: cmFuZG9tcGFzc3dvcmQ=
kind: Secret
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: sample-secrets
type: Opaque

Now time to apply the secret into the cluster:

kubectl apply -f sample-secrets.yaml

Now the changes have been applied. Let’s check to see if it’s there?

kubectl get secrets sample-secrets -o yaml
apiVersion: v1
data:
  PASSWORD: cmFuZG9tcGFzc3dvcmQ=
kind: Secret
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: sample-secrets
  resourceVersion: "88538"
  selfLink: /api/v1/namespaces/default/secrets/sample-secrets
  uid: 25ae681a-baa2-4239-9dfc-60ad0c8699fa
type: Opaque

Fantastic! Now it’s time to update the deployment file so that the secrets can be used as well:

cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
  labels:
    app: wordpress-nginx
  name: wordpress-nginx
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: wordpress-nginx
  strategy:
    type: RollingUpdate
  template:
    metadata:
      annotations:
      labels:
        app: wordpress-nginx
    spec:
      affinity:
        podAntiAffinity:                                 
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:          
              matchLabels:
                app: wordpress-nginx
      containers:
      - image: gcr.io/bearmoo-cloud-net/wordpress-nginx
        imagePullPolicy: Always
        name: wordpress-nginx
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
            scheme: HTTP
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /wp-login.php
            port: 80
          initialDelaySeconds: 20
          periodSeconds: 10
          successThreshold: 2
          timeoutSeconds: 5
        resources:
          limits:
            memory: 256Mi
          requests:
            cpu: 20m
            memory: 256Mi
        securityContext:
          allowPrivilegeEscalation: false
          privileged: false
          procMount: Default
          readOnlyRootFilesystem: false
          runAsNonRoot: false
        envFrom:
        - configMapRef:
            name: sample-configmaps
        - secretRef:
            name: wordpress-nginx-secrets

Now doing an apply of the file:


kubectl apply -f deployment.yaml

Apply the modified file, Kubernetes will change the state of the deployment for our app based on what the file describes. Now to check if it’s the variables are there:

kubectl exec -it wordpress-nginx-jj13123klj-gj981 /bin/bash
# env
...
IntIpAddress=10.42.0.0/16
PASSWORD: randompassword 
IntIpAddress: "192.168.0.0/24"
HOSTNAME: www.bearmoo.net
RUNAS: apache
...

Nice! I can see that the variables are inside of the container, and our deployment YAML file is a little simpler to read. All so reducing the possibility of the password getting logged in a logging program such as Splunk.

Five Minute Tip: Kubernetes helm in multiple namespaces

Five Minute Tip: Kubernetes helm in multiple namespaces

A while ago I’ve upgraded the infrastructure to work in Kubernetes and having the application separated though namespaces. I’ve created deployment files and use helm to install applications in the cluster. I’ve found that using helm in multiple namespaces requires a bit extra editing to make it work.

Assuming helm client has beenĀ installed and ready to go. We’ll create a service account:

kubectl -n myapp create serviceaccount tiller

This basically creates a serviceaccount called tiller in a namespace called myapp. Now will need to create a cluster role binding for the account.

kubectl create clusterrolebinding tiller -n myapp --clusterrole=cluster-admin --serviceaccount=myapp:tiller

The –clusterrole=cluster-admin is defining what role is the account going to have. In this case, a cluster-admin role. The account can go beyond a namespace and view resources cluster-wide. The last part –serviceaccount=myapp:some-tiller-account tells Kubernetes where to install the service account. In this case, inside the namespace myapp.

Then install helm into that namespace:

helm init --service-account tiller --tiller-namespace myapp

Oh wait, what if we need helm to work in multiple namespaces? That easy! We can recycle the last command and installed a to install a service account into a different namespace:

kubectl create clusterrolebinding some-tiller-account -n mydata --clusterrole=cluster-admin --serviceaccount=mydata:tiller

Next, will need to update the clusterrolebinding so that helm can see what’s inside of another namespace.

kubectl edit clusterrolebinding tiller -n myapp

Origonal:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: tiller
  namespace: myapp

Updated:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: tiller
  namespace: myapp
- <span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">kind: ServiceAccount</span>
<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">  name: tiller</span>
<span style="color: rgb(255, 0, 0);" data-mce-style="color: #ff0000;">  namespace: mydata</span>

That it! When you do a helm install or helm ls you should beable to see inside both namespaces.

%d bloggers like this: