flannel_helper.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package kubelet
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "os"
  18. "strconv"
  19. "strings"
  20. utildbus "k8s.io/kubernetes/pkg/util/dbus"
  21. utilexec "k8s.io/kubernetes/pkg/util/exec"
  22. utiliptables "k8s.io/kubernetes/pkg/util/iptables"
  23. "github.com/golang/glog"
  24. )
  25. // TODO: Move all this to a network plugin.
  26. const (
  27. // TODO: The location of default docker options is distro specific, so this
  28. // probably won't work on anything other than debian/ubuntu. This is a
  29. // short-term compromise till we've moved overlay setup into a plugin.
  30. dockerOptsFile = "/etc/default/docker"
  31. flannelSubnetKey = "FLANNEL_SUBNET"
  32. flannelNetworkKey = "FLANNEL_NETWORK"
  33. flannelMtuKey = "FLANNEL_MTU"
  34. dockerOptsKey = "DOCKER_OPTS"
  35. flannelSubnetFile = "/var/run/flannel/subnet.env"
  36. )
  37. // A Kubelet to flannel bridging helper.
  38. type FlannelHelper struct {
  39. subnetFile string
  40. iptablesHelper utiliptables.Interface
  41. }
  42. // NewFlannelHelper creates a new flannel helper.
  43. func NewFlannelHelper() *FlannelHelper {
  44. return &FlannelHelper{
  45. subnetFile: flannelSubnetFile,
  46. iptablesHelper: utiliptables.New(utilexec.New(), utildbus.New(), utiliptables.ProtocolIpv4),
  47. }
  48. }
  49. // Ensure the required MASQUERADE rules exist for the given network/cidr.
  50. func (f *FlannelHelper) ensureFlannelMasqRule(kubeNetwork, podCIDR string) error {
  51. // TODO: Investigate delegation to flannel via -ip-masq=true once flannel
  52. // issue #374 is resolved.
  53. comment := "Flannel masquerade facilitates pod<->node traffic."
  54. args := []string{
  55. "-m", "comment", "--comment", comment,
  56. "!", "-d", kubeNetwork, "-s", podCIDR, "-j", "MASQUERADE",
  57. }
  58. _, err := f.iptablesHelper.EnsureRule(
  59. utiliptables.Append,
  60. utiliptables.TableNAT,
  61. utiliptables.ChainPostrouting,
  62. args...)
  63. return err
  64. }
  65. // Handshake waits for the flannel subnet file and installs a few IPTables
  66. // rules, returning the pod CIDR allocated for this node.
  67. func (f *FlannelHelper) Handshake() (podCIDR string, err error) {
  68. // TODO: Using a file to communicate is brittle
  69. if _, err = os.Stat(f.subnetFile); err != nil {
  70. return "", fmt.Errorf("Waiting for subnet file %v", f.subnetFile)
  71. }
  72. glog.Infof("Found flannel subnet file %v", f.subnetFile)
  73. config, err := parseKVConfig(f.subnetFile)
  74. if err != nil {
  75. return "", err
  76. }
  77. if err = writeDockerOptsFromFlannelConfig(config); err != nil {
  78. return "", err
  79. }
  80. podCIDR, ok := config[flannelSubnetKey]
  81. if !ok {
  82. return "", fmt.Errorf("No flannel subnet, config %+v", config)
  83. }
  84. kubeNetwork, ok := config[flannelNetworkKey]
  85. if !ok {
  86. return "", fmt.Errorf("No flannel network, config %+v", config)
  87. }
  88. if f.ensureFlannelMasqRule(kubeNetwork, podCIDR); err != nil {
  89. return "", fmt.Errorf("Unable to install flannel masquerade %v", err)
  90. }
  91. return podCIDR, nil
  92. }
  93. // Take env variables from flannel subnet env and write to /etc/docker/defaults.
  94. func writeDockerOptsFromFlannelConfig(flannelConfig map[string]string) error {
  95. // TODO: Write dockeropts to unit file on systemd machines
  96. // https://github.com/docker/docker/issues/9889
  97. mtu, ok := flannelConfig[flannelMtuKey]
  98. if !ok {
  99. return fmt.Errorf("No flannel mtu, flannel config %+v", flannelConfig)
  100. }
  101. dockerOpts, err := parseKVConfig(dockerOptsFile)
  102. if err != nil {
  103. return err
  104. }
  105. opts, ok := dockerOpts[dockerOptsKey]
  106. if !ok {
  107. glog.Errorf("Did not find docker opts, writing them")
  108. opts = fmt.Sprintf(
  109. " --bridge=cbr0 --iptables=false --ip-masq=false")
  110. } else {
  111. opts, _ = strconv.Unquote(opts)
  112. }
  113. dockerOpts[dockerOptsKey] = fmt.Sprintf("\"%v --mtu=%v\"", opts, mtu)
  114. if err = writeKVConfig(dockerOptsFile, dockerOpts); err != nil {
  115. return err
  116. }
  117. return nil
  118. }
  119. // parseKVConfig takes a file with key-value env variables and returns a dictionary mapping the same.
  120. func parseKVConfig(filename string) (map[string]string, error) {
  121. config := map[string]string{}
  122. if _, err := os.Stat(filename); err != nil {
  123. return config, err
  124. }
  125. buff, err := ioutil.ReadFile(filename)
  126. if err != nil {
  127. return config, err
  128. }
  129. str := string(buff)
  130. glog.Infof("Read kv options %+v from %v", str, filename)
  131. for _, line := range strings.Split(str, "\n") {
  132. kv := strings.Split(line, "=")
  133. if len(kv) != 2 {
  134. glog.Warningf("Ignoring non key-value pair %v", kv)
  135. continue
  136. }
  137. config[string(kv[0])] = string(kv[1])
  138. }
  139. return config, nil
  140. }
  141. // writeKVConfig writes a kv map as env variables into the given file.
  142. func writeKVConfig(filename string, kv map[string]string) error {
  143. if _, err := os.Stat(filename); err != nil {
  144. return err
  145. }
  146. content := ""
  147. for k, v := range kv {
  148. content += fmt.Sprintf("%v=%v\n", k, v)
  149. }
  150. glog.Warningf("Writing kv options %+v to %v", content, filename)
  151. return ioutil.WriteFile(filename, []byte(content), 0644)
  152. }