Skip to content

Commit

Permalink
Merge pull request #92 from stelligent/develop
Browse files Browse the repository at this point in the history
v0.1.7
  • Loading branch information
cplee authored Feb 7, 2017
2 parents f556e1d + d290633 commit be7fa52
Show file tree
Hide file tree
Showing 45 changed files with 1,230 additions and 396 deletions.
169 changes: 126 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://circleci.com/gh/stelligent/mu.svg?style=shield)](https://circleci.com/gh/stelligent/mu) [![Join the chat at https://gitter.im/stelligent/mu](https://badges.gitter.im/stelligent/mu.svg)](https://gitter.im/stelligent/mu?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://circleci.com/gh/stelligent/mu.svg?style=shield)](https://circleci.com/gh/stelligent/mu) [![Join the chat at https://gitter.im/stelligent/mu](https://badges.gitter.im/stelligent/mu.svg)](https://gitter.im/stelligent/mu?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/stelligent/mu)](https://goreportcard.com/report/github.com/stelligent/mu)



Expand All @@ -7,7 +7,9 @@ Amazon ECS (EC2 Container Service) provides an excellent platform for deploying

To address these challenges, this tool was created to simplify the declaration and administration of the AWS resources necessary to support microservices. Similar to how the [Serverless Framework](https://serverless.com/) improved the developer experience of Lambda and API Gateway, this tool makes it easier for developers to use ECS as a microservices platform.

For more details on the intended architecture, see [Microservices Platform with ECS](https://stelligent.com/2016/10/06/microservices-platform-with-ecs/).
The `mu` tool uses CloudFormation stacks to manage all resources it creates. Additionally, `mu` will not create any databases or other AWS resources to support itself. 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.

![Architecture Diagram](docs/ms-architecture-3.png)

# Installation

Expand All @@ -19,57 +21,27 @@ curl -s https://raw.githubusercontent.com/stelligent/mu/master/install.sh | sh
curl -s https://raw.githubusercontent.com/stelligent/mu/master/install.sh | INSTALL_VERSION=0.1.0 INSTALL_DIR=~/bin sh
```

# Commands
# Environments
Environments are defined to become a target for deploying services to. Each environment is a CloudFormation stack consisting of the following resources:

```
# List all environments
> mu env list
# Show details about a specific environment (ECS container instances, Running services, etc)
> mu env show <environment_name>
# Upsert an environment
> mu env up <environment_name>
# Terminate an environment
> mu env terminate <environment_name>
* **VPC** – To provide the network infrastructure to launch the ECS container instances into. Optionally, you can target an existing VPC.
* **ECS Cluster** – The cluster that the services will be deployed into.
* **Auto Scaling Group** – To manage the ECS container instances that contain the compute resources for running the containers. Auto scaling policies will be defined based on memory entitlements in the cluster.
* **Application Load Balancer** – To provide load balancing for the microservices running in containers.

# Show details about a specific service (Which versions in which environments, pipeline status)
> mu service show [-s <service_name>]
# Build docker image and push to ECR
> mu service push
# Deploy the service to an environment
> mu service deploy <environment_name>
# Undeploy the service from an environment
> mu service undeploy <environment_name> [-s <service_name>]
# List the pipelines
> mu pipeline list
# Upsert the pipeline
> mu pipeline up [-t <repo_token>]
# Terminate the pipeline
> mu pipeline terminate [-s <service_name>]
```

# Configuration
The definition of your environments, services and pipelines is done via a YAML file (default `./mu.yml`).
![Environment Diagram](docs/ms-architecture-1.png)

## Configuration
```
---
### Region to utilize
region: us-west-2
### Define a list of environments
environments:
# The unique name of the environment (required)
- name: dev
### Attributes for the ECS container instances
cluster:
imageId: ami-xxxxxx # The AMI to use for the ECS container instances (default: latest ECS optimized AMI)
Expand All @@ -78,16 +50,58 @@ environments:
maxSize: 2 # Max size to scale the ECS ASG to (default: 2)
keyName: my-keypair # name of EC2 keypair to associate with ECS container instances (default: none)
sshAllow: 0.0.0.0/0 # CIDR block to allow SSH access from (default: 0.0.0.0/0)
httpProxy: 10.0.0.43:8080 # Host and port to use for HTTP proxy for yum, docker images, and ECS (default: none)
scaleOutThreshold: 80 # Threshold for % memory utilization to scale out ECS container instances (default: 80)
scaleInThreshold: 30 # Threshold for % memory utilization to scale in ECS container instances (default: 30)
### attributes for the VPC to target. If not defined, a VPC will be created. (default: none)
### Attributes for the ELB
loadBalancer:
internal: true # Whether to create an internal ELB or not (default: false)
hostedzone: mydomain.com # HostedZone in Route53 to create ELB DNS for. Leave blank to not create DNS (default: none)
name: api # Name to register in hostedzone for ELB DNS. (default: environment name)
certificate: arn:aws:acm:... # The ARN of a certificate in ACM. If defined, will create HTTPS listener in ELB. (default: none)
### Attributes for the VPC to target. If not defined, a VPC will be created. (default: none)
vpcTarget:
vpcId: vpc-xxxxx # The id of the VPC to launch ECS container instances into
publicSubnetIds: # The list of subnets to use for ECS container instances
ecsSubnetIds: # The list of subnets to use for ECS container instances
- subnet-xxxxx
- subnet-xxxxy
- subnet-xxxxz
elbSubnetIds: # The list of subnets to use for ELBs
- subnet-xxxxx
- subnet-xxxxy
- subnet-xxxxz
```

## Commands
```
# List all environments
> mu env list
# Show details about a specific environment (ECS container instances, Running services, etc)
> mu env show <environment_name>
# Upsert an environment
> mu env up <environment_name>
# Terminate an environment
> mu env terminate <environment_name>
```

# Services
Services are first pushed to an ECR repository and then deployed to a specific environment. Each service is a CloudFormation stack consisting of the following resources:

* **Task Definition** – An ECS task definition referencing the image and tag in the ECR repo.
* **Service** - An ECS service referencing the Task Definition.
* **Target Group** - An ALB target group for the Service to reference and register containers in.
* **Listener Rule** - A rule in the ALB listener from the environment to route specific URLs to the target group.

![Service Diagram](docs/ms-architecture-2.png)

## Configuration
```
---
### Define the service for this repo
service:
Expand All @@ -105,6 +119,46 @@ service:
- /bananas
- /apples
# The priority for resolving the pathPatterns from the ALB (between 1 and 99999)
priority: 25
```

## Commands
```
# Show details about a specific service (Which versions in which environments, pipeline status)
> mu service show [<service_name>]
# Build docker image and push to ECR
> mu service push
# Deploy the service to an environment
> mu service deploy <environment_name>
# Undeploy the service from an environment
> mu service undeploy <environment_name> [<service_name>]
```

# Pipelines
A pipeline can be created for each service that consists of the following steps:

* **Source** - Retrieve source from GitHub for a specific branch. Triggered on each commit.
* **Build Artifact** - Compile the source code via CodeBuild and a `buildspec.yml`.
* **Build Image** - Build the Docker image and push to ECR repository.
* **Acceptance** - Deploy to acceptance environment and run automated tests.
* **Production** - Wait for manual approval, then deploy to production environment.

<a href="docs/ms-pipeline-1.png"><img src="docs/ms-pipeline-1.png" width="110"></a>


## Configuration
```
---
service:
name: my-service
# ... service config goes here ...
# Define the behavior of the pipeline
pipeline:
source:
Expand All @@ -120,7 +174,36 @@ service:
environment: production # The environment name to deploy to for production (default: production)
```

## Commands
```
# List the pipelines
> mu pipeline list
# Upsert the pipeline
> mu pipeline up [-t <repo_token>]
# Terminate the pipeline
> mu pipeline terminate [<service_name>]
```

# Common flags
```
# Path to mu config
> mu -c path/to/mu.yml ...
# AWS region
> mu -r us-west-2 ...
# or via environment variable
> AWS_REGION=us-west-2 mu ...
# AWS profile
> mu -p my-profile ...
# or via environment variable
> AWS_PROFILE=my-profie mu ...
```

# Contributing

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.6
0.1.7
25 changes: 24 additions & 1 deletion cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"github.com/stelligent/mu/common"
"github.com/urfave/cli"
"io/ioutil"
)

// NewApp creates a new CLI app
Expand Down Expand Up @@ -33,7 +34,21 @@ func NewApp() *cli.App {
}

// initialize context
return context.InitializeFromFile(c.String("config"))
err := context.InitializeContext(c.String("profile"), c.String("region"))
if err != nil {
return err
}

if c.Bool("silent") {
context.DockerOut = ioutil.Discard
}

err = context.InitializeConfigFromFile(c.String("config"))
if err != nil {
log.Warningf("Unable to load mu config: %v", err)
}
return nil

}

app.Flags = []cli.Flag{
Expand All @@ -42,6 +57,14 @@ func NewApp() *cli.App {
Usage: "path to config file",
Value: "mu.yml",
},
cli.StringFlag{
Name: "region, r",
Usage: "AWS Region to use",
},
cli.StringFlag{
Name: "profile, p",
Usage: "AWS config profile to use",
},
cli.BoolFlag{
Name: "silent, s",
Usage: "silent mode, errors only",
Expand Down
8 changes: 5 additions & 3 deletions cli/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ func TestNewApp(t *testing.T) {
assert.Equal("0.0.0-local", app.Version, "Version should match")
assert.Equal("Microservice Platform on AWS", app.Usage, "usage should match")
assert.Equal(true, app.EnableBashCompletion, "bash completion should match")
assert.Equal(3, len(app.Flags), "Flags len should match")
assert.Equal(5, len(app.Flags), "Flags len should match")
assert.Equal("config, c", app.Flags[0].GetName(), "Flags name should match")
assert.Equal("silent, s", app.Flags[1].GetName(), "Flags name should match")
assert.Equal("verbose, V", app.Flags[2].GetName(), "Flags name should match")
assert.Equal("region, r", app.Flags[1].GetName(), "Flags name should match")
assert.Equal("profile, p", app.Flags[2].GetName(), "Flags name should match")
assert.Equal("silent, s", app.Flags[3].GetName(), "Flags name should match")
assert.Equal("verbose, V", app.Flags[4].GetName(), "Flags name should match")
assert.Equal(3, len(app.Commands), "Commands len should match")
assert.Equal("environment", app.Commands[0].Name, "Command[0].name should match")
assert.Equal("service", app.Commands[1].Name, "Command[1].name should match")
Expand Down
5 changes: 5 additions & 0 deletions cli/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cli

import "github.com/op/go-logging"

var log = logging.MustGetLogger("cli")
15 changes: 5 additions & 10 deletions cli/pipelines.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,12 @@ func newPipelinesListCommand(ctx *common.Context) *cli.Command {

func newPipelinesTerminateCommand(ctx *common.Context) *cli.Command {
cmd := &cli.Command{
Name: "terminate",
Aliases: []string{"term"},
Usage: "terminate pipeline",
Flags: []cli.Flag{
cli.StringFlag{
Name: "service, s",
Usage: "service to terminate pipeline",
},
},
Name: "terminate",
Aliases: []string{"term"},
Usage: "terminate pipeline",
ArgsUsage: "[<service>]",
Action: func(c *cli.Context) error {
service := c.String("service")
service := c.Args().First()
workflow := workflows.NewPipelineTerminator(ctx, service)
return workflow()
},
Expand Down
3 changes: 1 addition & 2 deletions cli/pipelines_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ func TestNewPipelinesTerminateCommand(t *testing.T) {

assert.NotNil(command)
assert.Equal("terminate", command.Name, "Name should match")
assert.Equal(1, len(command.Flags), "Flag len should match")
assert.Equal("service, s", command.Flags[0].GetName(), "Flag should match")
assert.Equal("[<service>]", command.ArgsUsage, "ArgsUsage should match")
assert.NotNil(command.Action)
}
func TestNewPipelinesUpsertCommand(t *testing.T) {
Expand Down
Loading

0 comments on commit be7fa52

Please sign in to comment.