-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow any process to expose an HTTP/TCP service #800
Conversation
To account for this, I've considered changing how we name ECS services to include a hash of the exposure settings. So if you change a processes exposure, it'll be a new ECS service and new ELB (like Probably wouldn't do that in this PR though. |
opts.SSLCert = e.Cert | ||
case *scheduler.HTTPExposure: | ||
default: | ||
return nil, fmt.Errorf("can't create %s load balancer", e.Protocol()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO
Ok, I think this is probably mergable once #801 is merged. I've addressed all of the todo's, added some tests and added a migration. |
|
||
```yaml | ||
expose: | ||
type: tcp // Possible options are tcp/http/https/ssl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One scenario that I haven't really thought through with this yet is when you want to have multiple listeners. For example, our existing behavior when creating https load balancers is to create both an https and an http listener. Maybe we should do something like this:
web:
visibility: public
ports:
- 80:80 # http listener
- 443:80 # https listener
- "1234:1234" # tcp listener
- # ??? not really a good way to infer an ssl listener.
This might be nice because it offers pretty close parity with ports
in docker-compose.yml.
The awkward bit is how we communicate the protocol when it can't be inferred. We could extend the port to be a map, like so:
web:
visibility: public
ports:
- "80:8080":
protocol: http
- "443:8080":
protocol: https
- "1234:8081":
protocol: tcp
- "4321:8081":
protocol: ssl
This would also mean we could avoid setting the PORT environment variable and just let containers bind to the port they specify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I need to check if this would even be possible with ECS. To do this, I think we'd need to allocate an instance port per container port (e.g. port 8080 and port 8081 get different instance ports). Last time I checked, you could only add 1 LoadBalancer object when creating an ECS service, and the load balancers can't be updated once the service is created.
Maybe the format above is good, but we just need some validations for ECS/ELB limitations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tested this with ECS. It looks like it's possible, but their docs don't make it very intuitive. When we create an ECS, we provide an array of ecs.LoadBalancer. That type makes you provide a ContainerPort and ContainerName, but those don't seem to serve any purpose other than validation (ensure that the task definition has a matching port mapping). So, if we create a load balancer with multiple listeners, and a task definition with multiple port mappings, that should be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like having the ability for each port mapping to define its protocol.
you could also support inference for the http/https use case just because it is pretty common.
ie:
web:
visibility: public
ports:
- 80:8080
- 443:8080
- 4321:8081
protocol: tcp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sooo looking forward to this
looks awesome 👍 |
For the record. Putting this on hold until CloudFormation is the default scheduling backend, which will make everything we want to do with extended Procfile much simpler. |
06bdabe
to
15f811e
Compare
I've updated this with complete support in the CloudFormation backend. You can now define a Procfile like this: web:
command: acme-inc server
ports:
- "80:80"
- "8080:8080":
protocol: "tcp"
api:
command: acme-inc api
ports:
- "80:80" Allowing you to define pretty sophisticated scenarios. HTTP, HTTPS, TCP and SSL protocols are all supported. The web:
ports:
- "80:8080":
protocol: "http" And web is still special, in that it gets a CNAME at Also, the visibility ( web:
visibility: public
ports:
- "80:8080" If you wanna try it out, you can run this and deploy the This requires some changes in |
👍 awesome |
15f811e
to
90e2f38
Compare
90e2f38
to
d57e899
Compare
Ok, rebased this. This can be merged once #1001 has been merged. |
6d89999
to
68e2e60
Compare
ALB wasn't released when I originally opened this, and there are some limitations when using ALB (see the last commit). Basically, when using ALB + dynamic port mapping, you cannot have multiple listeners going to multiple container ports. It makes sense when you think about it; ECS is registering the container in a target group with a single dynamically allocated port, so alb has no way to know how to route to multiple ports. The below will work fine when using ELB + instance ports, however, if ALB is enabled, it will yield an error now: web:
ports:
- "80:80"
- "8080:8080" This is totally fine with ALB + dynamic ports (all host ports map to a single container port): web:
ports:
- "80:8080"
- "443:8080":
protocol: https
- "8080:8080"
|
68e2e60
to
2ace70f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 small comment, LGTM though
exposure = standardWebExposure(release.App) | ||
env["PORT"] = "8080" | ||
} else { | ||
exposure = processExposure(release.App, p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there no way to share the port info? SEems like we should keep the env var around, no matter what, if at all possible. Users may be relying on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do something like what docker-compose does, where you would have environment vars like:
PORT_80_PORT
PORT_443_PORT
PORT_8080_PORT
(PORT
doesn't really make sense when you're specifying ports
in the Procfile)
But, I kinda feel like it doesn't add much value, since you could just do that in the Procfile and it'd be more explicit:
web:
environment:
PORT: "80"
ADMIN_PORT: "8080"
ports:
- "80:80"
- "80:8080"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, good point - so this only happens when there are multiple ports defined? That seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. For backwards compatibility, if you defined a web
proc, but no ports:
then it's equivalent to:
web:
environment:
PORT: "8080"
ports:
- "80:8080":
protocol: "http"
- "443:8080": # If a cert is provided
protocol: "https"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! Ignore me!
Unlike ELB, ALB will not transparently map listener ports to container ports. When using dynamically configured host ports, ALB can only route multiple listeners to a single container port.
13602e6
to
6a36e75
Compare
This will allow different processes to define what load balancer they want to use (e.g. a `web` proc can use ELB, while an `grpc` proc uses ALB)
Ref, not GetAtt
I'll go ahead and merge this for now. There will probably be some follow up polish, and additional docs, but overall this seems to be working great. |
This adds a new
ports
setting to the extended Procfile format, which allows you to attach a load balancer to any process in the app, not just the "web" process.This is definitely a WIP and looking for feedback. There's some open questions, and a lot of polish needed.
If you want to play around with this locally, you can
emp deploy
remind101/acme-inc:extended
TODO
Allow tcp/ssl load balancers to actually get created.First version of this will be http/https onlyIf the process originally had no load balancer, you can't add a load balancer to it (ECS limitation). The workaround is to remove the process definition, then add it again with theNot this PR.expose
setting. This is awkward though, because the existing ECS service kinda sticks around in a draining state for a while.Currently, I'm thinking that the extended Procfile format shouldn't make any assumptions about theI think this is fine once Only destroy load balancers when the app is destroyed. #801 is merged.web
process. But, when people switch to the extended Procfile format, they'll almost certainly forget to add the expose setting. Need to think about a good way to prevent this.Right now, I'm not creating CNAME's for anything other than the web process. That could probably be better.Probably not in this PR.For https/ssl exposures, it will use the single attached certificate on the app. People might want to use different certs for different processes.Not this PR.