Merge pull request #3618 from saad-ali/fix2410

Make master service IP static (no longer randomly assigned)
This commit is contained in:
Alex Robinson
2015-02-02 11:05:58 -08:00
9 changed files with 144 additions and 37 deletions

View File

@@ -98,8 +98,8 @@ type Config struct {
// Defaults to 443 if not set.
ReadWritePort int
// If empty, the first result from net.InterfaceAddrs will be used.
PublicAddress string
// If nil, the first result from net.InterfaceAddrs will be used.
PublicAddress net.IP
}
// Master contains state for a Kubernetes cluster master/api server.
@@ -134,9 +134,14 @@ type Master struct {
v1beta3 bool
nodeIPCache IPGetter
readOnlyServer string
readWriteServer string
masterServices *util.Runner
publicIP net.IP
publicReadOnlyPort int
publicReadWritePort int
serviceReadOnlyIP net.IP
serviceReadOnlyPort int
serviceReadWriteIP net.IP
serviceReadWritePort int
masterServices *util.Runner
// "Outputs"
Handler http.Handler
@@ -177,7 +182,7 @@ func setDefaults(c *Config) {
if c.ReadWritePort == 0 {
c.ReadWritePort = 443
}
for c.PublicAddress == "" {
for c.PublicAddress == nil {
// Find and use the first non-loopback address.
// TODO: potentially it'd be useful to skip the docker interface if it
// somehow is first in the list.
@@ -197,7 +202,7 @@ func setDefaults(c *Config) {
continue
}
found = true
c.PublicAddress = ip.String()
c.PublicAddress = ip
glog.Infof("Will report %v as public IP address.", ip)
break
}
@@ -245,6 +250,17 @@ func New(c *Config) *Master {
glog.Fatalf("master.New() called with config.KubeletClient == nil")
}
// Select the first two valid IPs from portalNet to use as the master service portalIPs
serviceReadOnlyIP, err := service.GetIndexedIP(c.PortalNet, 1)
if err != nil {
glog.Fatalf("Failed to generate service read-only IP for master service: %v", err)
}
serviceReadWriteIP, err := service.GetIndexedIP(c.PortalNet, 2)
if err != nil {
glog.Fatalf("Failed to generate service read-write IP for master service: %v", err)
}
glog.Infof("Setting master service IPs based on PortalNet subnet to %q (read-only) and %q (read-write).", serviceReadOnlyIP, serviceReadWriteIP)
m := &Master{
podRegistry: etcd.NewRegistry(c.EtcdHelper, boundPodFactory),
controllerRegistry: etcd.NewRegistry(c.EtcdHelper, nil),
@@ -269,9 +285,16 @@ func New(c *Config) *Master {
v1beta3: c.EnableV1Beta3,
nodeIPCache: NewIPCache(c.Cloud, util.RealClock{}, 30*time.Second),
masterCount: c.MasterCount,
readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))),
readWriteServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadWritePort))),
masterCount: c.MasterCount,
publicIP: c.PublicAddress,
publicReadOnlyPort: c.ReadOnlyPort,
publicReadWritePort: c.ReadWritePort,
serviceReadOnlyIP: serviceReadOnlyIP,
// TODO: serviceReadOnlyPort should be passed in as an argument, it may not always be 80
serviceReadOnlyPort: 80,
serviceReadWriteIP: serviceReadWriteIP,
// TODO: serviceReadWritePort should be passed in as an argument, it may not always be 443
serviceReadWritePort: 443,
}
if c.RestfulContainer != nil {
@@ -444,7 +467,7 @@ func (m *Master) init(c *Config) {
func (m *Master) InstallSwaggerAPI() {
// Enable swagger UI and discovery API
swaggerConfig := swagger.Config{
WebServicesUrl: m.readWriteServer,
WebServicesUrl: net.JoinHostPort(m.publicIP.String(), strconv.Itoa(int(m.publicReadWritePort))),
WebServices: m.handlerContainer.RegisteredWebServices(),
// TODO: Parameterize the path?
ApiPath: "/swaggerapi/",

View File

@@ -18,6 +18,8 @@ package master
import (
"fmt"
"net"
"strconv"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@@ -32,12 +34,11 @@ func (m *Master) serviceWriterLoop(stop chan struct{}) {
// TODO: when it becomes possible to change this stuff,
// stop polling and start watching.
// TODO: add endpoints of all replicas, not just the elected master.
if m.readWriteServer != "" {
// TODO: the public port should be part of the argument here, port will not always be 443
if err := m.createMasterServiceIfNeeded("kubernetes", 443); err != nil {
if m.serviceReadWriteIP != nil {
if err := m.createMasterServiceIfNeeded("kubernetes", m.serviceReadWriteIP, m.serviceReadWritePort); err != nil {
glog.Errorf("Can't create rw service: %v", err)
}
if err := m.ensureEndpointsContain("kubernetes", m.readWriteServer); err != nil {
if err := m.ensureEndpointsContain("kubernetes", net.JoinHostPort(m.publicIP.String(), strconv.Itoa(int(m.publicReadWritePort)))); err != nil {
glog.Errorf("Can't create rw endpoints: %v", err)
}
}
@@ -55,12 +56,11 @@ func (m *Master) roServiceWriterLoop(stop chan struct{}) {
// Update service & endpoint records.
// TODO: when it becomes possible to change this stuff,
// stop polling and start watching.
if m.readOnlyServer != "" {
// TODO: the public port should be part of the argument here, port will not always be 80
if err := m.createMasterServiceIfNeeded("kubernetes-ro", 80); err != nil {
if m.serviceReadOnlyIP != nil {
if err := m.createMasterServiceIfNeeded("kubernetes-ro", m.serviceReadOnlyIP, m.serviceReadOnlyPort); err != nil {
glog.Errorf("Can't create ro service: %v", err)
}
if err := m.ensureEndpointsContain("kubernetes-ro", m.readOnlyServer); err != nil {
if err := m.ensureEndpointsContain("kubernetes-ro", net.JoinHostPort(m.publicIP.String(), strconv.Itoa(int(m.publicReadOnlyPort)))); err != nil {
glog.Errorf("Can't create ro endpoints: %v", err)
}
}
@@ -75,7 +75,7 @@ func (m *Master) roServiceWriterLoop(stop chan struct{}) {
// createMasterServiceIfNeeded will create the specified service if it
// doesn't already exist.
func (m *Master) createMasterServiceIfNeeded(serviceName string, port int) error {
func (m *Master) createMasterServiceIfNeeded(serviceName string, serviceIP net.IP, servicePort int) error {
ctx := api.NewDefaultContext()
if _, err := m.serviceRegistry.GetService(ctx, serviceName); err == nil {
// The service already exists.
@@ -88,9 +88,10 @@ func (m *Master) createMasterServiceIfNeeded(serviceName string, port int) error
Labels: map[string]string{"provider": "kubernetes", "component": "apiserver"},
},
Spec: api.ServiceSpec{
Port: port,
Port: servicePort,
// maintained by this code, not by the pod selector
Selector: nil,
PortalIP: serviceIP.String(),
},
}
// Kids, don't do this at home: this is a hack. There's no good way to call the business

View File

@@ -47,7 +47,7 @@ import (
type APIServer struct {
Port int
Address util.IP
PublicAddressOverride string
PublicAddressOverride util.IP
ReadOnlyPort int
APIRate float32
APIBurst int
@@ -80,6 +80,7 @@ func NewAPIServer() *APIServer {
s := APIServer{
Port: 8080,
Address: util.IP(net.ParseIP("127.0.0.1")),
PublicAddressOverride: util.IP(net.ParseIP("")),
ReadOnlyPort: 7080,
APIRate: 10.0,
APIBurst: 200,
@@ -127,11 +128,10 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
"further assumed that port 443 on the cluster's public address is proxied to this "+
"port. This is performed by nginx in the default setup.")
fs.Var(&s.Address, "address", "The IP address on to serve on (set to 0.0.0.0 for all interfaces)")
fs.StringVar(&s.PublicAddressOverride, "public_address_override", s.PublicAddressOverride, ""+
"Public serving address. Read only port will be opened on this address, "+
"and it is assumed that port 443 at this address will be proxied/redirected "+
"to '-address':'-port'. If blank, the address in the first listed interface "+
"will be used.")
fs.Var(&s.PublicAddressOverride, "public_address_override", "Public serving address."+
"Read only port will be opened on this address, and it is assumed that port "+
"443 at this address will be proxied/redirected to '-address':'-port'. If "+
"blank, the address in the first listed interface will be used.")
fs.IntVar(&s.ReadOnlyPort, "read_only_port", s.ReadOnlyPort, ""+
"The port from which to serve read-only resources. If 0, don't serve on a "+
"read-only address. It is assumed that firewall rules are set up such that "+
@@ -251,7 +251,7 @@ func (s *APIServer) Run(_ []string) error {
CorsAllowedOriginList: s.CorsAllowedOriginList,
ReadOnlyPort: s.ReadOnlyPort,
ReadWritePort: s.Port,
PublicAddress: s.PublicAddressOverride,
PublicAddress: net.IP(s.PublicAddressOverride),
Authenticator: authenticator,
Authorizer: authorizer,
AdmissionControl: admissionController,
@@ -263,11 +263,11 @@ func (s *APIServer) Run(_ []string) error {
// We serve on 3 ports. See docs/reaching_the_api.md
roLocation := ""
if s.ReadOnlyPort != 0 {
roLocation = net.JoinHostPort(config.PublicAddress, strconv.Itoa(config.ReadOnlyPort))
roLocation = net.JoinHostPort(config.PublicAddress.String(), strconv.Itoa(config.ReadOnlyPort))
}
secureLocation := ""
if s.SecurePort != 0 {
secureLocation = net.JoinHostPort(config.PublicAddress, strconv.Itoa(s.SecurePort))
secureLocation = net.JoinHostPort(config.PublicAddress.String(), strconv.Itoa(s.SecurePort))
}
rwLocation := net.JoinHostPort(s.Address.String(), strconv.Itoa(int(s.Port)))
@@ -317,7 +317,7 @@ func (s *APIServer) Run(_ []string) error {
if s.TLSCertFile == "" && s.TLSPrivateKeyFile == "" {
s.TLSCertFile = "/var/run/kubernetes/apiserver.crt"
s.TLSPrivateKeyFile = "/var/run/kubernetes/apiserver.key"
if err := util.GenerateSelfSignedCert(config.PublicAddress, s.TLSCertFile, s.TLSPrivateKeyFile); err != nil {
if err := util.GenerateSelfSignedCert(config.PublicAddress.String(), s.TLSCertFile, s.TLSPrivateKeyFile); err != nil {
glog.Errorf("Unable to generate self signed cert: %v", err)
} else {
glog.Infof("Using self-signed cert (%s, %s)", s.TLSCertFile, s.TLSPrivateKeyFile)