Using MetalLB to expose services on the local network #
MetalLB provides bare-metal load balancing which we can use to expose services on our cluster to IP addresses on our local network. It is of course important that the addresses used by MetalLB are not already in use by your
Deploying MetalLB #
Helm chart #
First, we add modules to import the chart and set the namespace:
Note
The namespace must be set to metallb-system for the deployment to work
{
self,
inputs,
...
}:
{
flake.modules.nixos.metallb-charts =
{
config,
lib,
pkgs,
...
}:
let
metallbChart = {
name = "metallb";
repo = "https://metallb.github.io/metallb";
version = "0.15.2";
hash = "sha256-Tw/DE82XgZoceP/wo4nf4cn5i8SQ8z9SExdHXfHXuHM=";
};
in
{
config = lib.mkIf config.metallb.enable {
services.k3s.autoDeployCharts = {
metallb = metallbChart // {
targetNamespace = "metallb-system";
createNamespace = true;
};
};
};
};
}Images #
Then we preload each image used by the Helm chart:
{
self,
inputs,
...
}:
{
flake.modules.nixos.metallb-images =
{
config,
lib,
pkgs,
...
}:
let
controllerImage = pkgs.dockerTools.pullImage {
imageName = "quay.io/metallb/controller";
imageDigest = "sha256:417cdb6d6f9f2c410cceb84047d3a4da3bfb78b5ddfa30f4cf35ea5c667e8c2e";
sha256 = "sha256-AzOCFyOAeLsFw7ESAg8iYygzH4ygxgNQcJ5rpajbnio=";
finalImageTag = "v0.15.2";
arch = "amd64";
};
speakerImage = pkgs.dockerTools.pullImage {
imageName = "quay.io/metallb/speaker";
imageDigest = "sha256:260c9406f957c0830d4e6cd2e9ac8c05e51ac959dd2462c4c2269ac43076665a";
sha256 = "sha256-gdy9zFJjY9wTYKuF7j5NW16V6oPWdFwEji+Nvt5Qr7Y=";
finalImageTag = "v0.15.2";
arch = "amd64";
};
in
{
config = lib.mkIf config.metallb.enable {
services.k3s = {
images = [
controllerImage
speakerImage
];
autoDeployCharts.metallb.values = {
controller.image = {
repository = controllerImage.imageName;
tag = controllerImage.imageTag;
};
speaker.image = {
repository = speakerImage.imageName;
tag = speakerImage.imageTag;
};
};
};
};
};
}Adding a default address pool and L2 advertisement #
For MetalLB to work we must configure a pool of available IP Addresses and an L2 advertisement:
{
self,
inputs,
...
}:
{
flake.modules.nixos.metallb-settings =
{
config,
lib,
pkgs,
...
}:
{
config = lib.mkIf config.metallb.enable {
services.k3s.autoDeployCharts.metallb.extraDeploy = [
{
apiVersion = "metallb.io/v1beta1";
kind = "IPAddressPool";
metadata = {
name = "default";
namespace = "metallb-system";
};
spec = {
addresses = [ "192.168.1.200-192.168.1.210" ];
autoAssign = true;
};
}
{
apiVersion = "metallb.io/v1beta1";
kind = "L2Advertisement";
metadata = {
name = "default";
namespace = "metallb-system";
};
spec = {
ipAddressPools = [ "default" ];
};
}
];
};
};
}Exposing Services #
Once deployed, services can be exposed using The LoadBalancer service type in Kubernetes manifests:
{
self,
inputs,
...
}:
{
flake.modules.nixos.flaresolverr-services =
{
config,
lib,
pkgs,
...
}:
{
config = lib.mkIf (config.media-server.enable && config.media-server.flaresolverr.enable) {
services.k3s.manifests.flaresolverr.content = [
{
apiVersion = "v1";
kind = "Service";
metadata = {
name = "flaresolverr-lb";
namespace = "media";
annotations = {
"metallb.io/address-pool" = "default";
"metallb.io/allow-shared-ip" = "media";
};
};
spec = {
type = "LoadBalancer";
loadBalancerIP = "192.168.1.202";
selector = {
"app" = "flaresolverr";
};
ports = [
{
name = "http";
port = 8191;
targetPort = 8191;
protocol = "TCP";
}
];
};
}
];
};
};
}