We’ll be using the same architecture from the previous labs but this time, we’ll incorporate ConfigMaps and Secrets:
Our architecture looks like this:
To learn more, check out ConfigMaps and Secrets
Before we start, let’s first verify if we’re using the correct IAM user’s access keys. This should be the user we created from the pre-requisites section above.
$ aws sts get-caller-identity
{
"UserId": "AIDxxxxxxxxxxxxxx",
"Account": "1234567890",
"Arn": "arn:aws:iam::1234567890:user/k8s-admin"
}
For the cluster, we can reuse the eksops.yml file from the previous labs.
Launch the cluster.
time eksctl create cluster -f eksops.yml
Check the nodes and pods.
kubectl get nodes
Save the cluster, region, and AWS account ID in a variable. We’ll be using these in a lot of the commands later.
MYREGION=ap-southeast-1
MYCLUSTER=eksops
MYAWSID=$(aws sts get-caller-identity | python3 -c "import sys,json; print (json.load(sys.stdin)['Account'])")
We’ll use namespace-cm.yml to create the cm namespace.
apiVersion: v1
kind: Namespace
metadata:
name: cm
labels:
app: counter
Apply.
kubectl apply -f manifests/namespace-cm.yml
Verify.
$ kubectl get ns
NAME STATUS AGE
default Active 8h
volumes Active 18s
Let’s now create the config-redis.yml for the Redis.
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
data:
config: | # YAML for multi-line StringEquals
# Redis config file
tcp-keepalive 240
maxmemory 1mb
Here’s the tier-data.yml that we’ll use.
apiVersion: v1
kind: Service
metadata:
name: data-tier
namespace: cm
labels:
app: microservices
spec:
ports:
- port: 6379
protocol: TCP # default
name: redis # optional when only 1 port
selector:
tier: data
type: ClusterIP # default
---
apiVersion: apps/v1 # apps API group
kind: Deployment
metadata:
name: data-tier
namespace: cm
labels:
app: microservices
tier: data
spec:
replicas: 1
selector:
matchLabels:
tier: data
template:
metadata:
labels:
app: microservices
tier: data
spec: # Pod spec
containers:
- name: redis
image: redis:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
name: redis
livenessProbe:
tcpSocket:
port: redis # named port
initialDelaySeconds: 15
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
command:
- redis-server
- /etc/redis/redis.conf
volumeMounts:
- mountPath: /etc/redis
name: config
volumes:
- name: config
configMap:
name: redis-config
items:
- key: config
path: redis.conf
Let’s now create the configmap and the data tier.
cd manifests
kubectl apply -f config-redis.yml
kubectl apply -f tier-data.yml
Check the resources created.
$ kubectl get cm -n cm
NAME DATA AGE
kube-root-ca.crt 1 18m
redis-config 1 64s
$ kubectl get pods -n cm
NAME READY STATUS RESTARTS AGE
data-tier-674b7bcc64-h8czg 1/1 Running 0 76s
Let’s check if the config file was used inside the pod.
$ kubectl exec -n cm data-tier-674b7bcc64-h8czg -it -- cat /etc/redis/redis.conf
# Redis config file
tcp-keepalive 240
maxmemory 1mb
To prove that Redis actually loaded the config file:
$ kubectl exec -n cm data-tier-674b7bcc64-h8czg -it -- redis-cli CONFIG GET tcp-keepalive
1) "tcp-keepalive"
2) "240"
Here we can see that the keepalive time used by the Redis app is the same as the keepalive time defined in the config file.
Next, let’s set up the secrets-app.yml.
apiVersion: v1
kind: Secret
metadata:
name: app-tier-secret
namespace: cm
stringData: # unencoded data
api-key: LRcAmM1904ywzK3esX
decoded: hello
data: #for base-64 encoded data
encoded: aGVsbG8= # hello in base-64
# api-key secret (only) is equivalent to
# kubectl create secret generic app-tier-secret --from-literal=api-key=LRcAmM1904ywzK3esX
Note that manifests for the secrets are not usually checked into source control since they contain sensitive information. Another option is to create the secrets through the kubectl utility.
Secrets are stored as base64-encoded string text and defined as data. They are automatically decoded by Kubernetes when manifests is ran. Note that this encoding doesn’t improve any security since anyone can decode this using the correct tools.
If you don’t want to encode the secrets, you can define them under stringData and Kubernetes will take case of the encoding when the manifest is ran. In the manifest above, we’ve included both the encoded and decoded secret, which is “hello”.
Here’s the tier-app.yml that we’ll use for the app tier.
apiVersion: v1
kind: Service
metadata:
name: app-tier
namespace: cm
labels:
app: microservices
spec:
ports:
- port: 8080
selector:
tier: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-tier
namespace: cm
labels:
app: microservices
tier: app
spec:
replicas: 1
selector:
matchLabels:
tier: app
template:
metadata:
labels:
app: microservices
tier: app
spec:
containers:
- name: server
image: lrakai/microservices:server-v1
ports:
- containerPort: 8080
name: server
env:
- name: REDIS_URL
# Environment variable service discovery
# Naming pattern:
# IP address: <all_caps_service_name>_SERVICE_HOST
# Port: <all_caps_service_name>_SERVICE_PORT
# Named Port: <all_caps_service_name>_SERVICE_PORT_<all_caps_port_name>
value: redis://$(DATA_TIER_SERVICE_HOST):$(DATA_TIER_SERVICE_PORT_REDIS)
# In multi-container example value was
# value: redis://localhost:6379
- name: DEBUG
value: express:*
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-tier-secret
key: api-key
livenessProbe:
httpGet:
path: /probe/liveness
port: server
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /probe/readiness
port: server
initialDelaySeconds: 3
initContainers:
- name: await-redis
image: lrakai/microservices:server-v1
env:
- name: REDIS_URL
value: redis://$(DATA_TIER_SERVICE_HOST):$(DATA_TIER_SERVICE_PORT_REDIS)
command:
- npm
- run-script
- await-redis
We’ll now ran the manifest for both the secret and the app tier.
cd manifests
kubectl apply -f secrets-app.yml
kubectl apply -f tier-app.yml
We should see the secret created with the Opaque type.
$ kubectl get secrets -n cm
NAME TYPE DATA AGE
app-tier-secret Opaque 3 2m24s
default-token-2tzdf kubernetes.io/service-account-token 3 40m
We should now also see two pods running.
$ kubectl get pods -n cm
NAME READY STATUS RESTARTS AGE
app-tier-5569b58f55-9jb2j 1/1 Running 0 10s
data-tier-674b7bcc64-h8czg 1/1 Running 0 22m
To verify that the secret was created an environment inside the container:
$ kubectl exec -n cm app-tier-5569b58f55-9jb2j -- env | grep KEY
API_KEY=LRcAmM1904ywzK3esX
Before we finish this lab, we should know that since secrets and configmaps are managed separately from the deployments, any changes in the custom resources doesn’t automatically reflect in the deployments. The pods must be actively updated whenever there’s a new version of the configmaps and secrets.
Make sure to delete the cluster after the lab to save costs.
time eksctl delete cluster -f eksops.yml
When you delete your cluster, make sure to also double check the AWS Console and ensure that the Cloudformation stacks (which we created by eksctl) are dropped cleanly.