Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Control Plane Ingress IP configurable #3415

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@
LVMLogicalVolume volume in the UI
(PR[#3410](https://github.com/scality/metalk8s/pull/3410))

- [#2381](https://github.com/scality/metalk8s/issues/2381)) - Allow
configuring the Control Plane Ingress' external IP, to enable high
availability with failover of this (virtual) IP between control plane
nodes. This failover is not managed by MetalK8s.
(PR[#3415](https://github.com/scality/metalk8s/pull/3415))

### Breaking changes

- [#2199](https://github.com/scality/metalk8s/issues/2199) - Prometheus label
Expand Down
2 changes: 1 addition & 1 deletion buildchain/buildchain/salt_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,6 @@ def _get_parts(self) -> Iterator[str]:
Path("salt/metalk8s/addons/nginx-ingress-control-plane/deployed/init.sls"),
Path("salt/metalk8s/addons/nginx-ingress-control-plane/deployed/chart.sls"),
Path("salt/metalk8s/addons/nginx-ingress-control-plane/deployed/tls-secret.sls"),
Path("salt/metalk8s/addons/nginx-ingress-control-plane/control-plane-ip.sls"),
Path("salt/metalk8s/beacon/certificates.sls"),
Path("salt/metalk8s/container-engine/containerd/configured.sls"),
Path("salt/metalk8s/container-engine/containerd/files/50-metalk8s.conf.j2"),
Expand Down Expand Up @@ -546,6 +545,7 @@ def _get_parts(self) -> Iterator[str]:
Path("salt/metalk8s/orchestrate/downgrade/precheck.sls"),
Path("salt/metalk8s/orchestrate/downgrade/pre.sls"),
Path("salt/metalk8s/orchestrate/downgrade/post.sls"),
Path("salt/metalk8s/orchestrate/update-control-plane-ingress-ip.sls"),
Path("salt/metalk8s/orchestrate/upgrade/init.sls"),
Path("salt/metalk8s/orchestrate/upgrade/precheck.sls"),
Path("salt/metalk8s/orchestrate/upgrade/pre.sls"),
Expand Down
2 changes: 1 addition & 1 deletion charts/ingress-nginx-control-plane.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ controller:
type: ClusterIP

externalIPs:
- '{%- endraw -%}{{ grains.metalk8s.control_plane_ip }}{%- raw -%}'
- '{%- endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_ip() }}{%- raw -%}'

enableHttp: false

Expand Down
8 changes: 4 additions & 4 deletions charts/kube-prometheus-stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ grafana:

grafana.ini:
server:
root_url: '__escape__(https://{{ "{{ grains.metalk8s.control_plane_ip }}" }}:8443/grafana)'
root_url: '__escape__({{ "{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}" }}/grafana)'
analytics:
reporting_enabled: false
check_for_updates: false
Expand All @@ -188,9 +188,9 @@ grafana:
scopes: "openid profile email groups"
client_id: "grafana-ui"
client_secret: "4lqK98NcsWG5qBRHJUqYM1"
auth_url: '__escape__(https://{{ "{{ grains.metalk8s.control_plane_ip }}" }}:8443/oidc/auth)'
token_url: '__escape__(https://{{ "{{ grains.metalk8s.control_plane_ip }}" }}:8443/oidc/token)'
api_url: '__escape__(https://{{ "{{ grains.metalk8s.control_plane_ip }}" }}:8443/oidc/userinfo)'
auth_url: '__escape__({{ "{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}" }}/oidc/auth)'
token_url: '__escape__({{ "{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}" }}/oidc/token)'
api_url: '__escape__({{ "{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}" }}/oidc/userinfo)'
role_attribute_path: >-
contains(`{% endraw %}{{ "{{ dex.spec.config.staticPasswords | map(attribute='email') | list | tojson }}" }}{% raw %}`, email) && 'Admin'

Expand Down
12 changes: 12 additions & 0 deletions docs/installation/bootstrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Configuration
networks:
controlPlane:
cidr: <CIDR-notation>
ingress:
ip: <IP-for-ingress>
workloadPlane:
cidr: <CIDR-notation>
mtu: <network-MTU>
Expand Down Expand Up @@ -80,6 +82,16 @@ notation for it's various subfields.
network. This is an :ref:`advanced configuration<multiple CIDR network>`
which we do not recommend for non-experts.

For ``controlPlane`` entry, an ``ingress`` can also be provided. This
section allow to set the IP that will be used to connect to all the
control plane components, like MetalK8s-UI and the whole monitoring
stack. We suggest using a
`Virtual IP <https://en.wikipedia.org/wiki/Virtual_IP_address>`_ that
will sit on a working master Node. The default value for this
Ingress IP is the control plane IP of the Bootstrap node (which means
that if you lose the Bootstrap node, you no longer have access to any
control plane component).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...through the Ingress controller. But some components also listen on host ports directly, no? Especially K8s API, which is useful to access in case of Bootstrap issues.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but if you lose bootstrap you do not have access to OIDC so access to Kubernetes APIServer (should) do not work as well (if we generated kubeconfig specific to each user 😉)
But agree, I will replace "control plane component" with OIDC and various Control Plane UIs


For ``workloadPlane`` entry an
`MTU <https://en.wikipedia.org/wiki/Maximum_transmission_unit>`_ can
also be provided, this MTU value should be the lowest MTU value accross
Expand Down
11 changes: 6 additions & 5 deletions docs/installation/services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ and can be used for operating, extending and upgrading a MetalK8s cluster.

Gather Required Information
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get the control plane IP of the bootstrap node.
Get the ingress control plane IP.

.. code-block:: shell
.. code-block:: console

root@bootstrap $ salt-call grains.get metalk8s:control_plane_ip
local:
<the control plane IP>
root@bootstrap $ kubectl --kubeconfig=/etc/kubernetes/admin.conf \
get svc -n metalk8s-ingress ingress-nginx-control-plane-controller \
-o=jsonpath='{.spec.externalIPs[0]}{"\n"}'
<the ingress control plane IP>
Comment on lines +19 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😢

Copy link
Collaborator Author

@TeddyAndrieux TeddyAndrieux Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just to check that it's the IP we want, you do not like it ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of our commands are getting super verbose (same for running a command in the salt-master)... Not saying it's wrong 😉, just that it'd be nice if we could wrap those calls in some kubectl plugin... 😇

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess I agree


Use MetalK8s UI
^^^^^^^^^^^^^^^
Expand Down
44 changes: 44 additions & 0 deletions docs/operation/changing_control_plane_ingress_ip.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Changing the Control Plane Ingress IP
=====================================

#. On the Bootstrap node, update the ``ip`` field from
``networks.controlPlane.ingress`` in the Bootstrap configuration file.
(refer to :ref:`Bootstrap Configuration<Bootstrap Configuration>`)

#. Refresh the pillar.

.. code-block:: console

$ salt-call saltutil.refresh_pillar wait=True

#. Check that the change is taken into account.

.. code-block:: console

$ salt-call metalk8s_network.get_control_plane_ingress_ip
local:
<my-new-ip>

#. On the Bootstrap node, reconfigure apiServer:

.. parsed-literal::

$ salt-call state.sls \\
metalk8s.kubernetes.apiserver \\
saltenv=metalk8s-|version|

#. Reconfigure Control Plane components:

.. parsed-literal::

$ kubectl exec -n kube-system -c salt-master \\
--kubeconfig=/etc/kubernetes/admin.conf \\
$(kubectl --kubeconfig=/etc/kubernetes/admin.conf get pod \\
-l "app.kubernetes.io/name=salt-master" \\
--namespace=kube-system -o jsonpath='{.items[0].metadata.name}') \\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-o name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm right I could use it 🤔
It will have strange behavior if for whatever reason you have multiple salt-master running, maybe better to keep this one for the moment (so that this procedure may be compatible with future Bootstrap HA 😄 😉 )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha good point 👌 We'll have to make these commands simpler anyway, so if we change how we deploy things, we don't have to update it in hundreds of places across our docs.

-- salt-run state.orchestrate \\
metalk8s.orchestrate.update-control-plane-ingress-ip \\
saltenv=metalk8s-|version|

#. You can :ref:`access the MetalK8s GUI <installation-services-admin-ui>`
using this new IP.
1 change: 1 addition & 0 deletions docs/operation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ do not have a working MetalK8s_ setup.
disaster_recovery/index
solutions
changing_node_hostname
changing_control_plane_ingress_ip
metalk8s-utils
registry_ha
listening_processes
Expand Down
31 changes: 31 additions & 0 deletions salt/_modules/metalk8s_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,34 @@ def routes():
)

return ret


def get_control_plane_ingress_ip():
if "ingress" in __pillar__["networks"]["control_plane"]:
return __pillar__["networks"]["control_plane"]["ingress"]["ip"]

# Use Bootstrap Control Plane IP as Ingress Control plane IP
bootstrap_id = __salt__["metalk8s.minions_by_role"]("bootstrap")[0]

if __grains__["id"] == bootstrap_id:
return __grains__["metalk8s"]["control_plane_ip"]

if __opts__.get("__role") == "minion":
mine_ret = __salt__["mine.get"](tgt=bootstrap_id, fun="control_plane_ip")
else:
mine_ret = __salt__["saltutil.runner"](
"mine.get", tgt=bootstrap_id, fun="control_plane_ip"
)

if not isinstance(mine_ret, dict) or bootstrap_id not in mine_ret:
raise CommandExecutionError(
"Unable to get {} Control Plane IP: {}".format(bootstrap_id, mine_ret)
)

return mine_ret[bootstrap_id]


def get_control_plane_ingress_endpoint():
return "https://{}:8443".format(
__salt__["metalk8s_network.get_control_plane_ingress_ip"]()
)
8 changes: 5 additions & 3 deletions salt/metalk8s/addons/dex/config/dex.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
)
%}

{%- set control_plane_ingress_ep = salt.metalk8s_network.get_control_plane_ingress_endpoint() %}


# Defaults for configuration of Dex (OIDC)
apiVersion: addons.metalk8s.scality.com/v1alpha2
Expand All @@ -21,7 +23,7 @@ spec:

# Dex server configuration
config:
issuer: https://{{ grains.metalk8s.control_plane_ip }}:8443/oidc
issuer: {{ control_plane_ingress_ep }}/oidc

storage:
config:
Expand Down Expand Up @@ -65,12 +67,12 @@ spec:
- id: metalk8s-ui
name: MetalK8s UI
redirectURIs:
- https://{{ grains.metalk8s.control_plane_ip }}:8443/{{ metalk8s_ui_config.spec.basePath.lstrip('/') }}
- {{ control_plane_ingress_ep }}/{{ metalk8s_ui_config.spec.basePath.lstrip('/') }}
secret: ybrMJpVMQxsiZw26MhJzCjA2ut
- id: grafana-ui
name: Grafana UI
redirectURIs:
- https://{{ grains.metalk8s.control_plane_ip }}:8443/grafana/login/generic_oauth
- {{ control_plane_ingress_ep }}/grafana/login/generic_oauth
secret: 4lqK98NcsWG5qBRHJUqYM1

enablePasswordDB: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Create Control-Plane Ingress server private key:
- unless:
- test -f "{{ private_key_path }}"

{# TODO: add Ingress Service IP once stable (LoadBalancer probably) #}
{%- set certSANs = [
grains.fqdn,
'localhost',
Expand All @@ -30,7 +29,7 @@ Create Control-Plane Ingress server private key:
'nginx-ingress-control-plane.metalk8s-ingress',
'nginx-ingress-control-plane.metalk8s-ingress.svc',
'nginx-ingress-control-plane.metalk8s-ingress.svc.cluster.local',
grains.metalk8s.control_plane_ip,
salt.metalk8s_network.get_control_plane_ingress_ip(),
] %}

Generate Control-Plane Ingress server certificate:
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ metadata:
namespace: metalk8s-ingress
spec:
externalIPs:
- '{%- endraw -%}{{ grains.metalk8s.control_plane_ip }}{%- raw -%}'
- '{%- endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_ip() }}{%- raw
-%}'
ports:
- name: https
port: 8443
Expand Down
8 changes: 4 additions & 4 deletions salt/metalk8s/addons/prometheus-operator/deployed/chart.sls
Original file line number Diff line number Diff line change
Expand Up @@ -20826,15 +20826,15 @@ data:
[auth]
oauth_auto_login = true
[auth.generic_oauth]
api_url = "{% endraw -%}https://{{ grains.metalk8s.control_plane_ip }}:8443/oidc/userinfo{%- raw %}"
auth_url = "{% endraw -%}https://{{ grains.metalk8s.control_plane_ip }}:8443/oidc/auth{%- raw %}"
api_url = "{% endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/oidc/userinfo{%- raw %}"
auth_url = "{% endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/oidc/auth{%- raw %}"
client_id = grafana-ui
client_secret = 4lqK98NcsWG5qBRHJUqYM1
enabled = true
role_attribute_path = contains(`{% endraw %}{{ dex.spec.config.staticPasswords | map(attribute='email') | list | tojson }}{% raw %}`, email) && 'Admin'
scopes = openid profile email groups
tls_skip_verify_insecure = true
token_url = "{% endraw -%}https://{{ grains.metalk8s.control_plane_ip }}:8443/oidc/token{%- raw %}"
token_url = "{% endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/oidc/token{%- raw %}"
[grafana_net]
url = https://grafana.net
[log]
Expand All @@ -20845,7 +20845,7 @@ data:
plugins = /var/lib/grafana/plugins
provisioning = /etc/grafana/provisioning
[server]
root_url = "{% endraw -%}https://{{ grains.metalk8s.control_plane_ip }}:8443/grafana{%- raw %}"
root_url = "{% endraw -%}{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/grafana{%- raw %}"
kind: ConfigMap
metadata:
labels:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kind: ShellUIConfig
spec:
oidc:
providerUrl: "/oidc"
redirectUrl: "https://{{ grains.metalk8s.control_plane_ip }}:8443/{{ metalk8s_ui_config.spec.basePath.lstrip('/') }}"
redirectUrl: "{{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/{{ metalk8s_ui_config.spec.basePath.lstrip('/') }}"
clientId: "metalk8s-ui"
responseType: "id_token"
scopes: "openid profile email groups offline_access audience:server:client_id:oidc-auth-client"
Expand Down
2 changes: 1 addition & 1 deletion salt/metalk8s/addons/ui/deployed/ui-configuration.sls
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ metalk8s-ui-config ConfigMap already exist:
{%- endif %}

{%- set stripped_base_path = metalk8s_ui.spec.basePath.strip('/') %}
{%- set cp_ingress_url = "https://" ~ grains.metalk8s.control_plane_ip ~ ":8443" %}
{%- set cp_ingress_url = salt.metalk8s_network.get_control_plane_ingress_endpoint() %}
{%- set metalk8s_ui_url = cp_ingress_url ~ '/' ~ stripped_base_path ~
('/' if stripped_base_path else '') %}

Expand Down
5 changes: 0 additions & 5 deletions salt/metalk8s/addons/ui/deployed/ui.sls.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
include:
- .namespace

{%- from "metalk8s/addons/nginx-ingress-control-plane/control-plane-ip.sls"
import ingress_control_plane with context
%}


{%- set metalk8s_ui_defaults = salt.slsutil.renderer(
'salt://metalk8s/addons/ui/config/metalk8s-ui-config.yaml', saltenv=saltenv
)
Expand Down
6 changes: 3 additions & 3 deletions salt/metalk8s/defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ networks:
control_plane_ip:8080:
expected: nginx
description: MetalK8s repository
control_plane_ip:8443:
expected: kube-proxy
description: Control plane nginx ingress
master:
0.0.0.0:6443:
expected: kube-apiserver
Expand All @@ -87,6 +84,9 @@ networks:
127.0.0.1:7443:
expected: nginx
description: Apiserver proxy
ingress_control_plane_ip:8443:
expected: kube-proxy
description: Control plane nginx ingress
control_plane_ip:10257:
expected: kube-controller-manager
description: Kubernetes controller manager
Expand Down
5 changes: 1 addition & 4 deletions salt/metalk8s/kubernetes/apiserver/installed.sls
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
{%- from "metalk8s/map.jinja" import certificates with context %}
{%- from "metalk8s/map.jinja" import metalk8s with context %}
{%- from "metalk8s/map.jinja" import networks with context %}
{%- from "metalk8s/addons/nginx-ingress-control-plane/control-plane-ip.sls"
import ingress_control_plane with context
%}

{%- set encryption_k8s_path = "/etc/kubernetes/encryption.conf" %}

Expand Down Expand Up @@ -98,7 +95,7 @@ Create kube-apiserver Pod manifest:
# }
- --encryption-provider-config={{ encryption_k8s_path }}
- --cors-allowed-origins=.*
- --oidc-issuer-url=https://{{ ingress_control_plane }}/oidc
- --oidc-issuer-url={{ salt.metalk8s_network.get_control_plane_ingress_endpoint() }}/oidc
- --oidc-client-id=oidc-auth-client
- --oidc-ca-file=/etc/metalk8s/pki/nginx-ingress/ca.crt
- --oidc-username-claim=email
Expand Down
2 changes: 2 additions & 0 deletions salt/metalk8s/orchestrate/bootstrap/init.sls
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ Update pillar on bootstrap minion after highstate:
salt.function:
- name: saltutil.refresh_pillar
- tgt: {{ pillar.bootstrap_id }}
- kwarg:
wait: true
- require:
- salt: Configure bootstrap Node object

Expand Down
Loading