mirror of
https://github.com/dokku/dokku.git
synced 2026-09-01 19:49:25 +02:00
feat: attach vector container to additional docker networks
Adds a new global `vector-networks` property on the logs plugin that takes a comma-separated list of Docker networks. When set, the rendered compose file declares each network plus `bridge` as external and joins them on the vector service, so `docker compose up` reconciles attachments on every `logs:vector-start`. When unset, the existing `network_mode: bridge` template is preserved unchanged. The value is validated against `docker network inspect` at set time, rejects the reserved `bridge` entry, and is surfaced in `dokku logs:report` via `--logs-vector-global-networks`.
This commit is contained in:
@@ -178,6 +178,37 @@ Setting this to an empty string will reset the version to the version currently
|
||||
dokku logs:set --global vector-image
|
||||
```
|
||||
|
||||
#### Attaching Vector to additional Docker networks
|
||||
|
||||
By default, the Vector container runs with `network_mode: bridge` and can only reach app containers that are also on the default bridge network. Apps deployed onto a per-app network or a custom network - typically via `dokku network:set <app> initial-network <name>` - are not reachable from Vector over Docker's internal DNS, so sinks that need to talk to those apps directly (for example, an in-host log search service such as [Logpond](https://github.com/dokku/logpond)) would have to route traffic out through the external proxy.
|
||||
|
||||
The global `vector-networks` property accepts a comma-separated list of Docker networks that Vector should additionally join.
|
||||
|
||||
```shell
|
||||
dokku logs:set --global vector-networks dokku-logs
|
||||
```
|
||||
|
||||
Multiple networks may be specified by separating them with a comma.
|
||||
|
||||
```shell
|
||||
dokku logs:set --global vector-networks dokku-logs,observability
|
||||
```
|
||||
|
||||
Each network must already exist; setting a non-existent network or the reserved `bridge` value will fail. The list can be cleared by setting an empty value, which restores the default `network_mode: bridge` configuration.
|
||||
|
||||
```shell
|
||||
dokku logs:set --global vector-networks
|
||||
```
|
||||
|
||||
Network attachments are reconciled by `docker compose` on every `logs:vector-start`, so after changing the value the Vector container must be cycled.
|
||||
|
||||
```shell
|
||||
dokku logs:vector-stop
|
||||
dokku logs:vector-start
|
||||
```
|
||||
|
||||
Once attached, an app on `dokku-logs` (for example via `dokku network:set node-js-app initial-network dokku-logs`) is reachable from Vector at `<app>.<process>:<port>` over the shared network without round-tripping through the external proxy.
|
||||
|
||||
#### Configuring a log sink
|
||||
|
||||
Vector uses the concept of log "sinks" to send logs to a given endpoint. Log sinks may be configured globally or on a per-app basis by specifying a `vector-sink` in DSN form with the `logs:set` command. Specifying a sink value will reload any running vector container.
|
||||
|
||||
@@ -24,9 +24,10 @@ type vectorSource struct {
|
||||
}
|
||||
|
||||
type vectorTemplateData struct {
|
||||
DokkuLibRoot string
|
||||
DokkuLogsDir string
|
||||
VectorImage string
|
||||
DokkuLibRoot string
|
||||
DokkuLogsDir string
|
||||
VectorImage string
|
||||
VectorNetworks []string
|
||||
}
|
||||
|
||||
const vectorContainerName = "vector-vector-1"
|
||||
@@ -89,9 +90,10 @@ func startVectorContainer(vectorImage string) error {
|
||||
}
|
||||
|
||||
data := vectorTemplateData{
|
||||
DokkuLibRoot: dokkuLibRoot,
|
||||
DokkuLogsDir: dokkuLogsDir,
|
||||
VectorImage: vectorImage,
|
||||
DokkuLibRoot: dokkuLibRoot,
|
||||
DokkuLogsDir: dokkuLogsDir,
|
||||
VectorImage: vectorImage,
|
||||
VectorNetworks: getVectorNetworks(),
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(tmpFile, data); err != nil {
|
||||
@@ -104,6 +106,24 @@ func startVectorContainer(vectorImage string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func getVectorNetworks() []string {
|
||||
value := common.PropertyGet("logs", "--global", "vector-networks")
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
networks := []string{}
|
||||
for _, name := range strings.Split(value, ",") {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
networks = append(networks, name)
|
||||
}
|
||||
|
||||
return networks
|
||||
}
|
||||
|
||||
func getComputedVectorImage() string {
|
||||
return common.PropertyGetDefault("logs", "--global", "vector-image", getDefaultVectorImage())
|
||||
}
|
||||
@@ -151,9 +171,10 @@ func stopVectorContainer() error {
|
||||
}
|
||||
|
||||
data := vectorTemplateData{
|
||||
DokkuLibRoot: dokkuLibRoot,
|
||||
DokkuLogsDir: dokkuLogsDir,
|
||||
VectorImage: getComputedVectorImage(),
|
||||
DokkuLibRoot: dokkuLibRoot,
|
||||
DokkuLogsDir: dokkuLogsDir,
|
||||
VectorImage: getComputedVectorImage(),
|
||||
VectorNetworks: getVectorNetworks(),
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(tmpFile, data); err != nil {
|
||||
|
||||
@@ -35,6 +35,7 @@ var (
|
||||
"app-label-alias": true,
|
||||
"max-size": true,
|
||||
"vector-image": true,
|
||||
"vector-networks": true,
|
||||
"vector-sink": true,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
"--logs-global-max-size": reportGlobalMaxSize,
|
||||
"--logs-global-vector-sink": reportGlobalVectorSink,
|
||||
"--logs-vector-global-image": reportVectorGlobalImage,
|
||||
"--logs-vector-global-networks": reportVectorGlobalNetworks,
|
||||
}
|
||||
} else {
|
||||
flags = map[string]common.ReportFunc{
|
||||
@@ -35,6 +36,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
|
||||
"--logs-app-label-alias": reportAppLabelAlias,
|
||||
"--logs-max-size": reportMaxSize,
|
||||
"--logs-vector-global-image": reportVectorGlobalImage,
|
||||
"--logs-vector-global-networks": reportVectorGlobalNetworks,
|
||||
"--logs-vector-sink": reportVectorSink,
|
||||
}
|
||||
}
|
||||
@@ -84,6 +86,10 @@ func reportVectorGlobalImage(appName string) string {
|
||||
return getComputedVectorImage()
|
||||
}
|
||||
|
||||
func reportVectorGlobalNetworks(appName string) string {
|
||||
return common.PropertyGet("logs", "--global", "vector-networks")
|
||||
}
|
||||
|
||||
func reportGlobalVectorSink(appName string) string {
|
||||
value := common.PropertyGet("logs", "--global", "vector-sink")
|
||||
if value == "" {
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
func validateSetValue(appName string, key string, value string) error {
|
||||
@@ -11,6 +14,10 @@ func validateSetValue(appName string, key string, value string) error {
|
||||
return validateMaxSize(appName, value)
|
||||
}
|
||||
|
||||
if key == "vector-networks" {
|
||||
return validateVectorNetworks(appName, value)
|
||||
}
|
||||
|
||||
if key == "vector-sink" {
|
||||
return validateVectorSink(appName, value)
|
||||
}
|
||||
@@ -56,3 +63,30 @@ func validateVectorSink(appName string, value string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateVectorNetworks(appName string, value string) error {
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, name := range strings.Split(value, ",") {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return errors.New("Invalid vector-networks value, empty entry in comma-separated list")
|
||||
}
|
||||
|
||||
if name == "bridge" {
|
||||
return errors.New("Invalid vector-networks value, \"bridge\" is attached by default and must not be listed")
|
||||
}
|
||||
|
||||
result, err := common.CallExecCommand(common.ExecCommandInput{
|
||||
Command: common.DockerBin(),
|
||||
Args: []string{"network", "inspect", name},
|
||||
})
|
||||
if err != nil || result.ExitCode != 0 {
|
||||
return fmt.Errorf("Network %q does not exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,15 @@ services:
|
||||
org.label-schema.schema-version: "1.0"
|
||||
org.label-schema.vendor: dokku
|
||||
|
||||
{{- if $.VectorNetworks }}
|
||||
networks:
|
||||
bridge: {}
|
||||
{{- range $name := $.VectorNetworks }}
|
||||
{{ $name }}: {}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
network_mode: bridge
|
||||
{{- end }}
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -21,3 +29,15 @@ services:
|
||||
- "{{ $.DokkuLibRoot }}/data/logs:/etc/vector"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "{{ $.DokkuLogsDir }}/apps:/var/log/dokku/apps"
|
||||
{{- if $.VectorNetworks }}
|
||||
|
||||
networks:
|
||||
bridge:
|
||||
name: bridge
|
||||
external: true
|
||||
{{- range $name := $.VectorNetworks }}
|
||||
{{ $name }}:
|
||||
name: {{ $name }}
|
||||
external: true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -9,6 +9,8 @@ setup() {
|
||||
|
||||
teardown() {
|
||||
destroy_app
|
||||
docker network rm test-vector-net-a >/dev/null || true
|
||||
docker network rm test-vector-net-b >/dev/null || true
|
||||
global_teardown
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ teardown() {
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
assert_output_contains "$TEST_APP logs information" 0
|
||||
assert_output_contains "Invalid flag passed, valid flags: --logs-app-label-alias, --logs-computed-app-label-alias, --logs-computed-max-size, --logs-global-app-label-alias, --logs-global-max-size, --logs-global-vector-sink, --logs-max-size, --logs-vector-global-image, --logs-vector-sink"
|
||||
assert_output_contains "Invalid flag passed, valid flags: --logs-app-label-alias, --logs-computed-app-label-alias, --logs-computed-max-size, --logs-global-app-label-alias, --logs-global-max-size, --logs-global-vector-sink, --logs-max-size, --logs-vector-global-image, --logs-vector-global-networks, --logs-vector-sink"
|
||||
|
||||
run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1"
|
||||
echo "output: $output"
|
||||
@@ -458,6 +460,143 @@ teardown() {
|
||||
assert_output "10m"
|
||||
}
|
||||
|
||||
@test "(logs) logs:set --global vector-networks" {
|
||||
docker network create test-vector-net-a >/dev/null
|
||||
docker network create test-vector-net-b >/dev/null
|
||||
|
||||
run create_app
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku logs:set $TEST_APP vector-networks test-vector-net-a 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks does-not-exist 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
assert_output_contains "Network \"does-not-exist\" does not exist"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks bridge 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
assert_output_contains "\"bridge\" is attached by default and must not be listed"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks 'test-vector-net-a,' 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
assert_output_contains "empty entry in comma-separated list"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks test-vector-net-a 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "Setting vector-networks"
|
||||
|
||||
run /bin/bash -c "dokku logs:report --global --logs-vector-global-networks 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "test-vector-net-a"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks test-vector-net-a,test-vector-net-b 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "Setting vector-networks"
|
||||
|
||||
run /bin/bash -c "dokku logs:report --global --logs-vector-global-networks 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "test-vector-net-a,test-vector-net-b"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "Unsetting vector-networks"
|
||||
|
||||
run /bin/bash -c "dokku logs:report --global --logs-vector-global-networks 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_not_exists
|
||||
}
|
||||
|
||||
@test "(logs) logs:vector-start attaches configured networks" {
|
||||
docker network create test-vector-net-a >/dev/null
|
||||
docker network create test-vector-net-b >/dev/null
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks test-vector-net-a,test-vector-net-b 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-start 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "Vector container is running"
|
||||
|
||||
run /bin/bash -c "sudo docker inspect --format='{{range \$k, \$v := .NetworkSettings.Networks}}{{\$k}} {{end}}' vector-vector-1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "bridge"
|
||||
assert_output_contains "test-vector-net-a"
|
||||
assert_output_contains "test-vector-net-b"
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-stop 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-start 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "Vector container is running"
|
||||
|
||||
run /bin/bash -c "sudo docker inspect --format='{{range \$k, \$v := .NetworkSettings.Networks}}{{\$k}} {{end}}' vector-vector-1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "test-vector-net-a"
|
||||
assert_output_contains "test-vector-net-b"
|
||||
|
||||
run /bin/bash -c "dokku logs:set --global vector-networks 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-stop 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-start 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "sudo docker inspect --format='{{range \$k, \$v := .NetworkSettings.Networks}}{{\$k}} {{end}}' vector-vector-1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "bridge"
|
||||
|
||||
run /bin/bash -c "dokku logs:vector-stop 2>&1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "(logs) logs:set app-label-alias" {
|
||||
run create_app
|
||||
echo "output: $output"
|
||||
|
||||
Reference in New Issue
Block a user