2019-01-12 04:58:27 +00:00
/ *
Copyright 2014 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
// Package app does all of the work necessary to configure and run a
// Kubernetes app process.
package app
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
2019-09-27 21:51:53 +00:00
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
2019-12-12 01:27:03 +00:00
gerrors "github.com/pkg/errors"
2019-04-07 17:07:55 +00:00
v1 "k8s.io/api/core/v1"
2019-09-27 21:51:53 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-12-12 01:27:03 +00:00
"k8s.io/apimachinery/pkg/fields"
2019-09-27 21:51:53 +00:00
"k8s.io/apimachinery/pkg/labels"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/runtime"
2019-12-12 01:27:03 +00:00
"k8s.io/apimachinery/pkg/runtime/serializer"
2019-09-27 21:51:53 +00:00
"k8s.io/apimachinery/pkg/selection"
2019-01-12 04:58:27 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes"
utilfeature "k8s.io/apiserver/pkg/util/feature"
2019-08-30 18:33:25 +00:00
"k8s.io/client-go/informers"
2019-01-12 04:58:27 +00:00
clientset "k8s.io/client-go/kubernetes"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/tools/record"
2019-04-07 17:07:55 +00:00
cliflag "k8s.io/component-base/cli/flag"
componentbaseconfig "k8s.io/component-base/config"
2020-08-10 17:43:49 +00:00
"k8s.io/component-base/configz"
2019-09-27 21:51:53 +00:00
"k8s.io/component-base/metrics/legacyregistry"
2019-12-12 01:27:03 +00:00
"k8s.io/component-base/version"
"k8s.io/component-base/version/verflag"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
"k8s.io/kube-proxy/config/v1alpha1"
api "k8s.io/kubernetes/pkg/apis/core"
2020-12-01 01:06:26 +00:00
"k8s.io/kubernetes/pkg/cluster/ports"
2019-09-27 21:51:53 +00:00
"k8s.io/kubernetes/pkg/features"
2019-01-12 04:58:27 +00:00
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/proxy"
2019-04-07 17:07:55 +00:00
"k8s.io/kubernetes/pkg/proxy/apis"
2019-01-12 04:58:27 +00:00
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
2019-09-27 21:51:53 +00:00
proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme"
2019-12-12 01:27:03 +00:00
kubeproxyconfigv1alpha1 "k8s.io/kubernetes/pkg/proxy/apis/config/v1alpha1"
2019-01-12 04:58:27 +00:00
"k8s.io/kubernetes/pkg/proxy/apis/config/validation"
"k8s.io/kubernetes/pkg/proxy/config"
"k8s.io/kubernetes/pkg/proxy/healthcheck"
"k8s.io/kubernetes/pkg/proxy/iptables"
"k8s.io/kubernetes/pkg/proxy/ipvs"
"k8s.io/kubernetes/pkg/proxy/userspace"
2019-08-30 18:33:25 +00:00
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
2019-04-07 17:07:55 +00:00
"k8s.io/kubernetes/pkg/util/filesystem"
2019-01-12 04:58:27 +00:00
utilflag "k8s.io/kubernetes/pkg/util/flag"
utilipset "k8s.io/kubernetes/pkg/util/ipset"
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
"k8s.io/kubernetes/pkg/util/oom"
"k8s.io/utils/exec"
utilpointer "k8s.io/utils/pointer"
)
const (
proxyModeUserspace = "userspace"
proxyModeIPTables = "iptables"
proxyModeIPVS = "ipvs"
proxyModeKernelspace = "kernelspace"
)
2019-04-07 17:07:55 +00:00
// proxyRun defines the interface to run a specified ProxyServer
type proxyRun interface {
Run ( ) error
2019-06-12 21:00:25 +00:00
CleanupAndExit ( ) error
2019-04-07 17:07:55 +00:00
}
2019-01-12 04:58:27 +00:00
// Options contains everything necessary to create and run a proxy server.
type Options struct {
// ConfigFile is the location of the proxy server's configuration file.
ConfigFile string
// WriteConfigTo is the path where the default configuration will be written.
WriteConfigTo string
2019-09-27 21:51:53 +00:00
// CleanupAndExit, when true, makes the proxy server clean up iptables and ipvs rules, then exit.
2019-01-12 04:58:27 +00:00
CleanupAndExit bool
// WindowsService should be set to true if kube-proxy is running as a service on Windows.
// Its corresponding flag only gets registered in Windows builds
WindowsService bool
// config is the proxy server's configuration object.
config * kubeproxyconfig . KubeProxyConfiguration
2019-04-07 17:07:55 +00:00
// watcher is used to watch on the update change of ConfigFile
watcher filesystem . FSWatcher
// proxyServer is the interface to run the proxy server
proxyServer proxyRun
// errCh is the channel that errors will be sent
errCh chan error
2019-01-12 04:58:27 +00:00
// The fields below here are placeholders for flags that can't be directly mapped into
// config.KubeProxyConfiguration.
//
// TODO remove these fields once the deprecated flags are removed.
// master is used to override the kubeconfig's URL to the apiserver.
master string
// healthzPort is the port to be used by the healthz server.
healthzPort int32
2019-03-04 01:22:32 +00:00
// metricsPort is the port to be used by the metrics server.
metricsPort int32
2019-01-12 04:58:27 +00:00
// hostnameOverride, if set from the command line flag, takes precedence over the `HostnameOverride` value from the config file
hostnameOverride string
}
// AddFlags adds flags to fs and binds them to options.
func ( o * Options ) AddFlags ( fs * pflag . FlagSet ) {
o . addOSFlags ( fs )
2019-08-30 18:33:25 +00:00
2019-01-12 04:58:27 +00:00
fs . StringVar ( & o . ConfigFile , "config" , o . ConfigFile , "The path to the configuration file." )
fs . StringVar ( & o . WriteConfigTo , "write-config-to" , o . WriteConfigTo , "If set, write the default configuration values to this file and exit." )
2020-12-01 01:06:26 +00:00
fs . StringVar ( & o . config . ClientConnection . Kubeconfig , "kubeconfig" , o . config . ClientConnection . Kubeconfig , "Path to kubeconfig file with authorization information (the master location can be overridden by the master flag)." )
2019-08-30 18:33:25 +00:00
fs . StringVar ( & o . config . ClusterCIDR , "cluster-cidr" , o . config . ClusterCIDR , "The CIDR range of pods in the cluster. When configured, traffic sent to a Service cluster IP from outside this range will be masqueraded and traffic sent from pods to an external LoadBalancer IP will be directed to the respective cluster IP instead" )
fs . StringVar ( & o . config . ClientConnection . ContentType , "kube-api-content-type" , o . config . ClientConnection . ContentType , "Content type of requests sent to apiserver." )
fs . StringVar ( & o . master , "master" , o . master , "The address of the Kubernetes API server (overrides any value in kubeconfig)" )
fs . StringVar ( & o . hostnameOverride , "hostname-override" , o . hostnameOverride , "If non-empty, will use this string as identification instead of the actual hostname." )
fs . StringVar ( & o . config . IPVS . Scheduler , "ipvs-scheduler" , o . config . IPVS . Scheduler , "The ipvs scheduler type when proxy mode is ipvs" )
2020-03-26 21:07:15 +00:00
fs . StringVar ( & o . config . ShowHiddenMetricsForVersion , "show-hidden-metrics-for-version" , o . config . ShowHiddenMetricsForVersion ,
"The previous version for which you want to show hidden metrics. " +
"Only the previous minor version is meaningful, other values will not be allowed. " +
"The format is <major>.<minor>, e.g.: '1.16'. " +
"The purpose of this format is make sure you have the opportunity to notice if the next release hides additional metrics, " +
"rather than being surprised when they are permanently removed in the release after that." )
2019-08-30 18:33:25 +00:00
fs . StringSliceVar ( & o . config . IPVS . ExcludeCIDRs , "ipvs-exclude-cidrs" , o . config . IPVS . ExcludeCIDRs , "A comma-separated list of CIDR's which the ipvs proxier should not touch when cleaning up IPVS rules." )
fs . StringSliceVar ( & o . config . NodePortAddresses , "nodeport-addresses" , o . config . NodePortAddresses ,
"A string slice of values which specify the addresses to use for NodePorts. Values may be valid IP blocks (e.g. 1.2.3.0/24, 1.2.3.4/32). The default empty string slice ([]) means to use all local addresses." )
2019-01-12 04:58:27 +00:00
fs . BoolVar ( & o . CleanupAndExit , "cleanup" , o . CleanupAndExit , "If true cleanup iptables and ipvs rules and exit." )
2020-03-26 21:07:15 +00:00
fs . Var ( utilflag . IPVar { Val : & o . config . BindAddress } , "bind-address" , "The IP address for the proxy server to serve on (set to '0.0.0.0' for all IPv4 interfaces and '::' for all IPv6 interfaces)" )
fs . Var ( utilflag . IPPortVar { Val : & o . config . HealthzBindAddress } , "healthz-bind-address" , "The IP address with port for the health check server to serve on (set to '0.0.0.0:10256' for all IPv4 interfaces and '[::]:10256' for all IPv6 interfaces). Set empty to disable." )
fs . Var ( utilflag . IPPortVar { Val : & o . config . MetricsBindAddress } , "metrics-bind-address" , "The IP address with port for the metrics server to serve on (set to '0.0.0.0:10249' for all IPv4 interfaces and '[::]:10249' for all IPv6 interfaces). Set empty to disable." )
2020-08-10 17:43:49 +00:00
fs . BoolVar ( & o . config . BindAddressHardFail , "bind-address-hard-fail" , o . config . BindAddressHardFail , "If true kube-proxy will treat failure to bind to a port as fatal and exit" )
2019-01-12 04:58:27 +00:00
fs . Var ( utilflag . PortRangeVar { Val : & o . config . PortRange } , "proxy-port-range" , "Range of host ports (beginPort-endPort, single port or beginPort+offset, inclusive) that may be consumed in order to proxy service traffic. If (unspecified, 0, or 0-0) then ports will be randomly chosen." )
2020-08-10 17:43:49 +00:00
fs . Var ( & o . config . Mode , "proxy-mode" , "Which proxy mode to use: 'userspace' (older) or 'iptables' (faster) or 'ipvs' or 'kernelspace' (windows). If blank, 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." )
2019-08-30 18:33:25 +00:00
fs . Var ( cliflag . NewMapStringBool ( & o . config . FeatureGates ) , "feature-gates" , "A set of key=value pairs that describe feature gates for alpha/experimental features. " +
"Options are:\n" + strings . Join ( utilfeature . DefaultFeatureGate . KnownFeatures ( ) , "\n" ) )
fs . Int32Var ( & o . healthzPort , "healthz-port" , o . healthzPort , "The port to bind the health check server. Use 0 to disable." )
2020-03-26 21:07:15 +00:00
fs . MarkDeprecated ( "healthz-port" , "This flag is deprecated and will be removed in a future release. Please use --healthz-bind-address instead." )
2019-08-30 18:33:25 +00:00
fs . Int32Var ( & o . metricsPort , "metrics-port" , o . metricsPort , "The port to bind the metrics server. Use 0 to disable." )
2020-03-26 21:07:15 +00:00
fs . MarkDeprecated ( "metrics-port" , "This flag is deprecated and will be removed in a future release. Please use --metrics-bind-address instead." )
2019-08-30 18:33:25 +00:00
fs . Int32Var ( o . config . OOMScoreAdj , "oom-score-adj" , utilpointer . Int32PtrDerefOr ( o . config . OOMScoreAdj , int32 ( qos . KubeProxyOOMScoreAdj ) ) , "The oom-score-adj value for kube-proxy process. Values must be within the range [-1000, 1000]" )
2019-01-12 04:58:27 +00:00
fs . Int32Var ( o . config . IPTables . MasqueradeBit , "iptables-masquerade-bit" , utilpointer . Int32PtrDerefOr ( o . config . IPTables . MasqueradeBit , 14 ) , "If using the pure iptables proxy, the bit of the fwmark space to mark packets requiring SNAT with. Must be within the range [0, 31]." )
fs . Int32Var ( o . config . Conntrack . MaxPerCore , "conntrack-max-per-core" , * o . config . Conntrack . MaxPerCore ,
"Maximum number of NAT connections to track per CPU core (0 to leave the limit as-is and ignore conntrack-min)." )
fs . Int32Var ( o . config . Conntrack . Min , "conntrack-min" , * o . config . Conntrack . Min ,
"Minimum number of conntrack entries to allocate, regardless of conntrack-max-per-core (set conntrack-max-per-core=0 to leave the limit as-is)." )
2019-08-30 18:33:25 +00:00
fs . Int32Var ( & o . config . ClientConnection . Burst , "kube-api-burst" , o . config . ClientConnection . Burst , "Burst to use while talking with kubernetes apiserver" )
fs . DurationVar ( & o . config . IPTables . SyncPeriod . Duration , "iptables-sync-period" , o . config . IPTables . SyncPeriod . Duration , "The maximum interval of how often iptables rules are refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0." )
fs . DurationVar ( & o . config . IPTables . MinSyncPeriod . Duration , "iptables-min-sync-period" , o . config . IPTables . MinSyncPeriod . Duration , "The minimum interval of how often the iptables rules can be refreshed as endpoints and services change (e.g. '5s', '1m', '2h22m')." )
fs . DurationVar ( & o . config . IPVS . SyncPeriod . Duration , "ipvs-sync-period" , o . config . IPVS . SyncPeriod . Duration , "The maximum interval of how often ipvs rules are refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0." )
fs . DurationVar ( & o . config . IPVS . MinSyncPeriod . Duration , "ipvs-min-sync-period" , o . config . IPVS . MinSyncPeriod . Duration , "The minimum interval of how often the ipvs rules can be refreshed as endpoints and services change (e.g. '5s', '1m', '2h22m')." )
2020-03-26 21:07:15 +00:00
fs . DurationVar ( & o . config . IPVS . TCPTimeout . Duration , "ipvs-tcp-timeout" , o . config . IPVS . TCPTimeout . Duration , "The timeout for idle IPVS TCP connections, 0 to leave as-is. (e.g. '5s', '1m', '2h22m')." )
fs . DurationVar ( & o . config . IPVS . TCPFinTimeout . Duration , "ipvs-tcpfin-timeout" , o . config . IPVS . TCPFinTimeout . Duration , "The timeout for IPVS TCP connections after receiving a FIN packet, 0 to leave as-is. (e.g. '5s', '1m', '2h22m')." )
fs . DurationVar ( & o . config . IPVS . UDPTimeout . Duration , "ipvs-udp-timeout" , o . config . IPVS . UDPTimeout . Duration , "The timeout for IPVS UDP packets, 0 to leave as-is. (e.g. '5s', '1m', '2h22m')." )
2019-01-12 04:58:27 +00:00
fs . DurationVar ( & o . config . Conntrack . TCPEstablishedTimeout . Duration , "conntrack-tcp-timeout-established" , o . config . Conntrack . TCPEstablishedTimeout . Duration , "Idle timeout for established TCP connections (0 to leave as-is)" )
fs . DurationVar (
& o . config . Conntrack . TCPCloseWaitTimeout . Duration , "conntrack-tcp-timeout-close-wait" ,
o . config . Conntrack . TCPCloseWaitTimeout . Duration ,
"NAT timeout for TCP connections in the CLOSE_WAIT state" )
2019-08-30 18:33:25 +00:00
fs . DurationVar ( & o . config . ConfigSyncPeriod . Duration , "config-sync-period" , o . config . ConfigSyncPeriod . Duration , "How often configuration from the apiserver is refreshed. Must be greater than 0." )
fs . DurationVar ( & o . config . UDPIdleTimeout . Duration , "udp-timeout" , o . config . UDPIdleTimeout . Duration , "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" )
fs . BoolVar ( & o . config . IPVS . StrictARP , "ipvs-strict-arp" , o . config . IPVS . StrictARP , "Enable strict ARP by setting arp_ignore to 1 and arp_announce to 2" )
fs . BoolVar ( & o . config . IPTables . MasqueradeAll , "masquerade-all" , o . config . IPTables . MasqueradeAll , "If using the pure iptables proxy, SNAT all traffic sent via Service cluster IPs (this not commonly needed)" )
2019-01-12 04:58:27 +00:00
fs . BoolVar ( & o . config . EnableProfiling , "profiling" , o . config . EnableProfiling , "If true enables profiling via web interface on /debug/pprof handler." )
2019-08-30 18:33:25 +00:00
fs . Float32Var ( & o . config . ClientConnection . QPS , "kube-api-qps" , o . config . ClientConnection . QPS , "QPS to use while talking with kubernetes apiserver" )
2020-03-26 21:07:15 +00:00
fs . Var ( & o . config . DetectLocalMode , "detect-local-mode" , "Mode to use to detect local traffic" )
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
// NewOptions returns initialized Options
2019-01-12 04:58:27 +00:00
func NewOptions ( ) * Options {
return & Options {
config : new ( kubeproxyconfig . KubeProxyConfiguration ) ,
healthzPort : ports . ProxyHealthzPort ,
2019-03-04 01:22:32 +00:00
metricsPort : ports . ProxyStatusPort ,
2019-04-07 17:07:55 +00:00
errCh : make ( chan error ) ,
2019-01-12 04:58:27 +00:00
}
}
// Complete completes all the required options.
func ( o * Options ) Complete ( ) error {
if len ( o . ConfigFile ) == 0 && len ( o . WriteConfigTo ) == 0 {
klog . Warning ( "WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP." )
2019-08-30 18:33:25 +00:00
o . config . HealthzBindAddress = addressFromDeprecatedFlags ( o . config . HealthzBindAddress , o . healthzPort )
o . config . MetricsBindAddress = addressFromDeprecatedFlags ( o . config . MetricsBindAddress , o . metricsPort )
2019-01-12 04:58:27 +00:00
}
// Load the config file here in Complete, so that Validate validates the fully-resolved config.
if len ( o . ConfigFile ) > 0 {
2019-08-30 18:33:25 +00:00
c , err := o . loadConfigFromFile ( o . ConfigFile )
if err != nil {
2019-01-12 04:58:27 +00:00
return err
}
2019-08-30 18:33:25 +00:00
o . config = c
2019-04-07 17:07:55 +00:00
if err := o . initWatcher ( ) ; err != nil {
return err
}
2019-01-12 04:58:27 +00:00
}
if err := o . processHostnameOverrideFlag ( ) ; err != nil {
return err
}
2019-08-30 18:33:25 +00:00
return utilfeature . DefaultMutableFeatureGate . SetFromMap ( o . config . FeatureGates )
2019-04-07 17:07:55 +00:00
}
2019-01-12 04:58:27 +00:00
2019-04-07 17:07:55 +00:00
// Creates a new filesystem watcher and adds watches for the config file.
func ( o * Options ) initWatcher ( ) error {
fswatcher := filesystem . NewFsnotifyWatcher ( )
err := fswatcher . Init ( o . eventHandler , o . errorHandler )
if err != nil {
return err
}
err = fswatcher . AddWatch ( o . ConfigFile )
if err != nil {
return err
}
o . watcher = fswatcher
2019-01-12 04:58:27 +00:00
return nil
}
2019-04-07 17:07:55 +00:00
func ( o * Options ) eventHandler ( ent fsnotify . Event ) {
eventOpIs := func ( Op fsnotify . Op ) bool {
return ent . Op & Op == Op
}
if eventOpIs ( fsnotify . Write ) || eventOpIs ( fsnotify . Rename ) {
// error out when ConfigFile is updated
o . errCh <- fmt . Errorf ( "content of the proxy server's configuration file was updated" )
2019-12-12 01:27:03 +00:00
return
2019-04-07 17:07:55 +00:00
}
o . errCh <- nil
}
func ( o * Options ) errorHandler ( err error ) {
o . errCh <- err
}
2019-01-12 04:58:27 +00:00
// processHostnameOverrideFlag processes hostname-override flag
func ( o * Options ) processHostnameOverrideFlag ( ) error {
// Check if hostname-override flag is set and use value since configFile always overrides
if len ( o . hostnameOverride ) > 0 {
hostName := strings . TrimSpace ( o . hostnameOverride )
if len ( hostName ) == 0 {
return fmt . Errorf ( "empty hostname-override is invalid" )
}
o . config . HostnameOverride = strings . ToLower ( hostName )
}
return nil
}
// Validate validates all the required options.
2020-08-10 17:43:49 +00:00
func ( o * Options ) Validate ( ) error {
2019-01-12 04:58:27 +00:00
if errs := validation . Validate ( o . config ) ; len ( errs ) != 0 {
return errs . ToAggregate ( )
}
return nil
}
2019-08-30 18:33:25 +00:00
// Run runs the specified ProxyServer.
2019-01-12 04:58:27 +00:00
func ( o * Options ) Run ( ) error {
2019-04-07 17:07:55 +00:00
defer close ( o . errCh )
2019-01-12 04:58:27 +00:00
if len ( o . WriteConfigTo ) > 0 {
return o . writeConfigFile ( )
}
proxyServer , err := NewProxyServer ( o )
if err != nil {
return err
}
2019-06-12 21:00:25 +00:00
if o . CleanupAndExit {
return proxyServer . CleanupAndExit ( )
}
2019-04-07 17:07:55 +00:00
o . proxyServer = proxyServer
return o . runLoop ( )
}
// runLoop will watch on the update change of the proxy server's configuration file.
// Return an error when updated
func ( o * Options ) runLoop ( ) error {
if o . watcher != nil {
o . watcher . Run ( )
}
// run the proxy in goroutine
go func ( ) {
err := o . proxyServer . Run ( )
o . errCh <- err
} ( )
2019-01-12 04:58:27 +00:00
2019-04-07 17:07:55 +00:00
for {
2019-08-30 18:33:25 +00:00
err := <- o . errCh
if err != nil {
return err
2019-04-07 17:07:55 +00:00
}
}
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
func ( o * Options ) writeConfigFile ( ) ( err error ) {
const mediaType = runtime . ContentTypeYAML
2019-09-27 21:51:53 +00:00
info , ok := runtime . SerializerInfoForMediaType ( proxyconfigscheme . Codecs . SupportedMediaTypes ( ) , mediaType )
2019-08-30 18:33:25 +00:00
if ! ok {
return fmt . Errorf ( "unable to locate encoder -- %q is not a supported media type" , mediaType )
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
2019-09-27 21:51:53 +00:00
encoder := proxyconfigscheme . Codecs . EncoderForVersion ( info . Serializer , v1alpha1 . SchemeGroupVersion )
2019-01-12 04:58:27 +00:00
configFile , err := os . Create ( o . WriteConfigTo )
if err != nil {
return err
}
2019-08-30 18:33:25 +00:00
defer func ( ) {
ferr := configFile . Close ( )
if ferr != nil && err == nil {
err = ferr
}
} ( )
if err = encoder . Encode ( o . config , configFile ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
klog . Infof ( "Wrote configuration to: %s\n" , o . WriteConfigTo )
return nil
}
2019-08-30 18:33:25 +00:00
// addressFromDeprecatedFlags returns server address from flags
// passed on the command line based on the following rules:
// 1. If port is 0, disable the server (e.g. set address to empty).
// 2. Otherwise, set the port portion of the config accordingly.
func addressFromDeprecatedFlags ( addr string , port int32 ) string {
if port == 0 {
return ""
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
return proxyutil . AppendPortIfNeeded ( addr , port )
2019-03-04 01:22:32 +00:00
}
2019-12-12 01:27:03 +00:00
// newLenientSchemeAndCodecs returns a scheme that has only v1alpha1 registered into
// it and a CodecFactory with strict decoding disabled.
func newLenientSchemeAndCodecs ( ) ( * runtime . Scheme , * serializer . CodecFactory , error ) {
lenientScheme := runtime . NewScheme ( )
if err := kubeproxyconfig . AddToScheme ( lenientScheme ) ; err != nil {
return nil , nil , fmt . Errorf ( "failed to add kube-proxy config API to lenient scheme: %v" , err )
}
if err := kubeproxyconfigv1alpha1 . AddToScheme ( lenientScheme ) ; err != nil {
return nil , nil , fmt . Errorf ( "failed to add kube-proxy config v1alpha1 API to lenient scheme: %v" , err )
}
lenientCodecs := serializer . NewCodecFactory ( lenientScheme , serializer . DisableStrict )
return lenientScheme , & lenientCodecs , nil
}
2019-01-12 04:58:27 +00:00
// loadConfigFromFile loads the contents of file and decodes it as a
// KubeProxyConfiguration object.
func ( o * Options ) loadConfigFromFile ( file string ) ( * kubeproxyconfig . KubeProxyConfiguration , error ) {
data , err := ioutil . ReadFile ( file )
if err != nil {
return nil , err
}
return o . loadConfig ( data )
}
2019-12-12 01:27:03 +00:00
// loadConfig decodes a serialized KubeProxyConfiguration to the internal type.
2019-01-12 04:58:27 +00:00
func ( o * Options ) loadConfig ( data [ ] byte ) ( * kubeproxyconfig . KubeProxyConfiguration , error ) {
2019-12-12 01:27:03 +00:00
2019-09-27 21:51:53 +00:00
configObj , gvk , err := proxyconfigscheme . Codecs . UniversalDecoder ( ) . Decode ( data , nil , nil )
2019-01-12 04:58:27 +00:00
if err != nil {
2019-12-12 01:27:03 +00:00
// Try strict decoding first. If that fails decode with a lenient
// decoder, which has only v1alpha1 registered, and log a warning.
// The lenient path is to be dropped when support for v1alpha1 is dropped.
if ! runtime . IsStrictDecodingError ( err ) {
return nil , gerrors . Wrap ( err , "failed to decode" )
}
_ , lenientCodecs , lenientErr := newLenientSchemeAndCodecs ( )
if lenientErr != nil {
return nil , lenientErr
}
configObj , gvk , lenientErr = lenientCodecs . UniversalDecoder ( ) . Decode ( data , nil , nil )
if lenientErr != nil {
// Lenient decoding failed with the current version, return the
// original strict error.
return nil , fmt . Errorf ( "failed lenient decoding: %v" , err )
}
// Continue with the v1alpha1 object that was decoded leniently, but emit a warning.
klog . Warningf ( "using lenient decoding as strict decoding failed: %v" , err )
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
2019-08-30 18:33:25 +00:00
proxyConfig , ok := configObj . ( * kubeproxyconfig . KubeProxyConfiguration )
2019-01-12 04:58:27 +00:00
if ! ok {
return nil , fmt . Errorf ( "got unexpected config type: %v" , gvk )
}
2019-08-30 18:33:25 +00:00
return proxyConfig , nil
2019-01-12 04:58:27 +00:00
}
2019-08-30 18:33:25 +00:00
// ApplyDefaults applies the default values to Options.
2019-01-12 04:58:27 +00:00
func ( o * Options ) ApplyDefaults ( in * kubeproxyconfig . KubeProxyConfiguration ) ( * kubeproxyconfig . KubeProxyConfiguration , error ) {
2019-09-27 21:51:53 +00:00
external , err := proxyconfigscheme . Scheme . ConvertToVersion ( in , v1alpha1 . SchemeGroupVersion )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
2019-09-27 21:51:53 +00:00
proxyconfigscheme . Scheme . Default ( external )
2019-01-12 04:58:27 +00:00
2019-09-27 21:51:53 +00:00
internal , err := proxyconfigscheme . Scheme . ConvertToVersion ( external , kubeproxyconfig . SchemeGroupVersion )
2019-01-12 04:58:27 +00:00
if err != nil {
return nil , err
}
out := internal . ( * kubeproxyconfig . KubeProxyConfiguration )
return out , nil
}
// NewProxyCommand creates a *cobra.Command object with default parameters
func NewProxyCommand ( ) * cobra . Command {
opts := NewOptions ( )
cmd := & cobra . Command {
Use : "kube-proxy" ,
Long : ` The Kubernetes network proxy runs on each node . This
reflects services as defined in the Kubernetes API on each node and can do simple
TCP , UDP , and SCTP stream forwarding or round robin TCP , UDP , and SCTP forwarding across a set of backends .
Service cluster IPs and ports are currently found through Docker - links - compatible
environment variables specifying ports opened by the service proxy . There is an optional
addon that provides cluster DNS for these cluster IPs . The user must create a service
with the apiserver API to configure the proxy . ` ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
verflag . PrintAndExitIfRequested ( )
2020-08-10 17:43:49 +00:00
cliflag . PrintFlags ( cmd . Flags ( ) )
2019-01-12 04:58:27 +00:00
if err := initForOS ( opts . WindowsService ) ; err != nil {
klog . Fatalf ( "failed OS init: %v" , err )
}
if err := opts . Complete ( ) ; err != nil {
klog . Fatalf ( "failed complete: %v" , err )
}
2020-08-10 17:43:49 +00:00
if err := opts . Validate ( ) ; err != nil {
2019-01-12 04:58:27 +00:00
klog . Fatalf ( "failed validate: %v" , err )
}
2019-09-27 21:51:53 +00:00
if err := opts . Run ( ) ; err != nil {
klog . Exit ( err )
}
2019-01-12 04:58:27 +00:00
} ,
2020-08-10 17:43:49 +00:00
Args : func ( cmd * cobra . Command , args [ ] string ) error {
for _ , arg := range args {
if len ( arg ) > 0 {
return fmt . Errorf ( "%q does not take any arguments, got %q" , cmd . CommandPath ( ) , args )
}
}
return nil
} ,
2019-01-12 04:58:27 +00:00
}
var err error
opts . config , err = opts . ApplyDefaults ( opts . config )
if err != nil {
klog . Fatalf ( "unable to create flag defaults: %v" , err )
}
opts . AddFlags ( cmd . Flags ( ) )
2019-08-30 18:33:25 +00:00
// TODO handle error
2019-01-12 04:58:27 +00:00
cmd . MarkFlagFilename ( "config" , "yaml" , "yml" , "json" )
return cmd
}
// ProxyServer represents all the parameters required to start the Kubernetes proxy server. All
// fields are required.
type ProxyServer struct {
Client clientset . Interface
EventClient v1core . EventsGetter
IptInterface utiliptables . Interface
IpvsInterface utilipvs . Interface
IpsetInterface utilipset . Interface
execer exec . Interface
2019-09-27 21:51:53 +00:00
Proxier proxy . Provider
2019-01-12 04:58:27 +00:00
Broadcaster record . EventBroadcaster
Recorder record . EventRecorder
ConntrackConfiguration kubeproxyconfig . KubeProxyConntrackConfiguration
Conntracker Conntracker // if nil, ignored
ProxyMode string
NodeRef * v1 . ObjectReference
MetricsBindAddress string
2020-08-10 17:43:49 +00:00
BindAddressHardFail bool
2019-01-12 04:58:27 +00:00
EnableProfiling bool
2019-09-27 21:51:53 +00:00
UseEndpointSlices bool
2019-01-12 04:58:27 +00:00
OOMScoreAdj * int32
ConfigSyncPeriod time . Duration
2020-03-26 21:07:15 +00:00
HealthzServer healthcheck . ProxierHealthUpdater
2019-01-12 04:58:27 +00:00
}
// createClients creates a kube client and an event client from the given config and masterOverride.
// TODO remove masterOverride when CLI flags are removed.
2019-04-07 17:07:55 +00:00
func createClients ( config componentbaseconfig . ClientConnectionConfiguration , masterOverride string ) ( clientset . Interface , v1core . EventsGetter , error ) {
2019-01-12 04:58:27 +00:00
var kubeConfig * rest . Config
var err error
if len ( config . Kubeconfig ) == 0 && len ( masterOverride ) == 0 {
klog . Info ( "Neither kubeconfig file nor master URL was specified. Falling back to in-cluster config." )
kubeConfig , err = rest . InClusterConfig ( )
} else {
// This creates a client, first loading any specified kubeconfig
// file, and then overriding the Master flag, if non-empty.
kubeConfig , err = clientcmd . NewNonInteractiveDeferredLoadingClientConfig (
& clientcmd . ClientConfigLoadingRules { ExplicitPath : config . Kubeconfig } ,
& clientcmd . ConfigOverrides { ClusterInfo : clientcmdapi . Cluster { Server : masterOverride } } ) . ClientConfig ( )
}
if err != nil {
return nil , nil , err
}
kubeConfig . AcceptContentTypes = config . AcceptContentTypes
kubeConfig . ContentType = config . ContentType
kubeConfig . QPS = config . QPS
kubeConfig . Burst = int ( config . Burst )
client , err := clientset . NewForConfig ( kubeConfig )
if err != nil {
return nil , nil , err
}
eventClient , err := clientset . NewForConfig ( kubeConfig )
if err != nil {
return nil , nil , err
}
return client , eventClient . CoreV1 ( ) , nil
}
2020-08-10 17:43:49 +00:00
func serveHealthz ( hz healthcheck . ProxierHealthUpdater , errCh chan error ) {
if hz == nil {
return
}
fn := func ( ) {
err := hz . Run ( )
if err != nil {
klog . Errorf ( "healthz server failed: %v" , err )
if errCh != nil {
errCh <- fmt . Errorf ( "healthz server failed: %v" , err )
// if in hardfail mode, never retry again
blockCh := make ( chan error )
<- blockCh
}
} else {
klog . Errorf ( "healthz server returned without error" )
}
}
go wait . Until ( fn , 5 * time . Second , wait . NeverStop )
}
func serveMetrics ( bindAddress , proxyMode string , enableProfiling bool , errCh chan error ) {
if len ( bindAddress ) == 0 {
return
}
proxyMux := mux . NewPathRecorderMux ( "kube-proxy" )
healthz . InstallHandler ( proxyMux )
proxyMux . HandleFunc ( "/proxyMode" , func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "text/plain; charset=utf-8" )
w . Header ( ) . Set ( "X-Content-Type-Options" , "nosniff" )
fmt . Fprintf ( w , "%s" , proxyMode )
} )
//lint:ignore SA1019 See the Metrics Stability Migration KEP
proxyMux . Handle ( "/metrics" , legacyregistry . Handler ( ) )
if enableProfiling {
routes . Profiling { } . Install ( proxyMux )
}
configz . InstallHandler ( proxyMux )
fn := func ( ) {
err := http . ListenAndServe ( bindAddress , proxyMux )
if err != nil {
err = fmt . Errorf ( "starting metrics server failed: %v" , err )
utilruntime . HandleError ( err )
if errCh != nil {
errCh <- err
// if in hardfail mode, never retry again
blockCh := make ( chan error )
<- blockCh
}
}
}
go wait . Until ( fn , 5 * time . Second , wait . NeverStop )
}
2019-01-12 04:58:27 +00:00
// Run runs the specified ProxyServer. This should never exit (unless CleanupAndExit is set).
2019-06-12 21:00:25 +00:00
// TODO: At the moment, Run() cannot return a nil error, otherwise it's caller will never exit. Update callers of Run to handle nil errors.
2019-01-12 04:58:27 +00:00
func ( s * ProxyServer ) Run ( ) error {
// To help debugging, immediately log version
klog . Infof ( "Version: %+v" , version . Get ( ) )
// TODO(vmarmol): Use container config for this.
var oomAdjuster * oom . OOMAdjuster
if s . OOMScoreAdj != nil {
oomAdjuster = oom . NewOOMAdjuster ( )
if err := oomAdjuster . ApplyOOMScoreAdj ( 0 , int ( * s . OOMScoreAdj ) ) ; err != nil {
klog . V ( 2 ) . Info ( err )
}
}
if s . Broadcaster != nil && s . EventClient != nil {
s . Broadcaster . StartRecordingToSink ( & v1core . EventSinkImpl { Interface : s . EventClient . Events ( "" ) } )
}
2020-08-10 17:43:49 +00:00
// TODO(thockin): make it possible for healthz and metrics to be on the same port.
var errCh chan error
if s . BindAddressHardFail {
errCh = make ( chan error )
2019-01-12 04:58:27 +00:00
}
2020-08-10 17:43:49 +00:00
// Start up a healthz server if requested
serveHealthz ( s . HealthzServer , errCh )
2019-01-12 04:58:27 +00:00
// Start up a metrics server if requested
2020-08-10 17:43:49 +00:00
serveMetrics ( s . MetricsBindAddress , s . ProxyMode , s . EnableProfiling , errCh )
2019-01-12 04:58:27 +00:00
// Tune conntrack, if requested
// Conntracker is always nil for windows
if s . Conntracker != nil {
max , err := getConntrackMax ( s . ConntrackConfiguration )
if err != nil {
return err
}
if max > 0 {
err := s . Conntracker . SetMax ( max )
if err != nil {
2019-04-07 17:07:55 +00:00
if err != errReadOnlySysFS {
2019-01-12 04:58:27 +00:00
return err
}
2019-04-07 17:07:55 +00:00
// errReadOnlySysFS is caused by a known docker issue (https://github.com/docker/docker/issues/24000),
2019-01-12 04:58:27 +00:00
// the only remediation we know is to restart the docker daemon.
// Here we'll send an node event with specific reason and message, the
// administrator should decide whether and how to handle this issue,
2019-04-07 17:07:55 +00:00
// whether to drain the node and restart docker. Occurs in other container runtimes
// as well.
2019-01-12 04:58:27 +00:00
// TODO(random-liu): Remove this when the docker bug is fixed.
2019-04-07 17:07:55 +00:00
const message = "CRI error: /sys is read-only: " +
"cannot modify conntrack limits, problems may arise later (If running Docker, see docker issue #24000)"
2019-01-12 04:58:27 +00:00
s . Recorder . Eventf ( s . NodeRef , api . EventTypeWarning , err . Error ( ) , message )
}
}
if s . ConntrackConfiguration . TCPEstablishedTimeout != nil && s . ConntrackConfiguration . TCPEstablishedTimeout . Duration > 0 {
timeout := int ( s . ConntrackConfiguration . TCPEstablishedTimeout . Duration / time . Second )
if err := s . Conntracker . SetTCPEstablishedTimeout ( timeout ) ; err != nil {
return err
}
}
if s . ConntrackConfiguration . TCPCloseWaitTimeout != nil && s . ConntrackConfiguration . TCPCloseWaitTimeout . Duration > 0 {
timeout := int ( s . ConntrackConfiguration . TCPCloseWaitTimeout . Duration / time . Second )
if err := s . Conntracker . SetTCPCloseWaitTimeout ( timeout ) ; err != nil {
return err
}
}
}
2019-09-27 21:51:53 +00:00
noProxyName , err := labels . NewRequirement ( apis . LabelServiceProxyName , selection . DoesNotExist , nil )
if err != nil {
return err
}
noHeadlessEndpoints , err := labels . NewRequirement ( v1 . IsHeadlessService , selection . DoesNotExist , nil )
if err != nil {
return err
}
labelSelector := labels . NewSelector ( )
labelSelector = labelSelector . Add ( * noProxyName , * noHeadlessEndpoints )
2019-12-12 01:27:03 +00:00
// Make informers that filter out objects that want a non-default service proxy.
2019-04-07 17:07:55 +00:00
informerFactory := informers . NewSharedInformerFactoryWithOptions ( s . Client , s . ConfigSyncPeriod ,
2019-09-27 21:51:53 +00:00
informers . WithTweakListOptions ( func ( options * metav1 . ListOptions ) {
options . LabelSelector = labelSelector . String ( )
2019-04-07 17:07:55 +00:00
} ) )
2019-01-12 04:58:27 +00:00
2019-09-27 21:51:53 +00:00
// Create configs (i.e. Watches for Services and Endpoints or EndpointSlices)
2019-01-12 04:58:27 +00:00
// Note: RegisterHandler() calls need to happen before creation of Sources because sources
// only notify on changes, and the initial update (on process start) may be lost if no handlers
// are registered yet.
serviceConfig := config . NewServiceConfig ( informerFactory . Core ( ) . V1 ( ) . Services ( ) , s . ConfigSyncPeriod )
2019-08-30 18:33:25 +00:00
serviceConfig . RegisterEventHandler ( s . Proxier )
2019-01-12 04:58:27 +00:00
go serviceConfig . Run ( wait . NeverStop )
2020-03-26 21:07:15 +00:00
if s . UseEndpointSlices {
2019-12-12 01:27:03 +00:00
endpointSliceConfig := config . NewEndpointSliceConfig ( informerFactory . Discovery ( ) . V1beta1 ( ) . EndpointSlices ( ) , s . ConfigSyncPeriod )
2019-09-27 21:51:53 +00:00
endpointSliceConfig . RegisterEventHandler ( s . Proxier )
go endpointSliceConfig . Run ( wait . NeverStop )
} else {
endpointsConfig := config . NewEndpointsConfig ( informerFactory . Core ( ) . V1 ( ) . Endpoints ( ) , s . ConfigSyncPeriod )
endpointsConfig . RegisterEventHandler ( s . Proxier )
go endpointsConfig . Run ( wait . NeverStop )
}
2019-01-12 04:58:27 +00:00
// This has to start after the calls to NewServiceConfig and NewEndpointsConfig because those
// functions must configure their shared informer event handlers first.
2019-08-30 18:33:25 +00:00
informerFactory . Start ( wait . NeverStop )
2019-01-12 04:58:27 +00:00
2021-03-18 22:40:29 +00:00
if utilfeature . DefaultFeatureGate . Enabled ( features . ServiceTopology ) || utilfeature . DefaultFeatureGate . Enabled ( features . TopologyAwareHints ) {
2019-12-12 01:27:03 +00:00
// Make an informer that selects for our nodename.
currentNodeInformerFactory := informers . NewSharedInformerFactoryWithOptions ( s . Client , s . ConfigSyncPeriod ,
informers . WithTweakListOptions ( func ( options * metav1 . ListOptions ) {
options . FieldSelector = fields . OneTermEqualSelector ( "metadata.name" , s . NodeRef . Name ) . String ( )
} ) )
nodeConfig := config . NewNodeConfig ( currentNodeInformerFactory . Core ( ) . V1 ( ) . Nodes ( ) , s . ConfigSyncPeriod )
nodeConfig . RegisterEventHandler ( s . Proxier )
go nodeConfig . Run ( wait . NeverStop )
// This has to start after the calls to NewNodeConfig because that must
// configure the shared informer event handler first.
currentNodeInformerFactory . Start ( wait . NeverStop )
}
2019-01-12 04:58:27 +00:00
// Birth Cry after the birth is successful
s . birthCry ( )
2020-08-10 17:43:49 +00:00
go s . Proxier . SyncLoop ( )
return <- errCh
2019-01-12 04:58:27 +00:00
}
func ( s * ProxyServer ) birthCry ( ) {
s . Recorder . Eventf ( s . NodeRef , api . EventTypeNormal , "Starting" , "Starting kube-proxy." )
}
func getConntrackMax ( config kubeproxyconfig . KubeProxyConntrackConfiguration ) ( int , error ) {
if config . MaxPerCore != nil && * config . MaxPerCore > 0 {
floor := 0
if config . Min != nil {
floor = int ( * config . Min )
}
2021-03-18 22:40:29 +00:00
scaled := int ( * config . MaxPerCore ) * detectNumCPU ( )
2019-01-12 04:58:27 +00:00
if scaled > floor {
klog . V ( 3 ) . Infof ( "getConntrackMax: using scaled conntrack-max-per-core" )
return scaled , nil
}
klog . V ( 3 ) . Infof ( "getConntrackMax: using conntrack-min" )
return floor , nil
}
return 0 , nil
}
2019-06-12 21:00:25 +00:00
2020-12-01 01:06:26 +00:00
// CleanupAndExit remove iptables rules and ipset/ipvs rules in ipvs proxy mode
// and exit if success return nil
2019-06-12 21:00:25 +00:00
func ( s * ProxyServer ) CleanupAndExit ( ) error {
2020-12-01 01:06:26 +00:00
// cleanup IPv6 and IPv4 iptables rules
ipts := [ ] utiliptables . Interface {
utiliptables . New ( s . execer , utiliptables . ProtocolIPv4 ) ,
utiliptables . New ( s . execer , utiliptables . ProtocolIPv6 ) ,
}
var encounteredError bool
for _ , ipt := range ipts {
encounteredError = userspace . CleanupLeftovers ( ipt ) || encounteredError
encounteredError = iptables . CleanupLeftovers ( ipt ) || encounteredError
2021-03-18 22:40:29 +00:00
encounteredError = ipvs . CleanupLeftovers ( s . IpvsInterface , ipt , s . IpsetInterface ) || encounteredError
2020-12-01 01:06:26 +00:00
}
2019-06-12 21:00:25 +00:00
if encounteredError {
return errors . New ( "encountered an error while tearing down rules" )
}
return nil
}