Deploy microservices using AWS EKS
Part 1
One of the easiest option for deploying and managing Kubernetes on AWS is by using
AWS EKS. This is an exercise of deploying
a system composed of 2 microservices using AWS EKS, how to manage that cluster and how to test this deployment.
Install and configure eksctl
Push docker instance to AWS ECR
Using eksctl
Then you can delete the cluster
Steps to deploy cluster:
In order to do this I will use a very simple service that is exposing some endpoints. The git repository for this service can be found here.
ServicesInstall and configure eksctl
Push docker instance to AWS ECR
Using eksctl
Services
We will have the same service deployed twice. The service exposes following endpoints:- GET /v1/health - endpoint that we will use to verify that service is healthy. It will also return the name of service and up time.
- GET /v1/reports/simple - endpoint that we will return some data that is configured in the environment variables of the service.
- GET /v1/reports/complete - endpoint that we will return some data that is configured in the environment variables of the service. It will also request GET /v1/reports/simple from the second service, it will concatenate the data and the return it. This endpoint can then be used to fully test the microservice system
- PORT - port where service will listen for requests. (Required)
- NAME - service name. (Required)
- DATA - a simple sting that will be returned in the reports endpoints. (Required)
- NEXT - Next service name. (Optional). If not set then GET /v1/reports/complete will return just the own service data concatenated with string 'N/A'
Install and configure eksctl
For this exercise I used eksctl client - Official CLI for Amazon EKS.- First install eksctl as explained on documentation
- Install and configure AWS CLI sh
- Verify aws cli is configured correctly sh
$ aws configure AWS Access Key ID [None]: A.... AWS Secret Access Key [None]: 8... Default region name [None]: eu-west-1 Default output format [None]: json
$ aws s3 ls 2016-10-19 12:43:52 .....
Push docker instance to AWS ECR
We need a place to store our docker images. AWS ECR is perfect for this and you can find bellow the steps you need to follow in order to push our service Docker image to ECR.- Get AWS password sh
- Docker login sh
- Create ECR repository sh
- Create docker image for simple service sh
- Tag image and push to ECR sh
$ aws ecr get-login-password --region eu-west-1 eyJwYXlsb2FkIjoibkN4MlVzUm5kUUxTYk9WR3l0WjJwRmdyU0dheUZ0b1YyWlRRbm.....
$ docker login -u AWS -p eyJwYXlsb2 https://{your_aws_account}.dkr.ecr.eu-west-1.amazonaws.com ..... Login Succeeded
$ aws ecr create-repository --repository-name simple-service { "repository": { "repositoryArn": "arn:aws:ecr:eu-west-1:{aws_account_id}:repository/simple-service", "registryId": ".....", "repositoryName": "simple-service", "repositoryUri": "{aws_account_id}.dkr.ecr.eu-west-1.amazonaws.com/simple-service", "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": false } } }

$ docker build -t simple-service . Sending build context to Docker daemon 69.1MB Step 1/8 : FROM node:8.14.0-alpine ........ Successfully built ddddcb6941de Successfully tagged simple-service:latest
$ docker tag simple-service:latest {aws_account_id}.dkr.ecr.eu-west-1.amazonaws.com/simple-service $ docker push {aws_account_id}.dkr.ecr.eu-west-1.amazonaws.com/simple-service:latest The push refers to repository [{aws_account_id}.dkr.ecr.eu-west-1.amazonaws.com/simple-service] 0fd5e3eeb047: Pushed d26ee7e7e5a7: Pushed bd180bd6f1d8: Pushed 4964afdee176: Pushed 0c1c3171d8c7: Pushed 2283a7db78c9: Pushed df64d3292fd6: Pushed latest: digest: sha256:e112e9e3a6cf81d28ae97f830eea52c01354449f59e9ad7044b6bea44788a298 size: 1789

Using eksctl
A simple test for eksctl is to create a cluster using the default configuration:

Then you can delete the cluster

- Create cluster using the configuration file. sh
- Make sure aws-iam-authenticator is also installed on local machine.
- Now we can start using kubectl, everything is configured correctly. sh
- Create deployment/service and pod for service #1 sh
- Create deployment/service and pod for service #2 sh
- We can ssh to our node and try some curl commands sh
- external ip for our node is 54.77.67.122
- ip:port to access service for simple-service1 is 192.168.38.224:30000
- Kubernetes cluster deployed and managed on AWS EKS
- service docker images stored in AWS ECR repository
- Two deployments, each with replica of one pod
- Pods can communicate using the kubernetes service discovery
$ eksctl create cluster -f cluster.yaml [ℹ] eksctl version 0.13.0 [ℹ] using region eu-north-1 [ℹ] setting availability zones to [eu-north-1c eu-north-1b eu-north-1a] .....
using:
cluster.yaml
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: malex region: eu-west-1 nodeGroups: - name: ng-1 instanceType: m5.large desiredCapacity: 1 ssh: allow: true
$ kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME ip-192-168-42-23.eu-west-1.compute.internal Ready <none> 8m17s v1.14.8-eks-b8860f 192.168.42.23 18.202.253.46 Amazon Linux 2 4.14.154-128.181.amzn2.x86_64 docker://18.9.9
$ kubectl create -f deployment_service1.yml
using:
deployment_service1.yml
apiVersion: v1 kind: Service metadata: name: simple-service1 labels: app: simple-service1 spec: type: NodePort ports: - port: 30000 targetPort: 30000 protocol: TCP selector: app: simple-service1 --- apiVersion: apps/v1 kind: Deployment metadata: name: simple-service1 labels: app: simple-service1 spec: replicas: 1 selector: matchLabels: app: simple-service1 template: metadata: labels: app: simple-service1 spec: containers: - name: simple1 image: 210758531925.dkr.ecr.eu-west-1.amazonaws.com/simple-service:latest ports: - name: http-port containerPort: 30000 protocol: TCP env: - name: SERVICE_PORT value: "30000" - name: SERVICE_HOST value: "0.0.0.0" - name: NODE_ENV value: "development" - name: NEXT value: "simple-service2" - name: PROJECT_NAME value: "service1" - name: DATA value: "Dummy data from project 1" livenessProbe: httpGet: path: /v1/health port: http-port initialDelaySeconds: 5 timeoutSeconds: 30 readinessProbe: httpGet: path: /v1/health port: http-port initialDelaySeconds: 5 timeoutSeconds: 30
$ kubectl create -f deployment_service2.yml
using:
deployment_service2.yml
apiVersion: v1 kind: Service metadata: name: simple-service2 labels: app: simple-service2 spec: type: NodePort ports: - port: 30000 targetPort: 30000 protocol: TCP selector: app: simple-service2 --- apiVersion: apps/v1 kind: Deployment metadata: name: simple-service2 labels: app: simple-service2 spec: replicas: 1 selector: matchLabels: app: simple-service2 template: metadata: labels: app: simple-service2 spec: containers: - name: simple2 image: 210758531925.dkr.ecr.eu-west-1.amazonaws.com/simple-service:latest ports: - name: http-port containerPort: 30000 protocol: TCP env: - name: SERVICE_PORT value: "30000" - name: SERVICE_HOST value: "0.0.0.0" - name: NODE_ENV value: "development" - name: PROJECT_NAME value: "service2" - name: DATA value: "Dummy data from project 2" livenessProbe: httpGet: path: /v1/health port: http-port initialDelaySeconds: 5 timeoutSeconds: 30 readinessProbe: httpGet: path: /v1/health port: http-port initialDelaySeconds: 5 timeoutSeconds: 30
$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.100.0.1443/TCP 55m simple-service1 NodePort 10.100.255.92 30000:32698/TCP 37m simple-service2 NodePort 10.100.251.108 30000:30788/TCP 29m $ kubectl describe service simple-service1 Name: simple-service1 Namespace: default Labels: app=simple-service1 Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"simple-service1"},"name":"simple-service1","namespace":"default"},"sp... Selector: app=simple-service1 Type: NodePort IP: 10.100.255.92 Port: 30000/TCP TargetPort: 30000/TCP NodePort: 32698/TCP Endpoints: 192.168.38.224:30000 Session Affinity: None External Traffic Policy: Cluster Events: $ kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME ip-192-168-44-42.eu-west-1.compute.internal Ready <none> 46m v1.14.8-eks-b8860f 192.168.44.42 54.77.67.122 Amazon Linux 2 4.14.154-128.181.amzn2.x86_64 docker://18.9.9
from these commands we know now that:
$ ssh ec2-user@54.77.67.122 .... $ docker ps ..... $ curl 192.168.38.224:30000/v1/simple {"data":"Dummy data from project 1"} $ curl 192.168.38.224:30000/v1/complete {"data":"Dummy data from project 1, {\"data\":\"Dummy data from project 2\"}"}
So until now we have: