Skip to content
This repository was archived by the owner on Jul 30, 2021. It is now read-only.

Commit 04efef9

Browse files
authored
Merge pull request #318 from dghubble/cidr-customize
pkg,cmd: Add Kubernetes pod and service CIDR customziation
2 parents 2bf8d8d + 0942e1c commit 04efef9

File tree

11 files changed

+179
-45
lines changed

11 files changed

+179
-45
lines changed

cmd/bootkube/render.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ import (
1616
"github.com/kubernetes-incubator/bootkube/pkg/tlsutil"
1717
)
1818

19+
const (
20+
apiOffset = 1
21+
dnsOffset = 10
22+
etcdOffset = 15
23+
defaultServiceBaseIP = "10.3.0.0"
24+
)
25+
1926
var (
2027
cmdRender = &cobra.Command{
2128
Use: "render",
@@ -33,6 +40,8 @@ var (
3340
etcdServers string
3441
apiServers string
3542
altNames string
43+
podCIDR string
44+
serviceCIDR string
3645
selfHostKubelet bool
3746
cloudProvider string
3847
selfHostedEtcd bool
@@ -47,6 +56,8 @@ func init() {
4756
cmdRender.Flags().StringVar(&renderOpts.etcdServers, "etcd-servers", "http://127.0.0.1:2379", "List of etcd servers URLs including host:port, comma separated")
4857
cmdRender.Flags().StringVar(&renderOpts.apiServers, "api-servers", "https://127.0.0.1:443", "List of API server URLs including host:port, commma seprated")
4958
cmdRender.Flags().StringVar(&renderOpts.altNames, "api-server-alt-names", "", "List of SANs to use in api-server certificate. Example: 'IP=127.0.0.1,IP=127.0.0.2,DNS=localhost'. If empty, SANs will be extracted from the --api-servers flag.")
59+
cmdRender.Flags().StringVar(&renderOpts.podCIDR, "pod-cidr", "10.2.0.0/16", "The CIDR range of cluster pods.")
60+
cmdRender.Flags().StringVar(&renderOpts.serviceCIDR, "service-cidr", "10.3.0.0/24", "The CIDR range of cluster services.")
5061
cmdRender.Flags().BoolVar(&renderOpts.selfHostKubelet, "self-host-kubelet", false, "Create a self-hosted kubelet daemonset.")
5162
cmdRender.Flags().StringVar(&renderOpts.cloudProvider, "cloud-provider", "", "The provider for cloud services. Empty string for no provider")
5263
cmdRender.Flags().BoolVar(&renderOpts.selfHostedEtcd, "experimental-self-hosted-etcd", false, "Create self-hosted etcd assets.")
@@ -111,12 +122,52 @@ func flagsToAssetConfig() (c *asset.Config, err error) {
111122
return nil, err
112123
}
113124
}
125+
126+
_, podNet, err := net.ParseCIDR(renderOpts.podCIDR)
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
_, serviceNet, err := net.ParseCIDR(renderOpts.serviceCIDR)
132+
if err != nil {
133+
return nil, err
134+
}
135+
136+
if podNet.Contains(serviceNet.IP) || serviceNet.Contains(podNet.IP) {
137+
return nil, fmt.Errorf("Pod CIDR %s and service CIDR %s must not overlap", podNet.String(), serviceNet.String())
138+
}
139+
140+
apiServiceIP, err := offsetServiceIP(serviceNet, apiOffset)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
dnsServiceIP, err := offsetServiceIP(serviceNet, dnsOffset)
146+
if err != nil {
147+
return nil, err
148+
}
149+
150+
etcdServiceIP, err := offsetServiceIP(serviceNet, etcdOffset)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
// TODO: Find better option than asking users to make manual changes
156+
if serviceNet.IP.String() != defaultServiceBaseIP {
157+
fmt.Printf("You have selected a non-default service CIDR %s - be sure your kubelet service file uses --cluster-dns=%s\n", serviceNet.String(), dnsServiceIP.String())
158+
}
159+
114160
return &asset.Config{
115161
EtcdServers: etcdServers,
116162
CACert: caCert,
117163
CAPrivKey: caPrivKey,
118164
APIServers: apiServers,
119165
AltNames: altNames,
166+
PodCIDR: podNet,
167+
ServiceCIDR: serviceNet,
168+
APIServiceIP: apiServiceIP,
169+
DNSServiceIP: dnsServiceIP,
170+
ETCDServiceIP: etcdServiceIP,
120171
SelfHostKubelet: renderOpts.selfHostKubelet,
121172
CloudProvider: renderOpts.cloudProvider,
122173
SelfHostedEtcd: renderOpts.selfHostedEtcd,
@@ -195,3 +246,26 @@ func altNamesFromURLs(urls []*url.URL) *tlsutil.AltNames {
195246
}
196247
return &an
197248
}
249+
250+
// offsetServiceIP returns an IP offset by up to 255.
251+
// TODO: do numeric conversion to generalize this utility.
252+
func offsetServiceIP(ipnet *net.IPNet, offset int) (net.IP, error) {
253+
ip := make(net.IP, len(ipnet.IP))
254+
copy(ip, ipnet.IP)
255+
for i := 0; i < offset; i++ {
256+
incIPv4(ip)
257+
}
258+
if ipnet.Contains(ip) {
259+
return ip, nil
260+
}
261+
return net.IP([]byte("")), fmt.Errorf("Service IP %v is not in %s", ip, ipnet)
262+
}
263+
264+
func incIPv4(ip net.IP) {
265+
for j := len(ip) - 1; j >= 0; j-- {
266+
ip[j]++
267+
if ip[j] > 0 {
268+
break
269+
}
270+
}
271+
}

cmd/bootkube/render_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"net"
5+
"testing"
6+
)
7+
8+
func TestOffsetIP(t *testing.T) {
9+
cases := []struct {
10+
input string
11+
offset int
12+
expected string
13+
}{
14+
{"10.3.0.0/24", 1, "10.3.0.1"},
15+
{"10.3.0.0/24", 10, "10.3.0.10"},
16+
{"10.3.0.0/24", 15, "10.3.0.15"},
17+
{"10.3.0.0/16", 1, "10.3.0.1"},
18+
{"10.3.0.0/16", 10, "10.3.0.10"},
19+
{"10.3.0.0/16", 15, "10.3.0.15"},
20+
{"10.33.1.200/16", 1, "10.33.0.1"},
21+
{"10.33.1.200/16", 10, "10.33.0.10"},
22+
{"10.33.1.200/16", 15, "10.33.0.15"},
23+
{"192.168.1.0/24", 15, "192.168.1.15"},
24+
}
25+
26+
for _, c := range cases {
27+
_, cidr, err := net.ParseCIDR(c.input)
28+
if err != nil {
29+
t.Errorf("unexpected CIDR parse error: %v", err)
30+
}
31+
ip, err := offsetServiceIP(cidr, c.offset)
32+
if ip.String() != c.expected {
33+
t.Errorf("expected %s, got %s", c.expected, ip.String())
34+
}
35+
}
36+
}

hack/multi-node/Vagrantfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ if $worker_vm_memory < 1024
1818
puts "Workers should have at least 1024 MB of memory"
1919
end
2020

21-
CONTROLLER_CLUSTER_IP="10.3.0.1"
2221
CONTROLLER_USER_DATA_PATH = File.expand_path("./cluster/user-data-controller")
2322
WORKER_USER_DATA_PATH = File.expand_path("./cluster/user-data-worker")
2423

pkg/asset/asset.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/x509"
66
"fmt"
77
"io/ioutil"
8+
"net"
89
"net/url"
910
"os"
1011
"path/filepath"
@@ -50,6 +51,11 @@ type Config struct {
5051
CACert *x509.Certificate
5152
CAPrivKey *rsa.PrivateKey
5253
AltNames *tlsutil.AltNames
54+
PodCIDR *net.IPNet
55+
ServiceCIDR *net.IPNet
56+
APIServiceIP net.IP
57+
DNSServiceIP net.IP
58+
ETCDServiceIP net.IP
5359
SelfHostKubelet bool
5460
SelfHostedEtcd bool
5561
CloudProvider string
@@ -59,9 +65,12 @@ type Config struct {
5965
// configured via a user provided AssetConfig. Default assets include
6066
// TLS assets (certs, keys and secrets), and k8s component manifests.
6167
func NewDefaultAssets(conf Config) (Assets, error) {
62-
as := newStaticAssets(conf.SelfHostKubelet, conf.SelfHostedEtcd)
68+
as := newStaticAssets()
6369
as = append(as, newDynamicAssets(conf)...)
6470

71+
// Add kube-apiserver service IP
72+
conf.AltNames.IPs = append(conf.AltNames.IPs, conf.APIServiceIP)
73+
6574
// TLS assets
6675
tlsAssets, err := newTLSAssets(conf.CACert, conf.CAPrivKey, *conf.AltNames)
6776
if err != nil {

pkg/asset/internal/templates.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ spec:
4545
- --pod-manifest-path=/etc/kubernetes/manifests
4646
- --allow-privileged
4747
- --hostname-override=$(NODE_NAME)
48-
- --cluster-dns=10.3.0.10
48+
- --cluster-dns={{ .DNSServiceIP }}
4949
- --cluster-domain=cluster.local
5050
- --kubeconfig=/etc/kubernetes/kubeconfig
5151
- --require-kubeconfig
@@ -147,7 +147,7 @@ spec:
147147
- --etcd-servers={{ range $i, $e := .EtcdServers }}{{ if $i }},{{end}}{{ $e }}{{end}}
148148
- --storage-backend=etcd3
149149
- --allow-privileged=true
150-
- --service-cluster-ip-range=10.3.0.0/24
150+
- --service-cluster-ip-range={{ .ServiceCIDR }}
151151
- --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota
152152
- --runtime-config=api/all=true
153153
- --tls-cert-file=/etc/kubernetes/secrets/apiserver.crt
@@ -235,7 +235,7 @@ spec:
235235
- controller-manager
236236
- --allocate-node-cidrs=true
237237
- --configure-cloud-routes=false
238-
- --cluster-cidr=10.2.0.0/16
238+
- --cluster-cidr={{ .PodCIDR }}
239239
- --root-ca-file=/etc/kubernetes/secrets/ca.crt
240240
- --service-account-private-key-file=/etc/kubernetes/secrets/service-account.key
241241
- --leader-elect=true
@@ -326,7 +326,7 @@ spec:
326326
- --kubeconfig=/etc/kubernetes/kubeconfig
327327
- --proxy-mode=iptables
328328
- --hostname-override=$(NODE_NAME)
329-
- --cluster-cidr=10.2.0.0/16
329+
- --cluster-cidr={{ .PodCIDR }}
330330
env:
331331
- name: NODE_NAME
332332
valueFrom:
@@ -513,7 +513,7 @@ metadata:
513513
spec:
514514
selector:
515515
k8s-app: kube-dns
516-
clusterIP: 10.3.0.10
516+
clusterIP: {{ .DNSServiceIP }}
517517
ports:
518518
- name: dns
519519
port: 53
@@ -556,7 +556,7 @@ spec:
556556
selector:
557557
app: etcd
558558
etcd_cluster: kube-etcd
559-
clusterIP: 10.3.0.15
559+
clusterIP: {{ .ETCDServiceIP }}
560560
ports:
561561
- name: client
562562
port: 2379
@@ -582,7 +582,7 @@ data:
582582
}
583583
net-conf.json: |
584584
{
585-
"Network": "10.2.0.0/16",
585+
"Network": "{{ .PodCIDR }}",
586586
"Backend": {
587587
"Type": "vxlan"
588588
}

pkg/asset/k8s.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,37 @@ const (
1717
secretCMName = "kube-controller-manager"
1818
)
1919

20-
func newStaticAssets(selfHostKubelet, selfHostedEtcd bool) Assets {
20+
func newStaticAssets() Assets {
2121
var noData interface{}
2222
assets := Assets{
2323
mustCreateAssetFromTemplate(AssetPathScheduler, internal.SchedulerTemplate, noData),
2424
mustCreateAssetFromTemplate(AssetPathSchedulerDisruption, internal.SchedulerDisruptionTemplate, noData),
2525
mustCreateAssetFromTemplate(AssetPathControllerManagerDisruption, internal.ControllerManagerDisruptionTemplate, noData),
26-
mustCreateAssetFromTemplate(AssetPathProxy, internal.ProxyTemplate, noData),
2726
mustCreateAssetFromTemplate(AssetPathKubeDNSDeployment, internal.DNSDeploymentTemplate, noData),
28-
mustCreateAssetFromTemplate(AssetPathKubeDNSSvc, internal.DNSSvcTemplate, noData),
2927
mustCreateAssetFromTemplate(AssetPathCheckpointer, internal.CheckpointerTemplate, noData),
3028
mustCreateAssetFromTemplate(AssetPathKubeFlannel, internal.KubeFlannelTemplate, noData),
31-
mustCreateAssetFromTemplate(AssetPathKubeFlannelCfg, internal.KubeFlannelCfgTemplate, noData),
3229
}
33-
if selfHostKubelet {
34-
assets = append(assets, mustCreateAssetFromTemplate(AssetPathKubelet, internal.KubeletTemplate, noData))
35-
}
36-
if selfHostedEtcd {
37-
assets = append(assets,
38-
mustCreateAssetFromTemplate(AssetPathEtcdOperator, internal.EtcdOperatorTemplate, noData),
39-
mustCreateAssetFromTemplate(AssetPathEtcdSvc, internal.EtcdSvcTemplate, noData),
40-
)
41-
}
42-
4330
return assets
4431
}
4532

4633
func newDynamicAssets(conf Config) Assets {
47-
return Assets{
34+
assets := Assets{
4835
mustCreateAssetFromTemplate(AssetPathControllerManager, internal.ControllerManagerTemplate, conf),
4936
mustCreateAssetFromTemplate(AssetPathAPIServer, internal.APIServerTemplate, conf),
37+
mustCreateAssetFromTemplate(AssetPathProxy, internal.ProxyTemplate, conf),
38+
mustCreateAssetFromTemplate(AssetPathKubeFlannelCfg, internal.KubeFlannelCfgTemplate, conf),
39+
mustCreateAssetFromTemplate(AssetPathKubeDNSSvc, internal.DNSSvcTemplate, conf),
5040
}
41+
if conf.SelfHostKubelet {
42+
assets = append(assets, mustCreateAssetFromTemplate(AssetPathKubelet, internal.KubeletTemplate, conf))
43+
}
44+
if conf.SelfHostedEtcd {
45+
assets = append(assets,
46+
mustCreateAssetFromTemplate(AssetPathEtcdOperator, internal.EtcdOperatorTemplate, conf),
47+
mustCreateAssetFromTemplate(AssetPathEtcdSvc, internal.EtcdSvcTemplate, conf),
48+
)
49+
}
50+
return assets
5151
}
5252

5353
func newKubeConfigAsset(assets Assets, conf Config) (Asset, error) {

pkg/asset/tls.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package asset
33
import (
44
"crypto/rsa"
55
"crypto/x509"
6-
"net"
76

87
"github.com/kubernetes-incubator/bootkube/pkg/tlsutil"
98
)
@@ -78,7 +77,6 @@ func newAPIKeyAndCert(caCert *x509.Certificate, caPrivKey *rsa.PrivateKey, altNa
7877
if err != nil {
7978
return nil, nil, err
8079
}
81-
altNames.IPs = append(altNames.IPs, net.ParseIP("10.3.0.1"))
8280
altNames.DNSNames = append(altNames.DNSNames, []string{
8381
"kubernetes",
8482
"kubernetes.default",

pkg/bootkube/bootkube.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
scheduler "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options"
1616

1717
"github.com/kubernetes-incubator/bootkube/pkg/asset"
18+
"github.com/kubernetes-incubator/bootkube/pkg/util/etcdutil"
1819
)
1920

2021
const (
@@ -129,11 +130,23 @@ func (b *bootkube) Run() error {
129130
errch <- err
130131
}
131132
}()
132-
if b.selfHostedEtcd {
133-
requiredPods = append(requiredPods, "etcd-operator")
134-
}
135-
go func() { errch <- WaitUntilPodsRunning(requiredPods, assetTimeout, b.selfHostedEtcd) }()
136-
133+
go func() {
134+
if b.selfHostedEtcd {
135+
requiredPods = append(requiredPods, "etcd-operator")
136+
etcdServiceIP, err := detectEtcdIP(b.assetDir)
137+
if err != nil {
138+
errch <- err
139+
return
140+
}
141+
if err := WaitUntilPodsRunning(requiredPods, assetTimeout); err != nil {
142+
errch <- err
143+
return
144+
}
145+
errch <- etcdutil.Migrate(etcdServiceIP)
146+
} else {
147+
errch <- WaitUntilPodsRunning(requiredPods, assetTimeout)
148+
}
149+
}()
137150
// If any of the bootkube services exit, it means it is unrecoverable and we should exit.
138151
err := <-errch
139152
if err != nil {

pkg/bootkube/parse.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,20 @@ func detectPodCIDR(config Config) (string, error) {
7474
return "", fmt.Errorf("can't detect --cluster-cidr flag in %s", asset.AssetPathControllerManager)
7575
}
7676

77+
// detectEtcdIP deserializes the etcd-service ClusterIP.
78+
func detectEtcdIP(assetDir string) (string, error) {
79+
b, err := ioutil.ReadFile(filepath.Join(assetDir, asset.AssetPathEtcdSvc))
80+
if err != nil {
81+
return "", fmt.Errorf("can't read file %s: %v", asset.AssetPathEtcdSvc, err)
82+
}
83+
var service v1.Service
84+
err = yaml.Unmarshal(b, &service)
85+
if err != nil {
86+
return "", fmt.Errorf("can't unmarshal %s: %v", asset.AssetPathEtcdSvc, err)
87+
}
88+
return service.Spec.ClusterIP, nil
89+
}
90+
7791
func findFlag(flagName string, args []string) string {
7892
for _, arg := range args {
7993
if strings.HasPrefix(arg, flagName+"=") {

0 commit comments

Comments
 (0)