mirror of
https://github.com/docker/compose.git
synced 2025-07-15 17:54:29 +02:00
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpCommand(clusteropts *clusterOptions, opts *compose.ProjectOptions) *cobra.Command {
|
func UpCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "up",
|
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)
|
client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ecs"
|
"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/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/docker/ecs-plugin/pkg/compose"
|
"github.com/docker/ecs-plugin/pkg/compose"
|
||||||
)
|
)
|
||||||
@ -31,6 +32,7 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro
|
|||||||
sess: sess,
|
sess: sess,
|
||||||
ECS: ecs.New(sess),
|
ECS: ecs.New(sess),
|
||||||
EC2: ec2.New(sess),
|
EC2: ec2.New(sess),
|
||||||
|
ELB: elbv2.New(sess),
|
||||||
CW: cloudwatchlogs.New(sess),
|
CW: cloudwatchlogs.New(sess),
|
||||||
IAM: iam.New(sess),
|
IAM: iam.New(sess),
|
||||||
}, nil
|
}, nil
|
||||||
@ -42,6 +44,7 @@ type client struct {
|
|||||||
sess *session.Session
|
sess *session.Session
|
||||||
ECS *ecs.ECS
|
ECS *ecs.ECS
|
||||||
EC2 *ec2.EC2
|
EC2 *ec2.EC2
|
||||||
|
ELB *elbv2.ELBV2
|
||||||
CW *cloudwatchlogs.CloudWatchLogs
|
CW *cloudwatchlogs.CloudWatchLogs
|
||||||
IAM *iam.IAM
|
IAM *iam.IAM
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *client) ComposeDown(project *compose.Project) error {
|
func (c *client) ComposeDown(project *compose.Project) error {
|
||||||
|
err := c.DeleteLoadBalancer(project)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
services := []*string{}
|
services := []*string{}
|
||||||
// FIXME we should be able to retrieve services by tags, so we don't need the initial compose file to run "down"
|
// 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 {
|
for _, service := range project.Services {
|
||||||
@ -23,18 +28,17 @@ func (c *client) ComposeDown(project *compose.Project) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
services = append(services, out.Service.ServiceArn)
|
||||||
logrus.Debugf("Service deleted %q\n", *out.Service.ServiceName)
|
|
||||||
services = append(services, out.Service.ServiceName)
|
|
||||||
}
|
}
|
||||||
logrus.Info("All services stopped")
|
|
||||||
|
|
||||||
err := c.ECS.WaitUntilServicesInactive(&ecs.DescribeServicesInput{
|
logrus.Info("Stopping services...")
|
||||||
|
err = c.ECS.WaitUntilServicesInactive(&ecs.DescribeServicesInput{
|
||||||
Services: services,
|
Services: services,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
logrus.Info("All services stopped")
|
||||||
|
|
||||||
logrus.Debug("Deleting security groups")
|
logrus.Debug("Deleting security groups")
|
||||||
groups, err := c.EC2.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
|
groups, err := c.EC2.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
|
||||||
|
133
ecs/pkg/amazon/loadBalancer.go
Normal file
133
ecs/pkg/amazon/loadBalancer.go
Normal file
@ -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
|
package amazon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ecs"
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
@ -40,13 +41,37 @@ func (c *client) ComposeUp(project *compose.Project) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadBalancer, err := c.CreateLoadBalancer(project, subnets)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
logGroup, err := c.GetOrCreateLogGroup(project)
|
logGroup, err := c.GetOrCreateLogGroup(project)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mapping := range mappings {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -54,7 +79,7 @@ func (c *client) ComposeUp(project *compose.Project) error {
|
|||||||
return nil
|
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)
|
role, err := c.GetEcsTaskExecutionRole(service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -88,6 +113,7 @@ func (c *client) CreateService(project *compose.Project, service *types.ServiceC
|
|||||||
ServiceName: aws.String(service.Name),
|
ServiceName: aws.String(service.Name),
|
||||||
SchedulingStrategy: aws.String(ecs.SchedulingStrategyReplica),
|
SchedulingStrategy: aws.String(ecs.SchedulingStrategyReplica),
|
||||||
TaskDefinition: arn,
|
TaskDefinition: arn,
|
||||||
|
LoadBalancers: ingress,
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, port := range service.Ports {
|
for _, port := range service.Ports {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user