Skip to content
This repository was archived by the owner on Oct 27, 2025. It is now read-only.

Commit bad85fd

Browse files
committed
fix: loadbalancing with nginx ingress controller
Signed-off-by: 90DY <forward@90dy.ltd>
1 parent a4a1e78 commit bad85fd

File tree

17 files changed

+326
-67
lines changed

17 files changed

+326
-67
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ jobs:
4040
# Hide logs for public repositories
4141
run: make apply 1>/dev/null 2>/dev/null
4242

43+
- name: Tests changes
44+
run: make test
45+
4346
- name: Bump version and push tag
4447
id: tag_version
4548
uses: mathieudutour/github-tag-action@v6.2

Makefile

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ get-secret = $(shell read -s -p "$(1): " secret; echo $$secret; echo 1>&0)
33

44
docker-run := docker run $(shell [ -t 0 ] && echo -it || echo -i) -e DOMAIN_NAME=${DOMAIN_NAME}
55

6+
export DOMAIN_NAME ?= ${domain}
7+
68
.PHONY: build
79
build:
810
docker build -t kubespray -f Dockerfile .
@@ -42,16 +44,22 @@ ${HOME}/.cntb.yaml:
4244
.PHONY: generate
4345
generate: ## Generate the ansible inventory
4446
generate: .cntb .cntb/private-networks.json .cntb/instances.json
45-
@deno -A ./templates/_generate.ts
47+
@deno -A ./generate.ts
4648
.cntb:
4749
@mkdir -p .cntb
48-
.cntb/private-networks.json: .FORCE
50+
.cntb/private-networks.json:
4951
@cntb get privateNetworks --output json > .cntb/private-networks.json
50-
.cntb/instances.json: .FORCE
52+
.cntb/instances.json:
5153
@cntb get instances --output json > .cntb/instances.json
5254

5355
.PHONY: list-nodes
5456
list-nodes: ## List all nodes in the cluster
5557
list-nodes: generate
5658
@echo "Nodes:"
57-
@cat .cntb/instances.json | jq -r '.[] | .displayName' | grep '^${DOMAIN_NAME}-'
59+
@cat .cntb/instances.json | jq -r '.[] | .displayName' | grep '^${DOMAIN_NAME}-'
60+
61+
.PHONY: test
62+
test: ## Test basic cluster functionalities
63+
test: generate
64+
@echo "Test basic cluster functionalities..."
65+
@deno test -A

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ make apply
5757

5858
## Configuration & Node Naming
5959

60-
- **Core Files**: `templates/inventory.ini.ts`, `templates/group_vars/k8s_cluster/k8s-cluster.yml.ts`, `templates/group_vars/k8s_cluster/addons.yml.ts`
60+
- **Core Files**: `inventory.ini.ts`, `kubeconfig.yml.ts`, `group_vars/k8s_cluster/k8s-cluster.yml.ts`, `group_vars/k8s_cluster/addons.yml.ts`
6161
- **Node Naming**: Each node name must start with the domain name prefix (`ctnr.io-`) followed by the role:
6262
- Control Plane: `<domain-name>-control-plane-X[-etcd][-worker]`
6363
- ETCD: `<domain-name>-etcd-X`
@@ -74,9 +74,19 @@ The first control plane node is critical for cluster stability. Modifying or rem
7474
2. Apply the configuration
7575
3. Only then remove the original control plane node
7676

77+
## Testing
78+
79+
The project includes automated tests to verify that key components are working correctly:
80+
81+
- **Ingress Testing**: Tests that the Nginx ingress controller is properly configured and can route traffic to services
82+
- Run with: `make test-ingress` or `deno test -A tests/ingress_test.ts`
83+
- The test creates a test namespace, deployment, service, and ingress with a custom domain name
84+
- It verifies connectivity using host-based routing with the domain name from your configuration
85+
- Test fixtures are located in `tests/fixtures/` directory
86+
7787
## Next Steps & Roadmap
7888

79-
- **Next**: Validate deployment, implement volume provisioning, configure auto-scaling, deploy workloads, add monitoring
89+
- **Next**: Implement volume provisioning, configure auto-scaling, deploy workloads, add monitoring
8090
- **Roadmap**: Automated provisioning, one-click deployment, backup solution, multi-region support, performance benchmarks
8191

8292
## Professional Support
@@ -94,3 +104,4 @@ Email: contact@ctnr.io
94104
## License
95105

96106
MIT License - see [LICENSE](LICENSE) file
107+

generate.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env -S deno run -A
2+
3+
import "npm:dotenv/config";
4+
import fs from "node:fs/promises";
5+
import * as path from "jsr:@std/path";
6+
7+
const rootDir = path.dirname(path.fromFileUrl(import.meta.url));
8+
9+
const templateFiles = await fs
10+
.readdir(rootDir, { withFileTypes: true, recursive: true, encoding: "utf-8" })
11+
.then((files) =>
12+
files.filter((file) => file.isFile() && ["ini", "yml", "yaml", "json"].some((ext) => file.name.endsWith(`.${ext}.ts`)))
13+
);
14+
15+
await Promise.all(
16+
templateFiles.map(async (file) => {
17+
const templatePath = `${file.parentPath}/${file.name}`;
18+
const outputFilePath = `${file.parentPath}/${file.name.replace(/.ts$/, "")}`;
19+
const { default: content } = await import(templatePath);
20+
console.info("Generating", outputFilePath, "...");
21+
await fs.writeFile(outputFilePath, content);
22+
})
23+
);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { apiserverPort } from "../../_helpers.ts";
1+
import { apiserverPort } from "../../helpers.ts";
22

33
const yaml = String.raw
44

templates/group_vars/k8s_cluster/addons.yml.ts renamed to group_vars/k8s_cluster/addons.yml.ts

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { apiserverPrivateIp, apiserverPublicIp, nodes } from "../../_helpers.ts";
2-
import { privateNetworks } from "../../_helpers.ts";
1+
import { apiserverPrivateIp, workers } from "../../helpers.ts";
2+
import { privateNetworks } from "../../helpers.ts";
33

44
const yaml = String.raw;
55

6-
const privateIpRanges = "[" + privateNetworks.map((pn) => pn.cidr).join(", ") + "]";
7-
const publicIpRanges = "[" + nodes.map((node) => `${node.publicIp}/32`).join(", ") + "]";
6+
const internalIpRanges = "[" + privateNetworks.map((pn) => pn.cidr).join(", ") + "]";
7+
const externalIpRanges = "[" + workers.map((node) => `${node.publicIp}/32`).join(", ") + "]";
88

99
export default yaml`
1010
---
@@ -16,13 +16,13 @@ export default yaml`
1616
helm_enabled: false
1717
1818
# Registry deployment
19-
registry_enabled: false
19+
registry_enabled: false
2020
# registry_namespace: kube-system
2121
# registry_storage_class: ""
2222
# registry_disk_size: "10Gi"
2323
2424
# Metrics Server deployment
25-
metrics_server_enabled: false
25+
metrics_server_enabled: true
2626
# metrics_server_container_port: 10250
2727
# metrics_server_kubelet_insecure_tls: true
2828
# metrics_server_metric_resolution: 15s
@@ -31,7 +31,7 @@ metrics_server_enabled: false
3131
# metrics_server_replicas: 1
3232
3333
# Rancher Local Path Provisioner
34-
local_path_provisioner_enabled: false
34+
local_path_provisioner_enabled: true
3535
# local_path_provisioner_namespace: "local-path-storage"
3636
# local_path_provisioner_storage_class: "local-path"
3737
# local_path_provisioner_reclaim_policy: Delete
@@ -43,7 +43,7 @@ local_path_provisioner_enabled: false
4343
# local_path_provisioner_helper_image_tag: "latest"
4444
4545
# Local volume provisioner deployment
46-
local_volume_provisioner_enabled: false
46+
local_volume_provisioner_enabled: true
4747
# local_volume_provisioner_namespace: kube-system
4848
# local_volume_provisioner_nodelabels:
4949
# - kubernetes.io/hostname
@@ -110,21 +110,23 @@ gateway_api_enabled: true
110110
# gateway_api_experimental_channel: false
111111
112112
# Nginx ingress controller deployment
113-
ingress_nginx_enabled: false
114-
# ingress_nginx_host_network: false
115-
# ingress_nginx_service_type: LoadBalancer
116-
# ingress_nginx_service_annotations:
117-
# example.io/loadbalancerIPs: 1.2.3.4
113+
ingress_nginx_enabled: true
114+
ingress_nginx_host_network: false
115+
ingress_nginx_service_type: LoadBalancer
116+
ingress_nginx_service_annotations:
117+
metallb.universe.tf/address-pool: external
118+
# example.io/loadbalancerIPs: 1.2.3.4
118119
# ingress_nginx_service_nodeport_http: 30080
119120
# ingress_nginx_service_nodeport_https: 30081
120121
ingress_publish_status_address: ""
121122
# ingress_nginx_nodeselector:
122123
# kubernetes.io/os: "linux"
123-
# ingress_nginx_tolerations:
124-
# - key: "node-role.kubernetes.io/control-plane"
125-
# operator: "Equal"
126-
# value: ""
127-
# effect: "NoSchedule"
124+
# Permit ingress traffic from control plane nodes
125+
ingress_nginx_tolerations:
126+
- key: "node-role.kubernetes.io/control-plane"
127+
operator: "Equal"
128+
value: ""
129+
effect: "NoSchedule"
128130
# ingress_nginx_namespace: "ingress-nginx"
129131
# ingress_nginx_insecure_port: 80
130132
# ingress_nginx_secure_port: 443
@@ -217,16 +219,16 @@ metallb_config:
217219
# To use specific pool for services, you can annotate the service with the following annotation:
218220
# metallb.universe.tf/address-pool
219221
address_pools:
220-
public:
221-
ip_range: ${publicIpRanges}
222+
internal:
223+
ip_range: ${internalIpRanges}
222224
auto_assign: true
223-
private:
224-
ip_range: ${privateIpRanges}
225+
external:
226+
ip_range: ${externalIpRanges}
225227
auto_assign: true
226-
# Contabo cannot use BGP, so we use Layer 2 mode
227228
layer2:
228-
- public
229-
- private
229+
# internal ip is for all services, external ip is for ingresses like nginx ingress controller
230+
- internal
231+
- external
230232
# layer3:
231233
# defaults:
232234
# peer_port: 179
@@ -270,11 +272,11 @@ krew_enabled: false
270272
krew_root_dir: "/usr/local/krew"
271273
272274
# Kube VIP
273-
kube_vip_enabled: true
274-
kube_vip_arp_enabled: true
275-
kube_vip_lb_enable: true
275+
kube_vip_enabled: true
276+
kube_vip_arp_enabled: true
277+
kube_vip_lb_enable: false
276278
kube_vip_controlplane_enabled: true
277-
kube_vip_address: ${apiserverPublicIp} # This becomes the VIP
279+
kube_vip_address: ${apiserverPrivateIp} # This becomes the VIP
278280
loadbalancer_apiserver:
279281
address: "{{ kube_vip_address }}"
280282
port: 6443
@@ -283,10 +285,10 @@ loadbalancer_apiserver:
283285
# kube_vip_dns_mode: first
284286
# kube_vip_cp_detect: false
285287
# kube_vip_leasename: plndr-cp-lock
286-
# kube_vip_enable_node_labeling: false
288+
kube_vip_enable_node_labeling: true
287289
288290
# Node Feature Discovery
289-
node_feature_discovery_enabled: false
291+
node_feature_discovery_enabled: true
290292
# node_feature_discovery_gc_sa_name: node-feature-discovery
291293
# node_feature_discovery_gc_sa_create: false
292294
# node_feature_discovery_worker_sa_name: node-feature-discovery

templates/group_vars/k8s_cluster/k8s-cluster.yml.ts renamed to group_vars/k8s_cluster/k8s-cluster.yml.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { controlPlanes } from "../../_helpers.ts";
1+
import { controlPlanes, domainName } from "../../helpers.ts";
22

33
const yaml = String.raw;
44

5-
const supplementary_addresses_in_ssl_keys = `[${[...new Set(controlPlanes.map((node) => [node.publicIp, node.privateIp]).flat())].join(", ")}]`;
5+
const supplementary_addresses_in_ssl_keys = `[${domainName}, ${[...new Set(controlPlanes.map((node) => [node.publicIp, node.privateIp]).flat())].join(", ")}]`;
66

77
export default yaml`
88
# This configuration file is auto-generated by 'make generate'
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as process from "node:process";
22

3-
const { default: allInstances } = await import("../.cntb/instances.json", { with: { type: "json" } });
4-
const { default: allPrivateNetworks } = await import("../.cntb/private-networks.json", { with: { type: "json" } });
3+
const { default: allInstances } = await import("./.cntb/instances.json", { with: { type: "json" } });
4+
const { default: allPrivateNetworks } = await import("./.cntb/private-networks.json", { with: { type: "json" } });
55

66
export const domainName = process.env.DOMAIN_NAME;
77
if (!domainName) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { controlPlanes, etcds, Node, workers } from "./_helpers.ts";
1+
import { controlPlanes, etcds, Node, workers } from "./helpers.ts";
22

33
const ini = String.raw;
44

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { apiserverPublicIp, apiserverPort, domainName } from "./_helpers.ts";
1+
import { apiserverPublicIp, apiserverPort, domainName } from "./helpers.ts";
22
import * as util from "node:util";
33
import * as childProcess from "node:child_process";
44
import { encodeBase64 } from "jsr:@std/encoding/base64";

0 commit comments

Comments
 (0)