This moves the `log.Info` call inside the check for nil-ness of the
`gw.WANIPv6FirewallControlClient`. A UPnP portmap device that
has no IPv6 support crashes without this check.
To compensate, this adds logging statements for the `PortForward`
case.
Signed-off-by: Ivan Vučica <ivan@vucica.net>
Signed-off-by: Ivan Vučica <ivucica@google.com>
Fixes: #1335
- ipv6 has duplicate address dectection which prevents perserving the VIP on the old node
Signed-off-by: Brian Davis <slimm609@gmail.com>
Signed-off-by: Brian Davis <dbrian@vmware.com>
When in arp mode, when preserveVipOnLeadershipLoss is true, when a node loses leadership (or can't keep it),
this will stop performing ARPs but will wait until a new leader is elected before deleting the arp.
when the flag is set to false (default) it continue to function like normal.
Signed-off-by: Brian Davis <slimm609@gmail.com>
Signed-off-by: Brian Davis <dbrian@vmware.com>
Implements Issue #1301 - Adds support for dumping current configuration
when SIGUSR1 signal is received by the kube-vip process.
Changes:
- Add SIGUSR1 signal handling in all manager start methods
- Implement configuration dump functionality in pkg/manager/manager_dump.go:
* dumpConfiguration() - Main dump coordinator
* 7 helper methods for different configuration sections
- Update signal handlers to use switch statement pattern
- Add comprehensive unit tests (5 tests, all passing)
- Add E2E test for signal handling
Implementation details:
- Thread-safe with mutex protection
- Non-disruptive: process continues after dump
- Output format: human-readable plaintext via stdout
- File organization follows project convention (manager_*.go pattern)
Co-authored-by: SteveYi <steveyiyo@steveyi.net>
Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Moved functions from mod pkg vip to pkg utils
* remove the dependency of the kubevip config pkg on the bgp pkg
* introduce BGPConfig and BGPPeer to kubevip package and migrate label from node labeler
* Use the new BGPConfig and BGPPeer type of the kubevip pkg
* Removed utils functions from vip pkg and refactor to call utils instead
* migrate from pkg vip to utils for generic IP funcs
* migrate common annotations and labels into a central place + support for hostnames
* if any change happens we want to reconcile it here
* fix e2e tests which relied also on the vip package instead of utils
* Better debug logging
* Added info if it is egress
* added missing condition to dhcp specific actions
* Disable the service before cancelling the context
* lets try auto,auto instead of /32 and nothing
* Let's reset svcCtx to be nil to ensure a new context when the previous was garbage collected
* Added a space for project wide constants
---------
Signed-off-by: Cellebyte <marcel.fest@live.de>
fix table addIP missing
add more debug lines
start service listener before content plane because content plane is blocking main thread
Signed-off-by: Thorsten Bonhagen <Thorsten.Bonhagen@tbon.de>
- Change os.WriteFile permissions from 0644 to 0600 in config_file_test.go
- Addresses GoSec G306 linter requirement for secure file permissions
- Ensures test files are readable/writable by owner only
Signed-off-by: Yuval Weisz <yuval.weisz@vastdata.com>
This commit adds extensive unit tests for the new configuration file features:
Test Coverage:
- LoadConfigFromFile() function with YAML and JSON parsing
- MergeConfigFromFile() function with priority logic testing
- Configuration merging with complex nested structures (BGP, leader election)
- Error handling for invalid files, unsupported formats, and missing files
- Environment variable integration for config_file
- Priority validation: CLI flags > env vars > config file
- Boolean flag merging behavior
- BGP configuration merging
- Leader election configuration merging
Files:
- pkg/kubevip/config_file_test.go: New comprehensive test suite (550+ lines)
- pkg/kubevip/config_generator_test.go: Enhanced with config_file env var tests
Test Structure:
- TestLoadConfigFromFile: Tests JSON/YAML parsing and error cases
- TestMergeConfigFromFile: Tests configuration priority and merging
- TestMergeConfigValues: Tests detailed field-by-field merging logic
- TestParseEnvironmentConfigFile: Tests config_file environment variable
The tests validate all aspects of the configuration file implementation
including edge cases, error scenarios, and complex configuration structures.
All tests follow Go testing best practices with table-driven test patterns.
Signed-off-by: Yuval Weisz <yuval.weisz@vastdata.com>
This commit implements configuration file support for kube-vip, allowing
users to specify configuration through JSON or YAML files.
Features:
- Added --config-file command line flag that accepts JSON/YAML file paths
- Added config_file environment variable support
- Implemented proper configuration priority: CLI flags > env vars > config file
- Added comprehensive configuration merging for all fields including BGP,
leader election, routing table, and load balancer settings
- Supports both JSON and YAML file formats with automatic detection
- Maintains full backward compatibility with existing deployments
Changes:
- pkg/kubevip/config_envvar.go: Added configFile constant
- pkg/kubevip/config_types.go: Added ConfigFile field to Config struct
- pkg/kubevip/config_environment.go: Added LoadConfigFromFile() and
MergeConfigFromFile() functions with comprehensive merging logic
- cmd/kube-vip.go: Integrated config file loading into service and manager commands
Usage:
kube-vip manager --config-file /etc/kube-vip/config.yaml
export config_file=/etc/kube-vip/config.json && kube-vip manager
This enhancement significantly improves configuration management for complex
kube-vip deployments while maintaining the existing behavior and patterns.
Signed-off-by: Yuval Weisz <yuval.weisz@vastdata.com>
This causes IPv6 source address selection according to RFC 3484 to
ignore VIPs when choosing which address the host should use for egress
communication.
Signed-off-by: Jan-Philipp Litza <janphilipp@litza.de>
fmt target is stale, just removing it, also simplify target is not working under 1.21 toolchain,
update it as well.
Signed-off-by: Lin Lin <linlin152@foxmail.com>
When using DHCP for LB services, the resulting IP was assigned to the
`serviceInterface` while most (not all) DHCP communication was done
through a per-service `macvlan` interface that is created on demand.
This has been the cause of DHCP lease renewal failures for some users,
as the response packets from the server never reached kube-vip. For more
details, see issue #871.
To remedy this, the primary interface used for a service is now being
set to the `macvlan`. Consequently, the IP address is also assigned to
it.
As a result, all traffic related to an LB service (DHCP or not) is now
originating from and sent to the per-service `macvlan` interface that
is tied to the user-configured `serviceInterface`.
Fixes#871
Signed-off-by: Til Blechschmidt <me@tibl.dev>
In order to run the kubelet in kind containers on the vms provided by
github, the kubelet needed some extra configuration.
Signed-off-by: Guillermo Gaston <gaslor@amazon.com>
This adds a new leader election mode that instead of using the kube api
server to obtain a lease, it relies solely on etcd.
By removing the dependency on the api server, we can use it even before
the api server is up, before cluster creation.
In an external etcd topology, this way we can make the api server point
to the VIPs instead of to the etcd machines IPs. This configuration
isolates the CP configuration from the etcd machine IPS. It's is super
convenient to avoid having to roll new control plane nodes when the etcd
nodes change.
Signed-off-by: Guillermo Gaston <gaslor@amazon.com>
* Fix 2 broken tests
* Fix e2e ARP tests: killing one node in a 2 CP node cluster makes etcd
lose quorum. So it can't elect a new leader if the node we happen to
kill was the etcd leader.
* Separate unit tests, e2e tests, linter and scans in different jobs so
they run concurrently.
Signed-off-by: Guillermo Gaston <gaslor@amazon.com>
This feature is related to #611, to configure the
`CiliumEgressGatewayPolicy` EgressIP correctly.
As this is not a very common usage for this project, there is a default
disabled configuration flag added, `enable_node_labeling`.
Signed-off-by: Mario Trangoni <mjtrangoni@gmail.com>
Using Equinix Metal annotations was limited to only the first BGP peer. This PR adds support for multiple BGP peer by looking at all bgp peer node annotations.
Signed-off-by: Enkel Prifti <enkelprifti@live.com>
The Equinix Metal API integration was missing support for BGP session passwords so this PR adds the BGP password parameter.
Signed-off-by: Enkel Prifti <enkelprifti@live.com>
Skip service security configuration for the service whose annotation kube-vip.io/ignore-service-security is true
Signed-off-by: Canwu Yao <yaocanwu@gmail.com>
When IPv6 addresses are present in node.Status.Addresses, the
IPVSLoadBalancer parses the addresses and attempts to add them as an
IPv4 destinations. This results in useless entries in the service table
that only hold the last 32 bits of address.
This change does two things:
- uses the appropriate address family when creating the service
- ignores backend addresses that use a different address family than the
service
This avoids the immediate problem of using the wrong address family for
IPv6 destinations and the useless entries in the service table.
Signed-off-by: Matthew Sykes <matthew.sykes@gmail.com>
Add new annotation kube-vip.io/loadbalancerIPs to specify the loadbalancer ip for service
Now ip address fetched from loadbalancer service are first fetched from annotation, then fallback to service spec
Unit test
Signed-off-by: lubronzhan <lzhan@vmware.com>
Remove the address from in the interface case that a duplicate address is detected.
Without this change, kube-vip will take no corrective action, and the
address will remain in dadfailed state. Removing and re adding the
address causes duplicate address detection to happen again, hopefully
successfully the next time.
We suspect dadfailed happens when a new leader is elected and the old
leader is too slow or fails to delete the IP address.
Co-authored-by: Aidan Obley <aobley@vmware.com>
Co-authored-by: Christian Ang <angc@vmware.com>
Signed-off-by: Tyler Schultz <tschultz@vmware.com>
Ability to specify BGP port is removed, for two reasons:
1. IPv6 peer addresses contain colons, and the SpiltN func breaks IPv6 addresses.
2. Typical network devices do not allow changing BGP port, so this is rarely used.
Signed-off-by: zyxwvu Shi <i@shiyc.cn>
MP-BGP protocol requires IPv6 prefixes to be advertised in a path attribute called MP_REACH_NLRI. Without the attribute non-IPv4 prefixes can not be advertised. This prevents IPv6 loadBalancerIps and control plane addresses from being advertised to the network.
Signed-off-by: zyxwvu Shi <i@shiyc.cn>
According to UpdatePathAttrs in gobgp internal/pkg/table/path.go, gobgp will fill NEXT_HOP with BGP session local address if given NEXT_HOP is an unspecified address, such as 0.0.0.0 and ::. It is better to use that address as NEXT_HOP, since the BGP TCP connection ensures the address reaches the node.
See: https://github.com/osrg/gobgp/blob/v3.10.0/internal/pkg/table/path.go#L223
Signed-off-by: zyxwvu Shi <i@shiyc.cn>
Without this specific ClusterRole rule, kube-vip would not be able to listen for `endpoints` changes when deployed as a DaemonSet, thus causing the IP addresses not being announced and the following error message every second:
```
E1227 20:37:20.290479 1 retrywatcher.go:130] "Watch failed" err="unknown (get endpoints)"
```
The macvlan interface could not be deleted until the DHCP client has
released the IP.
If the network manager in the operating system uses DHCP to obtain an IP
for the network card, it will occupy the DHCP client port. In this case
the release function will fail because the unicast connection with UDP
socket need the same port. Update dhcp package and use a raw socket.
Signed-off-by: yaocw2020 <yaocanwu@gmail.com>
Previous commits addressed the issue of interface being incorrectly marked as
down for loopback (1a4465e) and point-to-point (051bb1f) interfaces by directly
passing the test when the interface is identified a any of those types. Both
commits failed to address the core issue, those interfaces (and many others) do
not publish their operational state resulting in the state UNKOWN. With this
commit, now interfaces with unknown status are accepted but the user is warned as
he needs to manually ensure that the interface is ready to handle traffic.
Fix#385
Signed-off-by: Claudio Yanes <me@claudio4.com>
- Runtime statistics (load balancer, Prometheus, health check settings)
- Output format: Human-readable plaintext via fmt.Printf()
- Thread-safe implementation using mutex protection
- Non-disruptive: Process continues running after configuration dump
- Added comprehensive unit tests for all dump methods
- Added E2E tests for signal handling
### Changed
- Updated signal handlers in manager_arp.go, manager_bgp.go, manager_wireguard.go, and manager_table.go to use switch statement pattern for handling multiple signals (SIGUSR1, SIGINT, SIGTERM)
## [v1.0.1] - Previous Release
### Previous changes
- See git history for changes prior to CHANGELOG.md introduction
E2E_IMAGE_PATH=$(REPOSITORY)/$(TARGET):$(DOCKERTAG) go run github.com/onsi/ginkgo/ginkgo -tags=e2e -v -p testing/e2e
GOMAXPROCS=4 E2E_IMAGE_PATH=$(REPOSITORY)/$(TARGET):$(DOCKERTAG) go run github.com/onsi/ginkgo/v2/ginkgo --tags=e2e -v -p ./testing/e2e ./testing/e2e/etcd
e2e-tests129-arp:
GOMAXPROCS=4TEST_MODE=arp V129=trueK8S_IMAGE_PATH=kindest/node:v1.29.0 E2E_IMAGE_PATH=$(REPOSITORY)/$(TARGET):$(DOCKERTAG) go run github.com/onsi/ginkgo/v2/ginkgo --tags=e2e -v -p ./testing/e2e
e2e-tests129-rt:
GOMAXPROCS=4TEST_MODE=rt V129=trueK8S_IMAGE_PATH=kindest/node:v1.29.0 E2E_IMAGE_PATH=$(REPOSITORY)/$(TARGET):$(DOCKERTAG) go run github.com/onsi/ginkgo/v2/ginkgo --tags=e2e -v -p ./testing/e2e
e2e-tests129-bgp:
GOMAXPROCS=4TEST_MODE=bgp V129=trueK8S_IMAGE_PATH=kindest/node:v1.29.0 E2E_IMAGE_PATH=$(REPOSITORY)/$(TARGET):$(DOCKERTAG) go run github.com/onsi/ginkgo/v2/ginkgo --tags=e2e -v -p ./testing/e2e
[](https://github.com/kube-vip/kube-vip/actions/workflows/main.yaml) [](https://insights.linuxfoundation.org/project/kube-vip) [&message=212&color=0094FF&logo=linuxfoundation&logoColor=white&style=flat)](https://insights.linuxfoundation.org/project/kube-vip)
## Overview
Kubernetes Virtual IP and Load-Balancer for both control plane and Kubernetes services
@@ -30,7 +32,8 @@ Kube-Vip was originally created to provide a HA solution for the Kubernetes cont
- Service LoadBalancer address pools per namespace or global
- Service LoadBalancer address via (existing network DHCP)
- Service LoadBalancer address exposure to gateway via UPNP
-... manifest generation, vendor API integrations and many nore...
-Egress! Kube-vip will utilise a service loadbalancer as both the ingress and **egress** for a pod.
- ... manifest generation, vendor API integrations and many more...
## Why?
@@ -56,3 +59,13 @@ All of these would require a separate level of configuration and in some infrast
## Troubleshooting and Feedback
Please raise issues on the GitHub repository and as mentioned check the documentation at [https://kube-vip.io](https://kube-vip.io/).
## Contributing
Thanks for taking the time to join our community and start contributing! We welcome pull requests. Feel free to dig through the [issues](https://github.com/kube-vip/kube-vip/issues) and jump in.
:warning: This project has issue compiling on MacOS, please compile it on linux distribution
## Star History
[](https://star-history.com/#kube-vip/kube-vip&Date)
// Disable the Virtual IP (bind to the existing network stack)
vardisableVIPbool
// Disable the Virtual IP (bind to the existing network stack)
// var controlPlane bool
// Run as a load balancer service (within a pod / kubernetes)
// var serviceArp bool
// Is kube-vip running within cluster
varinClusterbool
// ConfigMap name within a Kubernetes cluster
varconfigMapstring
// Configure the level of loggin
varlogLeveluint32
// Provider Config
varproviderConfigstring
// Points to a kubernetes configuration file
varkubeConfigPathstring
// Release - this struct contains the release information populated when building kube-vip
varReleasestruct{
@@ -49,11 +43,10 @@ var Release struct {
}
// Structs used via the various subcommands
varinitConfigkubevip.Config
varinitLoadBalancerkubevip.LoadBalancer
// Points to a kubernetes configuration file
varkubeConfigPathstring
var(
initConfigkubevip.Config
initLoadBalancerkubevip.LoadBalancer
)
varkubeVipCmd=&cobra.Command{
Use:"kube-vip",
@@ -61,42 +54,44 @@ var kubeVipCmd = &cobra.Command{
}
funcinit(){
// Basic flags
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Interface,"interface","","Name of the interface to bind to")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.ServicesInterface,"serviceInterface","","Name of the interface to bind to (for services)")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.VIP,"vip","","The Virtual IP address")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.VIPSubnet,"vipSubnet","","The Virtual IP address subnet e.g. /32 /24 /8 etc.. (Default to 32 for IPv4 and 128 for IPv6)")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.NodeName,"nodeName","","Name to be used for lease holder. Must be unique for each node/instance")
// VIP flags
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Address,"address","","an address (IP or DNS name) to use as a VIP")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.Port,"port",6443,"Port for the VIP")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableARP,"arp",false,"Enable Arp for Vip changes")
kubeVipCmd.PersistentFlags().Uint16Var(&initConfig.Port,"port",6443,"Port for the VIP")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableARP,"arp",false,"Enable Arp for VIP changes")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableWireguard,"wireguard",false,"Enable Wireguard for services VIPs")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableRoutingTable,"table",false,"Enable Routing Table for services VIPs")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.PreserveVIPOnLeadershipLoss,"preserveVipOnLeadershipLoss",false,"Preserve ARP VIP addresses on interface when leadership is lost (default: false for backward compatibility)")
// LoadBalancer flags
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableLoadBalancer,"enableLoadBalancer",false,"enable loadbalancing on the VIP with IPVS")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.LoadBalancerPort,"lbPort",6443,"loadbalancer port for the VIP")
kubeVipCmd.PersistentFlags().Uint16Var(&initConfig.LoadBalancerPort,"lbPort",6443,"loadbalancer port for the VIP")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.DDNS,"ddns",false,"use Dynamic DNS + DHCP to allocate VIP for address")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.MirrorDestInterface,"mirrorDestInterface","","network interface where all traffic that traverses the service interface will be mirrored to. Source interface will use default interface is servicesInterface is not set.")
// Clustering type (leaderElection)
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableLeaderElection,"leaderElection",false,"Use the Kubernetes leader election mechanism for clustering")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.LeaseDuration,"leaseDuration",5,"Length of time a Kubernetes leader lease can be held for")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RenewDeadline,"leaseRenewDuration",3,"Length of time a Kubernetes leader can attempt to renew its lease")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RetryPeriod,"leaseRetry",1,"Number of times the host will retry to hold a lease")
// Packet flags
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableMetal,"metal",false,"This will use the Equinix Metal API (requires the token ENV) to update the EIP <-> VIP")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.MetalAPIKey,"metalKey","","The API token for authenticating with the Equinix Metal API")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.MetalProject,"metalProject","","The name of project already created within Equinix Metal")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.MetalProjectID,"metalProjectID","","The ID of project already created within Equinix Metal")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.ProviderConfig,"provider-config","","The path to a provider configuration")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.LeaderElectionType,"leaderElectionType","kubernetes","Defines the backend to run the leader election: kubernetes or etcd. Defaults to kubernetes.")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.LeaseName,"leaseName","plndr-cp-lock","Name of the lease that is used for leader election")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.LeaseDuration,"leaseDuration",5,"Length of time (in seconds) a Kubernetes leader lease can be held for")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RenewDeadline,"leaseRenewDuration",3,"Length of time (in seconds) a Kubernetes leader can attempt to renew its lease")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RetryPeriod,"leaseRetry",1,"Length of time (in seconds) the LeaderElector clients should wait between tries of actions")
// BGP flags
kubeVipCmd.PersistentFlags().StringVar(&initConfig.VIPCIDR,"cidr","32","The CIDR range for the virtual IP address")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableBGP,"bgp",false,"This will enable BGP support within kube-vip")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPConfig.RouterID,"bgpRouterID","","The routerID for the bgp server")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPConfig.SourceIF,"sourceIF","","The source interface for bgp peering (not to be used with sourceIP)")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPConfig.SourceIP,"sourceIP","","The source address for bgp peering (not to be used with sourceIF)")
kubeVipCmd.PersistentFlags().Uint32Var(&initConfig.BGPConfig.AS,"localAS",65000,"The local AS number for the bgp server")
kubeVipCmd.PersistentFlags().Uint64Var(&initConfig.BGPConfig.HoldTime,"bgpHoldTimer",30,"The hold timer for all bgp peers (it defines the time a session is held)")
kubeVipCmd.PersistentFlags().Uint64Var(&initConfig.BGPConfig.KeepaliveInterval,"bgpKeepAliveInterval",10,"The keepalive interval for all bgp peers (it defines the heartbeat of keepalive messages)")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPPeerConfig.Address,"peerAddress","","The address of a BGP peer")
kubeVipCmd.PersistentFlags().Uint32Var(&initConfig.BGPPeerConfig.AS,"peerAS",65000,"The AS number for a BGP peer")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPPeerConfig.Password,"peerPass","","The md5 password for a BGP peer")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Annotations,"annotations","","Set Node annotations prefix for parsing")
// Control plane specific flags
kubeVipCmd.PersistentFlags().StringVarP(&initConfig.Namespace,"namespace","n","kube-system","The configuration map defined within the cluster")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.BGPConfig.Zebra.Enabled,"zebra",false,"This will enable Zebra support within kube-vip")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPConfig.Zebra.URL,"zebraUrl","unix:/var/run/frr/zserv.api","Path to the unix domain socket for connecting to Zebra daemon")
kubeVipCmd.PersistentFlags().Uint32Var(&initConfig.BGPConfig.Zebra.Version,"zebraVersion",6,"Zebra API Version")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.BGPConfig.Zebra.SoftwareName,"zebraSoftwareName","frr8.3","Software Name for Zebra")
// Namespace for kube-vip
kubeVipCmd.PersistentFlags().StringVarP(&initConfig.Namespace,"namespace","n","kube-system","The namespace for the configmap defined within the cluster")
// Manage logging
kubeVipCmd.PersistentFlags().Uint32Var(&logLevel,"log",4,"Set the level of logging")
kubeVipCmd.PersistentFlags().Int32Var(&initConfig.Logging,"log",0,"Set the level of logging")
// Service flags
kubeVipService.Flags().StringVarP(&configMap,"configMap","c","plndr","The configuration map defined within the cluster")
// Routing Table flags
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RoutingTableID,"tableID",198,"The routing table used for all table entries")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RoutingTableType,"tableType",unix.RTN_UNICAST,"The type of route that will be added to the routing table")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.RoutingProtocol,"routingProtocol",248,"The routing protocol value used to create routes")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.CleanRoutingTable,"cleanRoutingTable",false,"Clean routing table of redundant routes on start")
// Behaviour flags
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableControlPane,"controlplane",false,"Enable HA for control plane, hybrid mode")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableControlPlane,"controlplane",false,"Enable HA for control plane")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.DetectControlPlane,"autodetectcp",false,"Determine working address for control plane (from loopback)")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableServicesElection,"servicesElection",false,"Enable leader election per kubernetes service")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.LoadBalancerClassOnly,"lbClassOnly",false,fmt.Sprintf("Enable load balancing only for services with LoadBalancerClass %q",kubevip.LBClassName))
kubeVipCmd.PersistentFlags().StringVar(&initConfig.LoadBalancerClassName,"lbClassName",kubevip.LBClassName,fmt.Sprintf("Name of load balancer class for kube-VIP, defaults to %q",kubevip.LBClassName))
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.LoadBalancerClassLegacyHandling,"lbClassNameLegacyHandling",true,"Use legacy LoadBalancer class name handling (e.g. accepting services both with empty and non-empty class)")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableServiceSecurity,"onlyAllowTrafficServicePorts",false,"Only allow traffic to service ports, others will be dropped, defaults to false")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableNodeLabeling,"enableNodeLabeling",false,fmt.Sprintf("Enable leader node labeling with %q, defaults to false",kubevip.HasIP))
kubeVipCmd.PersistentFlags().StringVar(&initConfig.ServicesLeaseName,"servicesLeaseName","plndr-svcs-lock","Name of the lease that is used for leader election for services (in arp mode)")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.DNSMode,"dnsMode","first","Name of the mode that DNS lookup will be performed (first, ipv4, ipv6, dual)")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.DisableServiceUpdates,"disableServiceUpdates",false,"If true, kube-vip will process services as usual, but will not update service's Status.LoadBalancer.Ingress slice")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.EnableEndpoints,"enableEndpoints",false,"If enabled, kube-vip will only advertise services, but will use the (deprecated since v1.33) endpoints for IP addresses")
kubeVipCmd.PersistentFlags().BoolVar(&initConfig.LoInterfaceGlobalScope,"loInterfaceGlobalScope",false,"If true, kube-vip will set global scope when using the lo interface, otherwise a host scope will be used by default")
kubeVipCmd.PersistentFlags().IntVar(&initConfig.HealthCheckPort,"healthCheckPort",0,"If set to non-zero (> 1024), then this is the port that the healthcheck will listen on")
// Prometheus HTTP Server
kubeVipCmd.PersistentFlags().StringVar(&initConfig.PrometheusHTTPServer,"promethuesHTTPServer",":2112","Host and port used to expose Prometheus metrics via an HTTP server")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.PrometheusHTTPServer,"prometheusHTTPServer",":2112","Host and port used to expose Prometheus metrics via an HTTP server")
// Etcd
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Etcd.CAFile,"etcdCACert","","Verify certificates of TLS-enabled secure servers using this CA bundle file")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Etcd.ClientCertFile,"etcdCert","","Identify secure client using this TLS certificate file")
kubeVipCmd.PersistentFlags().StringVar(&initConfig.Etcd.ClientKeyFile,"etcdKey","","Identify secure client using this TLS key file")
kubeVipCmd.PersistentFlags().StringSliceVar(&initConfig.Etcd.Endpoints,"etcdEndpoints",nil,"Etcd member endpoints")
// Kubernetes client specific flags
kubeVipCmd.PersistentFlags().StringVar(&initConfig.K8sConfigFile,"k8sConfigPath","/etc/kubernetes/admin.conf","Path to the configuration file used with the Kubernetes client")
// Configuration file flag
kubeVipCmd.PersistentFlags().StringVar(&initConfig.ConfigFile,"config-file","","Path to a JSON/YAML configuration file to load settings from")
kubeVipCmd.AddCommand(kubeKubeadm)
kubeVipCmd.AddCommand(kubeManifest)
kubeVipCmd.AddCommand(kubeVipManager)
kubeVipCmd.AddCommand(kubeVipSample)
kubeVipCmd.AddCommand(kubeVipService)
kubeVipCmd.AddCommand(kubeVipStart)
kubeVipCmd.AddCommand(kubeVipVersion)
// Set the logging level for all subsequent functions
This contains some example code to determine how long "failovers" are taking within kube-vip, the server component should live within the cluster and the client should be externally.
## Deploy the server
Simply apply the manifest to a working cluster that has kube-vip deployed:
```
kubectl apply -f ./demo/server/deploy.yaml
```
Retrieve the loadBalancer IP that is fronting the service:
From elsewhere, clone the kube-vip repository and connect the client to the server endpoint (loadBalancer IP) with the following command:
```
go run ./demo/client/main.go -address=<vip>
```
You will only see output when the client has reconcilled the connection to a pod beneath the service, where it will print the timestamp to reconnection along with the time in milliseconds it took:
```
15:58:35.916952 3008
15:58:45.947506 2005
15:58:57.983151 3007
15:59:08.013450 2005
15:59:20.046491 3008
15:59:30.076341 2507
15:59:42.110747 3008
```
## Kill some pods to test
On a machine or control plane that has `kubectl` and has the credentials to speak to the cluster we will run a command to find the demo pod and kill it every 10 seconds:
`while true ; do kubectl delete pod $(kubectl get pods | grep -v NAME | grep vip| awk '{ print $1 }'); sleep 10; done`
This section covers two parts of the architecture:
1. The technical capabilities of `kube-vip`.
2. The components to build a load balancing service within Kubernetes.
The `kube-vip` project is designed to provide both a highly available networking endpoint and load balancing functionality for underlying networking services. The project was originally designed for the purpose of providing a resilient control plane for Kubernetes but has since expanded to provide the same functionality for Service resources within a Kubernetes cluster.
Additionally, `kube-vip` is designed to be lightweight and multi-architecture. All of the components are built for Linux on `x86`, `armv7`, `armhvf`, and `ppc64le` architectures. This means that `kube-vip` will run fine in bare metal, virtual, and edge (Raspberry Pi or small ARM SoC) use cases.
## Technologies
There are a number of technologies or functional design choices that provide high availability and networking functions as part of a VIP/load balancing solution.
### Cluster
The `kube-vip` service builds a multi-node or multi-pod cluster to provide high availability. In ARP mode, a leader is elected which will inherit the virtual IP and become the leader of the load balancing within the cluster whereas with BGP all nodes will advertise the VIP address.
When using ARP or [Layer 2](https://osi-model.com/data-link-layer/) it will use [leader election](https://godoc.org/k8s.io/client-go/tools/leaderelection).
### Virtual IP
The leader within the cluster will assume the VIP and will have it bound to the selected interface that is declared within the configuration. When the leader changes, it will evacuate the VIP first or in failure scenarios the VIP will be directly assumed by the next elected leader.
When the VIP moves from one host to another, any host that has been using the VIP will retain the previous VIP-to-MAC address mapping until the old ARP entry expires (typically within 30 seconds) and retrieves a new mapping. This can be improved by using [Gratuitous ARP](https://wiki.wireshark.org/Gratuitous_ARP) broadcasts when enabled (detailed below).
### ARP
`kube-vip` can optionally be configured to broadcast a Gratuitous ARP that will typically immediately notify all local hosts that the VIP-to-MAC address mapping has changed.
Below we can see that the failover is typically done within a few seconds as the ARP broadcast is received.
```
64 bytes from 192.168.0.75: icmp_seq=146 ttl=64 time=0.258 ms
64 bytes from 192.168.0.75: icmp_seq=147 ttl=64 time=0.240 ms
92 bytes from 192.168.0.70: Redirect Host(New addr: 192.168.0.75)
64 bytes from 192.168.0.75: icmp_seq=151 ttl=64 time=0.245 ms
```
### Load Balancing
`kube-vip` has the capability to provide a high availability address for both the Kubernetes control plane and for a Kubernetes Service. As of v0.4.0, `kube-vip` implements support for true load balancing for the control plane to distribute API requests across control plane nodes.
#### Kubernetes Service Load Balancing
The following is required in the `kube-vip` manifest to enable Service of type `LoadBalancer`:
```yaml
- name:svc_enable
value:"true"
```
This section details the flow of events in order for `kube-vip` to advertise a Kubernetes Service:
1. An end user exposes an application through Kubernetes as a Service type `LoadBalancer`. For example, imperatively using `kubectl expose deployment nginx-deployment --port=80 --type=LoadBalancer --name=nginx`
2. Within the Kubernetes cluster, a Service object is created with the `spec.type` set to `LoadBalancer`.
3. A controller (typically a [Cloud Controller](/usage/on-prem)) has a loop that "watches" for Services of the type `LoadBalancer`.
4. The controller now has the responsibility of providing an IP address for this Service along with doing anything that is network specific for the environment where the cluster is running.
5. Once the controller has an IP address, it will update the Service field `spec.loadBalancerIP` with the IP address.
6.`kube-vip` Pods implement a "watcher" for Services that have a `svc.Spec.loadBalancerIP` address attached.
7. When a new Service appears, `kube-vip` will start advertising this address to the wider network (through BGP/ARP) which will allow traffic to come into the cluster and hit the Service network.
8. Finally, `kube-vip` will update the Service status so that the API reflects the object is ready. This is done by updating the `status.loadBalancer.ingress` with the VIP address.
#### Control Plane Load-Balancing
As of `kube-vip` v0.4.0, IPVS load balancing is configured for having the VIP in the same subnet as the control plane nodes. NAT-based load balancing will follow later.
To enable control plane load balancing using IPVS, the environment variable `lb_enable` is required in the `kube-vip` manifest:
```yaml
- name :lb_enable
value:"true"
```
The load balancing is provided through IPVS (IP Virtual Server) and provides a Layer 4 (TCP-based) round-robin across all of the control plane nodes. By default, the load balancer will listen on the default port of 6443 as the Kubernetes API server. The IPVS virtual server lives in kernel space and doesn't create an "actual" service that listens on port 6443. This allows the kernel to parse packets before they're sent to an actual TCP port. This is important to know because it means we don't have any port conflicts having the IPVS load balancer listening on the same port as the API server on the same host.
The load balancer port can be customised by changing the `lb_port` environment variable in the `kube-vip` manifest:
```yaml
- name:lb_port
value:"6443"
```
##### How it works
Once the `lb_enable` variable is set to `true`, `kube-vip` will do the following:
- In Layer 2 it will create an IPVS service on the leader.
- In Layer 3 all nodes will create an IPVS service.
- It will start a Kubernetes node watcher for nodes with the control plane label.
- It will add/delete them as they're added and removed from the cluster.
#### Debugging control plane load balancing
In order to inspect and debug traffic, install the `ipvsadm` tool.
##### View the configuration
The command `sudo ipvsadm -ln` will display the load balancer configuration.
# Load Balancing a Kubernetes Cluster (Control-Plane) (pre 0.1.5)
This document covers all of the details for using `kube-vip` to build a HA Kubernetes cluster
`tl;dr version`
- Generate/modify first node `kube-vip` config/manifest
-`init` first node
-`join` remaining nodes
- Add remaining config/manifests
## Infrastructure architecture
The infrastructure for our example HA Kubernetes cluster is as follows:
| Node | Address |
|----------------|------------|
| VIP | 10.0.0.75 |
| controlPlane01 | 10.0.0.70 |
| controlPlane02 | 10.0.0.71 |
| controlPlane03 | 10.0.0.72 |
All nodes are running Ubuntu 18.04, Docker CE and will use Kubernetes 1.17.0.
### Generate the `kube-vip` configuration
Make sure that the config directory exists: `sudo mkdir -p /etc/kube-vip/`, this directory can be any directory however the `hostPath` in the manifest will need modifying to point to the correct path.
```
sudo docker run -it --rm plndr/kube-vip:0.1 /kube-vip sample config | sudo tee /etc/kube-vip/config.yaml
```
### Modify the configuration
**Cluster Configuration**
Modify the `remotePeers` to point to the correct addresses of the other two nodes, ensure that their `id` is unique otherwise this will confuse the raft algorithm. The `localPeer` should be the configuration of the current node (`controlPlane01`), which is where this instance of the cluster will run.
As this node will be the first node, it will need to elect itself leader as until this occurs the VIP won’t be activated!
`startAsLeader: true`
**VIP Config**
We will need to set our VIP address to `192.168.0.75` and to ensure all hosts are updated when the VIP moves we will enable ARP broadcasts `gratuitousARP: true`
**Load Balancer**
We will configure the load balancer to sit on the standard API-Server port `6443` and we will configure the backends to point to the API-servers that will be configured to run on port `6444`. Also for the Kubernetes Control Plane we will configure the load balancer to be of `type: tcp`.
We can also use `6443` for both the VIP and the API-Servers, in order to do this we need to specify that the api-server is bound to it's local IP. To do this we use the `--apiserver-advertise-address` flag as part of the `init`, this means that we can then bind the same port to the VIP and we wont have a port conflict.
To generate the basic Kubernetes static pod `yaml` configuration:
Make sure that the manifest directory exists: `sudo mkdir -p /etc/kubernetes/manifests/`
```
sudo docker run -it --rm plndr/kube-vip:0.1 /kube-vip sample manifest | sudo tee /etc/kubernetes/manifests/kube-vip.yaml
```
Ensure that `image: plndr/kube-vip:<x>` is modified to point to a specific version (`0.1` at the time of writing), refer to [docker hub](https://hub.docker.com/r/plndr/kube-vip/tags) for details. Also ensure that the `hostPath` points to the correct `kube-vip` configuration, if it isn’t the above path.
The **vip** is set to `192.168.0.75` and this first node will elect itself as leader, and as part of the `kubeadm init` it will use the VIP in order to speak back to the initialising api-server.
We first will need to create the `kube-vip` configuration that resides in `/etc/kube-vip/config.yaml` or we can regenerate it from scratch using the above example. Ensure that the configuration is almost identical with the `localPeer` and `remotePeers` sections are updated for each node. Finally, ensure that the remaining nodes will behave as standard cluster nodes by setting `startAsLeader: false`.
At this point **DON’T** generate the manifests, this is due to some bizarre `kubeadm/kubelet` behaviour.
```
kubeadm join 192.168.0.75:6443 --token <tkn> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>
```
**After** this node has been added to the cluster, we can add the manifest to also add this node as a `kube-vip` member. (Adding the manifest afterwards doesn’t interfere with `kubeadm`).
```
sudo docker run -it --rm plndr/kube-vip:0.1 /kube-vip sample manifest | sudo tee /etc/kubernetes/manifests/kube-vip.yaml
```
Once this node is added we will be able to see that the `kube-vip` pod is up and running as expected:
```
user@controlPlane01:~$ kubectl get pods -A | grep vip
# Load Balancing a Kubernetes Cluster (Control-Plane)
This document covers the newer (post `0.1.5`) method for using `kube-vip` to provide HA for a Kubernetes Cluster. The documentation for older releases can be found [here](./0.1.4/)
This document covers all of the details for using `kube-vip` to build a HA Kubernetes cluster
`tl;dr version`
- Generate/modify first node `kube-vip` config/manifest
-`init` first node
-`join` remaining nodes
- Add remaining config/manifests
Below are examples of the steps required:
```
# First Node
sudo docker run --network host --rm plndr/kube-vip:0.1.5 kubeadm init --interface ens192 --vip 192.168.0.81 --startAsLeader=true | sudo tee /etc/kubernetes/manifests/vip.yaml
sudo docker run -v /etc/kubernetes/admin.conf:/etc/kubernetes/admin.conf --network host --rm plndr/kube-vip:0.1.5 kubeadm join --interface ens192 --vip 192.168.0.81 --startAsLeader=false | sudo tee /etc/kubernetes/manifests/vip.yaml
```
## Infrastructure architecture
The infrastructure for our example HA Kubernetes cluster is as follows:
| Node | Address |
|----------------|------------|
| VIP | 10.0.0.75 |
| controlPlane01 | 10.0.0.70 |
| controlPlane02 | 10.0.0.71 |
| controlPlane03 | 10.0.0.72 |
All nodes are running Ubuntu 18.04, Docker CE and will use Kubernetes 1.17.0.
### Generate the `kube-vip` configuration
Kube-Vip no longer requires storing it's configuration in a seperate directory and will now store its configuration in the actual manifest that defines the static pods.
```
sudo docker run --network host \
--rm plndr/kube-vip:0.1.5 \
kubeadm init \
--interface ens192 \
--vip 192.168.0.75 \
--startAsLeader=true | sudo tee /etc/kubernetes/manifests/vip.yaml
```
The above command will "initialise" the manifest within the `/etc/kubernetes/manifests` directory, that will be started when we actually initialise our Kubernetes cluster with `kubeadm init`
### Modify the configuration
**Cluster Configuration**
As this node will be the first node, it will need to elect itself leader as until this occurs the VIP won’t be activated!
`--startAsLeader=true`
**VIP Config**
We will need to set our VIP address to `192.168.0.75` with `--vip 192.168.0.75` and to ensure all hosts are updated when the VIP moves we will enable ARP broadcasts `--arp` (defaults to `true`)
**Load Balancer**
We will configure the load balancer to sit on the standard API-Server port `6443` and we will configure the backends to point to the API-servers that will be configured to run on port `6444`. Also for the Kubernetes Control Plane we will configure the load balancer to be of `type: tcp`.
We can also use `6443` for both the VIP and the API-Servers, in order to do this we need to specify that the api-server is bound to it's local IP. To do this we use the `--apiserver-advertise-address` flag as part of the `init`, this means that we can then bind the same port to the VIP and we wont have a port conflict.
**vip.yaml** Static-pod Manifest
`$ sudo cat /etc/kubernetes/manifests/vip.yaml`
```
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-vip
namespace: kube-system
spec:
containers:
- args:
- start
env:
- name: vip_arp
value: "true"
- name: vip_interface
value: ens192
- name: vip_address
value: 192.168.0.81
- name: vip_startleader
value: "true"
- name: vip_addpeerstolb
value: "true"
- name: vip_localpeer
value: controlPlane01:192.168.0.70:10000
- name: lb_backendport
value: "6443"
- name: lb_name
value: Kubeadm Load Balancer
- name: lb_type
value: tcp
- name: lb_bindtovip
value: "true"
image: plndr/kube-vip:0.1.5
imagePullPolicy: Always
name: kube-vip
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
- SYS_TIME
hostNetwork: true
status: {}
```
### First Node
To generate the basic Kubernetes static pod `yaml` configuration:
Make sure that the manifest directory exists: `sudo mkdir -p /etc/kubernetes/manifests/`
```
sudo docker run --network host \
--rm plndr/kube-vip:0.1.5 \
kubeadm init \
--interface ens192 \
--vip 192.168.0.75 \
--startAsLeader=true | sudo tee /etc/kubernetes/manifests/vip.yaml
```
Ensure that `image: plndr/kube-vip:<x>` is modified to point to a specific version (`0.1.5` at the time of writing), refer to [docker hub](https://hub.docker.com/r/plndr/kube-vip/tags) for details.
The **vip** is set to `192.168.0.75` and this first node will elect itself as leader, and as part of the `kubeadm init` it will use the VIP in order to speak back to the initialising api-server.
At this point **DON’T** generate the manifests, this is due to some bizarre `kubeadm/kubelet` behaviour.
```
kubeadm join 192.168.0.75:6443 --token <tkn> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>
```
**After** this node has been added to the cluster, we can add the manifest to also add this node as a `kube-vip` member. (Adding the manifest afterwards doesn’t interfere with `kubeadm`).
# Load Balancing a Kubernetes Cluster (Control-Plane)
**Note**: The most common deployment currently for HA Kubernetes clusters w/`kube-vip` involved `kubeadm`, however recently we've worked to bring a method of bringing `kube-vip` to other types of Kubernetes cluster. Typically this deployment method makes use of a daemonset that is usually brought up during the cluster instantiation.. So for those wanting to deploy [k3s](https://k3s.io), we now have installation steps available [here](https://kube-vip.io/control-plane/#k3s),
This document covers the newer (post `0.1.6`) method for using `kube-vip` to provide HA for a Kubernetes Cluster. The documentation for older releases can be found [here](./0.1.5/)
From version `0.1.6` we've moved `kube-vip` from raft to leaderElection within the Kubernetes cluster. After a lot of testing it became clear that the leaderElection gave quicker reconciliation when removing nodes etc.. during upgrades and failures.
For **more** configuration around LeaderElection click [here](https://kube-vip.io/control-plane/#leaderelection-configuration).
This document covers all of the details for using `kube-vip` to build a HA Kubernetes cluster
`tl;dr version`
- Generate/modify first node `kube-vip` config/manifest
-`init` first node
-`join` remaining nodes
- Add remaining config/manifests
Below are examples of the steps required:
```
# First Node
sudo docker run --network host --rm ghcr.io/kube-vip/kube-vip:0.3.7 manifest pod \
--interface ens192 \
--vip 192.168.0.75 \
--arp \
--leaderElection | sudo tee /etc/kubernetes/manifests/vip.yaml
sudo docker run --network host --rm ghcr.io/kube-vip/kube-vip:0.3.7 manifest pod \
--interface ens192 \
--vip 192.168.0.75 \
--arp \
--leaderElection | sudo tee /etc/kubernetes/manifests/vip.yaml
```
## Infrastructure architecture
The infrastructure for our example HA Kubernetes cluster is as follows:
| Node | Address |
|----------------|------------|
| VIP | 10.0.0.75 |
| controlPlane01 | 10.0.0.70 |
| controlPlane02 | 10.0.0.71 |
| controlPlane03 | 10.0.0.72 |
All nodes are running Ubuntu 18.04, Docker CE and will use Kubernetes 1.17.0.
### Generate the `kube-vip` configuration
`kube-vip` no longer requires storing its configuration in a separate directory and will now store its configuration in the actual manifest that defines the static pods.
```
sudo docker run --network host \
--rm ghcr.io/kube-vip/kube-vip:0.3.7 \
manifest pod \
--interface ens192 \
--vip 192.168.0.75 \
--arp \
--leaderElection | sudo tee /etc/kubernetes/manifests/vip.yaml
```
The above command will "initialise" the manifest within the `/etc/kubernetes/manifests` directory, that will be started when we actually initialise our Kubernetes cluster with `kubeadm init`
### Modify the configuration
**Cluster Configuration**
To enable Kubernetes leader Election passing the `--leaderElection` flag will enable `kube-vip` to use the Kubernetes leaderElection functionality to work out which member is the leader.
**VIP Config**
We will need to set our VIP address to `192.168.0.75` with `--vip 192.168.0.75` and to ensure all hosts are updated when the VIP moves we will enable ARP broadcasts `--arp` (defaults to `true`)
**vip.yaml** Static-pod Manifest
`$ sudo cat /etc/kubernetes/manifests/vip.yaml`
```
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-vip
namespace: kube-system
spec:
containers:
- args:
- start
env:
- name: vip_arp
value: "true"
- name: vip_interface
value: ens160
- name: vip_leaderelection
value: "true"
- name: vip_leaseduration
value: "5"
- name: vip_renewdeadline
value: "3"
- name: vip_retryperiod
value: "1"
- name: vip_address
value: 192.168.0.75
image: ghcr.io/kube-vip/kube-vip:0.3.7
imagePullPolicy: Always
name: kube-vip
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
- SYS_TIME
hostNetwork: true
status: {}
```
### First Node
To generate the basic Kubernetes static pod `yaml` configuration:
Make sure that the manifest directory exists: `sudo mkdir -p /etc/kubernetes/manifests/`
```
sudo docker run --network host \
--rm ghcr.io/kube-vip/kube-vip:0.3.7 \
manifest pod \
--interface ens192 \
--vip 192.168.0.75 \
--arp \
--leaderElection | sudo tee /etc/kubernetes/manifests/vip.yaml
```
Ensure that `image: ghcr.io/kube-vip/kube-vip:<x>` is modified to point to a specific version (`0.3.7` at the time of writing), refer to [GitHyb](https://github.com/kube-vip/kube-vip/pkgs/container/kube-vip) for details.
The **vip** is set to `192.168.0.75` and this first node will elect itself as leader, and as part of the `kubeadm init` it will use the VIP in order to speak back to the initialising api-server.
At this point **DON’T** generate the manifests, this is due to some bizarre `kubeadm/kubelet` behaviour.
```
kubeadm join 192.168.0.75:6443 --token <tkn> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key>
```
**After** this node has been added to the cluster, we can add the manifest to also add this node as a `kube-vip` member. (Adding the manifest afterwards doesn’t interfere with `kubeadm`).
```
sudo docker run --network host \
--rm ghcr.io/kube-vip/kube-vip:0.3.7 \
manifest pod \
--interface ens192 \
--vip 192.168.0.75 \
--arp \
--leaderElection | sudo tee /etc/kubernetes/manifests/vip.yaml
```
Once this node is added we will be able to see that the `kube-vip` pod is up and running as expected:
```
user@controlPlane01:~$ kubectl get pods -A | grep vip
A new flag `--address` is introduced to support using a DNS record as the control plane endpoint. `kube-vip` will do a dns lookup to retrieve the IP for the DNS record, and use that IP as the VIP. An `dnsUpdater` periodically checks and updates the system if IP changes for the DNS record.
### Dynamic DNS Support (added in 0.2.1)
`kube-vip` was also updated to support DHCP + [Dynamic DNS](https://en.wikipedia.org/wiki/Dynamic_DNS), for the use case where it's not able to reserve a static IP for the control plane endpoint.
A new flag `--ddns` is introduced. Once enabled, `kube-vip` expects the input `--address` will be a FQDN without binding to an IP. Then `kube-vip` will start a dhcp client to allocate an IP for the hostname of FQDN, and maintain the lease for it.
Once DHCP returns an IP for the FQDN, the same `dnsUpdater` runs to periodically checks and updates if IP got changed.
## BGP Support (added in 0.1.8)
In version `0.1.8`+ `kube-vip` was updated to support [BGP](https://en.wikipedia.org/wiki/Border_Gateway_Protocol) as a VIP failover mechanism. When a node is elected as a leader then it will update it's peers so that they are aware to route traffic to that node in order to access the VIP.
The following new flags are used:
- `--bgp` This will enable BGP support within kube-vip
- `--localAS` The local AS number
- `--bgpRouterID` The local router address
- `--peerAS` The AS number for a BGP peer
- `--peerAddress` The address of a BGP peer
### BGP Packet support
If the `--bgp` flag is passed alone with the Packet flags `packet, packetKey and packetProject`, then the Packet API will be used in order to determine the BGP configuration for the nodes being used in the cluster. This automates a lot of the process and makes using BGP within Packet much simpler.
## Packet Support (added in 0.1.7)
Recently in version `0.1.7` of `kube-vip` we added the functionality to use a Packet Elastic IP as the virtual IP fronting the Kubernetes Control plane cluster. In order to first get out virtual IP we will need to use our Packet account and create a EIP (either public (eek) or private). We will only need a single address so a `/32` will suffice, once this is created as part of a Packet project we can now apply this address to the servers that live in the same project.
In this example we've logged into the UI can created a new EIP of `147.75.1.2`, and we've deployed three small server instances with Ubuntu.
The following new flags are used:
- `--packet` which enables the use of the Packet API
- `--packetKey` which is our API key
- `--packetProject`which is the name of our Packet project where our servers and EIP are located.
*Also* the `--arp` flag should NOT be used as it wont work within the Packet network.
### Variables
```
export EIP=1.1.1.1
export PACKET_AUTH_TOKEN=XYZ
```
### First node
```
# Generate the manifest
sudo docker run --network host --rm ghcr.io/kube-vip/kube-vip:0.3.7 manifest pod \
--arp=false \
--interface lo \
--vip $EIP \
--leaderElection \
--packet \
--packetKey $PACKET_AUTH_TOKEN \
--packetProject vipTest | sudo tee /etc/kubernetes/manifests/vip.yaml\
A kubeadm join will fail as the `controlPlane01` still exists as an endpoint, so we have two options (manual steps and configmap edit to remove all mention of this node, or we can bring this node up and `kubeadm reset` the node (which we will do)).
```
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane01 NotReady master 14m v1.17.0
controlplane02 Ready master 13m v1.17.2
controlplane03 Ready master 13m v1.17.0
controlplane04 NotReady master 9s v1.17.0
```
After this we can add this node into `kube-vip` with the same manifest created by `docker run`.
## LeaderElection configuration
The Kubernetes LeaderElection that is used to manage the election of a new leader now supports having it's settings managed through flags.
- `--leaseDuration` Length of time a Kubernetes leader lease can be held for
- `--leaseRenewDuration` Length of time a Kubernetes leader can attempt to renew its lease
- `--leaseRetry` Number of times the host will retry to hold a lease
For larger clusters the `--leaseDuration` and `--leaseRenewDuration` may need extending due to slower `etcd` performance. (Tested with 2000 nodes)
## k3s
This section details the steps required to deploye `k3s` in a Highly available manner, using kube-vip deployed within k3s as a daemonset on the control plane nodes. As of `k3s` v1 the persistent datastore is back to etcd, however this guide will also include the steps for using `mysql`.
### Example MySQL deployment (optional)
To quickly validate this we can use docker on a host to quickly spin up a mysql database to store the persistent Kubernetes data.
The `kube-vip` manifest contains all the configuration for starting up `kube-vip` within the `k3s` cluster, it runs as a daemonset with affinity/taints for the control-plane nodes. As `k3s` starts it will parse all manifests in the manifests folder and start the highly available VIP across all control plane nodes in the cluster.
#### Create the `k3` manifests directory
Create the manifests directory, this directory is used by `k3s` for all of it's other deployments once it's up and running.
In Hybrid mode `kube-vip` will manage a virtual IP address that is passed through it's configuration for a Highly Available Kubernetes cluster, it will also "watch" services of `type:LoadBalancer` and once their `spec.LoadBalancerIP` is updated (typically by a cloud controller) it will advertise this address using BGP/ARP.
**Note about Daemonsets**
The "hybrid" mode is now the default mode in `kube-vip` from `0.2.3` onwards, and allows both modes to be enabled at the same time.
If the Kubernetes installer allows for adding a Virtual IP as an additional [SAN](https://en.wikipedia.org/wiki/Subject_Alternative_Name) to the API server certificate then we can apply `kube-vip` to the cluster once the first node has been brought up.
Unlike generating the static manifest there are a few more things that may need configuring, this page will cover most scenarios.
## Create the RBAC settings
As a daemonSet runs within the Kubernetes cluster it needs the correct access to be able to watch Kubernetes services and other objects. In order to do this we create a User, Role, and a binding.. we can apply this with the command:
This section only covers generating a simple *BGP* configuration, as the main focus is will be on additional changes to the manifest. For more examples we can look at [here](/hybrid/static/).
**Note:** Pay attention if using the "static" examples, as the `manifest` subcommand should use `daemonset` and NOT `pod`.
### Set configuration details
`export VIP=192.168.0.40`
`export INTERFACE=<interface>`
### Configure to use a container runtime
The easiest method to generate a manifest is using the container itself, below will create an alias for different container runtimes.
#### containerd
`alias kube-vip="ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:0.3.7 vip"`
#### Docker
`alias kube-vip="docker run --network host --rm ghcr.io/kube-vip/kube-vip:0.3.7"`
### BGP Example
This configuration will create a manifest that will start `kube-vip` providing **controlplane** and **services** management. **Unlike** ARP, all nodes in the BGP configuration will advertise virtual IP addresses.
**Note** we bind the address to `lo` as we don't want multiple devices that have the same address on public interfaces. We can specify all the peers in a comma seperate list in the format of `address:AS:password:multihop`.
**Note 2** we pass the `--inCluster` flag as this is running as a daemonSet within the Kubernetes cluster and therefore will have access to the token inside the running pod.
**Note 2** we pass the `--taint` flag as we're deploying `kube-vip` as both a daemonset and as advertising controlplane, we want to taint this daemonset to only run on the worker nodes.
-`nodeSelector` - Ensures that this particular daemonset only runs on control plane nodes
-`serviceAccountName: kube-vip` - this specifies the user in the `rbac` that will give us the permissions to get/update services.
-`hostNetwork: true` - This pod will need to modify interfaces (for VIPs)
-`env {...}` - We pass the configuration into the kube-vip pod through environment variables.
## Equinix Metal Overview (using the [Equinix Metal CCM](https://github.com/packethost/packet-ccm))
The below example is for running `type:LoadBalancer` services on worker nodes only and will create a daemonset that will run `kube-vip`.
**NOTE** This use-case requires the [Equinix Metal CCM](https://github.com/packethost/packet-ccm) to be installed and that the cluster/kubelet is configured to use an "external" cloud provider.
This is important as the CCM will apply the BGP configuration to the [node annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) making it easy for `kube-vip` to find the networking configuration it needs to expose load balancer addresses. The `--annotations metal.equinix.com` will cause kube-vip to "watch" the annotations of the worker node that it is running on, once all of the configuarion has been applied by the CCM then the `kube-vip` pod is ready to advertise BGP addresses for the service.
```
kube-vip manifest daemonset \
--interface $INTERFACE \
--services \
--bgp \
--annotations metal.equinix.com \
--inCluster | k apply -f -
```
### Troubleshooting
If `kube-vip` has been sat waiting for a long time then you may need to investigate that the annotations have been applied correctly by doing running the `describe` on the node:
If there are errors regarding `169.254.255.1` or `169.254.255.2` in the `kube-vip` logs then the routes to the ToR switches that provide BGP peering may by missing from the nodes. They can be replaced with the below command:
Additionally examining the logs of the Packet CCM may reveal why the node is not yet ready.
## K3s overview (on Equinix Metal)
### Step 1: TIDY (best if something was running before)
`rm -rf /var/lib/rancher /etc/rancher ~/.kube/*; ip addr flush dev lo; ip addr add 127.0.0.1/8 dev lo; mkdir -p /var/lib/rancher/k3s/server/manifests/`
We can deploy kube-vip in two different methods, which completely depends on your use-case and method for installing Kubernetes:
- Static Pods (hybrid)
- Daemonset (hybrid, requires taint)
## Prerequisites
In order for `kube-vip` to be able to speak with the Kubernetes API server, we need to be able to resolve the hostname within the pod. In order to ensure this will work as expected the `/etc/hosts` file should have the `hostname` of the server within it. The `/etc/hosts` file is passed into the running container and will ensure that the pod isn't "confused" by any Kubernetes networking.
## Kubernetes Services (`type:LoadBalancer`)
To learn more about how `kube-vip` in hybrid works with the LoadBalancer services within a kubernetes cluster the documentation is [here](./services/). To get `kube-vip` deployed read on!
## Static Pods
Static pods are a Kubernetes pod that is ran by the `kubelet` on a single node, and is **not** managed by the Kubernetes cluster itself. This means that whilst the pod can appear within Kubernetes it can't make use of a variety of kubernetes functionality (such as the kubernetes token or `configMaps`). The static pod approach is primarily required for [kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/), this is due to the sequence of actions performed by `kubeadm`. Ideally we want `kube-vip` to be part of the kubernetes cluster, for various bits of functionality we also need `kube-vip` to provide a HA virtual IP as part of the installation.
The sequence of events for this to work follows:
1. Generate a `kube-vip` manifest in the static pods manifest folder
2. Run `kubeadm init`, this generates the manifests for the control plane and wait to connect to the VIP
3. The `kubelet` will parse and execute all manifest, including the `kube-vip` manifest
4.`kube-vip` starts and advertises our VIP
5. The `kubeadm init` finishes successfully.
## Daemonset
Other Kubernetes distributions can bring up a Kubernetes cluster, without depending on a VIP (BUT they are configured to support one). A prime example of this would be k3s, that can be configured to start and also sign the certificates to allow incoming traffic to a virtual IP. Given we don't need the VIP to exist **before** the cluster, we can bring up the k3s node(s) and then add `kube-vip` as a daemonset for all control plane nodes.
## Deploying `kube-vip`
The simplest method for generating the Kubernetes manifests is with `kube-vip` itself.. The subcommand `manifest pod|daemonset` can be used to generate specific types of Kubernetes manifests for use in a cluster. These subcommands can be configured with additional flags to enable/disable BGP/ARP/LeaderElection and a host of other options.
Both Examples will use the same Architecture:
## Infrastructure architecture
The infrastructure for our example HA Kubernetes cluster is as follows:
| Node | Address |
| -------------- | --------- |
| VIP | 10.0.0.40 |
| controlPlane01 | 10.0.0.41 |
| controlPlane02 | 10.0.0.42 |
| controlPlane03 | 10.0.0.43 |
| worker01 | 10.0.0.44 |
All nodes are running Ubuntu 18.04, Docker CE and will use Kubernetes 1.19.0, we only have one worker as we're going to use our controlPlanes in "hybrid" mode.
## As a static Pod (for kubeadm)
The details for creating a static pod are available [here](./static/)
## As a daemonset
When using `kube-vip` as a daemonset the details are available [here](./daemonset/)
| | `--leaderElection` | Enables Kubernetes LeaderElection | Used by ARP, as only the leader can broadcast |
| **Services** | | | |
| | `--cidr` | Defaults "32" | Used when advertising BGP addresses (typically as `x.x.x.x/32`) |
| **Kubernetes** | | | |
| | `--inCluster` | Defaults to looking inside the Pod for the token | |
| | `--taint` | Enables a taint, stopping control plane daemonset being on workers | |
| **LeaderElection** | | | |
| | `--leaseDuration` | default 5 | Seconds a lease is held for |
| | `--leaseRenewDuration` | default 3 | Seconds a leader can attempt to renew the lease |
| | `--leaseRetry` | default 1 | Number of times the leader will hold the lease for |
| | `--namespace` | "kube-vip" | The namespace where the lease will reside |
| **BGP** | | | |
| | `--bgpRouterID` | `<IP Address>` | Typically the address of the local node |
| | `--localAS` | default 65000 | The AS we peer from |
| | `--bgppeers` | `<address:AS:password:multihop>` | Comma separated list of BGP peers |
| | `--peerAddress` | `<IP Address>` | Address of a single BGP Peer |
| | `--peerAS` | default 65000 | AS of a single BGP Peer |
| | `--peerPass` | "" | Password to work with a single BGP Peer |
| | `--multiHop` | Enables eBGP MultiHop | Enable multiHop with a single BGP Peer |
| | `--annotations` | `<provider string>` | Startup will be paused until the node annotations contain the BGP configuration |
| **Equinix Metal** | | | (May be deprecated) |
| | `--metal` | Enables Equinix Metal API calls | |
| | `--metalKey` | Equinix Metal API token | |
| | `--metalProject` | Equinix Metal Project (Name) | |
| | `--metalProjectID` | Equinix Metal Project (UUID) | |
| | `--provider-config` | Path to the Equinix Metal provider configuration | Requires the Equinix Metal CCM |
## Changelog
### Static DNS Support (added in 0.2.0)
A new flag `--address` is introduced to support using a DNS record as the control plane endpoint. `kube-vip` will do a dns lookup to retrieve the IP for the DNS record, and use that IP as the VIP. An `dnsUpdater` periodically checks and updates the system if IP changes for the DNS record.
### Dynamic DNS Support (added in 0.2.1)
`kube-vip` was also updated to support DHCP + [Dynamic DNS](https://en.wikipedia.org/wiki/Dynamic_DNS), for the use case where it's not able to reserve a static IP for the control plane endpoint.
A new flag `--ddns` is introduced. Once enabled, `kube-vip` expects the input `--address` will be a FQDN without binding to an IP. Then `kube-vip` will start a dhcp client to allocate an IP for the hostname of FQDN, and maintain the lease for it.
Once DHCP returns an IP for the FQDN, the same `dnsUpdater` runs to periodically checks and updates if IP got changed.
## BGP Support (added in 0.1.8)
In version `0.1.8``kube-vip` was updated to support [BGP](https://en.wikipedia.org/wiki/Border_Gateway_Protocol) as a VIP failover mechanism. When a node is elected as a leader then it will update it's peers so that they are aware to route traffic to that node in order to access the VIP.
The following new flags are used:
-`--bgp` This will enable BGP support within kube-vip
-`--localAS` The local AS number
-`--bgpRouterID` The local router address
-`--peerAS` The AS number for a BGP peer
-`--peerAddress` The address of a BGP peer
### Equinix Metal BGP support
If the `--bgp` flag is passed along with the Equinix Metal flags `metal, metalKey and metalProject`, then Equinix Metal API will be used in order to determine the BGP configuration for the nodes being used in the cluster. This automates a lot of the process and makes using BGP within Equinix Metal much simpler.
## Equinix Metal Control Plane Support (added in 0.1.8)
Recently in version `0.1.7` of `kube-vip` we added the functionality to use a Equinix Metal Elastic IP as the virtual IP fronting the Kubernetes Control plane cluster. In order to first get out virtual IP we will need to use our Equinix Metal account and create a EIP (either public or private). We will only need a single address so a `/32` will suffice, once this is created as part of a Equinix Metal project we can now apply this address to the servers that live in the same project.
In this example we've logged into the UI can created a new EIP of `147.75.1.2`, and we've deployed three small server instances with Ubuntu.
The following new flags are used:
-`--metal` which enables the use of the Equinix Metal API
-`--metalKey` which is our API key
-`--metalProject`which is the name of our Equinix Metal project where our servers and EIP are located.
*Also* the `--arp` flag should NOT be used as it wont work within the Equinix Metal network.
We've designed `kube-vip` to be as de-coupled or agnostic from other components that may exist within a Kubernetes cluster as possible. This has lead to `kube-vip` having a very simplistic but robust approach to advertising Kubernetes services to the outside world and marking these services as ready to use.
## Flow
This section details the flow of events in order for `kube-vip` to advertise a Kubernetes service:
1. An end user exposes a application through Kubernetes as a LoadBalancer => `kubectl expose deployment nginx-deployment --port=80 --type=LoadBalancer --name=nginx`
2. Within the Kubernetes cluster a service object is created with the `svc.Spec.Type = LoadBalancer`
3. A controller (typically a Cloud Controller) has a loop that "watches" for services of the type `LoadBalancer`.
4. The controller now has the responsibility of providing an IP address for this service along with doing anything that is network specific for the environment where the cluster is running.
5. Once the controller has an IP address it will update the service `svc.Spec.LoadBalancerIP` with it's new IP address.
6. The `kube-vip` pods also implement a "watcher" for services that have a `svc.Spec.LoadBalancerIP` address attached.
7. When a new service appears `kube-vip` will start advertising this address to the wider network (through BGP/ARP) which will allow traffic to come into the cluster and hit the service network.
8. Finally `kube-vip` will update the service status so that the API reflects that this LoadBalancer is ready. This is done by updating the `svc.Status.LoadBalancer.Ingress` with the VIP address.
## CCM
We can see from the [flow](#Flow) above that `kube-vip` isn't coupled to anything other than the Kubernetes API, and will only act upon an existing Kubernetes primative (in this case the object of type `Service`). This makes it easy for existing CCMs to simply apply their logic to services of type LoadBalancer and leave `kube-vip` to take the next steps to advertise these load-balancers to the outside world.
## Using the Kube-vip Cloud Provider
The below instructions *should just work* on Kubernetes regardless of architecture (Linux Operating System is the only requirement) - you can quickly install the "latest" components:
Creating services of `type: LoadBalancer` in *any namespace* will now take addresses from the **global** cidr defined in the `configmap` unless a specific
To manage the IP address ranges for the load balancer instances the `kube-vip-cloud-provider` uses a `configmap` held in the `kube-system` namespace. IP address ranges can be configured using:
- IP address pools by CIDR
- IP ranges [start address - end address]
- Multiple pools by CIDR per namespace
- Multiple IP ranges per namespace (handles overlapping ranges)
- Setting of static addresses through --load-balancer-ip=x.x.x.x
To control which IP address range is used for which service the following rules are applied:
- Global address pools (`cidr-global` or `range-global`) are available for use by *any*`service` in *any*`namespace`
- Namespace specific address pools (`cidr-<namespace>` or `range-<namespace>`) are *only* available for use by `service` in the *specific*`namespace`
- Static IP addresses can be applied to a load balancer `service` using the `loadbalancerIP` setting, even outside of the assigned ranges
Example Configmap:
```
$ kubectl get configmap -n kube-system kubevip -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: kubevip
namespace: kube-system
data:
cidr-default: 192.168.0.200/29 # CIDR-based IP range for use in the default namespace
range-development: 192.168.0.210-192.168.0.219 # Range-based IP range for use in the development namespace
cidr-finance: 192.168.0.220/29,192.168.0.230/29 # Multiple CIDR-based ranges for use in the finance namespace
cidr-global: 192.168.0.240/29 # CIDR-based range which can be used in any namespace
```
### Expose a service
We can now expose a service and once the cloud provider has provided an address `kube-vip` will start to advertise that address to the outside world as shown below!
With the latest release of `kube-vip` > 0.2.1, it is possible to use the local network DHCP server to provide `kube-vip` with a load-balancer address that can be used to access a
Kubernetes service on the network.
In order to do this we need to signify to `kube-vip` and the cloud-provider that we don't need one of their managed addresses. We do this by explicitly exposing a service on the
address `0.0.0.0`. When `kube-vip` sees a service on this address it will create a `macvlan` interface on the host and request a DHCP address, once this address is provided it will assign it as the VIP and update the Kubernetes service!
```
$ k expose deployment nginx-deployment --port=80 --type=LoadBalancer --name=nginx-dhcp --load-balancer-ip=0.0.0.0; k get svc
### Using UPNP to expose a service to the outside world
With the latest release of `kube-vip` > 0.2.1, it is possible to expose a load-balancer on a specific port and using UPNP (on a supported gateway) expose this service to the inte
rnet.
Most simple networks look something like the following:
Using UPNP we can create a matching port on the `<external network address>` allowing your service to be exposed to the internet.
#### Enable UPNP
Add the following to the `kube-vip` `env:` section, and the rest should be completely automated.
**Note** some environments may require (Unifi) will require `Secure mode` being `disabled` (this allows a host with a different address to register a port)
```
- name: enableUPNP
value: "true"
```
#### Exposing a service
To expose a port successfully we'll need to change the command slightly:
`--target-port=80` the port of the application in the pods (HTT/NGINX)
`--port=32380` the port the service will be exposed on (and what you should connect to in order to receive traffic from the service)
## Equinix Metal Overview (using the [Equinix Metal CCM](https://github.com/packethost/packet-ccm))
Below are two examples for running `type:LoadBalancer` services on worker nodes only and will create a daemonset that will run `kube-vip`.
**NOTE** This use-case requires the [Equinix Metal CCM](https://github.com/packethost/packet-ccm) to be installed and that the cluster/kubelet is configured to use an "external" cloud provider.
### Using Annotations
This is important as the CCM will apply the BGP configuration to the [node annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) making it easy for `kube-vip` to find the networking configuration it needs to expose load balancer addresses. The `--annotations metal.equinix.com` will cause kube-vip to "watch" the annotations of the worker node that it is running on, once all of the configuarion has been applied by the CCM then the `kube-vip` pod is ready to advertise BGP addresses for the service.
```
kube-vip manifest daemonset \
--interface $INTERFACE \
--services \
--bgp \
--annotations metal.equinix.com \
--inCluster | k apply -f -
```
### Using the existing CCM secret
Alternatively it is possible to create a daemonset that will use the existing CCM secret to do an API lookup, this will allow for discovering the networking configuration needed to advertise loadbalancer addresses through BGP.
In Hybrid mode `kube-vip` will manage a virtual IP address that is passed through it's configuration for a Highly Available Kubernetes cluster, it will also "watch" services of `type:LoadBalancer` and once their `spec.LoadBalancerIP` is updated (typically by a cloud controller) it will advertise this address using BGP/ARP.
The "hybrid" mode is now the default mode in `kube-vip` from `0.2.3` onwards, and allows both modes to be enabled at the same time.
## Generating a Manifest
This section details creating a number of manifests for various use cases
### Set configuration details
`export VIP=192.168.0.40`
`export INTERFACE=<interface>`
### Configure to use a container runtime
The easiest method to generate a manifest is using the container itself, below will create an alias for different container runtimes.
#### containerd
`alias kube-vip="ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:0.3.7 vip /kube-vip"`
#### Docker
`alias kube-vip="docker run --network host --rm ghcr.io/kube-vip/kube-vip:0.3.7"`
### ARP
This configuration will create a manifest that starts `kube-vip` providing **controlplane** and **services** management, using **leaderElection**. When this instance is elected as the leader it will bind the `vip` to the specified `interface`, this is also the same for services of `type:LoadBalancer`.
`export INTERFACE=eth0`
```
kube-vip manifest pod \
--interface $INTERFACE \
--vip $VIP \
--controlplane \
--services \
--arp \
--leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml
```
### BGP
This configuration will create a manifest that will start `kube-vip` providing **controlplane** and **services** management. **Unlike** ARP, all nodes in the BGP configuration will advertise virtual IP addresses.
**Note** we bind the address to `lo` as we don't want multiple devices that have the same address on public interfaces. We can specify all the peers in a comma seperate list in the format of `address:AS:password:multihop`.
`export INTERFACE=lo`
```
kube-vip manifest pod \
--interface $INTERFACE \
--vip $VIP \
--controlplane \
--services \
--bgp \
--localAS 65000 \
--bgpRouterID 192.168.0.2 \
--bgppeers 192.168.0.10:65000::false,192.168.0.11:65000::false | tee /etc/kubernetes/manifests/kube-vip.yaml
```
### BGP with Equinix Metal
When deploying Kubernetes with Equinix Metal with the `--controlplane` functionality we need to pre-populate the BGP configuration in order for the control plane to be advertised and work in a HA scenario. Luckily Equinix Metal provides the capability to "look up" the configuration details (for BGP) that we need in order to advertise our virtual IP for HA functionality. We can either make use of the [Equinix Metal API](https://metal.equinix.com/developers/api/) or we can parse the [Equinix Metal Metadata service](https://metal.equinix.com/developers/docs/servers/metadata/).
**Note** If this cluster will be making use of Equinix Metal for `type:LoadBalancer` (by using the [Equinix Metal CCM](https://github.com/packethost/packet-ccm)) then we will need to ensure that nodes are set to use an external cloud-provider. Before doing a `kubeadm init|join` ensure the kubelet has the correct flags by using the following command `echo KUBELET_EXTRA_ARGS=\"--cloud-provider=external\" > /etc/default/kubelet`.
#### Creating a manifest using the API
We can enable `kube-vip` with the capability to discover the required configuration for BGP by passing the `--metal` flag and the API Key and our project ID.
```
kube-vip manifest pod \
--interface $INTERFACE\
--vip $VIP \
--controlplane \
--services \
--bgp \
--metal \
--metalKey xxxxxxx \
--metalProjectID xxxxx | tee /etc/kubernetes/manifests/kube-vip.yaml
```
#### Creating a manifest using the metadata
We can parse the metadata, *however* it requires that the tools `curl` and `jq` are installed.
Due to an oddity with `kubeadm` we can't have our `kube-vip` manifest present **before** joining our additional nodes. So on these control plane nodes we will add them first to the cluster.
```
sudo kubeadm join $VIP:6443 \
--token w5atsr.blahblahblah
--control-plane \
--certificate-key abc123
```
**Once**, joined these nodes can have the same command that we ran on the first node to populate the `/etc/kubernetes/manifests/` folder with the `kube-vip` manifest.
## Services
At this point your `kube-vip` static pods will be up and running and where used with the `--services` flag will also be watching for Kubernetes services that they can advertise. In order for `kube-vip` to advertise a service it needs a CCM or other controller to apply an IP address to the `spec.LoadBalancerIP`, which marks the loadbalancer as defined.
Kube-Vip provides Kubernetes clusters a virtual IP and load balancer for both control plane and Kubernetes Services.
The idea behind `kube-vip` is a small, self-contained, highly-available option for all environments, especially:
- Bare metal
- On-Premises
- Edge (ARM / Raspberry Pi)
- Virtualisation
- Pretty much anywhere else :)
## Features
Kube-Vip was originally created to provide a HA solution for the Kubernetes control plane, but over time it has evolved to incorporate that same functionality for Kubernetes Services of type [LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). Some of the features include:
- VIP addresses can be either IPv4 or IPv6
- Control Plane with ARP (Layer 2) or BGP (Layer 3)
- Control Plane using either [leader election](https://godoc.org/k8s.io/client-go/tools/leaderelection) or [raft](https://en.wikipedia.org/wiki/Raft_(computer_science))
- Control Plane HA with kubeadm (static Pods)
- Control Plane HA with K3s/and others (DaemonSets)
- Control Plane LoadBalancing with IPVS (kube-vip ≥ 0.4)
- Service LoadBalancer using [leader election](https://godoc.org/k8s.io/client-go/tools/leaderelection) for ARP (Layer 2)
- Service LoadBalancer using multiple nodes with BGP
- Service LoadBalancer address pools per namespace or global
- Service LoadBalancer address via (existing network DHCP)
- Service LoadBalancer address exposure to gateway via UPnP
- ... manifest generation, vendor API integrations and many more...
## Why?
The "original" purpose of `kube-vip` was to simplify the building of HA Kubernetes clusters, which at the time involved a few components and configurations that all needed to be managed. This was blogged about in detail by [thebsdbox](https://twitter.com/thebsdbox/) [here](https://thebsdbox.co.uk/2020/01/02/Designing-Building-HA-bare-metal-Kubernetes-cluster/#Networking-load-balancing). Since the project has evolved, it can now use those same technologies to provide load balancing capabilities within a Kubernetes Cluster.
## Architecture
The architecture for `kube-vip` (and associated Kubernetes components) is covered in detail [here](/architecture/).
## Installation
There are two main routes for deploying `kube-vip`: either through a [static Pod](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/) when bringing up a Kubernetes cluster with [kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/) or as a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) (typically with distributions like [K3s](https://k3s.io)).
- [Static Pod](/install_static)
- [DaemonSet](/install_daemonset)
## Usage
- [On-Prem with the kube-vip cloud controller](/usage/on-prem)
Some Kubernetes distributions can bring up a Kubernetes cluster without depending on a pre-existing VIP (but they may be configured to support one). A prime example of this would be K3s which can be configured to start and also sign the certificates to allow incoming traffic to a virtual IP. Given we don't need the VIP to exist before the cluster, we can bring up the K3s node(s) and then add `kube-vip` as a DaemonSet for all control plane nodes.
If the Kubernetes installer allows for adding a virtual IP as an additional [SAN](https://en.wikipedia.org/wiki/Subject_Alternative_Name) to the API server certificate, we can apply `kube-vip` to the cluster once the first node has been brought up.
Unlike running `kube-vip` as a [static Pod](/install_static) there are a few more things that may need configuring when running `kube-vip` as a DaemonSet. This page will cover primarily the differences.
## Kube-Vip as HA, Load Balancer, or both
The functionality of `kube-vip` depends on the flags used to create the static Pod manifest. By passing in `--controlplane` we instruct `kube-vip` to provide and advertise a virtual IP to be used by the control plane. By passing in `--services` we tell `kube-vip` to provide load balancing for Kubernetes Service resources created inside the cluster. With both enabled, `kube-vip` will manage a virtual IP address that is passed through its configuration for a highly available Kubernetes cluster. It will also watch Services of type `LoadBalancer` and once their `spec.LoadBalancerIP` is updated (typically by a cloud controller, including (optionally) the one provided by kube-vip in [on-prem](/usage/on-prem) scenarios) it will advertise this address using BGP/ARP. In this example, we will use both when generating the manifest.
## Create the RBAC settings
Since `kube-vip` as a DaemonSet runs as a regular resource instead of a static Pod, it still needs the correct access to be able to watch Kubernetes Services and other objects. In order to do this, RBAC resources must be created which include a ServiceAccount, ClusterRole, and ClusterRoleBinding and can be applied this with the command:
In order to create an easier experience of consuming the various functionality within `kube-vip`, we can use the `kube-vip` container itself to generate our DaemonSet manifest. We do this by running the `kube-vip` image as a container and passing in the various [flags](/flags/) for the capabilities we want to enable. Generating a `kube-vip` manifest for running as a DaemonSet is almost identical to the process when running `kube-vip` as a [static Pod](/install_static). Only a few flags are different between the two processes. Therefore, refer back to the [Generating a Manifest](/install_static/#generating-a-manifest) section on the [static Pod installation page](/install_static) for the main process steps.
### ARP Example for DaemonSet
When creating the `kube-vip` installation manifest as a DaemonSet, the `manifest` subcommand takes the value `daemonset` as opposed to the `pod` value. The flags `--inCluster` and `--taint` are also needed to configure the DaemonSet to use a ServiceAccount and affine the `kube-vip` Pods to control plane nodes thereby preventing them from running on worker instances.
```
kube-vip manifest daemonset \
--interface $INTERFACE \
--address $VIP \
--inCluster \
--taint \
--controlplane \
--services \
--arp \
--leaderElection
```
#### Example ARP Manifest
```yaml
apiVersion:apps/v1
kind:DaemonSet
metadata:
creationTimestamp:null
name:kube-vip-ds
namespace:kube-system
spec:
selector:
matchLabels:
name:kube-vip-ds
template:
metadata:
creationTimestamp:null
labels:
name:kube-vip-ds
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key:node-role.kubernetes.io/master
operator:Exists
- matchExpressions:
- key:node-role.kubernetes.io/control-plane
operator:Exists
containers:
- args:
- manager
env:
- name:vip_arp
value:"true"
- name:port
value:"6443"
- name:vip_interface
value:ens160
- name:vip_cidr
value:"32"
- name:cp_enable
value:"true"
- name:cp_namespace
value:kube-system
- name:vip_ddns
value:"false"
- name:svc_enable
value:"true"
- name:vip_leaderelection
value:"true"
- name:vip_leaseduration
value:"5"
- name:vip_renewdeadline
value:"3"
- name:vip_retryperiod
value:"1"
- name:address
value:192.168.0.40
image:ghcr.io/kube-vip/kube-vip:v0.4.0
imagePullPolicy:Always
name:kube-vip
resources:{}
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
- SYS_TIME
hostNetwork:true
serviceAccountName:kube-vip
tolerations:
- effect:NoSchedule
operator:Exists
- effect:NoExecute
operator:Exists
updateStrategy:{}
status:
currentNumberScheduled:0
desiredNumberScheduled:0
numberMisscheduled:0
numberReady:0
```
### BGP Example for DaemonSet
This configuration will create a manifest that starts `kube-vip` providing control plane VIP and Kubernetes Service management. Unlike ARP, all nodes in the BGP configuration will advertise virtual IP addresses.
**Note** we bind the address to `lo` as we don't want multiple devices that have the same address on public interfaces. We can specify all the peers in a comma-separated list in the format of `address:AS:password:multihop`.
The `routerID` needs to be unique on each node that participates in BGP advertisements. In order to do this, we can modify the manifest so that when `kube-vip` starts it will look up its local address and use that as the `routerID`. Add the following to the `env[]` array of the container:
```yaml
- name:bgp_routerinterface
value:"ens160"
```
### DaemonSet Manifest Overview
Once the manifest for `kube-vip` as a DaemonSet is generated, these are some of the notable differences over the [static Pod](/install_static) manifest and their significance.
-`nodeSelector`: Ensures that DaemonSet Pods only run on control plane nodes.
-`serviceAccountName: kube-vip`: Specifies the ServiceAccount name that will be used to get/update Kubernetes Service resources.
-`tolerations`: Allows scheduling to control plane nodes that normally specify `NoSchedule` or `NoExecute` taints.
[Static Pods](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/) are Kubernetes Pods that are run by the `kubelet` on a single node and are not managed by the Kubernetes cluster itself. This means that whilst the Pod can appear within Kubernetes, it can't make use of a variety of Kubernetes functionality (such as the Kubernetes token or ConfigMap resources). The static Pod approach is primarily required for [kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/) as this is due to the sequence of actions performed by `kubeadm`. Ideally, we want `kube-vip` to be part of the Kubernetes cluster, but for various bits of functionality we also need `kube-vip` to provide a HA virtual IP as part of the installation.
The sequence of events for building a highly available Kubernetes cluster with `kubeadm` and `kube-vip` are as follows:
1. Generate a `kube-vip` manifest in the static Pods manifest directory (see the [generating a manifest](#generating-a-manifest) section below).
2. Run `kubeadm init` with the `--control-plane-endpoint` flag using the VIP address provided when generating the static Pod manifest.
3. The `kubelet` will parse and execute all manifests, including the `kube-vip` manifest generated in step one and the other control plane components including `kube-apiserver`.
4.`kube-vip` starts and advertises the VIP address.
5. The `kubelet` on this first control plane will connect to the VIP advertised in the previous step.
6.`kubeadm init` finishes successfully on the first control plane.
7. Using the output from the `kubeadm init` command on the first control plane, run the `kubeadm join` command on the remainder of the control planes.
8. Copy the generated `kube-vip` manifest to the remainder of the control planes and place in their static Pods manifest directory (default of `/etc/kubernetes/manifests/`).
## Kube-Vip as HA, Load Balancer, or both
The functionality of `kube-vip` depends on the flags used to create the static Pod manifest. By passing in `--controlplane` we instruct `kube-vip` to provide and advertise a virtual IP to be used by the control plane. By passing in `--services` we tell `kube-vip` to provide load balancing for Kubernetes Service resources created inside the cluster. With both enabled, `kube-vip` will manage a virtual IP address that is passed through its configuration for a highly available Kubernetes cluster. It will also watch Services of type `LoadBalancer` and once their `spec.LoadBalancerIP` is updated (typically by a cloud controller, including (optionally) the one provided by kube-vip in [on-prem](/usage/on-prem) scenarios) it will advertise this address using BGP/ARP. In this example, we will use both when generating the manifest.
## Generating a Manifest
In order to create an easier experience of consuming the various functionality within `kube-vip`, we can use the `kube-vip` container itself to generate our static Pod manifest. We do this by running the `kube-vip` image as a container and passing in the various [flags](/flags/) for the capabilities we want to enable.
### Set configuration details
We use environment variables to predefine the values of the inputs to supply to `kube-vip`.
Set the `VIP` address to be used for the control plane:
`export VIP=192.168.0.40`
Set the `INTERFACE` name to the name of the interface on the control plane(s) which will announce the VIP. In many Linux distributions this can be found with the `ip a` command.
`export INTERFACE=ens160`
Get the latest version of the `kube-vip` release by parsing the GitHub API. This step requires that `jq` and `curl` are installed.
To set manually instead, find the desired [release tag](https://github.com/kube-vip/kube-vip/releases):
`export KVVERSION=v0.4.0`
### Creating the manifest
With the input values now set, we can pull and run the `kube-vip` image supplying it the desired flags and values. Once the static Pod manifest is generated for your desired method (ARP or BGP), if running multiple control plane nodes, ensure it is placed in each control plane's static manifest directory (by default, `/etc/kubernetes/manifests`).
Depending on the container runtime, use one of the two aliased commands to create a `kube-vip` command which runs the `kube-vip` image as a container.
For containerd, run the below command:
`alias kube-vip="ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip /kube-vip"`
For Docker, run the below command:
`alias kube-vip="docker run --network host --rm ghcr.io/kube-vip/kube-vip:$KVVERSION"`
### ARP
With the inputs and alias command set, we can run the `kube-vip` container to generate a static Pod manifest which will be directed to a file at `/etc/kubernetes/manifests/kube-vip.yaml`. As such, this is assumed to run on the first control plane node.
This configuration will create a manifest that starts `kube-vip` providing control plane VIP and Kubernetes Service management using the `leaderElection` method and ARP. When this instance is elected as the leader, it will bind the `vip` to the specified `interface`. This is the same behavior for Services of type `LoadBalancer`.
> Note: When running these commands on a to-be control plane node, `sudo` access may be required along with pre-creation of the `/etc/kubernetes/manifests/` directory.
```
kube-vip manifest pod \
--interface $INTERFACE \
--address $VIP \
--controlplane \
--services \
--arp \
--leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml
```
#### Example ARP Manifest
```
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-vip
namespace: kube-system
spec:
containers:
- args:
- manager
env:
- name: vip_arp
value: "true"
- name: port
value: "6443"
- name: vip_interface
value: ens192
- name: vip_cidr
value: "32"
- name: cp_enable
value: "true"
- name: cp_namespace
value: kube-system
- name: vip_ddns
value: "false"
- name: svc_enable
value: "true"
- name: vip_leaderelection
value: "true"
- name: vip_leaseduration
value: "5"
- name: vip_renewdeadline
value: "3"
- name: vip_retryperiod
value: "1"
- name: address
value: 192.168.0.40
image: ghcr.io/kube-vip/kube-vip:v0.4.0
imagePullPolicy: Always
name: kube-vip
resources: {}
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
- SYS_TIME
volumeMounts:
- mountPath: /etc/kubernetes/admin.conf
name: kubeconfig
hostAliases:
- hostnames:
- kubernetes
ip: 127.0.0.1
hostNetwork: true
volumes:
- hostPath:
path: /etc/kubernetes/admin.conf
name: kubeconfig
status: {}
```
### BGP
This configuration will create a manifest that starts `kube-vip` providing control plane VIP and Kubernetes Service management. Unlike ARP, all nodes in the BGP configuration will advertise virtual IP addresses.
**Note** we bind the address to `lo` as we don't want multiple devices that have the same address on public interfaces. We can specify all the peers in a comma-separated list in the format of `address:AS:password:multihop`.
`export INTERFACE=lo`
```
kube-vip manifest pod \
--interface $INTERFACE \
--address $VIP \
--controlplane \
--services \
--bgp \
--localAS 65000 \
--bgpRouterID 192.168.0.2 \
--bgppeers 192.168.0.10:65000::false,192.168.0.11:65000::false | tee /etc/kubernetes/manifests/kube-vip.yaml
To deploy the [latest] then `kubectl apply -f https://kube-vip.io/manifests/kube-vip.yaml`, specific versions should be found in the repository as detailed below:
From the GitHub repository [https://github.com/kube-vip/kube-vip/tree/master/example/deploy](https://github.com/kube-vip/kube-vip/tree/master/example/deploy) find the version of the `kube-vip` to deploy (although typically the highest version number will provider more functionality/stability). The [raw] option in Github will provide the url that can be applied directly with a `kubectl apply -f <url>`.
The following output should appear when the manifest is applied:
```
serviceaccount/vip created
role.rbac.authorization.k8s.io/vip-role created
rolebinding.rbac.authorization.k8s.io/vip-role-bind created
deployment.apps/kube-vip-cluster created
```
*NOTE* The manifest for the `kube-vip` deployment has rules to ensure affinity (pods are always distributed to different nodes for HA). By default the replicas are set to `3` in the event you have less than `3` worker nodes then those replicas will sit as `pending`. This in itself isn't an issue, it means when new workers are added then they will be scheduled. *However*, tooling such as `kapps` will inspect the manifest before it's applied an error because of issues such as this.
### Editing `kube-vip` configuration
Either download and edit the manifest locally or apply as above and edit the deployment with `kubectl edit deploy/kube-vip-cluster` (change namespace where appropriate `-n`)
```
- name: vip_interface
value: ens192
- name: vip_configmap
value: plndr
- name: vip_arp
value: "true"
- name: vip_loglevel
value: "5"
```
-`vip_interface` - defines the interface that the VIP will bind to
-`vip_configmap` - defines the `configmap` that `kube-vip` will watch for service configuration
-`vip_arp` - determines if ARP broadcasts are enabled
-`vip_loglevel` - determines the verbosity of logging
## Using other namespaces
In this example we'll deploy and load-balance within the namespace `plunder`
### Create the namespace
`kubectl create namespace plunder`
### Add a network range/cidr for this namespace
`kubectl edit -n kube-system configmap/plndr`
We will add the range 192.168.0.210/29 for the namespace plunder underneath the existing range for the namespace default:
```
apiVersion: v1
data:
cidr-default: 192.168.0.200/29
cidr-global: 192.168.0.210/29
cidr-plunder: 192.168.0.220/29
<...>
```
### Deploy `kube-vip` in the namespace **plunder**
In the same way we deployed `kube-vip` into the default namespace we can deploy the same manifest into a different namespace using `-n namespace` e.g.
**Note** change the version of manifest when actually deploying!
This example will deploy into the namespace `plunder` as mention in the [Using other namespaces](Using other namespaces) example. Remove the `-n plunder` to deploy within the `default` namespace.
With the latest release of `kube-vip` > 0.2.1, it is possible to use the local network DHCP server to provide `kube-vip` with a load-balancer address that can be used to access a Kubernetes service on the network.
In order to do this we need to signify to `kube-vip` and the cloud-provider that we don't need one of their managed addresses. We do this by explicitly exposing a service on the address `0.0.0.0`. When `kube-vip` sees a service on this address it will create a `macvlan` interface on the host and request a DHCP address, once this address is provided it will assign it as the VIP and update the Kubernetes service!
```
$ k expose deployment nginx-deployment --port=80 --type=LoadBalancer --name=nginx-dhcp --load-balancer-ip=0.0.0.0; k get svc
## Using UPNP to expose a service to the outside world
With the latest release of `kube-vip` > 0.2.1, it is possible to expose a load-balancer on a specific port and using UPNP (on a supported gateway) expose this service to the internet.
Most simple networks look something like the following:
Using UPNP we can create a matching port on the `<external network address>` allowing your service to be exposed to the internet.
### Enable UPNP
Add the following to the `kube-vip``env:` section, and the rest should be completely automated.
**Note** some environments may require (Unifi) will require `Secure mode` being `disabled` (this allows a host with a different address to register a port)
```
- name: enableUPNP
value: "true"
```
### Exposing a service
To expose a port successfully we'll need to change the command slightly:
`--target-port=80` the port of the application in the pods (HTT/NGINX)
`--port=32380` the port the service will be exposed on (and what you should connect to in order to receive traffic from the service)
To deploy the [latest] then `kubectl apply -f https://kube-vip.io/manifests/kube-vip.yaml`, specific versions should be found in the repository as detailed below:
From the GitHub repository [https://github.com/kube-vip/kube-vip/tree/master/example/deploy](https://github.com/kube-vip/kube-vip/tree/master/example/deploy) find the version of the `kube-vip` to deploy (although typically the highest version number will provider more functionality/stability). The [raw] option in Github will provide the url that can be applied directly with a `kubectl apply -f <url>`.
The following output should appear when the manifest is applied:
```
serviceaccount/vip created
role.rbac.authorization.k8s.io/vip-role created
rolebinding.rbac.authorization.k8s.io/vip-role-bind created
deployment.apps/kube-vip-cluster created
```
*NOTE* The manifest for the `kube-vip` deployment has rules to ensure affinity (pods are always distributed to different nodes for HA). By default the replicas are set to `3` in the event you have less than `3` worker nodes then those replicas will sit as `pending`. This in itself isn't an issue, it means when new workers are added then they will be scheduled. *However*, tooling such as `kapps` will inspect the manifest before it's applied an error because of issues such as this.
### Editing `kube-vip` configuration
Either download and edit the manifest locally or apply as above and edit the deployment with `kubectl edit deploy/kube-vip-cluster` (change namespace where appropriate `-n`)
Ensure the `vip_arp` isn't enabled as ARP and BGP can't be used at the same time (today), also that the `vip_interface` is set to localhost (`lo`).
```
- name: vip_interface
value: "lo"
- name: vip_configmap
value: "plndr"
- name: bgp_enable
value: "true"
- name: vip_loglevel
value: "5"
```
### BGP Specific configuration
Additionally for BGP we'll need some configuration details, your local friendly network admin should be able to help here:
```
- name: bgp_routerid
value: "192.168.0.45"
- name: bgp_as
value: "65000"
- name: bgp_peeraddress
value: "10.0.0.1"
- name: bgp_peeras
value: "65522"
```
### BGP on Packet
If you're lucky enough to be running services on Packet then The above BGP information can be found from the API, instead of specifying the above we need to use the following:
```
- name: vip_packet
value: "true"
- name: vip_packetproject
value: "My Project"
- name: PACKET_AUTH_TOKEN
value: "XXYZZYVVY"
```
With the above configuration in place, all `kube-vip` pods will start in active mode and when a service is exposed then all nodes will advertise the VIP to the routers.
## Expose a service
Given that `kube-vip` doesn't know your network (at this point) ask your local friendly network OPs for an address you can advertise. That is the address you can expose to the outside world as shown below!
Creating services of `type: LoadBalancer` in the default namespace will now take addresses from the **global** cidr defined in the `configmap`.
**Additional namespaces**
Edit the `configmap` and add in the cidr ranges for those namespaces, the key in the cidr should be `cidr-<namespace>`, then ensure that `kube-vip` is deployed into that namespace with the above `apply` command with the `-n namespace` flag.
## The Detailed guide
### Deploy the `plndr-cloud-provider`
To deploy the [latest] then `kubectl apply -f https://kube-vip.io/manifests/controller.yaml`, specific versions should be found in the repository as detailed below:
From the GitHub repository [https://github.com/kube-vip/plndr-cloud-provider/tree/master/example/pod](https://github.com/kube-vip/plndr-cloud-provider/tree/master/example/pod), find the version of the plunder cloud provider manifest (although typically the highest version number will provider more functionality/stability). The [raw] option in Github will provide the url that can be applied directly with a `kubectl apply -f <url>`.
The following output should appear when the manifest is applied:
```
serviceaccount/plunder-cloud-controller created
clusterrole.rbac.authorization.k8s.io/system:plunder-cloud-controller-role created
clusterrolebinding.rbac.authorization.k8s.io/system:plunder-cloud-controller-binding created
pod/plndr-cloud-provider created
```
We can validate the cloud-provider by examining the pods:
The `configmap` details a CIDR range *per* namespace, however as of (`kube-vip 0.2.1` and `plnder-cloud-provider 0.1.4`), there is now the option of having a **global** CIDR range (`cidr-global)`.
To manage the ranges for the load-balancer instances, the `plndr-cloud-provider` has a `configmap` held in the `kube-system` namespace. The structure for the key/values within the `configmap` should be that the key is in the format `cidr-<namespace>` and the value should be the cidr range.
Example Configmap:
```
apiVersion: v1
kind: ConfigMap
metadata:
name: plndr
namespace: kube-system
data:
cidr-default: 192.168.0.200/29
cidr-global: 192.168.0.210/29
```
### Deploying `kube-vip`
To use `kube-vip` in Layer2/ARP the follow this [guide](/kubernetes/arp/)
To use `kube-vip` in Layer3/BGP the follow this [guide](/kubernetes/bgp/)
# Equinix Metal Overview (using the [Equinix Metal CCM](https://github.com/equinix/cloud-provider-equinix-metal))
## BGP with Equinix Metal
When deploying Kubernetes with Equinix Metal with the `--controlplane` functionality we need to pre-populate the BGP configuration in order for the control plane to be advertised and work in a HA scenario. Luckily Equinix Metal provides the capability to "look up" the configuration details (for BGP) that we need in order to advertise our virtual IP (VIP) for HA functionality. We can either make use of the [Equinix Metal API](https://metal.equinix.com/developers/api/) or we can parse the [Equinix Metal Metadata service](https://metal.equinix.com/developers/docs/servers/metadata/).
**Note** If this cluster will be making use of Equinix Metal for `type:LoadBalancer` (by using the [Equinix Metal CCM](https://github.com/equinix/cloud-provider-equinix-metal)) then we will need to ensure that nodes are set to use an external cloud-provider. Before doing a `kubeadm init|join` ensure the kubelet has the correct flags by using the following command `echo KUBELET_EXTRA_ARGS=\"--cloud-provider=external\" > /etc/default/kubelet`.
## Configure to use a container runtime
### Get latest version
We can parse the GitHub API to find the latest version (or we can set this manually)
The easiest method to generate a manifest is using the container itself, below will create an alias for different container runtimes.
### containerd
`alias kube-vip="ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip /kube-vip"`
### Docker
`alias kube-vip="docker run --network host --rm ghcr.io/kube-vip/kube-vip:KVVERSION"`
## Creating HA clusters in Equinix Metal
### Creating a manifest using the API
We can enable `kube-vip` with the capability to discover the required configuration for BGP by passing the `--metal` flag and the API Key and our project ID.
```
export VIP= metal_EIP
export INTERFACE=<interface>
```
where metal_EIP is the Elastic IP (EIP) address your requested via Metal's UI or API. For more informaiton on how to request a Metal's EIP, please see the following [Equinix Metal's EIP document](https://metal.equinix.com/developers/docs/networking/elastic-ips/#elastic-ip-addresses)
<interface> is the interface you announce your VIP from via BGP. By default it's lo:0 in Equinix Metal.
```
kube-vip manifest pod \
--interface $INTERFACE\
--vip $VIP \
--controlplane \
--services \
--bgp \
--metal \
--metalKey xxxxxxx \
--metalProjectID xxxxx | tee /etc/kubernetes/manifests/kube-vip.yaml
```
where metalKey is your "personal API key" under "Personal Settings" of your Metal's portal, and MetalProjectID is your Metal's "Project ID" under "Project Settings"
### Creating a manifest using the metadata
We can parse the metadata, *however* it requires that the tools `curl` and `jq` are installed.
Below are two examples for running `type:LoadBalancer` services on worker nodes only and will create a daemonset that will run `kube-vip`.
**NOTE** This use-case requires the [Equinix Metal CCM](https://github.com/equinix/cloud-provider-equinix-metal) to be installed prior to the kube-vip setup and that the cluster/kubelet is configured to use an "external" cloud provider.
```
export INTERFACE=<interface>
```
where <interface> is the interface you announce your VIP from via BGP. By default it's lo:0 in Equinix Metal.
### Using Annotations
This is important as the CCM will apply the BGP configuration to the [node annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) making it easy for `kube-vip` to find the networking configuration it needs to expose load balancer addresses. The `--annotations metal.equinix.com` will cause kube-vip to "watch" the annotations of the worker node that it is running on, once all of the configuarion has been applied by the CCM then the `kube-vip` pod is ready to advertise BGP addresses for the service.
```
kube-vip manifest daemonset \
--interface $INTERFACE \
--services \
--bgp \
--annotations metal.equinix.com \
--inCluster | k apply -f -
```
### Using the existing CCM secret
Alternatively it is possible to create a daemonset that will use the existing CCM secret to do an API lookup, this will allow for discovering the networking configuration needed to advertise loadbalancer addresses through BGP.
### Expose with [Equinix Metal CCM](https://github.com/equinix/cloud-provider-equinix-metal)
Follow the [Equinix Metal's Elastic IP (EIP) document](https://metal.equinix.com/developers/docs/networking/elastic-ips/#elastic-ip-addresses) either through the API, CLI or through the UI, to create a public IPv4 EIP address, for example (145.75.75.1) and this is the address you can expose through BGP as the service loadbalancer.
```
# metal ip request -p xxx-bbb-ccc -f ams1 -q 1 -t public_ipv4
If `kube-vip` has been sat waiting for a long time then you may need to investigate that the annotations have been applied correctly by doing running the `describe` on the node:
If there are errors regarding `169.254.255.1` or `169.254.255.2` in the `kube-vip` logs then the routes to the ToR switches that provide BGP peering may by missing from the nodes. They can be replaced with the below command:
`kube-vip` works on [K3s environments](https://k3s.io/) similar to most others with the exception of how it gets deployed. Because K3s is able to bootstrap a single server (control plane node) without the availability of the load balancer fronting it, `kube-vip` can be installed as a DaemonSet.
## Prerequisites (on Equinix Metal)
In order to make ARP work on Equinix Metal, follow the [metal-gateway](https://metal.equinix.com/developers/docs/networking/metal-gateway/) guide to have public VLAN subnet which can be used for the load balancer IP.
## Clean Environment
This step is optional but recommended if a K3s installation previously existed.
```
rm -rf /var/lib/rancher /etc/rancher ~/.kube/*; \
ip addr flush dev lo; \
ip addr add 127.0.0.1/8 dev lo;
```
## Step 1: Create Manifests Folder
K3s has an optional manifests directory that will be searched to [auto-deploy](https://rancher.com/docs/k3s/latest/en/advanced/#auto-deploying-manifests) any manifests found within. Create this directory first in order to later place the `kube-vip` resources inside.
```
mkdir -p /var/lib/rancher/k3s/server/manifests/
```
## Step 2: Upload Kube-Vip RBAC Manifest
As `kube-vip` runs as a DaemonSet under K3s and not a static Pod, we will need to ensure that the required permissions exist for it to communicate with the API server. RBAC resources are needed to ensure a ServiceAccount exists with those permissions and bound appropriately.
Get the RBAC manifest and place in the auto-deploy directory:
Refer to the [DaemonSet manifest generation documentation](/install_daemonset/#generating-a-manifest) for the process to complete this step.
Either store this generated manifest separately in the `/var/lib/rancher/k3s/server/manifests/` directory, or append to the existing RBAC manifest called `kube-vip-rbac.yaml`. As a general best practice, it is a cleaner approach to place all related resources into a single YAML file.
> Note: Remember to include YAML document delimiters (`---`) when composing multiple documents.
## Step 4: Install a HA K3s Cluster
There are multiple ways to install K3s including `[k3sup](https://k3sup.dev/)` or [running the binary](https://rancher.com/docs/k3s/latest/en/quick-start/) locally. Whichever method you choose, the `--tls-san` flag must be passed with the same IP when generating the `kube-vip` DaemonSet manifest when installing the first server (control plane) instance. This is so that K3s generates an API server certificate with the `kube-vip` virtual IP address.
Once the cluster is installed, you should be able to edit the `kubeconfig` file generated from the process and use the `kube-vip` VIP address to access the control plane.
## Step 5: Service Load Balancing
If wanting to use the `kube-vip` [cloud controller](/usage/on-prem), pass the `--disable servicelb` flag so K3s will not attempt to render Kubernetes Service resources of type `LoadBalancer`. If building with `k3sup`, the flag should be given as an argument to the `--k3s-extra-args` flag itself: `--k3s-extra-args "--disable servicelb"`. To install the `kube-vip` cloud controller, follow the additional steps in the [cloud controller guide](/on-prem/#install-the-kube-vip-cloud-provider).
The documentation for KIND is fantastic and its [quick start](https://kind.sigs.k8s.io/docs/user/quick-start/) guide will have you up and running in no time.
## Find Address Pool for Kube-Vip
We will need to find addresses that can be used by Kube-Vip:
```
docker network inspect kind -f '{{ range $i, $a := .IPAM.Config }}{{ println .Subnet }}{{ end }}'
```
This will return a CIDR range such as `172.18.0.0/16` and from here we can select a range.
Since `kube-vip` as a DaemonSet runs as a regular resource instead of a static Pod, it still needs the correct access to be able to watch Kubernetes Services and other objects. In order to do this, RBAC resources must be created which include a ServiceAccount, ClusterRole, and ClusterRoleBinding and can be applied this with the command:
We've designed `kube-vip` to be as decoupled or agnostic from other components that may exist within a Kubernetes cluster as possible. This has lead to `kube-vip` having a very simplistic but robust approach to advertising Kubernetes Services to the outside world and marking these Services as ready to use.
## Cloud Controller Manager
`kube-vip` isn't coupled to anything other than the Kubernetes API and will only act upon an existing Kubernetes primitive (in this case the object of type `Service`). This makes it easy for existing [cloud controller managers (CCMs)](https://kubernetes.io/docs/concepts/architecture/cloud-controller/) to simply apply their logic to services of type LoadBalancer and leave `kube-vip` to take the next steps to advertise these load balancers to the outside world.
## Using the Kube-Vip Cloud Provider
The `kube-vip` cloud provider can be used to populate an IP address for Services of type `LoadBalancer` similar to what public cloud providers allow through a Kubernetes CCM. The below instructions *should just work* on Kubernetes regardless of the architecture (a Linux OS being the only requirement) and will install the latest components.
## Install the Kube-Vip Cloud Provider
The `kube-vip` cloud provider can be installed from the latest release in the `main` branch by using the following command:
In order for `kube-vip` to set an IP address for a Service of type `LoadBalancer`, it needs to have an availability of IP address to assign. This information is stored in a Kubernetes ConfigMap to which `kube-vip` has access. You control the scope of the IP allocations with the `key` within the ConfigMap. Either CIDR blocks or IP ranges may be specified and scoped either globally (cluster-side) or per-Namespace.
To allow a global (cluster-wide) CIDR block which `kube-vip` can use to allocate an IP to Services of type `LoadBalancer` in any Namespace, create a ConfigMap named `kubevip` with the key `cidr-global` and value equal to a CIDR block available in your environment. For example, the below command creates a global CIDR with value `192.168.0.220/29` from which `kube-vip` will allocate IP addresses.
To use a global range instead, create the key `range-global` with the value set to a valid range of IP addresses. For example, the below command creates a global range using the pool `192.168.1.220-192.168.1.230`.
Creating services of type `LoadBalancer` in any Namespace will now take addresses from one of the global pools defined in the ConfigMap unless a Namespace-specific pool is created.
### The Kube-Vip Cloud Provider ConfigMap
To manage the IP address ranges for Services of type `LoadBalancer`, the `kube-vip-cloud-provider` uses a ConfigMap held in the `kube-system` Namespace. IP addresses can be configured using one or multiple formats:
- CIDR blocks
- IP ranges [start address - end address]
- Multiple pools by CIDR per Namespace
- Multiple IP ranges per Namespace (handles overlapping ranges)
- Setting of static addresses through --load-balancer-ip=x.x.x.x (`kubectl expose` command)
To control which IP address range is used for which Service, the following rules are applied:
- Global address pools (`cidr-global` or `range-global`) are available for use by *any* Service in *any* Namespace
- Namespace specific address pools (`cidr-<namespace>` or `range-<namespace>`) are *only* available for use by a Service in the *specific* Namespace
- Static IP addresses can be applied to a Service of type `LoadBalancer` using the `spec.loadBalancerIP` field, even outside of the assigned ranges
Example Configmap:
```yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:kubevip
namespace:kube-system
data:
cidr-default:192.168.0.200/29 # CIDR-based IP range for use in the default Namespace
range-development:192.168.0.210-192.168.0.219# Range-based IP range for use in the development Namespace
cidr-finance:192.168.0.220/29,192.168.0.230/29 # Multiple CIDR-based ranges for use in the finance Namespace
cidr-global:192.168.0.240/29 # CIDR-based range which can be used in any Namespace
```
### Expose a Service
We can now expose a Service and once the cloud provider has provided an address, `kube-vip` will start to advertise that address to the outside world as shown below:
With `kube-vip` > 0.2.1, it is possible to use the local network DHCP server to provide `kube-vip` with a load balancer address that can be used to access a Kubernetes service on the network.
In order to do this, we need to signify to `kube-vip` and the cloud provider that we don't need one of their managed addresses. We do this by explicitly exposing a Service on the address `0.0.0.0`. When `kube-vip` sees a Service on this address, it will create a `macvlan` interface on the host and request a DHCP address. Once this address is provided, it will assign it as the `LoadBalancer` IP and update the Kubernetes Service.
### Using UPnP to expose a Service to the outside world
With `kube-vip` > 0.2.1, it is possible to expose a Service of type `LoadBalancer` on a specific port to the Internet by using UPnP (on a supported gateway).
Most simple networks look something like the following:
log.Errorf(spew.Sprintf("Received an error which is not *metav1.Status but %#+v",event.Object))
log.Error(spew.Sprintf("Received an error which is not *metav1.Status but %#+v",event.Object))
}
status:=statusErr.ErrStatus
log.Errorf("%v",status)
log.Error("watcher","status",status)
default:
}
}
log.Infoln("Exiting Annotations watcher")
log.Info("Exiting Node watcher")
returnnil
}
funccheckIfNodeIsReady(node*v1.Node)bool{
ifnode==nil{
returnfalse
}
for_,condition:=rangenode.Status.Conditions{
ifcondition.Type==v1.NodeReady{
ifcondition.Status==v1.ConditionTrue{
returntrue
}
}
}
returnfalse
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.