feat: allow exposing non-web processes as kubernetes services

Closes #7204
This commit is contained in:
Jose Diaz-Gonzalez
2025-11-22 19:39:56 -05:00
parent 72c519f7b3
commit 0c96c4b6de
6 changed files with 140 additions and 0 deletions

View File

@@ -43,6 +43,7 @@
- `autoscaling` (map of string to object, optional) autoscaling rules. See the autoscaling section for more details
- `max_parallel`: (int, optional) number of instances to deploy in parallel at a given time
- `quantity`: (int, optional) number of processes to maintain. Default 1 for web processes, 0 for all others.
- `service`: (map of string to oject, optional) governs how non-web processes are exposed as services on the network
### Autoscaling
@@ -82,6 +83,24 @@ An autoscaling trigger consists of the following properties:
- `type`: (string, optional)
- `metadata`: (object, optional)
### Service
```json
{
"formation": {
"internal-web": {
"service": {
"exposed": true
}
}
}
}
```
(object, optional) A key-value object specifying how to expose non-web processes as services.
- `service`: (boolean, optional) Whether to expose a process as a network service. The `PORT` variable will be set to 5000.
## Healthchecks
```json

View File

@@ -232,6 +232,27 @@ The global default value may be set by passing an empty value for the option.
dokku scheduler-k3s:set --global deploy-timeout
```
### Exposing services on the network
Dokku will automatically expose the `web` process as a Kubernetes Service, with all others being treated as background processes. In some cases, it may be useful to have other processes exposed as Kubernetes Service objects so as to segregate internal http endpoints from public http endpoints. This can be done by modifying the `app.json` Formation entry for your process type.
```json
{
"formation": {
"internal-web": {
"service": {
"exposed": true
}
}
}
}
```
In the above example, the `internal-web` process is exposed as a service. The `PORT` variable for the process will be set to `5000`, and a kubernetes `Service` object will be created pointing at your processes.
> [!NOTE]
> It is not possible to modify the port mapping, nor is it possible to assign domains or SSL to a non-web process.
### SSL Certificates
#### Enabling letsencrypt integration

View File

@@ -76,6 +76,16 @@ type Formation struct {
// MaxParallel is the maximum number of processes to start in parallel
MaxParallel *int `json:"max_parallel"`
// Service is a struct that represents how to expose the process to the network
// This only applies to non-web processes
Service *FormationService `json:"service"`
}
// FormationService is a struct that represents how to expose a process to the network
type FormationService struct {
// Exposed is whether or not the process is exposed as a service
Exposed bool `json:"exposed"`
}
// FormationAutoscaling is a struct that represents the autoscaling configuration for a process from an app.json file

View File

@@ -64,7 +64,11 @@ spec:
{{- if hasKey $config "web" }}
env:
- name: PORT
{{- if eq $processName "web" }}
value: "{{ $.Values.global.network.primary_port }}"
{{- else }}
value: "5000"
{{- end }}
{{- end }}
envFrom:
- secretRef:

View File

@@ -623,6 +623,22 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
}
sort.Sort(NameSorter(processValues.Web.PortMaps))
} else if appJSON.Formation[processType].Service != nil && appJSON.Formation[processType].Service.Exposed {
processValues.Web = ProcessWeb{
Domains: []ProcessDomains{},
PortMaps: []ProcessPortMap{},
TLS: ProcessTls{
Enabled: false,
},
}
processValues.Web.PortMaps = append(processValues.Web.PortMaps, ProcessPortMap{
ContainerPort: 5000,
HostPort: 5000,
Name: "http-5000-5000",
Protocol: PortmapProtocol_TCP,
Scheme: "http",
})
}
values.Processes[processType] = processValues

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bats
load test_helper
TEST_APP="rdmtestapp"
setup() {
uninstall_k3s || true
global_setup
dokku nginx:stop
export KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
}
teardown() {
global_teardown
dokku nginx:start
uninstall_k3s || true
}
@test "(scheduler-k3s) app.json defined service" {
if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then
skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN"
fi
INGRESS_CLASS=nginx install_k3s
run /bin/bash -c "dokku apps:create $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku ps:scale $TEST_APP web=1 worker=1"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_app_json
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "kubectl get services $TEST_APP-web -o json | jq -r '.spec.ports[0].port'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "80"
run /bin/bash -c "kubectl get services $TEST_APP-web -o json | jq -r '.spec.ports[0].targetPort'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "5000"
}
inject_app_json() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
cat <<EOF >"$APP_REPO_DIR/app.json"
{
"formation": {
"worker": {
"service": {
"exposed": true
}
}
}
}
EOF
}