Enable iptables kube-proxy by default in master

pull/6/head
Tim Hockin 2015-10-26 23:08:37 -07:00
parent 12ad0b208a
commit 970c045848
16 changed files with 607 additions and 415 deletions

View File

@ -91,7 +91,7 @@ func (s *ProxyServerConfig) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.Kubeconfig, "kubeconfig", s.Kubeconfig, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
fs.Var(&s.PortRange, "proxy-port-range", "Range of host ports (beginPort-endPort, inclusive) that may be consumed in order to proxy service traffic. If unspecified (0-0) then ports will be randomly chosen.")
fs.StringVar(&s.HostnameOverride, "hostname-override", s.HostnameOverride, "If non-empty, will use this string as identification instead of the actual hostname.")
fs.StringVar(&s.ProxyMode, "proxy-mode", "", "Which proxy mode to use: 'userspace' (older, stable) or 'iptables' (experimental). If blank, look at the Node object on the Kubernetes API and respect the '"+experimentalProxyModeAnnotation+"' annotation if provided. Otherwise use the best-available proxy (currently userspace, but may change in future versions). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.")
fs.StringVar(&s.ProxyMode, "proxy-mode", "", "Which proxy mode to use: 'userspace' (older) or 'iptables' (faster). If blank, look at the Node object on the Kubernetes API and respect the '"+experimentalProxyModeAnnotation+"' annotation if provided. Otherwise use the best-available proxy (currently iptables). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.")
fs.DurationVar(&s.IptablesSyncPeriod, "iptables-sync-period", s.IptablesSyncPeriod, "How often iptables rules are refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0.")
fs.DurationVar(&s.ConfigSyncPeriod, "config-sync-period", s.ConfigSyncPeriod, "How often configuration from the apiserver is refreshed. Must be greater than 0.")
fs.BoolVar(&s.MasqueradeAll, "masquerade-all", false, "If using the pure iptables proxy, SNAT everything")
@ -238,17 +238,8 @@ func NewProxyServerDefault(config *ProxyServerConfig) (*ProxyServer, error) {
var proxier proxy.ProxyProvider
var endpointsHandler proxyconfig.EndpointsConfigHandler
useIptablesProxy := false
if mayTryIptablesProxy(config.ProxyMode, client.Nodes(), hostname) {
var err error
// guaranteed false on error, error only necessary for debugging
useIptablesProxy, err = iptables.ShouldUseIptablesProxier()
if err != nil {
glog.Errorf("Can't determine whether to use iptables proxy, using userspace proxier: %v", err)
}
}
if useIptablesProxy {
proxyMode := getProxyMode(config.ProxyMode, client.Nodes(), hostname, iptInterface)
if proxyMode == proxyModeIptables {
glog.V(2).Info("Using iptables Proxier.")
proxierIptables, err := iptables.NewProxier(iptInterface, execer, config.IptablesSyncPeriod, config.MasqueradeAll)
if err != nil {
@ -340,27 +331,28 @@ type nodeGetter interface {
Get(hostname string) (*api.Node, error)
}
func mayTryIptablesProxy(proxyMode string, client nodeGetter, hostname string) bool {
if proxyMode == proxyModeIptables {
glog.V(1).Infof("Flag proxy-mode allows iptables proxy")
return true
func getProxyMode(proxyMode string, client nodeGetter, hostname string, iptver iptables.IptablesVersioner) string {
if proxyMode == proxyModeUserspace {
return proxyModeUserspace
} else if proxyMode == proxyModeIptables {
return tryIptablesProxy(iptver)
} else if proxyMode != "" {
glog.V(1).Infof("Flag proxy-mode=%q forbids iptables proxy", proxyMode)
return false
glog.V(1).Infof("Flag proxy-mode=%q unknown, assuming iptables proxy", proxyMode)
return tryIptablesProxy(iptver)
}
// proxyMode == "" - choose the best option.
if client == nil {
glog.Errorf("Not trying iptables proxy: nodeGetter is nil")
return false
glog.Errorf("nodeGetter is nil: assuming iptables proxy")
return tryIptablesProxy(iptver)
}
node, err := client.Get(hostname)
if err != nil {
glog.Errorf("Not trying iptables proxy: can't get Node %q: %v", hostname, err)
return false
glog.Errorf("Can't get Node %q, assuming iptables proxy: %v", hostname, err)
return tryIptablesProxy(iptver)
}
if node == nil {
glog.Errorf("Not trying iptables proxy: got nil Node %q", hostname)
return false
glog.Errorf("Got nil Node %q, assuming iptables proxy: %v", hostname)
return tryIptablesProxy(iptver)
}
proxyMode, found := node.Annotations[betaProxyModeAnnotation]
if found {
@ -372,12 +364,27 @@ func mayTryIptablesProxy(proxyMode string, client nodeGetter, hostname string) b
glog.V(1).Infof("Found experimental annotation %q = %q", experimentalProxyModeAnnotation, proxyMode)
}
}
if proxyMode == proxyModeIptables {
glog.V(1).Infof("Annotation allows iptables proxy")
return true
if proxyMode == proxyModeUserspace {
glog.V(1).Infof("Annotation demands userspace proxy")
return proxyModeUserspace
}
glog.V(1).Infof("Not trying iptables proxy: %+v", node)
return false
return tryIptablesProxy(iptver)
}
func tryIptablesProxy(iptver iptables.IptablesVersioner) string {
var err error
// guaranteed false on error, error only necessary for debugging
useIptablesProxy, err := iptables.CanUseIptablesProxier(iptver)
if err != nil {
glog.Errorf("Can't determine whether to use iptables proxy, using userspace proxier: %v", err)
return proxyModeUserspace
}
if useIptablesProxy {
return proxyModeIptables
}
// Fallback.
glog.V(1).Infof("Can't use iptables proxy, using userspace proxier: %v", err)
return proxyModeUserspace
}
func (s *ProxyServer) birthCry() {

View File

@ -17,10 +17,12 @@ limitations under the License.
package app
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/iptables"
)
type fakeNodeInterface struct {
@ -31,40 +33,176 @@ func (fake *fakeNodeInterface) Get(hostname string) (*api.Node, error) {
return &fake.node, nil
}
func Test_mayTryIptablesProxy(t *testing.T) {
type fakeIptablesVersioner struct {
version string // what to return
err error // what to return
}
func (fake *fakeIptablesVersioner) GetVersion() (string, error) {
return fake.version, fake.err
}
func Test_getProxyMode(t *testing.T) {
var cases = []struct {
flag string
annKey string
annVal string
expected bool
flag string
annotationKey string
annotationVal string
iptablesVersion string
iptablesError error
expected string
}{
{"userspace", "", "", false},
{"iptables", "", "", true},
{"", "", "", false},
{"", "net.experimental.kubernetes.io/proxy-mode", "userspace", false},
{"", "net.experimental.kubernetes.io/proxy-mode", "iptables", true},
{"", "net.experimental.kubernetes.io/proxy-mode", "other", false},
{"", "net.experimental.kubernetes.io/proxy-mode", "", false},
{"", "net.beta.kubernetes.io/proxy-mode", "userspace", false},
{"", "net.beta.kubernetes.io/proxy-mode", "iptables", true},
{"", "net.beta.kubernetes.io/proxy-mode", "other", false},
{"", "net.beta.kubernetes.io/proxy-mode", "", false},
{"", "proxy-mode", "iptables", false},
{"userspace", "net.experimental.kubernetes.io/proxy-mode", "userspace", false},
{"userspace", "net.experimental.kubernetes.io/proxy-mode", "iptables", false},
{"iptables", "net.experimental.kubernetes.io/proxy-mode", "userspace", true},
{"iptables", "net.experimental.kubernetes.io/proxy-mode", "iptables", true},
{"userspace", "net.beta.kubernetes.io/proxy-mode", "userspace", false},
{"userspace", "net.beta.kubernetes.io/proxy-mode", "iptables", false},
{"iptables", "net.beta.kubernetes.io/proxy-mode", "userspace", true},
{"iptables", "net.beta.kubernetes.io/proxy-mode", "iptables", true},
{ // flag says userspace
flag: "userspace",
expected: proxyModeUserspace,
},
{ // flag says iptables, error detecting version
flag: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // flag says iptables, version too low
flag: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // detect, error
flag: "",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // detect, version too low
flag: "",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // detect, version ok
flag: "",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says userspace
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "userspace",
expected: proxyModeUserspace,
},
{ // annotation says iptables, error detecting
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // annotation says iptables, version too low
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // annotation says iptables, version ok
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says something else, version ok
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "other",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says nothing, version ok
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says userspace
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "userspace",
expected: proxyModeUserspace,
},
{ // annotation says iptables, error detecting
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // annotation says iptables, version too low
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // annotation says iptables, version ok
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says something else, version ok
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "other",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // annotation says nothing, version ok
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // flag says userspace, annotation disagrees
flag: "userspace",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeUserspace,
},
{ // flag says iptables, annotation disagrees
flag: "iptables",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "userspace",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
{ // flag says userspace, annotation disagrees
flag: "userspace",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeUserspace,
},
{ // flag says iptables, annotation disagrees
flag: "iptables",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "userspace",
iptablesVersion: iptables.MinCheckVersion,
expected: proxyModeIptables,
},
}
for i, c := range cases {
getter := &fakeNodeInterface{}
getter.node.Annotations = map[string]string{c.annKey: c.annVal}
r := mayTryIptablesProxy(c.flag, getter, "host")
getter.node.Annotations = map[string]string{c.annotationKey: c.annotationVal}
versioner := &fakeIptablesVersioner{c.iptablesVersion, c.iptablesError}
r := getProxyMode(c.flag, getter, "host", versioner)
if r != c.expected {
t.Errorf("Case[%d] Expected %t, got %t", i, c.expected, r)
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)
}
}
}

View File

@ -68,13 +68,13 @@ kube-proxy
--masquerade-all[=false]: If using the pure iptables proxy, SNAT everything
--master="": The address of the Kubernetes API server (overrides any value in kubeconfig)
--oom-score-adj=-999: The oom-score-adj value for kube-proxy process. Values must be within the range [-1000, 1000]
--proxy-mode="": Which proxy mode to use: 'userspace' (older, stable) or 'iptables' (experimental). If blank, look at the Node object on the Kubernetes API and respect the 'net.experimental.kubernetes.io/proxy-mode' annotation if provided. Otherwise use the best-available proxy (currently userspace, but may change in future versions). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.
--proxy-mode="": Which proxy mode to use: 'userspace' (older) or 'iptables' (faster). If blank, look at the Node object on the Kubernetes API and respect the 'net.experimental.kubernetes.io/proxy-mode' annotation if provided. Otherwise use the best-available proxy (currently iptables). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.
--proxy-port-range=: Range of host ports (beginPort-endPort, inclusive) that may be consumed in order to proxy service traffic. If unspecified (0-0) then ports will be randomly chosen.
--resource-container="/kube-proxy": Absolute name of the resource-only container to create and run the Kube-proxy in (Default: /kube-proxy).
--udp-timeout=250ms: How long an idle UDP connection will be kept open (e.g. '250ms', '2s'). Must be greater than 0. Only applicable for proxy-mode=userspace
```
###### Auto generated by spf13/cobra on 23-Oct-2015
###### Auto generated by spf13/cobra on 9-Nov-2015
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->

View File

@ -55,6 +55,8 @@ This document will hopefully help you to figure out what's going wrong.
- [Is the kube-proxy working?](#is-the-kube-proxy-working)
- [Is kube-proxy running?](#is-kube-proxy-running)
- [Is kube-proxy writing iptables rules?](#is-kube-proxy-writing-iptables-rules)
- [Userspace](#userspace)
- [Iptables](#iptables)
- [Is kube-proxy proxying?](#is-kube-proxy-proxying)
- [Seek help](#seek-help)
- [More information](#more-information)
@ -468,24 +470,16 @@ depends on your `Node` OS. On some OSes it is a file, such as
should see something like:
```console
I0707 17:34:53.945651 30031 server.go:88] Running in resource-only container "/kube-proxy"
I0707 17:34:53.945921 30031 proxier.go:121] Setting proxy IP to 10.240.115.247 and initializing iptables
I0707 17:34:54.053023 30031 roundrobin.go:262] LoadBalancerRR: Setting endpoints for default/kubernetes: to [10.240.169.188:443]
I0707 17:34:54.053175 30031 roundrobin.go:262] LoadBalancerRR: Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]
I0707 17:34:54.053284 30031 roundrobin.go:262] LoadBalancerRR: Setting endpoints for default/kube-dns:dns to [10.244.3.3:53]
I0707 17:34:54.053310 30031 roundrobin.go:262] LoadBalancerRR: Setting endpoints for default/kube-dns:dns-tcp to [10.244.3.3:53]
I0707 17:34:54.054780 30031 proxier.go:306] Adding new service "default/kubernetes:" at 10.0.0.1:443/TCP
I0707 17:34:54.054903 30031 proxier.go:247] Proxying for service "default/kubernetes:" on TCP port 40074
I0707 17:34:54.079181 30031 proxier.go:306] Adding new service "default/hostnames:default" at 10.0.1.175:80/TCP
I0707 17:34:54.079273 30031 proxier.go:247] Proxying for service "default/hostnames:default" on TCP port 48577
I0707 17:34:54.113665 30031 proxier.go:306] Adding new service "default/kube-dns:dns" at 10.0.0.10:53/UDP
I0707 17:34:54.113776 30031 proxier.go:247] Proxying for service "default/kube-dns:dns" on UDP port 34149
I0707 17:34:54.120224 30031 proxier.go:306] Adding new service "default/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
I0707 17:34:54.120297 30031 proxier.go:247] Proxying for service "default/kube-dns:dns-tcp" on TCP port 53476
I0707 17:34:54.902313 30031 proxysocket.go:130] Accepted TCP connection from 10.244.3.3:42670 to 10.244.3.1:40074
I0707 17:34:54.903107 30031 proxysocket.go:130] Accepted TCP connection from 10.244.3.3:42671 to 10.244.3.1:40074
I0707 17:35:46.015868 30031 proxysocket.go:246] New UDP connection from 10.244.3.2:57493
I0707 17:35:46.017061 30031 proxysocket.go:246] New UDP connection from 10.244.3.2:55471
I1027 22:14:53.995134 5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163 5063 server.go:247] Using iptables Proxier.
I1027 22:14:53.999055 5063 server.go:255] Tearing down userspace rules. Errors here are acceptable.
I1027 22:14:54.038140 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164 5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209 5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238 5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048 5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223 5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP
```
If you see error messages about not being able to contact the master, you
@ -497,6 +491,12 @@ One of the main responsibilities of `kube-proxy` is to write the `iptables`
rules which implement `Service`s. Let's check that those rules are getting
written.
The kube-proxy can run in either "userspace" mode or "iptables" mode.
Hopefully you are using the newer, faster, more stable "iptables" mode. You
should see one of the following cases.
#### Userspace
```console
u@node$ iptables-save | grep hostnames
-A KUBE-PORTALS-CONTAINER -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames:default" -m tcp --dport 80 -j REDIRECT --to-ports 48577
@ -508,6 +508,27 @@ example) - a "KUBE-PORTALS-CONTAINER" and a "KUBE-PORTALS-HOST". If you do
not see these, try restarting `kube-proxy` with the `-V` flag set to 4, and
then look at the logs again.
#### Iptables
```console
u@node$ iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x4d415351/0xffffffff
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x4d415351/0xffffffff
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x4d415351/0xffffffff
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
```
There should be 1 rule in `KUBE-SERVICES`, 1 or 2 rules per endpoint in
`KUBE-SVC-(hash)` (depending on `SessionAffinity`), one `KUBE-SEP-(hash)` chain
per endpoint, and a few rules in each `KUBE-SEP-(hash)` chain. The exact rules
will vary based on your exact config (including node-ports and load-balancers).
### Is kube-proxy proxying?
Assuming you do see the above rules, try again to access your `Service` by IP:
@ -517,10 +538,12 @@ u@node$ curl 10.0.1.175:80
hostnames-0uton
```
If this fails, we can try accessing the proxy directly. Look back at the
`iptables-save` output above, and extract the port number that `kube-proxy` is
using for your `Service`. In the above examples it is "48577". Now connect to
that:
If this fails and you are using the userspace proxy, you can try accessing the
proxy directly. If you are using the iptables proxy, skip this section.
Look back at the `iptables-save` output above, and extract the
port number that `kube-proxy` is using for your `Service`. In the above
examples it is "48577". Now connect to that:
```console
u@node$ curl localhost:48577

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -13,9 +13,9 @@
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="services_detail.svg"
inkscape:export-filename="/usr/local/google/home/thockin/src/kubernetes/docs/services_overview.png"
inkscape:version="0.48.4 r9939"
sodipodi:docname="services-iptables-overview.svg"
inkscape:export-filename="/usr/local/google/home/thockin/src/kubernetes/docs/services-userspace-overview.png"
inkscape:export-xdpi="76.910004"
inkscape:export-ydpi="76.910004">
<defs
@ -27,16 +27,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.99604166"
inkscape:cx="436.19361"
inkscape:cy="503.28586"
inkscape:zoom="1.0318369"
inkscape:cx="115.9218"
inkscape:cy="392.30545"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1228"
inkscape:window-height="848"
inkscape:window-x="364"
inkscape:window-y="24"
inkscape:window-width="1552"
inkscape:window-height="822"
inkscape:window-x="203"
inkscape:window-y="50"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
@ -54,18 +54,26 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.842547px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3115"
width="545.78632"
height="253.1044"
x="-43.690273"
y="541.54382" />
<g
transform="matrix(1,0,0,-1.1300076,-23.256225,1365.3668)"
id="g4178-3-98">
transform="matrix(1,0,0,-0.92578962,15.303948,1193.1996)"
id="g4178-3-0"
style="stroke-width:2.078614;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.82215285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:2.078614;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-7"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-8"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.078614;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-1"
sodipodi:sides="3"
sodipodi:cx="308.85715"
sodipodi:cy="753.79077"
@ -81,15 +89,46 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
id="g4324">
transform="matrix(-0.83212211,-1.1231515,0.89570092,-0.89430772,-72.942206,1678.3161)"
id="g4178-3-8"
style="stroke-width:2.078614;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:transform-center-x="84.098741"
inkscape:transform-center-y="1.4572787e-05">
<path
style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:2.078614;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-4"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.078614;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-0"
sodipodi:sides="3"
sodipodi:cx="308.85715"
sodipodi:cy="753.79077"
sodipodi:r1="10"
sodipodi:r2="5"
sodipodi:arg1="2.6179939"
sodipodi:arg2="3.6651914"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 300.19689,758.79077 8.66026,-15 8.66025,15 z"
transform="translate(28.571429,-62.857143)"
inkscape:transform-center-y="-2.5" />
</g>
<g
id="g4324"
transform="matrix(0.96592583,0.25881905,0.25881905,-0.96592583,-272.81074,1126.238)"
style="stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 340.43856,497.06486 C 238.47092,383.2788 238.47092,383.2788 238.47092,383.2788"
id="path4174-3-2"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.70358849;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-9"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -106,16 +145,17 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(-1,0,0,1,718.68427,0.32076964)"
id="g4324-8">
transform="matrix(-0.96592583,0.25881905,-0.25881905,-0.96592583,654.32964,1126.238)"
id="g4324-8"
style="stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 340.43856,497.06486 C 238.47092,383.2788 238.47092,383.2788 238.47092,383.2788"
id="path4174-3-2-7"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.70358849;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-9-3"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -132,16 +172,17 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(1,0,0,1.3566066,10.430689,-549.99231)"
id="g4178-3-9">
transform="matrix(1,0,0,-1.3566066,-154.75999,1749.5431)"
id="g4178-3-9"
style="stroke-width:4.29282379;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.57569385;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:4.29282379;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-8"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.57569408;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.29282379;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-5"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -157,37 +198,11 @@
transform="translate(28.571429,-62.857143)"
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(1,0,0,0.83995083,5.8686441,145.11325)"
id="g4178">
<path
style="fill:none;stroke:#000000;stroke-width:3.27336383;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:3.27336407;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176"
sodipodi:sides="3"
sodipodi:cx="308.85715"
sodipodi:cy="753.79077"
sodipodi:r1="10"
sodipodi:r2="5"
sodipodi:arg1="2.6179939"
sodipodi:arg2="3.6651914"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 300.19689,758.79077 8.66026,-15 8.66025,15 z"
transform="translate(28.571429,-62.857143)"
inkscape:transform-center-y="-2.5" />
</g>
<g
id="g3937"
transform="translate(-27.782873,191.54649)">
transform="translate(-212.35646,769.73074)">
<g
transform="translate(0,6.5250001e-6)"
transform="matrix(0.88792337,0,0,1,43.50975,6.5250001e-6)"
id="g3868">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
@ -233,7 +248,7 @@
</g>
<g
id="g3868-7"
transform="translate(246.07142,6.5250001e-6)">
transform="matrix(0.88792337,0,0,1,262.00231,6.5250001e-6)">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2985-1"
@ -278,7 +293,7 @@
</g>
<g
id="g3868-3"
transform="translate(492.14285,6.5250001e-6)">
transform="matrix(0.88792337,0,0,1,480.49489,6.5250001e-6)">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2985-2"
@ -323,42 +338,17 @@
</g>
</g>
<g
transform="matrix(-0.5569815,0.8305249,-0.93849945,-0.62939332,1043.1434,624.89979)"
id="g4178-3-4">
transform="matrix(0.91192623,-0.41035418,-0.37990164,-0.84425184,113.60453,1385.4009)"
id="g4178-3"
style="stroke-width:5.19653511;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.82215285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-9"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-1"
sodipodi:sides="3"
sodipodi:cx="308.85715"
sodipodi:cy="753.79077"
sodipodi:r1="10"
sodipodi:r2="5"
sodipodi:arg1="2.6179939"
sodipodi:arg2="3.6651914"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 300.19689,758.79077 8.66026,-15 8.66025,15 z"
transform="translate(28.571429,-62.857143)"
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(1,0,0,1.1300076,19.868644,-230.41621)"
id="g4178-3">
<path
style="fill:none;stroke:#000000;stroke-width:2.82215285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:5.19653511;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:5.19653511;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -375,7 +365,7 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="translate(9.4642913,66)"
transform="matrix(0.89067003,0,0,1,-194.97295,-142.36286)"
id="g4090">
<rect
y="704.50507"
@ -385,47 +375,26 @@
id="rect2985-4"
style="fill:#f1cb85;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g
transform="translate(249.2817,652.74516)"
transform="translate(217.6177,652.82516)"
id="g3861-6">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="36.710861"
y="91.845612"
x="67.574867"
y="91.765617"
id="text3755-32"
sodipodi:linespacing="125%"
inkscape:transform-center-x="-70"
inkscape:transform-center-y="-11.264"><tspan
sodipodi:role="line"
id="tspan3757-9"
x="36.710861"
y="91.845612"
style="font-size:32px;text-align:start;text-anchor:start">Client</tspan></text>
x="67.574867"
y="91.765617"
style="font-size:32px;text-align:start;text-anchor:start">Client </tspan></text>
</g>
</g>
<g
transform="translate(24.285715,159.42857)"
id="g4114">
<path
inkscape:connector-curvature="0"
style="fill:#ededed;fill-opacity:1;stroke:#000000;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 282.87054,438.5755 c -23.66935,0 -42.875,11.54365 -42.875,25.78345 0,1.69709 0.29232,3.36317 0.8125,4.96869 -5.77989,-1.60822 -12.0611,-2.49777 -18.65625,-2.49777 -28.00873,0 -50.71875,15.92203 -50.71875,35.58653 0,19.66449 22.71002,35.61339 50.71875,35.61339 9.72296,0 18.78316,-1.93319 26.5,-5.26412 10.70208,13.21239 35.10628,22.45308 63.5,22.45308 23.13948,0 43.60406,-6.13049 56.1875,-15.55064 12.16376,6.53313 29.85326,10.63567 49.53125,10.63567 36.68749,0 66.40625,-14.27678 66.40625,-31.90702 0,-17.63023 -29.71876,-31.93387 -66.40625,-31.93387 -0.61492,0 -1.23284,0.0189 -1.84375,0.0268 0.72778,-1.79609 1.125,-3.66107 1.125,-5.55955 0,-15.93503 -26.86291,-28.84524 -60,-28.84524 -12.3074,0 -23.75966,1.77775 -33.28125,4.8344 -5.31552,-10.60488 -21.63938,-18.34385 -41,-18.34385 z"
id="path4096" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="270.39322"
y="507.15195"
id="text4108"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="270.39322"
y="507.15195"
id="tspan4112"
style="font-size:22px">iptables</tspan></text>
</g>
<g
transform="translate(167.67856,-111.42858)"
transform="matrix(0.89067003,0,0,1,199.65922,-24.251798)"
id="g4168">
<rect
y="588.79077"
@ -435,26 +404,26 @@
id="rect2985-4-0"
style="fill:#b9f185;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g
transform="translate(58.491433,534.63087)"
transform="translate(34.747433,534.26287)"
id="g3861-6-2">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="36.710861"
y="91.845612"
x="60.454861"
y="92.213608"
id="text3755-32-8"
sodipodi:linespacing="125%"
inkscape:transform-center-x="-70"
inkscape:transform-center-y="-11.264"><tspan
sodipodi:role="line"
id="tspan3757-9-4"
x="36.710861"
y="91.845612"
x="60.454861"
y="92.213608"
style="font-size:32px;text-align:start;text-anchor:start">kube-proxy</tspan></text>
</g>
</g>
<g
transform="translate(-102.23193,-119.15421)"
transform="translate(188.04333,-29.041887)"
id="g4168-5">
<g
transform="translate(22.087429,-86.34177)"
@ -486,85 +455,44 @@
</g>
</g>
</g>
<path
style="fill:#ffe680;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.77870166;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 184.45745,671.01905 c -14.46832,0 -26.98388,8.64301 -32.95619,21.20091 -5.04913,-0.90778 -10.34543,-1.41525 -15.81006,-1.41525 -34.32572,0 -62.154694,19.09673 -62.154694,42.62382 0,23.52709 27.828974,42.59606 62.154694,42.59606 16.91161,0 32.24392,-4.64352 43.44984,-12.15444 7.36101,16.27536 34.00477,28.33262 65.74535,28.33262 33.5174,0 61.35664,-13.44827 66.80308,-31.10761 17.02922,-5.30597 28.58615,-15.7069 28.58615,-27.66663 0,-17.34826 -24.35383,-31.41286 -54.38884,-31.41286 -8.45761,0 -16.46469,1.0906 -23.60375,3.08023 -2.04151,-10.49178 -14.83542,-18.59242 -30.33973,-18.59242 -5.80798,0 -11.23748,1.16534 -15.86573,3.13574 -6.38532,-11.14574 -18.16606,-18.62017 -31.62012,-18.62017 z"
id="path3884"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.89067003,0,0,1,34.810052,629.61733)"
id="g3861-6-28">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="93.689468"
y="89.827324"
id="text3755-32-4"
sodipodi:linespacing="125%"
inkscape:transform-center-x="-70"
inkscape:transform-center-y="-11.264"><tspan
sodipodi:role="line"
id="tspan3757-9-5"
x="93.689468"
y="89.827324"
style="font-size:32px;text-align:start;text-anchor:start">ServiceIP</tspan><tspan
sodipodi:role="line"
x="93.689468"
y="129.82733"
style="font-size:32px;text-align:start;text-anchor:start"
id="tspan3919">(iptables) </tspan></text>
</g>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="354.03052"
y="752.17395"
id="text4777"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="389.59647"
y="786.81635"
id="text3885"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4779"
x="354.03052"
y="752.17395"
style="font-size:22px">3) connect to 10.0.0.1:1234</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="381.81412"
y="563.21899"
id="text4777-1"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="381.81412"
y="563.21899"
style="font-size:22px"
id="tspan4804">4) redirect to (random)</tspan><tspan
sodipodi:role="line"
x="381.81412"
y="590.71899"
style="font-size:22px"
id="tspan3060">proxy port</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="-11.495128"
y="476.92422"
id="text4777-1-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="-11.495128"
y="476.92422"
style="font-size:22px"
id="tspan4804-8">1) watch Services </tspan><tspan
sodipodi:role="line"
x="-11.495128"
y="504.42422"
style="font-size:22px"
id="tspan3056">and Endpoints</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="53.554245"
y="557.18707"
id="text4777-1-3-5"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="53.554245"
y="557.18707"
style="font-size:22px"
id="tspan4804-8-5">2) open proxy port </tspan><tspan
sodipodi:role="line"
x="53.554245"
y="584.68707"
style="font-size:22px"
id="tspan3058">and set portal rules</tspan></text>
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="450.63913"
y="442.09073"
id="text4777-1-2"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="450.63913"
y="442.09073"
style="font-size:22px"
id="tspan4804-9">5) proxy to a backend</tspan><tspan
sodipodi:role="line"
x="450.63913"
y="469.59073"
style="font-size:22px"
id="tspan3060-8" /></text>
id="tspan3887"
x="389.59647"
y="786.81635">Node</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -13,8 +13,8 @@
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="services_overview.svg"
inkscape:version="0.48.4 r9939"
sodipodi:docname="services-userspace-overview.svg"
inkscape:export-filename="/usr/local/google/home/thockin/src/kubernetes/docs/services_overview.png"
inkscape:export-xdpi="76.910004"
inkscape:export-ydpi="76.910004">
@ -28,15 +28,15 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.0318369"
inkscape:cx="351.19865"
inkscape:cy="624.90035"
inkscape:cx="291.9254"
inkscape:cy="392.30545"
inkscape:document-units="px"
inkscape:current-layer="g4090"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1228"
inkscape:window-height="848"
inkscape:window-x="364"
inkscape:window-y="24"
inkscape:window-width="1552"
inkscape:window-height="822"
inkscape:window-x="46"
inkscape:window-y="47"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
@ -55,15 +55,44 @@
inkscape:groupmode="layer"
id="layer1">
<g
id="g4324">
transform="matrix(0,-1,-0.92578962,0,936.44413,1029.2686)"
id="g4178-3-8"
style="stroke-width:5.19653493;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:5.19653493;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-4"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:5.19653493;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-0"
sodipodi:sides="3"
sodipodi:cx="308.85715"
sodipodi:cy="753.79077"
sodipodi:r1="10"
sodipodi:r2="5"
sodipodi:arg1="2.6179939"
sodipodi:arg2="3.6651914"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 300.19689,758.79077 8.66026,-15 8.66025,15 z"
transform="translate(28.571429,-62.857143)"
inkscape:transform-center-y="-2.5" />
</g>
<g
id="g4324"
transform="matrix(0.96592583,0.25881905,0.25881905,-0.96592583,-38.810744,1076.238)"
style="stroke-width:4.99999998;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:4.99999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 340.43856,497.06486 C 238.47092,383.2788 238.47092,383.2788 238.47092,383.2788"
id="path4174-3-2"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.70358849;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-9"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -80,16 +109,17 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(-1,0,0,1,718.68427,0.32076964)"
id="g4324-8">
transform="matrix(-0.96592583,0.25881905,-0.25881905,-0.96592583,888.32964,1076.238)"
id="g4324-8"
style="stroke-width:4.99999998;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:4.99999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 340.43856,497.06486 C 238.47092,383.2788 238.47092,383.2788 238.47092,383.2788"
id="path4174-3-2-7"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.70358849;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-9-3"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -106,16 +136,17 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(1,0,0,1.3566066,10.430689,-549.99231)"
id="g4178-3-9">
transform="matrix(1,0,0,-1.3566066,79.240014,1699.5431)"
id="g4178-3-9"
style="stroke-width:4.29282359;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.57569385;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:4.29282359;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-8"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.57569408;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:4.29282359;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-5"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -133,9 +164,9 @@
</g>
<g
id="g3937"
transform="translate(-27.782873,191.54649)">
transform="translate(21.643544,719.73074)">
<g
transform="translate(0,6.5250001e-6)"
transform="matrix(0.88792337,0,0,1,43.50975,6.5250001e-6)"
id="g3868">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
@ -181,7 +212,7 @@
</g>
<g
id="g3868-7"
transform="translate(246.07142,6.5250001e-6)">
transform="matrix(0.88792337,0,0,1,262.00231,6.5250001e-6)">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2985-1"
@ -226,7 +257,7 @@
</g>
<g
id="g3868-3"
transform="translate(492.14285,6.5250001e-6)">
transform="matrix(0.88792337,0,0,1,480.49489,6.5250001e-6)">
<rect
style="fill:#85bff1;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect2985-2"
@ -271,16 +302,17 @@
</g>
</g>
<g
transform="matrix(-0.5569815,0.8305249,-0.93849945,-0.62939332,1043.1434,624.89979)"
id="g4178-3-4">
transform="matrix(-0.8305249,-0.5569815,0.62939332,-0.93849945,365.54855,1487.8396)"
id="g4178-3-4"
style="stroke-width:1.88143539;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.82215285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:1.88143539;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3-9"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.88143539;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9-1"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -297,16 +329,17 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="matrix(1,0,0,1.1300076,5.8686441,-230.41621)"
id="g4178-3">
transform="matrix(1,0,0,-0.92578962,-170.98136,1268.7699)"
id="g4178-3"
style="stroke-width:5.19653511;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:none;stroke:#000000;stroke-width:2.82215285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:none;stroke:#000000;stroke-width:5.19653511;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 337.14286,757.95172 c 0,-71.30383 0,-71.30383 0,-71.30383"
id="path4174-3"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.82215309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:5.19653511;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path4176-9"
sodipodi:sides="3"
sodipodi:cx="308.85715"
@ -323,7 +356,7 @@
inkscape:transform-center-y="-2.5" />
</g>
<g
transform="translate(11.472239,-104.6279)"
transform="matrix(0.89067003,0,0,1,-130.97295,-172.36286)"
id="g4090">
<rect
y="704.50507"
@ -352,7 +385,7 @@
</g>
</g>
<g
transform="translate(167.67856,-111.42858)"
transform="matrix(0.89067003,0,0,1,263.65922,74.205473)"
id="g4168">
<rect
y="588.79077"
@ -381,7 +414,7 @@
</g>
</g>
<g
transform="translate(-102.23193,-119.15421)"
transform="translate(478.82336,27.291965)"
id="g4168-5">
<g
transform="translate(22.087429,-86.34177)"
@ -413,5 +446,51 @@
</g>
</g>
</g>
<path
style="fill:#ffe680;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.77870166;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 118.55556,629.08076 c -14.46832,0 -26.983883,8.64301 -32.956193,21.20091 -5.04913,-0.90778 -10.34543,-1.41525 -15.81006,-1.41525 -34.32572,0 -62.1546887,19.09673 -62.1546887,42.62382 0,23.52709 27.8289687,42.59606 62.1546887,42.59606 16.91161,0 32.243923,-4.64352 43.449843,-12.15444 7.36101,16.27536 34.00477,28.33262 65.74535,28.33262 33.5174,0 61.35664,-13.44827 66.80308,-31.10761 17.02922,-5.30597 28.58615,-15.7069 28.58615,-27.66663 0,-17.34826 -24.35383,-31.41286 -54.38884,-31.41286 -8.45761,0 -16.46469,1.0906 -23.60375,3.08023 -2.04151,-10.49178 -14.83542,-18.59242 -30.33973,-18.59242 -5.80798,0 -11.23748,1.16534 -15.86573,3.13574 -6.38532,-11.14574 -18.16606,-18.62017 -31.62012,-18.62017 z"
id="path3884"
inkscape:connector-curvature="0" />
<g
transform="matrix(0.89067003,0,0,1,-31.091836,587.67904)"
id="g3861-6-28">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu Mono;-inkscape-font-specification:Ubuntu Mono"
x="93.689468"
y="89.827324"
id="text3755-32-4"
sodipodi:linespacing="125%"
inkscape:transform-center-x="-70"
inkscape:transform-center-y="-11.264"><tspan
sodipodi:role="line"
id="tspan3757-9-5"
x="93.689468"
y="89.827324"
style="font-size:32px;text-align:start;text-anchor:start">ServiceIP</tspan><tspan
sodipodi:role="line"
x="93.689468"
y="129.82733"
style="font-size:32px;text-align:start;text-anchor:start"
id="tspan3919">(iptables) </tspan></text>
</g>
<rect
style="fill:none;stroke:#000000;stroke-width:0.92393565px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3889"
width="544.73572"
height="267.56021"
x="-3.9146113"
y="484.40494" />
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="0.969145"
y="521.27051"
id="text4399"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4401"
x="0.969145"
y="521.27051">Node</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -41,6 +41,8 @@ Documentation for other releases can be found at
- [Defining a service](#defining-a-service)
- [Services without selectors](#services-without-selectors)
- [Virtual IPs and service proxies](#virtual-ips-and-service-proxies)
- [Proxy-mode: userspace](#proxy-mode-userspace)
- [Proxy-mode: iptables](#proxy-mode-iptables)
- [Multi-Port Services](#multi-port-services)
- [Choosing your own IP address](#choosing-your-own-ip-address)
- [Why not use round-robin DNS?](#why-not-use-round-robin-dns)
@ -57,6 +59,8 @@ Documentation for other releases can be found at
- [The gory details of virtual IPs](#the-gory-details-of-virtual-ips)
- [Avoiding collisions](#avoiding-collisions)
- [IPs and VIPs](#ips-and-vips)
- [Userspace](#userspace)
- [Iptables](#iptables)
- [API Object](#api-object)
<!-- END MUNGE: GENERATED_TOC -->
@ -206,26 +210,55 @@ this example).
## Virtual IPs and service proxies
Every node in a Kubernetes cluster runs a `kube-proxy`. This application
watches the Kubernetes master for the addition and removal of `Service`
and `Endpoints` objects. For each `Service` it opens a port (randomly chosen)
on the local node. Any connections to `service` port will be proxied to one of
the corresponding backend `Pods`. Which backend `Pod` to use is decided based on the
is responsible for implementing a form of virtual IP for `Service`s. In
Kubernetes v1.0 the proxy was purely in userspace. In Kubernetes v1.1 an
iptables proxy was added, but was not the default operating mode. In
Kubernetes v1.2 we expect the iptables proxy to be the default.
As of Kubernetes v1.0, `Services` are a "layer 3" (TCP/UDP over IP) construct.
In Kubernetes v1.1 the `Ingress` API was added (beta) to represent "layer 7"
(HTTP) services.
### Proxy-mode: userspace
In this mode, kube-proxy watches the Kubernetes master for the addition and
removal of `Service` and `Endpoints` objects. For each `Service` it opens a
port (randomly chosen) on the local node. Any connections to this "proxy port"
will be proxied to one of the `Service`'s backend `Pods` (as reported in
`Endpoints`). Which backend `Pod` to use is decided based on the
`SessionAffinity` of the `Service`. Lastly, it installs iptables rules which
capture traffic to the `Service`'s cluster IP (which is virtual) and `Port` then
redirects that traffic to the backend `Pod` (`Endpoints`).
capture traffic to the `Service`'s `clusterIP` (which is virtual) and `Port`
and redirects that traffic to the proxy port which proxies the a backend `Pod`.
The net result is that any traffic bound for the `Service` is proxied to an
appropriate backend without the clients knowing anything about Kubernetes or
`Services` or `Pods`.
![Services overview diagram](services-overview.png)
The net result is that any traffic bound for the `Service`'s IP:Port is proxied
to an appropriate backend without the clients knowing anything about Kubernetes
or `Services` or `Pods`.
By default, the choice of backend is round robin. Client-IP based session affinity
can be selected by setting `service.spec.sessionAffinity` to `"ClientIP"` (the
default is `"None"`).
As of Kubernetes 1.0, `Services` are a "layer 3" (TCP/UDP over IP) construct. We do not
yet have a concept of "layer 7" (HTTP) services.
![Services overview diagram for userspace proxy](services-userspace-overview.png)
### Proxy-mode: iptables
In this mode, kube-proxy watches the Kubernetes master for the addition and
removal of `Service` and `Endpoints` objects. For each `Service` it installs
iptables rules which capture traffic to the `Service`'s `clusterIP` (which is
virtual) and `Port` and redirects that traffic to one of the `Service`'s
backend sets. For each `Endpoints` object it installs iptables rules which
select a backend `Pod`.
By default, the choice of backend is random. Client-IP based session affinity
can be selected by setting `service.spec.sessionAffinity` to `"ClientIP"` (the
default is `"None"`).
As with the userspace proxy, the net result is that any traffic bound for the
`Service`'s IP:Port is proxied to an appropriate backend without the clients
knowing anything about Kubernetes or `Services` or `Pods`. This should be
faster and more reliable than the userspace proxy.
![Services overview diagram for iptables proxy](services-iptables-overview.png)
## Multi-Port Services
@ -494,14 +527,14 @@ In the example below, my-service can be accessed by clients on 80.11.12.10:80 (e
## Shortcomings
We expect that using iptables and userspace proxies for VIPs will work at
small to medium scale, but may not scale to very large clusters with thousands
of Services. See [the original design proposal for
portals](http://issue.k8s.io/1107) for more
details.
Using the userspace proxy for VIPs will work at small to medium scale, but will
not scale to very large clusters with thousands of Services. See [the original
design proposal for portals](http://issue.k8s.io/1107) for more details.
Using the kube-proxy obscures the source-IP of a packet accessing a `Service`.
This makes some kinds of firewalling impossible.
Using the userspace proxy obscures the source-IP of a packet accessing a `Service`.
This makes some kinds of firewalling impossible. The iptables proxier does not
obscure in-cluster source IPs, but it does still impact clients coming through
a load-balancer or node-port.
LoadBalancers only support TCP, not UDP.
@ -517,13 +550,7 @@ simple round robin balancing, for example master-elected or sharded. We also
envision that some `Services` will have "real" load balancers, in which case the
VIP will simply transport the packets there.
There's a
[proposal](http://issue.k8s.io/3760) to
eliminate userspace proxying in favor of doing it all in iptables. This should
perform better and fix the source-IP obfuscation, though is less flexible than
arbitrary userspace code.
We intend to have first-class support for L7 (HTTP) `Services`.
We intend to improve our support for L7 (HTTP) `Services`.
We intend to have more flexible ingress modes for `Services` which encompass
the current `ClusterIP`, `NodePort`, and `LoadBalancer` modes and more.
@ -565,6 +592,11 @@ VIP, their traffic is automatically transported to an appropriate endpoint.
The environment variables and DNS for `Services` are actually populated in
terms of the `Service`'s VIP and port.
We support two proxy modes - userspace and iptables, which operate slightly
differently.
#### Userspace
As an example, consider the image processing application described above.
When the backend `Service` is created, the Kubernetes master assigns a virtual
IP address, for example 10.0.0.1. Assuming the `Service` port is 1234, the
@ -581,7 +613,24 @@ This means that `Service` owners can choose any port they want without risk of
collision. Clients can simply connect to an IP and port, without being aware
of which `Pods` they are actually accessing.
![Services detailed diagram](services-detail.png)
#### Iptables
Again, consider the image processing application described above.
When the backend `Service` is created, the Kubernetes master assigns a virtual
IP address, for example 10.0.0.1. Assuming the `Service` port is 1234, the
`Service` is observed by all of the `kube-proxy` instances in the cluster.
When a proxy sees a new `Service`, it installs a series of iptables rules which
redirect from the VIP to per-`Service` rules. The per-`Service` rules link to
per-`Endpoint` rules which redirect (Destination NAT) to the backends.
When a client connects to the VIP the iptables rule kicks in. A backend is
chosen (either based on session affinity or randomly) and packets are
redirected to the backend. Unlike the userspace proxy, packets are never
copied to userspace, the kube-proxy does not have to be running for the VIP to
work, and the client IP is not altered.
This same basic flow executes when traffic comes in through a node-port or
through a load-balancer, though in those cases the client IP does get altered.
## API Object

View File

@ -62,19 +62,24 @@ const iptablesNodePortsChain utiliptables.Chain = "KUBE-NODEPORTS"
// the mark we apply to traffic needing SNAT
const iptablesMasqueradeMark = "0x4d415351"
// ShouldUseIptablesProxier returns true if we should use the iptables Proxier
// IptablesVersioner can query the current iptables version.
type IptablesVersioner interface {
// returns "X.Y.Z"
GetVersion() (string, error)
}
// CanUseIptablesProxier returns true if we should use the iptables Proxier
// instead of the "classic" userspace Proxier. This is determined by checking
// the iptables version and for the existence of kernel features. It may return
// an error if it fails to get the iptables version without error, in which
// case it will also return false.
func ShouldUseIptablesProxier() (bool, error) {
exec := utilexec.New()
func CanUseIptablesProxier(iptver IptablesVersioner) (bool, error) {
minVersion, err := semver.NewVersion(iptablesMinVersion)
if err != nil {
return false, err
}
// returns "X.X.X", err
versionString, err := utiliptables.GetIptablesVersionString(exec)
// returns "X.Y.Z"
versionString, err := iptver.GetVersion()
if err != nil {
return false, err
}
@ -89,6 +94,7 @@ func ShouldUseIptablesProxier() (bool, error) {
// Check for the required sysctls. We don't care about the value, just
// that it exists. If this Proxier is chosen, we'll iniialize it as we
// need.
// TODO: we should inject a sysctl.Interface like we do for iptables
_, err = utilsysctl.GetSysctl(sysctlRouteLocalnet)
if err != nil {
return false, err

View File

@ -32,7 +32,7 @@ import (
"k8s.io/kubernetes/pkg/proxy"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/iptables"
ipttest "k8s.io/kubernetes/pkg/util/iptables/testing"
)
const (
@ -82,55 +82,6 @@ func waitForClosedPortUDP(p *Proxier, proxyPort int) error {
return fmt.Errorf("port %d still open", proxyPort)
}
// The iptables logic has to be tested in a proper end-to-end test, so this just stubs everything out.
type fakeIptables struct{}
func (fake *fakeIptables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
return false, nil
}
func (fake *fakeIptables) DeleteChain(table iptables.Table, chain iptables.Chain) error {
return nil
}
func (fake *fakeIptables) FlushChain(table iptables.Table, chain iptables.Chain) error {
return nil
}
func (fake *fakeIptables) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) {
return false, nil
}
func (fake *fakeIptables) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error {
return nil
}
func (fake *fakeIptables) IsIpv6() bool {
return false
}
func (fake *fakeIptables) Save(table iptables.Table) ([]byte, error) {
return []byte{}, nil
}
func (fake *fakeIptables) SaveAll() ([]byte, error) {
return []byte{}, nil
}
func (fake *fakeIptables) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
return nil
}
func (fake *fakeIptables) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
return nil
}
func (fake *fakeIptables) AddReloadFunc(reloadFunc func()) {
}
func (fake *fakeIptables) Destroy() {
}
var tcpServerPort int
var udpServerPort int
@ -249,7 +200,7 @@ func TestTCPProxy(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -276,7 +227,7 @@ func TestUDPProxy(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -303,7 +254,7 @@ func TestUDPProxyTimeout(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -339,7 +290,7 @@ func TestMultiPortProxy(t *testing.T) {
}},
}})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -366,7 +317,7 @@ func TestMultiPortOnServiceUpdate(t *testing.T) {
serviceQ := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "testnamespace", Name: "echo"}, Port: "q"}
serviceX := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "testnamespace", Name: "echo"}, Port: "x"}
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -429,7 +380,7 @@ func TestTCPProxyStop(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -473,7 +424,7 @@ func TestUDPProxyStop(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -511,7 +462,7 @@ func TestTCPProxyUpdateDelete(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -548,7 +499,7 @@ func TestUDPProxyUpdateDelete(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -585,7 +536,7 @@ func TestTCPProxyUpdateDeleteUpdate(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -637,7 +588,7 @@ func TestUDPProxyUpdateDeleteUpdate(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -689,7 +640,7 @@ func TestTCPProxyUpdatePort(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -737,7 +688,7 @@ func TestUDPProxyUpdatePort(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -782,7 +733,7 @@ func TestProxyUpdatePublicIPs(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}
@ -834,7 +785,7 @@ func TestProxyUpdatePortal(t *testing.T) {
},
})
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
p, err := createProxier(lb, net.ParseIP("0.0.0.0"), ipttest.NewFake(), net.ParseIP("127.0.0.1"), nil, time.Minute, udpIdleTimeoutForTest)
if err != nil {
t.Fatal(err)
}

View File

@ -41,6 +41,8 @@ const (
// An injectable interface for running iptables commands. Implementations must be goroutine-safe.
type Interface interface {
// GetVersion returns the "X.Y.Z" semver string for iptables.
GetVersion() (string, error)
// EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true.
EnsureChain(table Table, chain Chain) (bool, error)
// FlushChain clears the specified chain. If the chain did not exist, return error.
@ -135,7 +137,7 @@ type runner struct {
// New returns a new Interface which will exec iptables.
func New(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol) Interface {
vstring, err := GetIptablesVersionString(exec)
vstring, err := getIptablesVersionString(exec)
if err != nil {
glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err)
vstring = MinCheckVersion
@ -186,6 +188,11 @@ func (runner *runner) connectToFirewallD() {
go runner.dbusSignalHandler(bus)
}
// GetVersion returns the version string.
func (runner *runner) GetVersion() (string, error) {
return getIptablesVersionString(runner.exec)
}
// EnsureChain is part of Interface.
func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) {
fullArgs := makeFullArgs(table, chain)
@ -505,9 +512,9 @@ func getIptablesWaitFlag(vstring string) []string {
}
}
// GetIptablesVersionString runs "iptables --version" to get the version string
// getIptablesVersionString runs "iptables --version" to get the version string
// in the form "X.X.X"
func GetIptablesVersionString(exec utilexec.Interface) (string, error) {
func getIptablesVersionString(exec utilexec.Interface) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner
bytes, err := exec.Command(cmdIptables, "--version").CombinedOutput()
if err != nil {

View File

@ -451,7 +451,7 @@ func TestGetIptablesHasCheckCommand(t *testing.T) {
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
version, err := GetIptablesVersionString(&fexec)
version, err := getIptablesVersionString(&fexec)
if (err != nil) != testCase.Err {
t.Errorf("Expected error: %v, Got error: %v", testCase.Err, err)
}

View File

@ -25,6 +25,10 @@ func NewFake() *fake {
return &fake{}
}
func (*fake) GetVersion() (string, error) {
return "0.0.0", nil
}
func (*fake) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
return true, nil
}