Kubernetes(10) Configmap and Secret
Some additioanl knowledge...
How to configure a docker image:
- Customized command line arguments -> args: []
- Copy configuration file into docker image
- By parsing enviorment parameter
1) Cloud Native application are usually configurable with enviorment parameter
2) Using entrypoint script to configure/overwirte existing configuration file in docker image
- Using configuration file within Volumes as mount point in Docker
1 Configmap
Configmap provide configuration parameters as plaintext from outside of the container to container image.
There are two ways to injects configuration parameters in a docker instance:
- Mounting configuration file in docker by using volume
- Reading infos from Configmap and add them to enviorment parameters
1.1 Creating a Configmap by using command line
With kubectl create configmap --help command we can see, there are many ways to parse parameters to Configmap. In this section I will take the one with "--from-literal" as example.
[root@k8smaster pod_storage]# kubectl create configmap --help Create a configmap based on a file, directory, or specified literal value. ... Examples: # Create a new configmap named my-config based on folder bar kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config with specified keys instead of file basenames on disk kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt # Create a new configmap named my-config with key1=config1 and key2=config2 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2 # Create a new configmap named my-config from the key=value pairs in the file kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config from an env file kubectl create configmap my-config --from-env-file=path/to/bar.env
...
Example -> setting nginx port and servername:
[root@k8smaster pod_storage]# kubectl create configmap myconfig --from-literal=nginx_port=80 --from-literal=server_name=k8smaster configmap/myconfig created
[root@k8smaster pod_storage]# kubectl get cm -o yaml ...
[root@k8smaster pod_storage]# kubectl describe cm myconfig Name: myconfig Namespace: default Labels: <none> Annotations: <none> Data ==== nginx_port: ---- 80 server_name: ---- k8smaster Events: <none>
As result above nginx_port is 80 and servername is setted to k8smaster.
1.2 Deploy a new Pod with Configmap
Pod template:
apiVersion: v1 kind: Pod metadata: name: pod-configmap-demo namespace: default labels: app: myapp tier: frontend annotations: crazy-chinese.com/created-by: "cluster-admin" spec: containers: - name: mypp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: NGINX_SERVER_PORT valueFrom: configMapKeyRef: name: myconfig # pre configured configmap name: kubectl create configmap myconfig --from-literal=nginx_port=80 --from-literal=server_name=k8smaster key: nginx_port - name: NGINX_SERVER_NAME valueFrom: configMapKeyRef: name: myconfig key: server_name
With env we can define enviorment parameters for docker image. env.name is the name of defined parameter, valueFrom.configMapKeyRef will search key in configmap and return value back and assign it to env.name.
Invesgating configuration in docker container:
[root@k8smaster pod_configmap_secret]# kubectl exec -it pod-configmap-demo -- /bin/sh / # env MYAPP_SVC_PORT_80_TCP_ADDR=10.98.57.156 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_SERVICE_PORT=443 MYAPP_SVC_PORT_80_TCP_PORT=80 HOSTNAME=pod-configmap-demo SHLVL=1 MYAPP_SVC_PORT_80_TCP_PROTO=tcp HOME=/root NGINX_SERVER_PORT=80 NGINX_SERVER_NAME=k8smaster MYAPP_SVC_PORT_80_TCP=tcp://10.98.57.156:80 TERM=xterm NGINX_VERSION=1.12.2 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_PROTO=tcp MYAPP_SVC_SERVICE_HOST=10.98.57.156 KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 PWD=/ KUBERNETES_SERVICE_HOST=10.96.0.1 MYAPP_SVC_SERVICE_PORT=80 MYAPP_SVC_PORT=tcp://10.98.57.156:80
We have seen in the output of command env are NGINX_SERVER_PORT and NGINX_SERVER_NAME configured.
* you can update Configmap with kubectl edit cm name_configmap. The new Update will deployed in Pod after you restart this pod.
1.3 Deploy a Pod with Configmap as Volumes
We have seen in the last section, our update for Configmap was not activ before we restart the Pod. But if you mount Configmap as Volumes on the Pod, the configuration of configmap will be automatically updated by changes!
a new template for deployment:
apiVersion: v1 kind: Pod metadata: name: pod-configmap-demo namespace: default labels: app: myapp tier: frontend annotations: crazy-chinese.com/created-by: "cluster-admin" spec: containers: - name: mypp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - mountPath: /etc/nginx/conf.d name: configmap-myconfig readOnly: true volumes: - name: configmap-myconfig configMap: name: myconfig
In this template we defined, that Configmap myconfig is mounted as volumes with mount path /etc/nginx/conf.d
[root@k8smaster pod_configmap_secret]# kubectl apply -f configmap-volumes-demo.yaml pod/pod-configmap-demo created
[root@k8smaster pod_configmap_secret]# kubectl exec -it pod/pod-configmap-demo -- /bin/sh [root@k8smaster pod_configmap_secret]# kubectl exec -it pod-configmap-demo -- /bin/sh / # cd /etc/nginx/conf.d/ /etc/nginx/conf.d # ls nginx_port server_name /etc/nginx/conf.d # cat server_name k8smaster
With this configuration Configmap create two new files in /etc/nginx/conf.d with Configmap key as filename and value as Content of the file.
Now, if we change the value of Configmap, it will update the configuration immediatlly (with several seconds delay)
If we reload nginx pod now, the new changes will be activ! (the reload should be done by scripting)
2 Secret
In the section above we learned that Configmap could be used for setting configuration file or enviorment parameter of pod (container). They are present as plaintext, which means, everyone can read the content... If we want pass a critical information to the container like password of a database, this must be encrypted. Therefore we have Secret!
[root@k8smaster pod_configmap_secret]# kubectl create secret --help Available Commands: docker-registry Create a secret for use with a Docker registry generic Create a secret from a local file, directory or literal value tls Create a TLS secret ...
- docker-registry is used to store authentication information to able kubelet pull image from docker-registry
- tls stores tls certificates
- generic stores common secret like username and password etc.
2.1 Create a Secret
Now we will create a secret for mysql database manually:
[root@k8smaster pod_configmap_secret]# kubectl create secret generic mysql-credentials --from-literal=username=db_user1 --from-literal=password=myp@ssword123 secret/mysql-credentials created
[root@k8smaster pod_configmap_secret]# kubectl get secret NAME TYPE DATA AGE default-token-rxs5t kubernetes.io/service-account-token 3 25d mysql-credentials Opaque 2 21s [root@k8smaster pod_configmap_secret]# kubectl describe secret mysql-credentials Name: mysql-credentials Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== password: 13 bytes username: 8 bytes
The funny thing is, that the k8s secret is not that secure as we think...
[root@k8smaster pod_configmap_secret]# kubectl get secret mysql-credentials -o yaml apiVersion: v1 data: password: bXlwQHNzd29yZDEyMw== username: ZGJfdXNlcjE= kind: Secret metadata: creationTimestamp: "2019-01-27T21:58:18Z" name: mysql-credentials namespace: default resourceVersion: "399813" selfLink: /api/v1/namespaces/default/secrets/mysql-credentials uid: a753efe4-227e-11e9-84e6-000c297191df type: Opaque
[root@k8smaster pod_configmap_secret]# echo bXlwQHNzd29yZDEyMw== | base64 -d myp@ssword123
See command above, we can decode this password by using base64
PS: the best way might be using Secret, but set permission to 600, so that only the people who create the container can read the credentials.
2.2 Deploy a Pod with Secret
Assume that you have followed this artical and already created a secret (see above), now deploy a pod with following template.
apiVersion: v1 kind: Pod metadata: name: pod-configmap-demo namespace: default labels: app: myapp tier: frontend annotations: crazy-chinese.com/created-by: "cluster-admin" spec: containers: - name: mypp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: password name: mysql-credentials
Invesgating in the Container:
[root@k8smaster pod_configmap_secret]# kubectl apply -f secret-demo.yaml pod/pod-secret-demo created
[root@k8smaster pod_configmap_secret]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-secret-demo 1/1 Running 0 7s
[root@k8smaster pod_configmap_secret]# kubectl exec -it pod-secret-demo -- /bin/sh / # env MYAPP_SVC_PORT_80_TCP_ADDR=10.98.57.156 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_SERVICE_PORT=443 MYAPP_SVC_PORT_80_TCP_PORT=80 HOSTNAME=pod-secret-demo SHLVL=1 MYAPP_SVC_PORT_80_TCP_PROTO=tcp HOME=/root MYSQL_ROOT_PASSWORD=myp@ssword123 ...
Again... this is not a secure way, to store the secret!
2.3 Deploy a Pod with Secret as Volumes Mounts
Use template below
apiVersion: v1 kind: Pod metadata: name: pod-secret-volumes-demo namespace: default labels: app: myapp tier: frontend annotations: crazy-chinese.com/created-by: "cluster-admin" spec: containers: - name: mypp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - mountPath: /home/credentials name: secret-mysql-credentials readOnly: true volumes: - name: secret-mysql-credentials secret: secretName: mysql-credentials defaultMode: 600
Invesgating in Container
[root@k8smaster pod_configmap_secret]# kubectl get pods No resources found. [root@k8smaster pod_configmap_secret]# kubectl apply -f secret- secret-demo.yaml secret-volumes-demo.yaml [root@k8smaster pod_configmap_secret]# kubectl apply -f secret-volumes-demo.yaml pod/pod-secret-volumes-demo created [root@k8smaster pod_configmap_secret]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-secret-volumes-demo 1/1 Running 0 10s [root@k8smaster pod_configmap_secret]# kubectl exec -it pod-secret-volumes-demo -- /bin/sh / # cd /home/credentials/ /home/credentials # ls password username/home/credentials # cat username db_user1