mirror of https://github.com/docker/compose.git
Expose services using a LoadBalancer
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
17f3ff9db1
commit
a44ee2a4ed
|
@ -76,10 +76,10 @@ func ComposeCommand(clusteropts *clusterOptions) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func UpCommand(clusteropts *clusterOptions, opts *compose.ProjectOptions) *cobra.Command {
|
||||
func UpCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "up",
|
||||
RunE: compose.WithProject(opts, func(project *compose.Project, args []string) error {
|
||||
RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error {
|
||||
client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ecs"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/docker/ecs-plugin/pkg/compose"
|
||||
)
|
||||
|
@ -31,6 +32,7 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro
|
|||
sess: sess,
|
||||
ECS: ecs.New(sess),
|
||||
EC2: ec2.New(sess),
|
||||
ELB: elbv2.New(sess),
|
||||
CW: cloudwatchlogs.New(sess),
|
||||
IAM: iam.New(sess),
|
||||
}, nil
|
||||
|
@ -42,6 +44,7 @@ type client struct {
|
|||
sess *session.Session
|
||||
ECS *ecs.ECS
|
||||
EC2 *ec2.EC2
|
||||
ELB *elbv2.ELBV2
|
||||
CW *cloudwatchlogs.CloudWatchLogs
|
||||
IAM *iam.IAM
|
||||
}
|
||||
|
|
|
@ -9,6 +9,11 @@ import (
|
|||
)
|
||||
|
||||
func (c *client) ComposeDown(project *compose.Project) error {
|
||||
err := c.DeleteLoadBalancer(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
services := []*string{}
|
||||
// FIXME we should be able to retrieve services by tags, so we don't need the initial compose file to run "down"
|
||||
for _, service := range project.Services {
|
||||
|
@ -23,18 +28,17 @@ func (c *client) ComposeDown(project *compose.Project) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("Service deleted %q\n", *out.Service.ServiceName)
|
||||
services = append(services, out.Service.ServiceName)
|
||||
services = append(services, out.Service.ServiceArn)
|
||||
}
|
||||
logrus.Info("All services stopped")
|
||||
|
||||
err := c.ECS.WaitUntilServicesInactive(&ecs.DescribeServicesInput{
|
||||
logrus.Info("Stopping services...")
|
||||
err = c.ECS.WaitUntilServicesInactive(&ecs.DescribeServicesInput{
|
||||
Services: services,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Info("All services stopped")
|
||||
|
||||
logrus.Debug("Deleting security groups")
|
||||
groups, err := c.EC2.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package amazon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/docker/ecs-plugin/pkg/compose"
|
||||
"github.com/sirupsen/logrus"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
func (c client) CreateLoadBalancer(project *compose.Project, subnets []*string) (*string, error) {
|
||||
logrus.Debug("Create Load Balancer")
|
||||
alb, err := c.ELB.CreateLoadBalancer(&elbv2.CreateLoadBalancerInput{
|
||||
IpAddressType: nil,
|
||||
Name: aws.String(fmt.Sprintf("%s-LoadBalancer", project.Name)),
|
||||
Subnets: subnets,
|
||||
Type: aws.String(elbv2.LoadBalancerTypeEnumNetwork),
|
||||
Tags: []*elbv2.Tag{
|
||||
{
|
||||
Key: aws.String("com.docker.compose.project"),
|
||||
Value: aws.String(project.Name),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return alb.LoadBalancers[0].LoadBalancerArn, nil
|
||||
}
|
||||
|
||||
func (c client) DeleteLoadBalancer(project *compose.Project) error {
|
||||
logrus.Debug("Delete Load Balancer")
|
||||
// FIXME We can tag LoadBalancer but not search by tag ?
|
||||
loadBalancer, err := c.ELB.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{
|
||||
Names: aws.StringSlice([]string{fmt.Sprintf("%s-LoadBalancer", project.Name)}),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arn := loadBalancer.LoadBalancers[0].LoadBalancerArn
|
||||
|
||||
err = c.DeleteListeners(arn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.DeleteTargetGroups(arn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.ELB.DeleteLoadBalancer(&elbv2.DeleteLoadBalancerInput{LoadBalancerArn: arn})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c client) CreateTargetGroup(name string, vpc *string, port types.ServicePortConfig) (*string, error) {
|
||||
logrus.Debugf("Create Target Group %d/%s\n", port.Target, port.Protocol)
|
||||
group, err := c.ELB.CreateTargetGroup(&elbv2.CreateTargetGroupInput{
|
||||
Name: aws.String(name),
|
||||
Port: aws.Int64(int64(port.Target)),
|
||||
Protocol: aws.String(strings.ToUpper(port.Protocol)),
|
||||
TargetType: aws.String("ip"),
|
||||
VpcId: vpc,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arn := group.TargetGroups[0].TargetGroupArn
|
||||
return arn, nil
|
||||
}
|
||||
|
||||
func (c client) DeleteTargetGroups(loadBalancer *string) error {
|
||||
groups, err := c.ELB.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{
|
||||
LoadBalancerArn: loadBalancer,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups.TargetGroups {
|
||||
logrus.Debugf("Delete Target Group %s\n", *group.TargetGroupArn)
|
||||
_, err := c.ELB.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{
|
||||
TargetGroupArn: group.TargetGroupArn,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c client) CreateListener(port types.ServicePortConfig, arn *string, target *string) error {
|
||||
logrus.Debugf("Create Listener %d\n", port.Published)
|
||||
_, err := c.ELB.CreateListener(&elbv2.CreateListenerInput{
|
||||
DefaultActions: []*elbv2.Action{
|
||||
{
|
||||
ForwardConfig: &elbv2.ForwardActionConfig{
|
||||
TargetGroups: []*elbv2.TargetGroupTuple{
|
||||
{
|
||||
TargetGroupArn: target,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: aws.String(elbv2.ActionTypeEnumForward),
|
||||
},
|
||||
},
|
||||
LoadBalancerArn: arn,
|
||||
Port: aws.Int64(int64(port.Published)),
|
||||
Protocol: aws.String(strings.ToUpper(port.Protocol)),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c client) DeleteListeners(loadBalancer *string) error {
|
||||
listeners, err := c.ELB.DescribeListeners(&elbv2.DescribeListenersInput{
|
||||
LoadBalancerArn: loadBalancer,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, listener := range listeners.Listeners {
|
||||
logrus.Debugf("Delete Listener %s\n", *listener.ListenerArn)
|
||||
_, err := c.ELB.DeleteListener(&elbv2.DeleteListenerInput{
|
||||
ListenerArn: listener.ListenerArn,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package amazon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ecs"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
|
@ -40,13 +41,37 @@ func (c *client) ComposeUp(project *compose.Project) error {
|
|||
return err
|
||||
}
|
||||
|
||||
loadBalancer, err := c.CreateLoadBalancer(project, subnets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logGroup, err := c.GetOrCreateLogGroup(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, mapping := range mappings {
|
||||
_, err = c.CreateService(project, mapping.service, mapping.task, securityGroup, subnets, logGroup)
|
||||
ingress := []*ecs.LoadBalancer{}
|
||||
for _, port := range mapping.service.Ports {
|
||||
name := fmt.Sprintf("%s-%s-%d-%s", project.Name, mapping.service.Name, port.Target, port.Protocol)
|
||||
targetgroup, err := c.CreateTargetGroup(name, vpc, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ingress = append(ingress, &ecs.LoadBalancer{
|
||||
ContainerName: aws.String(mapping.service.Name),
|
||||
ContainerPort: aws.Int64(int64(port.Target)),
|
||||
TargetGroupArn: targetgroup,
|
||||
})
|
||||
|
||||
err = c.CreateListener(port, loadBalancer, targetgroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = c.CreateService(project, mapping.service, mapping.task, securityGroup, subnets, logGroup, ingress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -54,7 +79,7 @@ func (c *client) ComposeUp(project *compose.Project) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *client) CreateService(project *compose.Project, service *types.ServiceConfig, task *ecs.RegisterTaskDefinitionInput, securityGroup *string, subnets []*string, logGroup *string) (*string, error) {
|
||||
func (c *client) CreateService(project *compose.Project, service *types.ServiceConfig, task *ecs.RegisterTaskDefinitionInput, securityGroup *string, subnets []*string, logGroup *string, ingress []*ecs.LoadBalancer) (*string, error) {
|
||||
role, err := c.GetEcsTaskExecutionRole(service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -88,6 +113,7 @@ func (c *client) CreateService(project *compose.Project, service *types.ServiceC
|
|||
ServiceName: aws.String(service.Name),
|
||||
SchedulingStrategy: aws.String(ecs.SchedulingStrategyReplica),
|
||||
TaskDefinition: arn,
|
||||
LoadBalancers: ingress,
|
||||
})
|
||||
|
||||
for _, port := range service.Ports {
|
||||
|
|
Loading…
Reference in New Issue