Skip to content
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

Port forward pods automatically during skaffold dev #945

Merged
merged 8 commits into from
Sep 12, 2018

Conversation

r2d4
Copy link
Contributor

@r2d4 r2d4 commented Aug 30, 2018

Skaffold dev will now automatically port-forward any pods that expose
container ports.

Ports are assigned on a first-come-first-serve basis,
and that container will hold the lock on that port for the remainder of the dev
cycle.

Updates to a container will trigger an update which replaces the
forwarded port with the newest version.

Scaling up a deployment will continue to port-forward the latest pod.
However, scaling down a deployment has the potential to kill the
port-forwarded pod without replacement - since we do not keep track of
candidates that are "waiting" on that port.

r2d4 added 6 commits August 30, 2018 10:50
Skaffold dev will now automatically port-forward any pods that expose
container ports.

Ports are assigned on a first-come-first-serve basis,
and that container will hold the lock on that port for the remainder of the dev
cycle.

Updates to a container will trigger an update which replaces the
forwarded port with the newest version.

Scaling up a deployment will continue to port-forward the latest pod.
However, scaling down a deployment has the potential to kill the
port-forwarded pod without replacement - since we do not keep track of
candidates that are "waiting" on that port.
@r2d4 r2d4 requested review from balopat and dgageot as code owners August 30, 2018 20:58
@r2d4 r2d4 force-pushed the port-forward-pods branch from 6c51ac9 to 2873a1c Compare August 30, 2018 21:15
@codecov-io
Copy link

Codecov Report

Merging #945 into master will decrease coverage by 0.14%.
The diff coverage is 33.01%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #945      +/-   ##
==========================================
- Coverage   43.28%   43.14%   -0.15%     
==========================================
  Files          63       65       +2     
  Lines        2629     2726      +97     
==========================================
+ Hits         1138     1176      +38     
- Misses       1372     1429      +57     
- Partials      119      121       +2
Impacted Files Coverage Δ
pkg/skaffold/kubernetes/log.go 7.01% <0%> (+2.91%) ⬆️
pkg/skaffold/kubernetes/watcher.go 0% <0%> (ø)
pkg/skaffold/runner/runner.go 54.74% <33.33%> (-0.37%) ⬇️
pkg/skaffold/kubernetes/port_forward.go 36.95% <36.95%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 61a96ad...425f9ac. Read the comment docs.

@r2d4
Copy link
Contributor Author

r2d4 commented Aug 31, 2018

Fixes #94

@dgageot dgageot self-assigned this Sep 5, 2018
Copy link
Contributor

@dlorenc dlorenc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits/questions. Otherwise looks good.

An integration test here would probably be useful as well.

func (*kubectlForwader) Forward(pfe *portForwardEntry) error {
logrus.Debugf("Port forwarding %s", pfe)
portNumber := fmt.Sprintf("%d", pfe.port)
cmd := exec.Command("kubectl", "port-forward", fmt.Sprintf("pod/%s", pfe.podName), portNumber, portNumber)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this will probably fail on Windows, right? That's probably fine though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this should work on windows as well.

pfe.cmd = cmd

buf := &bytes.Buffer{}
cmd.Stdout = buf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: would it make sense to separate these buffers to make the error easier to read later?


// Key is an identifer for the lock on a port during the skaffold dev cycle.
func (p *portForwardEntry) key() string {
return fmt.Sprintf("%s-%d", p.containerName, p.port)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also include namespace and podName here to make it fully keyed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to consider all pods created by a workloads object to be the same - so if a deployment creates three replicas of "pod": "pod-a", "pod-b", "pod-c", they should be considered the same key.

Namespace might be fine, but I can't think of a use case. The case would be where I deploy the same container to two different namespaces. The current code would port-forward the latest deployed container, while the proposed namespace key wouldn't.

Copy link
Contributor

@balopat balopat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on the integration test & some nits. It looks good, and I'm going to try it out now too :)


// PortForwarder is responsible for selecting pods satisfying a certain condition and port-forwarding the exposed
// container ports within those pods. It also tracks and manages the port-forward connections.
type PortForwarder struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would rename this to ExclusivePortForwarder (or similar) and rename the Forwarder interface to PortForwarder.

containerName: c.Name,
port: port.ContainerPort,
}
v, ok := p.forwardedPods.Load(entry.key())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe ok -> isPodForwarded

if !ok {
continue
}
if p.podSelector.Select(pod) && pod.Status.Phase == v1.PodRunning && pod.DeletionTimestamp == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we extract the event handling logic and have it covered by unit test? I feel like it's an important piece to protect especially if we want to merge it later with the logging podwatcher.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@balopat
Copy link
Contributor

balopat commented Sep 6, 2018

tested it it on @ahmetb's microservices example, and it's running nicely 👍

Maybe we could add a doc to list the ports as well, it was a bit hard to find what service was mapped where before I found this - but this works only for unix:

$ ps ax | grep port-forw
10369 s006  S+     0:00.19 kubectl port-forward pod/productcatalogservice-7bdb6dc844-cm9qq 3550 3550
10407 s006  S+     0:00.19 kubectl port-forward pod/paymentservice-5977d9d586-4lt4d 50051 50051
10838 s006  S+     0:00.18 kubectl port-forward pod/cartservice-567b597d5-t4dc6 7070 7070
11486 s006  S+     0:00.20 kubectl port-forward pod/checkoutservice-856cf9dd46-bs9hb 5050 5050
11511 s006  S+     0:00.20 kubectl port-forward pod/currencyservice-76cbd59d48-sh5fm 7000 7000
20538 s006  S+     0:00.16 kubectl port-forward pod/frontend-86f9dc4cc-smfzs 8080 8080
```

@ahmetb
Copy link
Contributor

ahmetb commented Sep 6, 2018

What does it do in case of port conflicts? We deliberately kept the port numbers different so we can develop locally easier –but we actually have plans to standardize port numbers.

@viglesiasce
Copy link
Contributor

I just tested this out and it is failing when the pods expose a privileged port (ie 80). We need to be able to detect that and map to a higher port maybe by adding a constant (80->10080).

@balopat
Copy link
Contributor

balopat commented Sep 6, 2018

In this PR the first one wins. See https://github.com/GoogleContainerTools/skaffold/pull/945/files#diff-f0665c32a151485f611d6517eb543409R175 for conflict "resolution" with known ports.
The same question comes up with already bound ports on localhost.
One "mode" would be automatically find an unbound port (+1, random, etc.): pros: no config, cons: user has to "discover" the assigned ports, they are unstable.
Another "mode" could be to enable the user in Skaffold yaml to specify the ports. pros: user knows the ports, they are stable, cons: user has to configure stuff.

@ahmetb
Copy link
Contributor

ahmetb commented Sep 6, 2018

I think if you print something on the screen in the format:

export A_ADDR=localhost:28937
export B_ADDR=localhost:12788

then you can randomly choose from available ports, and I'll just paste this to my terminal where I'm launching my app from.

@balopat
Copy link
Contributor

balopat commented Sep 11, 2018

Added #969 for follow-up on the issues for more complex cases. We should document the caveats and then merge.

@r2d4 r2d4 merged commit 24aefd2 into GoogleContainerTools:master Sep 12, 2018
@r2d4 r2d4 deleted the port-forward-pods branch September 12, 2018 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants