We’ll redesigned the architecture from the previous lab and break down the containers into their own pods. This will no introduce some networking issues since the containers are not on the same pod anymore.
To resolve this, we’ll use Services which will provide a static endpoint that will handle communication between pods.
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 svc-discovery-namespace.yml to create the service-discovery namespace for this project.
apiVersion: v1
kind: Namespace
metadata:
name: service-discovery
labels:
app: counter
Apply.
kubectl apply -f svc-discovery-namespace.yml
Next, we’ll use three manifests for each tier, starting with the data-tier.yml which will create the Redis pod and the Service that will allow it to talk to the NodeJS pod in a separate container.
When we run the command below, the resources will be created in the order the are specified in the file.
kubectl apply -f data-tier.yml
The pod has a label “tier:data” which will be used by the Service as its selector. The service resource also publishes a port (it can publish as many ports as you need) and name it “redis”.
Lastly, the Service is specified as a ClusterIP type which is the default. ClusterIP creates an internal virtual IP for access between resources inside the cluster only.
Verify that the pod is running.
$ kubectl get pods -n service-discovery
NAME READY STATUS RESTARTS AGE
data-tier 1/1 Running 0 59s
Check for the service.
$ kubectl get svc -n service-discovery
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
data-tier ClusterIP 10.100.66.141 <none> 6379/TCP 113s
Let’s use app-tier.yml to create the resources for the NodeJS server. It follows the same structure as the data tier manifest, but now we’re using an environment variable that is set by Kubernetes.
apiVersion: v1
kind: Service
metadata:
name: app-tier
labels:
app: microservices
spec:
ports:
- port: 8080
selector:
tier: app
---
apiVersion: v1
kind: Pod
metadata:
name: app-tier
namespace: service-discovery
labels:
app: microservices
tier: app
spec:
containers:
- name: server
image: lrakai/microservices:server-v1
ports:
- containerPort: 8080
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
In the previous lab, the REDIS_URL is set to localhost because all containers are contained the same pod and they talk over localhost. Since the containers are now in separate pods, the containers cannot rely on the IP address of each Pod because the IP address may change as Pods are created and deleted.
Instead, Kubernetes will use environment variables for the service that will provide the static endpoint for each Pod. We can use the variable DATA_TIER_SERVICE_HOST and Kubernetes will automatically know that we’re referring to the service resource in the data tier. THe same way goes for the variable for the port, DATA_TIER_SERVICE_PORT_REDIS, making sure we specify the name of the port.
Services must first be created before using an environment variable
Since environment variables rely on Kubernetes to find the service, it is important that the service are created first before the Pod resource can use the variable.
Environment variables has to be on the same namespace as the resources
As we’ve learn, namespace is use to group resources. This includes environment variables.
Let’s now create the app tier.
kubectl apply -f app-tier.yml
Check the pods.
$ kubectl get pods -n service-discovery
NAME READY STATUS RESTARTS AGE
app-tier 1/1 Running 0 22s
data-tier 1/1 Running 0 25m
Finally, let’s create the support tier using support-tier.yml. Instead of just environment variables, we’ll tell Kubernetes to rely on DNS to find the app tier resources. Note that the poller uses variables while the counter uses DNS.
apiVersion: v1
kind: Pod
metadata:
name: support-tier
labels:
app: microservices
tier: support
spec:
containers:
- name: counter
image: lrakai/microservices:counter-v1
env:
- name: API_URL
# DNS for service discovery
# Naming pattern:
# IP address: <service_name>.<service_namespace>
# Port: needs to be extracted from SRV DNS record
value: http://app-tier.service-discovery:8080
- name: poller
image: lrakai/microservices:poller-v1
env:
- name: API_URL
# omit namespace to only search in the same namespace
value: http://app-tier:$(APP_TIER_SERVICE_PORT)
Apply.
kubectl apply -f support-tier.yml
We now have three running pods at this point.
$ kubectl get pods -n service-discovery
NAME READY STATUS RESTARTS AGE
app-tier 1/1 Running 0 6m25s
data-tier 1/1 Running 0 40m
support-tier 2/2 Running 0 39s
Let’s now check if the is able to continuously send GET requests from the NodejS server.
$ kubectl logs support-tier poller -n service-discovery --tail 10
Current counter: 1625
Current counter: 1637
Current counter: 1647
Current counter: 1657
Current counter: 1673
Current counter: 1688
Current counter: 1702
Current counter: 1711
Current counter: 1720
Current counter: 1739
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 double check the AWS Console and that the Cloudformation stacks (which we created by eksctl) are dropped cleanly.