Introducing mu: a tool for managing your microservices in AWS
mu is a tool that Stelligent has created to make it simple and cost-efficient for developers to use AWS as the platform for running their microservices. In this first post of the blog series focused on the mu tool, we will be introducing the motivation for the tool and demonstrating the deployment of a microservice with it.
Why microservices?
The architectural pattern of decomposing an application into microservices has proven extremely effective at increasing an organization’s ability to deliver software faster. This is due to the fact that microservices are independently deployable components that are decoupled from other components and highly cohesive around a single business capability. Those attributes of a microservice yield smaller team sizes that are able to operate with a high level of autonomy to deliver what the business wants at the pace the market demands.
What’s the catch?
When teams begin their journey with microservices, they usually face cost duplication on two fronts: infrastructure and re-engineering. The first duplication cost is found in the “infrastructure overhead” used to support the microservice deployment. For example, if you are deploying your microservices on AWS EC2 instances, then for each microservice, you need a cluster of EC2 instances to ensure adequate capacity and tolerance to failures. If a single microservice requires 12 t2.small instances to meet capacity requirements and we want to be able to survive an outage in 1 out of 4 availability zones, then we would need to run 16 instances total, 4 per availability zone. This leaves an overhead cost of 4 t2.small instances. Then multiply this cost by the number of microservices for a given application and it is easy to see that the overhead cost of microservices deployed in this manner can add up quickly.
Containers to the rescue!
An approach to addressing this challenge of overhead costs is to use containers for deploying microservices. Each microservice would be deployed as a series of containers to a cluster of hosts that is shared by all microservices. This allows for greater density of microservices on EC2 instances and allows the overhead to be shared by all microservices. Amazon ECS (EC2 Container Service) provides an excellent platform for deploying microservices as containers. ECS leverages many AWS services to provide a robust container management solution. Additionally, a developer can use tools like CodeBuild and CodePipeline to create continuous delivery pipelines for their microservices.
That sounds complicated…
This approach leads to the second duplication cost of microservices: the cost of “reengineering”. There is a significant learning curve for developers to learn how to use all these different AWS resources to deploy their microservices in an efficient manner. If each team is using their autonomy to engineer a platform on AWS for their microservices then a significant level of engineering effort is being duplicated. This duplication not only causes additional engineering costs, but also impedes a team’s ability to deliver the differentiating business capabilities that they were commissioned to do in the first place.
Let mu help!
To address these challenges, mu was created to simplify the declaration and administration of the AWS resources necessary to support microservices. mu is a tool that a developer uses from their workstation to deploy their microservices to AWS quickly and efficiently as containers. It codifies best practices for microservices, containers and continuous delivery pipelines into the AWS resources it creates on your behalf. It does this from a simple CLI application that can be installed on the developer’s workstation in seconds. Similar to how the Serverless Framework improved the developer experience of Lambda and API Gateway, this tool makes it easier for developers to use ECS as a microservices platform.
Additionally, mu does not require any servers, databases or other AWS resources to support itself. All state information is managed via CloudFormation stacks. It will only create resources (via CloudFormation) necessary to run your microservices. This means at any point you can stop using mu and continue to manage the AWS resources that it created via AWS tools such as the CLI or the console.
Core components
The mu tool consists of three main components:
- Environments – an environment includes a shared network (VPC) and cluster of hosts (ECS and EC2 instances) necessary to run microservices as clusters. The environments include the ability to automatically scale out or scale in based on resource requirements across all the microservices that are deployed to it. Many environments can exist (e.g. development, staging, production)
- Services – a microservice that will be deployed to a given environment (or environments) as a set of containers.
- Pipeline – a continuous delivery pipeline that will manage the building, testing, and deploying of a microservice in the various environments.
Installing and configuring mu
First let’s install mu:
$ curl -s http://getmu.io/install.sh | sh
If you’re appalled at the idea of curl | bash installers, then you can always just download the latest version directly.
mu will use the same mechanism as aws-cli to authenticate with the AWS services. If you haven’t configured your AWS credentials yet, the easiest way to configure them is to install the aws-cli and then follow the aws configure instructions:
$ aws configure AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY Default region name [None]: us-west-2 Default output format [None]: json
Setup your microservice
In order for mu to setup a continuous delivery pipeline for your microservice, you’ll need to run mu from within a git repo. For this demo, we’ll be using the stelligent/banana-service repo for our microservice. If you want to follow along and try this on your own, you’ll want to fork the repo and clone your fork.
Let’s begin with cloning the microservice repo:
$ git clone git@github.com:myuser/banana-service.git $ cd banana-service
Next, we will initialize mu configuration for our microservice:
$ mu init --env Writing config to '/Users/casey.lee/Dev/mu/banana-service/mu.yml' Writing buildspec to '/Users/casey.lee/Dev/mu/banana-service/buildspec.yml'
We need to update the mu.yml that was generated with the URL paths that we want to route to this microservice and the CodeBuild image to use:
environments: - name: acceptance - name: production service: name: banana-service port: 8080 pathPatterns: - /bananas pipeline: source: provider: GitHub repo: myuser/banana-service build: image: aws/codebuild/java:openjdk-8
Next, we need to update the generated buildspec.yml to include the gradle build command:
version: 0.1 phases: build: commands: - gradle build artifacts: files: - '**/*'
Finally, commit and push our changes:
$ git add --all && git commit -m "mu init" && git push
Create the pipeline
Make sure you have GitHub token with repo and admin:repo_hook scopes to provide to the pipeline in order to integrate with your GitHub repo. Then you can create the pipeline:
$ mu pipeline up Upserting Bucket for CodePipeline Upserting Pipeline for service 'banana-service' ... GitHub token: XXXXXXXXXXXXXXX
Now that the pipeline is created, it will build and deploy for every commit to your git repo. You can monitor the status of the pipeline as it builds and deploys the microservice:
$ mu svc show Pipeline URL: https://console.aws.amazon.com/codepipeline/home?region=us-west-2#/view/mu-pipeline-banana-service-Pipeline-1B3A94CZR6WH +------------+----------+------------------------------------------+-------------+---------------------+ | STAGE | ACTION | REVISION | STATUS | LAST UPDATE | +------------+----------+------------------------------------------+-------------+---------------------+ | Source | Source | 1f1b09f0bbc3f42170b8d32c68baf683f1e3f801 | Succeeded | 2017-04-07 15:12:35 | | Build | Artifact | - | Succeeded | 2017-04-07 15:14:49 | | Build | Image | - | Succeeded | 2017-04-07 15:19:02 | | Acceptance | Deploy | - | InProgress | 2017-04-07 15:19:07 | | Acceptance | Test | - | - | - | | Production | Approve | - | - | - | | Production | Deploy | - | - | - | | Production | Test | - | - | - | +------------+----------+------------------------------------------+-------------+---------------------+ Deployments: +-------------+-------+-------+--------+-------------+------------+ | ENVIRONMENT | STACK | IMAGE | STATUS | LAST UPDATE | MU VERSION | +-------------+-------+-------+--------+-------------+------------+ +-------------+-------+-------+--------+-------------+------------+
You can also monitor the build logs:
$ mu pipeline logs -f [Container] 2017/04/07 22:25:43 Running command mu -c mu.yml svc deploy acceptance [Container] 2017/04/07 22:25:43 Upsert repo for service 'banana-service' [Container] 2017/04/07 22:25:43 No changes for stack 'mu-repo-banana-service' [Container] 2017/04/07 22:25:43 Deploying service 'banana-service' to 'dev' from '324320755747.dkr.ecr.us-west-2.amazonaws.com/banana-service:1f1b09f'
Once the pipeline has completed deployment of the service, you can view logs from service:
$ mu service logs -f acceptance . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' | ____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.4.0.RELEASE) 2017-04-07 22:30:08.788 INFO 5 --- [ main] com.stelligent.BananaApplication : Starting BananaApplication on 6a4d5544d9de with PID 5 (/app.jar started by root in /) 2017-04-07 22:30:08.824 INFO 5 --- [ main] com.stelligent.BananaApplication : No active profile set, falling back to default profiles: default 2017-04-07 22:30:09.342 INFO 5 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@108c4c35: startup date [Fri Apr 07 22:30:09 UTC 2017]; root of context hierarchy 2017-04-07 22:30:09.768 INFO 5 --- [ main] com.stelligent.BananaApplication : Starting BananaApplication on 7818361f6f45 with PID 5 (/app.jar started by root in /)
Testing the service
Finally, we can get the information about the ELB endpoint in the acceptance environment to test the service:
$ mu env show acceptance Environment: acceptance Cluster Stack: mu-cluster-dev (UPDATE_COMPLETE) VPC Stack: mu-vpc-dev (UPDATE_COMPLETE) Bastion Host: Base URL: http://mu-cl-EcsEl-1K74542METR82-1781937931.us-west-2.elb.amazonaws.com Container Instances: +---------------------+----------+--------------+------------+-----------+--------+---------+-----------+-----------+ | EC2 INSTANCE | TYPE | AMI | AZ | CONNECTED | STATUS | # TASKS | CPU AVAIL | MEM AVAIL | +---------------------+----------+--------------+------------+-----------+--------+---------+-----------+-----------+ | i-093b788b4f39dd14b | t2.micro | ami-62d35c02 | us-west-2a | true | ACTIVE | 3 | 604 | 139 | +---------------------+----------+--------------+------------+-----------+--------+---------+-----------+-----------+ Services: +----------------+---------------------------------------------------------------------+------------------+---------------------+ | SERVICE | IMAGE | STATUS | LAST UPDATE | +----------------+---------------------------------------------------------------------+------------------+---------------------+ | banana-service | 324320755747.dkr.ecr.us-west-2.amazonaws.com/banana-service:1f1b09f | CREATE_COMPLETE | 2017-04-07 15:25:43 | +----------------+---------------------------------------------------------------------+------------------+---------------------+
$ curl -s http://mu-cl-EcsEl-1K74542METR82-1781937931.us-west-2.elb.amazonaws.com/bananas | jq [ { "pickedAt": "2017-04-10T10:34:27.911", "peeled": false, "links": [ { "rel": "self", "href": "http://mu-cl-EcsEl-1K74542METR82-1781937931.us-west-2.elb.amazonaws.com/bananas/1" } ] } ]
Cleanup
To cleanup the resources that mu created, run the following commands:
$ mu pipeline term
$ mu env term acceptance
$ mu env term production
Conclusion
As you can see, mu addresses infrastructure and engineering overhead costs associated with microservices. It makes deployment of microservices via containers simple and cost-efficient. Additionally, it ensures the deployments are repeatable and non-dramatic by utilizing a continuous delivery pipeline for orchestrating the flow of software changes into production.
In the upcoming posts in this blog series, we will look into:
- Test Automation – add test automation to the continuous delivery pipeline with mu
- Custom Resources – create custom resources like DynamoDB with mu during our microservice deployment
- Service Discovery – use mu to enable service discovery via Consul to allow for inter-service communication
- Additional Use Cases – deploy applications other than microservices via mu, like a wordpress stack
Until then, head over to stelligent/mu on GitHub and get started. Keep in touch with us in our Gitter room and share your feedback!
Additional Resources
- Microservices Platform with ECS – Blog post demonstrating the use of ECS for running Microservices. Shows the level of engineering effort required to run microservices on ECS without a tool like mu.
- Automating ECS: Provisioning in CloudFormation (Part 1) – Blog post showing how to use CloudFormation to provision ECS infrastructure.
- Automating ECS: Orchestrating in CodePipeline and CloudFormation (Part 2) – Continuation of previous post that includes the use of CodePipeline to manage deployment to ECS
- An Introduction to AWS CodeBuild – Blog post introducing the CodeBuild service
- Deploy to Production using AWS CodeBuild and the AWS Developer Tools Suite – In depth post demonstrating the capabilities of AWS CodeBuild
- cplee/banana-service – The final state of my repo that was used for this post, forked from stelligent/banana-service.
Did you find this post interesting? Are you passionate about working with the latest AWS technologies? If you are Stelligent is hiring and we would love to hear from you!
Stelligent Amazon Pollycast
|