Worker Load Balancer

Spin up a new load balancer for services exposed on the
sub-cluster(ControlPlane and Worker)

TODO: Support node port range
Map each port to same port on sub-cluster (instead of 6443)
Optimize haproxy config for generic workload services

Change-Id: I59125d7af06886fe128d068f657f0d9f1be7e926
This commit is contained in:
francisy 2021-03-16 18:16:56 -04:00
parent 8c7d108831
commit 03085759e8
11 changed files with 498 additions and 93 deletions

View File

@ -178,10 +178,36 @@ spec:
- nodeSSHPrivateKeys - nodeSSHPrivateKeys
type: object type: object
type: array type: array
loadBalancer: loadBalancerControlPlane:
description: LoadBalancer defines the sub-cluster load balancer description: LoadBalancer defines the sub-cluster load balancer
services. services.
items: items:
description: LoadBalancerServiceControlPlane is an infrastructure
service type that represents the sub-cluster load balancer service.
properties:
clusterIP:
type: string
image:
type: string
nodeInterfaceId:
type: string
nodeLabels:
additionalProperties:
type: string
type: object
nodePort:
type: integer
required:
- image
- nodePort
type: object
type: array
loadBalancerWorker:
description: ' LoadBalancer defines the sub-cluster load balancer
services.'
items:
description: LoadBalancerServiceWorker is an infrastructure service
type that represents the sub-cluster load balancer service.
properties: properties:
clusterIP: clusterIP:
type: string type: string

View File

@ -17,8 +17,10 @@ rules:
- update - update
- apiGroups: - apiGroups:
- "" - ""
- apps
resources: resources:
- secrets - secrets
- deployments
verbs: verbs:
- get - get
- list - list

View File

@ -8,19 +8,19 @@ metadata:
spec: spec:
nodes: nodes:
ControlPlane: ControlPlane:
labelSelector: labelSelector:
vino.airshipit.org/flavor: control-plane vino.airshipit.org/flavor: control-plane
topologyKey: vino.airshipit.org/rack topologyKey: vino.airshipit.org/rack
count: count:
active: 1 active: 1
standby: 1 standby: 1
Worker: Worker:
labelSelector: labelSelector:
vino.airshipit.org/flavor: worker vino.airshipit.org/flavor: worker
topologyKey: vino.airshipit.org/host topologyKey: vino.airshipit.org/host
count: count:
active: 1 active: 1
standby: 1 # Slew for upgrades standby: 1 # Slew for upgrades
services: services:
# NOTE: The auth service has not yet been implemented. # NOTE: The auth service has not yet been implemented.
# auth: # auth:
@ -36,7 +36,7 @@ spec:
# NOTE: nodeLabels not yet implemented. # NOTE: nodeLabels not yet implemented.
# nodeLabels: # nodeLabels:
# kubernetes.io/os: linux # kubernetes.io/os: linux
nodePort: 30001 nodePort: 30000
nodeInterfaceId: oam-ipv4 nodeInterfaceId: oam-ipv4
# NOTE: clusterIP has not yet been implemented. # NOTE: clusterIP has not yet been implemented.
# clusterIP: 1.2.3.4 # IP of the base cluster VIP # clusterIP: 1.2.3.4 # IP of the base cluster VIP
@ -46,13 +46,21 @@ spec:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyaozS8kZRw2a1d0O4YXhxtJlDPThqIZilGCsXLbukIFOyMUmMTwQAtwWp5epwU1+5ponC2uBENB6xCCj3cl5Rd43d2/B6HxyAPQGKo6/zKYGAKW2nzYDxSWMl6NUSsiJAyXUA7ZlNZQe0m8PmaferlkQyLLZo3NJpizz6U6ZCtxvj43vEl7NYWnLUEIzGP9zMqltIGnD4vYrU9keVKKXSsp+DkApnbrDapeigeGATCammy2xRrUQDuOvGHsfnQbXr2j0onpTIh0PiLrXLQAPDg8UJRgVB+ThX+neI3rQ320djzRABckNeE6e4Kkwzn+QdZsmA2SDvM9IU7boK1jVQlgUPp7zF5q3hbb8Rx7AadyTarBayUkCgNlrMqth+tmTMWttMqCPxJRGnhhvesAHIl55a28Kzz/2Oqa3J9zwzbyDIwlEXho0eAq3YXEPeBhl34k+7gOt/5Zdbh+yacFoxDh0LrshQgboAijcVVaXPeN0LsHEiVvYIzugwIvCkoFMPWoPj/kEGzPY6FCkVneDA7VoLTCoG8dlrN08Lf05/BGC7Wllm66pTNZC/cKXP+cjpQn1iEuiuPxnPldlMHx9sx2y/BRoft6oT/GzqkNy1NTY/xI+MfmxXnF5kwSbcTbzZQ9fZ8xjh/vmpPBgDNrxOEAT4N6OG7GQIhb9HEhXQCQ== example-key - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyaozS8kZRw2a1d0O4YXhxtJlDPThqIZilGCsXLbukIFOyMUmMTwQAtwWp5epwU1+5ponC2uBENB6xCCj3cl5Rd43d2/B6HxyAPQGKo6/zKYGAKW2nzYDxSWMl6NUSsiJAyXUA7ZlNZQe0m8PmaferlkQyLLZo3NJpizz6U6ZCtxvj43vEl7NYWnLUEIzGP9zMqltIGnD4vYrU9keVKKXSsp+DkApnbrDapeigeGATCammy2xRrUQDuOvGHsfnQbXr2j0onpTIh0PiLrXLQAPDg8UJRgVB+ThX+neI3rQ320djzRABckNeE6e4Kkwzn+QdZsmA2SDvM9IU7boK1jVQlgUPp7zF5q3hbb8Rx7AadyTarBayUkCgNlrMqth+tmTMWttMqCPxJRGnhhvesAHIl55a28Kzz/2Oqa3J9zwzbyDIwlEXho0eAq3YXEPeBhl34k+7gOt/5Zdbh+yacFoxDh0LrshQgboAijcVVaXPeN0LsHEiVvYIzugwIvCkoFMPWoPj/kEGzPY6FCkVneDA7VoLTCoG8dlrN08Lf05/BGC7Wllm66pTNZC/cKXP+cjpQn1iEuiuPxnPldlMHx9sx2y/BRoft6oT/GzqkNy1NTY/xI+MfmxXnF5kwSbcTbzZQ9fZ8xjh/vmpPBgDNrxOEAT4N6OG7GQIhb9HEhXQCQ== example-key
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwpOyZjZ4gB0OTvmofH3llh6cBCWaEiEmHZWSkDXr8Bih6HcXVOtYMcFi/ZnUVGUBPw3ATNQBZUaVCYKeF+nDfKTJ9hmnlsyHxV2LeMsVg1o15Pb6f+QJuavEqtE6HI7mHyId4Z1quVTJXDWDW8OZEG7M3VktauqAn/e9UJvlL0bGmTFD1XkNcbRsWMRWkQgt2ozqlgrpPtvrg2/+bNucxX++VUjnsn+fGgAT07kbnrZwppGnAfjbYthxhv7GeSD0+Z0Lf1kiKy/bhUqXsZIuexOfF0YrRyUH1KBl8GCX2OLBYvXHyusByqsrOPiROqRdjX5PsK6HSAS0lk0niTt1p example-key-2 - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwpOyZjZ4gB0OTvmofH3llh6cBCWaEiEmHZWSkDXr8Bih6HcXVOtYMcFi/ZnUVGUBPw3ATNQBZUaVCYKeF+nDfKTJ9hmnlsyHxV2LeMsVg1o15Pb6f+QJuavEqtE6HI7mHyId4Z1quVTJXDWDW8OZEG7M3VktauqAn/e9UJvlL0bGmTFD1XkNcbRsWMRWkQgt2ozqlgrpPtvrg2/+bNucxX++VUjnsn+fGgAT07kbnrZwppGnAfjbYthxhv7GeSD0+Z0Lf1kiKy/bhUqXsZIuexOfF0YrRyUH1KBl8GCX2OLBYvXHyusByqsrOPiROqRdjX5PsK6HSAS0lk0niTt1p example-key-2
nodeSSHPrivateKeys: ssh-private-keys nodeSSHPrivateKeys: ssh-private-keys
loadBalancer: loadBalancerControlPlane:
- image: haproxy:2.3.2 - image: haproxy:2.3.2
# NOTE: nodeLabels not yet implemented. # NOTE: nodeLabels not yet implemented.
# nodeLabels: # nodeLabels:
# kubernetes.io/os: linux # kubernetes.io/os
nodePort: 30000 nodePort: 30001
nodeInterfaceId: oam-ipv4
# NOTE: clusterIP has not yet been implemented.
# clusterIP: 1.2.3.4 # IP of the base cluster VIP
loadBalancerWorker:
- image: haproxy:2.3.2
# NOTE: nodeLabels not yet implemented.
# nodeLabels:
# kubernetes.io/os
nodePort: 30002
nodeInterfaceId: oam-ipv4 nodeInterfaceId: oam-ipv4
# NOTE: clusterIP has not yet been implemented. # NOTE: clusterIP has not yet been implemented.
# clusterIP: 1.2.3.4 # IP of the base cluster VIP # clusterIP: 1.2.3.4 # IP of the base cluster VIP

View File

@ -116,6 +116,78 @@ directory, and then configured as identity files in the SSH config file of the d
</table> </table>
</div> </div>
</div> </div>
<h3 id="airship.airshipit.org/v1.LoadBalancerServiceControlPlane">LoadBalancerServiceControlPlane
</h3>
<p>
(<em>Appears on:</em>
<a href="#airship.airshipit.org/v1.SIPClusterServices">SIPClusterServices</a>)
</p>
<p>LoadBalancerServiceControlPlane is an infrastructure service type that represents the sub-cluster load balancer service.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>SIPClusterService</code><br>
<em>
<a href="#airship.airshipit.org/v1.SIPClusterService">
SIPClusterService
</a>
</em>
</td>
<td>
<p>
(Members of <code>SIPClusterService</code> are embedded into this type.)
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="airship.airshipit.org/v1.LoadBalancerServiceWorker">LoadBalancerServiceWorker
</h3>
<p>
(<em>Appears on:</em>
<a href="#airship.airshipit.org/v1.SIPClusterServices">SIPClusterServices</a>)
</p>
<p>LoadBalancerServiceWorker is an infrastructure service type that represents the sub-cluster load balancer service.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>SIPClusterService</code><br>
<em>
<a href="#airship.airshipit.org/v1.SIPClusterService">
SIPClusterService
</a>
</em>
</td>
<td>
<p>
(Members of <code>SIPClusterService</code> are embedded into this type.)
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="airship.airshipit.org/v1.NodeCount">NodeCount <h3 id="airship.airshipit.org/v1.NodeCount">NodeCount
</h3> </h3>
<p> <p>
@ -317,6 +389,8 @@ SIPClusterStatus
<p> <p>
(<em>Appears on:</em> (<em>Appears on:</em>
<a href="#airship.airshipit.org/v1.JumpHostService">JumpHostService</a>, <a href="#airship.airshipit.org/v1.JumpHostService">JumpHostService</a>,
<a href="#airship.airshipit.org/v1.LoadBalancerServiceControlPlane">LoadBalancerServiceControlPlane</a>,
<a href="#airship.airshipit.org/v1.LoadBalancerServiceWorker">LoadBalancerServiceWorker</a>,
<a href="#airship.airshipit.org/v1.SIPClusterServices">SIPClusterServices</a>) <a href="#airship.airshipit.org/v1.SIPClusterServices">SIPClusterServices</a>)
</p> </p>
<div class="md-typeset__scrollwrap"> <div class="md-typeset__scrollwrap">
@ -402,10 +476,23 @@ string
<tbody> <tbody>
<tr> <tr>
<td> <td>
<code>loadBalancer</code><br> <code>loadBalancerControlPlane</code><br>
<em> <em>
<a href="#airship.airshipit.org/v1.SIPClusterService"> <a href="#airship.airshipit.org/v1.LoadBalancerServiceControlPlane">
[]SIPClusterService []LoadBalancerServiceControlPlane
</a>
</em>
</td>
<td>
<p>LoadBalancer defines the sub-cluster load balancer services.</p>
</td>
</tr>
<tr>
<td>
<code>loadBalancerWorker</code><br>
<em>
<a href="#airship.airshipit.org/v1.LoadBalancerServiceWorker">
[]LoadBalancerServiceWorker
</a> </a>
</em> </em>
</td> </td>

View File

@ -53,7 +53,10 @@ type SIPClusterSpec struct {
// SIPClusterServices defines the services that are deployed when a SIPCluster is provisioned. // SIPClusterServices defines the services that are deployed when a SIPCluster is provisioned.
type SIPClusterServices struct { type SIPClusterServices struct {
// LoadBalancer defines the sub-cluster load balancer services. // LoadBalancer defines the sub-cluster load balancer services.
LoadBalancer []SIPClusterService `json:"loadBalancer,omitempty"` LoadBalancerControlPlane []LoadBalancerServiceControlPlane `json:"loadBalancerControlPlane,omitempty"`
// LoadBalancer defines the sub-cluster load balancer services.
LoadBalancerWorker []LoadBalancerServiceWorker `json:"loadBalancerWorker,omitempty"`
// Auth defines the sub-cluster authentication services. // Auth defines the sub-cluster authentication services.
Auth []SIPClusterService `json:"auth,omitempty"` Auth []SIPClusterService `json:"auth,omitempty"`
// JumpHost defines the sub-cluster jump host services. // JumpHost defines the sub-cluster jump host services.
@ -62,8 +65,11 @@ type SIPClusterServices struct {
func (s SIPClusterServices) GetAll() []SIPClusterService { func (s SIPClusterServices) GetAll() []SIPClusterService {
all := []SIPClusterService{} all := []SIPClusterService{}
for _, s := range s.LoadBalancer { for _, s := range s.LoadBalancerControlPlane {
all = append(all, s) all = append(all, s.SIPClusterService)
}
for _, s := range s.LoadBalancerWorker {
all = append(all, s.SIPClusterService)
} }
for _, s := range s.Auth { for _, s := range s.Auth {
all = append(all, s) all = append(all, s)
@ -86,6 +92,18 @@ type JumpHostService struct {
NodeSSHPrivateKeys string `json:"nodeSSHPrivateKeys"` NodeSSHPrivateKeys string `json:"nodeSSHPrivateKeys"`
} }
/*
LoadBalancerServiceControlPlane is an infrastructure service type that represents the sub-cluster load balancer service.
*/
type LoadBalancerServiceControlPlane struct {
SIPClusterService `json:",inline"`
}
// LoadBalancerServiceWorker is an infrastructure service type that represents the sub-cluster load balancer service.
type LoadBalancerServiceWorker struct {
SIPClusterService `json:",inline"`
}
// SIPClusterStatus defines the observed state of SIPCluster // SIPClusterStatus defines the observed state of SIPCluster
type SIPClusterStatus struct { type SIPClusterStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty"` Conditions []metav1.Condition `json:"conditions,omitempty"`

View File

@ -66,6 +66,38 @@ func (in *JumpHostService) DeepCopy() *JumpHostService {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LoadBalancerServiceControlPlane) DeepCopyInto(out *LoadBalancerServiceControlPlane) {
*out = *in
in.SIPClusterService.DeepCopyInto(&out.SIPClusterService)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerServiceControlPlane.
func (in *LoadBalancerServiceControlPlane) DeepCopy() *LoadBalancerServiceControlPlane {
if in == nil {
return nil
}
out := new(LoadBalancerServiceControlPlane)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LoadBalancerServiceWorker) DeepCopyInto(out *LoadBalancerServiceWorker) {
*out = *in
in.SIPClusterService.DeepCopyInto(&out.SIPClusterService)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerServiceWorker.
func (in *LoadBalancerServiceWorker) DeepCopy() *LoadBalancerServiceWorker {
if in == nil {
return nil
}
out := new(LoadBalancerServiceWorker)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeCount) DeepCopyInto(out *NodeCount) { func (in *NodeCount) DeepCopyInto(out *NodeCount) {
*out = *in *out = *in
@ -191,9 +223,16 @@ func (in *SIPClusterService) DeepCopy() *SIPClusterService {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SIPClusterServices) DeepCopyInto(out *SIPClusterServices) { func (in *SIPClusterServices) DeepCopyInto(out *SIPClusterServices) {
*out = *in *out = *in
if in.LoadBalancer != nil { if in.LoadBalancerControlPlane != nil {
in, out := &in.LoadBalancer, &out.LoadBalancer in, out := &in.LoadBalancerControlPlane, &out.LoadBalancerControlPlane
*out = make([]SIPClusterService, len(*in)) *out = make([]LoadBalancerServiceControlPlane, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.LoadBalancerWorker != nil {
in, out := &in.LoadBalancerWorker, &out.LoadBalancerWorker
*out = make([]LoadBalancerServiceWorker, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }

View File

@ -128,14 +128,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ipv4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }
@ -179,14 +193,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ipv4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }
@ -228,14 +256,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ipv4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }
@ -281,14 +323,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ip4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }
@ -328,14 +384,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ipv4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }
@ -374,14 +444,28 @@ var _ = Describe("MachineList", func() {
sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3) sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{ sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{ LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{ {
Image: "haproxy:latest", SIPClusterService: airshipv1.SIPClusterService{
NodeLabels: map[string]string{ Image: "haproxy:latest",
"test": "true", NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30001,
NodeInterface: "oam-ipv4",
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
Image: "haproxy:latest",
NodeLabels: map[string]string{
"test": "true",
},
NodePort: 30002,
NodeInterface: "oam-ipv4",
}, },
NodePort: 30000,
NodeInterface: "oam-ipv4",
}, },
}, },
} }

View File

@ -16,6 +16,7 @@ package services
import ( import (
"bytes" "bytes"
"strings"
"html/template" "html/template"
airshipv1 "sipcluster/pkg/api/v1" airshipv1 "sipcluster/pkg/api/v1"
@ -44,7 +45,7 @@ func (lb loadBalancer) Deploy() error {
lb.config.Image = DefaultBalancerImage lb.config.Image = DefaultBalancerImage
} }
instance := LoadBalancerServiceName + "-" + lb.sipName.Name instance := LoadBalancerServiceName + "-" + strings.ToLower(string(lb.bmhRole)) + "-" + lb.sipName.Name
labels := map[string]string{ labels := map[string]string{
// See https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/#labels // See https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/#labels
"app.kubernetes.io/part-of": "sip", "app.kubernetes.io/part-of": "sip",
@ -149,7 +150,7 @@ func (lb loadBalancer) generateSecret(instance string) (*corev1.Secret, error) {
Backends: make([]backend, 0), Backends: make([]backend, 0),
} }
for _, machine := range lb.machines.Machines { for _, machine := range lb.machines.Machines {
if machine.BMHRole == airshipv1.RoleControlPlane { if machine.BMHRole == lb.bmhRole {
name := machine.BMH.Name name := machine.BMH.Name
namespace := machine.BMH.Namespace namespace := machine.BMH.Namespace
ip, exists := machine.Data.IPOnInterface[lb.config.NodeInterface] ip, exists := machine.Data.IPOnInterface[lb.config.NodeInterface]
@ -163,7 +164,7 @@ func (lb loadBalancer) generateSecret(instance string) (*corev1.Secret, error) {
p.Backends = append(p.Backends, backend{IP: ip, Name: machine.BMH.Name, Port: 6443}) p.Backends = append(p.Backends, backend{IP: ip, Name: machine.BMH.Name, Port: 6443})
} }
} }
secretData, err := generateTemplate(p) secretData, err := lb.generateTemplate(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -216,22 +217,59 @@ type loadBalancer struct {
logger logr.Logger logger logr.Logger
config airshipv1.SIPClusterService config airshipv1.SIPClusterService
machines *bmh.MachineList machines *bmh.MachineList
bmhRole airshipv1.BMHRole
template string
} }
func newLB(name, namespace string, type loadBalancerControlPlane struct {
loadBalancer
config airshipv1.LoadBalancerServiceControlPlane
}
type loadBalancerWorker struct {
loadBalancer
config airshipv1.LoadBalancerServiceWorker
}
func newLBControlPlane(name, namespace string,
logger logr.Logger, logger logr.Logger,
config airshipv1.SIPClusterService, config airshipv1.LoadBalancerServiceControlPlane,
machines *bmh.MachineList, machines *bmh.MachineList,
client client.Client) loadBalancer { client client.Client) loadBalancerControlPlane {
return loadBalancer{ return loadBalancerControlPlane{loadBalancer{
sipName: types.NamespacedName{ sipName: types.NamespacedName{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
}, },
logger: logger, logger: logger,
config: config, config: config.SIPClusterService,
machines: machines, machines: machines,
client: client, client: client,
bmhRole: airshipv1.RoleControlPlane,
template: templateControlPlane,
},
config,
}
}
func newLBWorker(name, namespace string,
logger logr.Logger,
config airshipv1.LoadBalancerServiceWorker,
machines *bmh.MachineList,
client client.Client) loadBalancerWorker {
return loadBalancerWorker{loadBalancer{
sipName: types.NamespacedName{
Name: name,
Namespace: namespace,
},
logger: logger,
config: config.SIPClusterService,
machines: machines,
client: client,
bmhRole: airshipv1.RoleWorker,
template: templateWorker,
},
config,
} }
} }
@ -240,8 +278,8 @@ func (lb loadBalancer) Finalize() error {
return nil return nil
} }
func generateTemplate(p proxy) ([]byte, error) { func (lb loadBalancer) generateTemplate(p proxy) ([]byte, error) {
tmpl, err := template.New("haproxy-config").Parse(defaultTemplate) tmpl, err := template.New("haproxy-config").Parse(lb.template)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -255,7 +293,7 @@ func generateTemplate(p proxy) ([]byte, error) {
return rendered, nil return rendered, nil
} }
var defaultTemplate = `global var templateControlPlane = `global
log stdout format raw local0 notice log stdout format raw local0 notice
daemon daemon
@ -305,3 +343,52 @@ backend kube-apiservers
{{- $backEnd := . }} {{- $backEnd := . }}
server {{ $backEnd.Name }} {{ $backEnd.IP }}:{{ $backEnd.Port }} server {{ $backEnd.Name }} {{ $backEnd.IP }}:{{ $backEnd.Port }}
{{ end -}}` {{ end -}}`
// TODO Update this template to work for workload services, as it currently references api server(control plane)
var templateWorker = `global
log stdout format raw local0 notice
daemon
defaults
mode http
log global
option httplog
option dontlognull
retries 1
# Configures the timeout for a connection request to be left pending in a queue
# (connection requests are queued once the maximum number of connections is reached).
timeout queue 30s
# Configures the timeout for a connection to a backend server to be established.
timeout connect 30s
# Configures the timeout for inactivity during periods when we would expect
# the client to be speaking. For usability of 'kubectl exec', the timeout should
# be long enough to cover inactivity due to idleness of interactive sessions.
timeout client 600s
# Configures the timeout for inactivity during periods when we would expect
# the server to be speaking. For usability of 'kubectl log -f', the timeout should
# be long enough to cover inactivity due to the lack of new logs.
timeout server 600s
#---------------------------------------------------------------------
# apiserver frontend which proxys to the masters
#---------------------------------------------------------------------
frontend apiserver
bind *:{{ .FrontPort }}
mode tcp
option tcplog
default_backend kube-apiservers
#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
backend kube-apiservers
mode tcp
balance roundrobin
option httpchk GET /readyz
http-check expect status 200
option log-health-checks
# Observed apiserver returns 500 for around 10s when 2nd cp node joins.
# downinter 2s makes it check more frequently to recover from that state sooner.
# Also changing fall to 4 so that it takes longer (4 failures) for it to take down a backend.
default-server check check-ssl verify none inter 5s downinter 2s fall 4 on-marked-down shutdown-sessions
{{- range .Backends }}
{{- $backEnd := . }}
server {{ $backEnd.Name }} {{ $backEnd.IP }}:{{ $backEnd.Port }}
{{ end -}}`

View File

@ -3,6 +3,7 @@ package services_test
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"strings"
airshipv1 "sipcluster/pkg/api/v1" airshipv1 "sipcluster/pkg/api/v1"
@ -106,7 +107,7 @@ var _ = Describe("Service Set", func() {
set := services.NewServiceSet(logger, *sipCluster, machineList, k8sClient) set := services.NewServiceSet(logger, *sipCluster, machineList, k8sClient)
serviceList, err := set.ServiceList() serviceList, err := set.ServiceList()
Expect(serviceList).To(HaveLen(2)) Expect(serviceList).To(HaveLen(3))
Expect(err).To(Succeed()) Expect(err).To(Succeed())
for _, svc := range serviceList { for _, svc := range serviceList {
err := svc.Deploy() err := svc.Deploy()
@ -121,7 +122,8 @@ var _ = Describe("Service Set", func() {
It("Does not deploy a Jump Host when an invalid SSH key is provided", func() { It("Does not deploy a Jump Host when an invalid SSH key is provided", func() {
sip, _ := testutil.CreateSIPCluster("default", "default", 1, 1) sip, _ := testutil.CreateSIPCluster("default", "default", 1, 1)
sip.Spec.Services.Auth = []airshipv1.SIPClusterService{} sip.Spec.Services.Auth = []airshipv1.SIPClusterService{}
sip.Spec.Services.LoadBalancer = []airshipv1.SIPClusterService{} sip.Spec.Services.LoadBalancerControlPlane = []airshipv1.LoadBalancerServiceControlPlane{}
sip.Spec.Services.LoadBalancerWorker = []airshipv1.LoadBalancerServiceWorker{}
sip.Spec.Services.JumpHost[0].SSHAuthorizedKeys = []string{ sip.Spec.Services.JumpHost[0].SSHAuthorizedKeys = []string{
"sshrsaAAAAAAAAAAAAAAAAAAAAAinvalidkey", "sshrsaAAAAAAAAAAAAAAAAAAAAAinvalidkey",
} }
@ -141,29 +143,62 @@ var _ = Describe("Service Set", func() {
}) })
func testDeployment(sip *airshipv1.SIPCluster, machineList bmh.MachineList) error { func testDeployment(sip *airshipv1.SIPCluster, machineList bmh.MachineList) error {
loadBalancerDeployment := &appsv1.Deployment{} loadBalancerControlPlaneDeployment := &appsv1.Deployment{}
err := k8sClient.Get(context.Background(), types.NamespacedName{ err := k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default", Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + sip.GetName(), Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleControlPlane)) + "-" +
}, loadBalancerDeployment) sip.GetName(),
}, loadBalancerControlPlaneDeployment)
if err != nil { if err != nil {
return err return err
} }
loadBalancerSecret := &corev1.Secret{} loadBalancerWorkerDeployment := &appsv1.Deployment{}
err = k8sClient.Get(context.Background(), types.NamespacedName{ err = k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default", Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + sip.GetName(), Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleWorker)) + "-" +
}, loadBalancerSecret) sip.GetName(),
}, loadBalancerWorkerDeployment)
if err != nil { if err != nil {
return err return err
} }
loadBalancerService := &corev1.Service{} loadBalancerControlPlaneSecret := &corev1.Secret{}
err = k8sClient.Get(context.Background(), types.NamespacedName{ err = k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default", Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + sip.GetName(), Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleControlPlane)) + "-" +
}, loadBalancerService) sip.GetName(),
}, loadBalancerControlPlaneSecret)
if err != nil {
return err
}
loadBalancerWorkerSecret := &corev1.Secret{}
err = k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleWorker)) + "-" +
sip.GetName(),
}, loadBalancerWorkerSecret)
if err != nil {
return err
}
loadBalancerControlPlaneService := &corev1.Service{}
err = k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleControlPlane)) + "-" +
sip.GetName(),
}, loadBalancerControlPlaneService)
if err != nil {
return err
}
loadBalancerWorkerService := &corev1.Service{}
err = k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: "default",
Name: services.LoadBalancerServiceName + "-" + strings.ToLower(string(airshipv1.RoleWorker)) + "-" +
sip.GetName(),
}, loadBalancerWorkerService)
if err != nil { if err != nil {
return err return err
} }

View File

@ -64,9 +64,18 @@ func (ss ServiceSet) Finalize() error {
func (ss ServiceSet) ServiceList() ([]InfraService, error) { func (ss ServiceSet) ServiceList() ([]InfraService, error) {
serviceList := []InfraService{} serviceList := []InfraService{}
services := ss.sip.Spec.Services services := ss.sip.Spec.Services
for _, svc := range services.LoadBalancer { for _, svc := range services.LoadBalancerControlPlane {
serviceList = append(serviceList, serviceList = append(serviceList,
newLB(ss.sip.GetName(), newLBControlPlane(ss.sip.GetName(),
ss.sip.GetNamespace(),
ss.logger,
svc,
ss.machines,
ss.client))
}
for _, svc := range services.LoadBalancerWorker {
serviceList = append(serviceList,
newLBWorker(ss.sip.GetName(),
ss.sip.GetNamespace(), ss.sip.GetNamespace(),
ss.logger, ss.logger,
svc, svc,

View File

@ -261,17 +261,11 @@ func CreateSIPCluster(name string, namespace string, controlPlanes int, workers
}, },
}, },
Services: airshipv1.SIPClusterServices{ Services: airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
NodeInterface: "eno3",
NodePort: 30000,
},
},
JumpHost: []airshipv1.JumpHostService{ JumpHost: []airshipv1.JumpHostService{
{ {
SIPClusterService: airshipv1.SIPClusterService{ SIPClusterService: airshipv1.SIPClusterService{
Image: "quay.io/airshipit/jump-host", Image: "quay.io/airshipit/jump-host",
NodePort: 30001, NodePort: 30000,
NodeInterface: "eno3", NodeInterface: "eno3",
}, },
SSHAuthorizedKeys: []string{ SSHAuthorizedKeys: []string{
@ -281,6 +275,22 @@ func CreateSIPCluster(name string, namespace string, controlPlanes int, workers
NodeSSHPrivateKeys: sshPrivateKeySecretName, NodeSSHPrivateKeys: sshPrivateKeySecretName,
}, },
}, },
LoadBalancerControlPlane: []airshipv1.LoadBalancerServiceControlPlane{
{
SIPClusterService: airshipv1.SIPClusterService{
NodeInterface: "eno3",
NodePort: 30001,
},
},
},
LoadBalancerWorker: []airshipv1.LoadBalancerServiceWorker{
{
SIPClusterService: airshipv1.SIPClusterService{
NodeInterface: "eno3",
NodePort: 30002,
},
},
},
}, },
}, },
Status: airshipv1.SIPClusterStatus{}, Status: airshipv1.SIPClusterStatus{},