Skip to content

Commit c988d06

Browse files
api/vlagent: support logs collection
This commit adds Kubernetes logs collection with vlagent. It transfers vlagent into daemonset deployment type. Adds needed RBAC permissions and volume mounts. Fixes #1501
1 parent c4f8482 commit c988d06

File tree

19 files changed

+795
-57
lines changed

19 files changed

+795
-57
lines changed

api/operator/v1/vlagent_types.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ type VLAgentSpec struct {
7373
// +optional
7474
SyslogSpec *SyslogServerSpec `json:"syslogSpec,omitempty"`
7575

76+
// K8sCollector configures VLAgent logs collection from K8s pods
77+
K8sCollector VLAgentK8sCollector `json:"k8sCollector,omitempty"`
78+
7679
// ServiceAccountName is the name of the ServiceAccount to use to run the pods
7780
// +optional
7881
ServiceAccountName string `json:"serviceAccountName,omitempty"`
@@ -82,6 +85,40 @@ type VLAgentSpec struct {
8285
vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"`
8386
}
8487

88+
type VLAgentK8sCollector struct {
89+
// Enabled switches VLAgent to log collection mode.
90+
// Note, for this purpose operator uses DaemonSet, while by default VLAgent uses StatefulSet.
91+
// Switching this option will drop all persisted data.
92+
Enabled bool `json:"enabled,omitempty"`
93+
94+
// LogsPath configures root for logs path
95+
// By default VLAgent collects logs from /var/log/containers
96+
LogsPath string `json:"logsPath,omitempty"`
97+
98+
// CheckpointsPath configures path to file where logs checkpoints are stored.
99+
// By default it's stored at host's /var/lib/vlagent_checkpoints/checkpoints.json.
100+
CheckpointsPath string `json:"checkpointsPath,omitempty"`
101+
102+
// TenantID defines default tenant ID to use for logs collected from pods in format: <accountID>:<projectID>
103+
TenantID string `json:"tenantID,omitempty"`
104+
105+
// IgnoreFields defines fields to ignore across logs ingested from Kubernetes
106+
IgnoreFields []string `json:"ignoreFields,omitempty"`
107+
108+
// DecolorizeFields defines fields to remove ANSI color codes across logs ingested from Kubernetes
109+
DecolorizeFields []string `json:"decolorizeFields,omitempty"`
110+
111+
// MsgField defines fields that may contain the _msg field
112+
MsgFields []string `json:"msgFields,omitempty"`
113+
114+
// TimeFields defines fields that may contain the _time field
115+
TimeFields []string `json:"timeFields,omitempty"`
116+
117+
// ExtraFields defines extra fields as JSON string which should be added to each collected log line
118+
// Example: '{"env":"dev","cluster":"staging"}'
119+
ExtraFields string `json:"extraFields,omitempty"`
120+
}
121+
85122
// SetLastSpec implements objectWithLastAppliedState interface
86123
func (cr *VLAgent) SetLastSpec(prevSpec VLAgentSpec) {
87124
cr.ParsedLastAppliedSpec = &prevSpec
@@ -98,6 +135,14 @@ func (cr *VLAgent) Validate() error {
98135
if len(cr.Spec.RemoteWrite) == 0 {
99136
return fmt.Errorf("spec.remoteWrite cannot be empty array, provide at least one remoteWrite")
100137
}
138+
if cr.Spec.K8sCollector.Enabled {
139+
if len(cr.Spec.K8sCollector.ExtraFields) > 0 {
140+
var raw map[string]any
141+
if err := json.Unmarshal([]byte(cr.Spec.K8sCollector.ExtraFields), &raw); err != nil {
142+
return fmt.Errorf("spec.k8sCollector.extraFields is not a valid JSON: %w", err)
143+
}
144+
}
145+
}
101146
for idx, rw := range cr.Spec.RemoteWrite {
102147
if rw.URL == "" {
103148
return fmt.Errorf("remoteWrite.url cannot be empty at idx: %d", idx)
@@ -298,6 +343,11 @@ func (cr *VLAgent) FinalAnnotations() map[string]string {
298343
return v
299344
}
300345

346+
// AsCRDOwner implements interface
347+
func (*VLAgent) AsCRDOwner() *metav1.OwnerReference {
348+
return vmv1beta1.GetCRDAsOwner(vmv1beta1.VLAgentCRD)
349+
}
350+
301351
// SelectorLabels returns selector labels for querying any vlagent related resources
302352
func (cr *VLAgent) SelectorLabels() map[string]string {
303353
return map[string]string{
@@ -402,6 +452,10 @@ func (cr *VLAgent) ProbePort() string {
402452
return cr.Spec.Port
403453
}
404454

455+
func (cr *VLAgent) GetClusterRoleName() string {
456+
return fmt.Sprintf("monitoring:%s:vlagent-%s", cr.Namespace, cr.Name)
457+
}
458+
405459
// ProbeNeedLiveness implements build.probeCRD interface
406460
func (*VLAgent) ProbeNeedLiveness() bool {
407461
return true

api/operator/v1/zz_generated.deepcopy.go

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/operator/v1beta1/owner.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ import (
1313
type CRDName int
1414

1515
const (
16-
Agent CRDName = iota
16+
VMAgentCRD CRDName = iota
17+
VLAgentCRD
1718
)
1819

1920
func (c CRDName) String() string {
20-
return []string{"vmagents.operator.victoriametrics.com", "vmalerts.operator.victoriametrics.com", "vmsingles.operator.victoriametrics.com", "vmclusters.operator.victoriametrics.com", "vmauths.operator.victoriametrics.com", "vmalertmanagers.operator.victoriametrics.com"}[c]
21+
return []string{
22+
"vmagents.operator.victoriametrics.com",
23+
"vlagents.operator.victoriametrics.com",
24+
}[c]
2125
}
2226

2327
type crdInfo struct {
@@ -39,7 +43,9 @@ func Init(ctx context.Context, rclient client.Client) error {
3943
var n CRDName
4044
switch item.Name {
4145
case "vmagents.operator.victoriametrics.com":
42-
n = Agent
46+
n = VMAgentCRD
47+
case "vlagents.operator.victoriametrics.com":
48+
n = VLAgentCRD
4349
default:
4450
continue
4551
}
@@ -54,17 +60,15 @@ func Init(ctx context.Context, rclient client.Client) error {
5460

5561
// GetCRDAsOwner returns owner references with global CustomResourceDefinition object as owner
5662
// useful for non-namespaced objects, like clusterRole
57-
func GetCRDAsOwner(name CRDName) []metav1.OwnerReference {
63+
func GetCRDAsOwner(name CRDName) *metav1.OwnerReference {
5864
crdData := crdCache[name]
5965
if crdData == nil {
6066
return nil
6167
}
62-
return []metav1.OwnerReference{
63-
{
64-
Name: name.String(),
65-
UID: crdData.uuid,
66-
Kind: "CustomResourceDefinition",
67-
APIVersion: crdData.apiVersion,
68-
},
68+
return &metav1.OwnerReference{
69+
Name: name.String(),
70+
UID: crdData.uuid,
71+
Kind: "CustomResourceDefinition",
72+
APIVersion: crdData.apiVersion,
6973
}
7074
}

api/operator/v1beta1/vmagent_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,8 +787,8 @@ func (cr *VMAgent) AsURL() string {
787787
}
788788

789789
// AsCRDOwner implements interface
790-
func (*VMAgent) AsCRDOwner() []metav1.OwnerReference {
791-
return GetCRDAsOwner(Agent)
790+
func (*VMAgent) AsCRDOwner() *metav1.OwnerReference {
791+
return GetCRDAsOwner(VMAgentCRD)
792792
}
793793

794794
func (cr *VMAgent) Probe() *EmbeddedProbes {

config/crd/overlay/crd.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,60 @@ spec:
736736
type: object
737737
x-kubernetes-preserve-unknown-fields: true
738738
type: array
739+
k8sCollector:
740+
description: K8sCollector configures VLAgent logs collection from
741+
K8s pods
742+
properties:
743+
checkpointsPath:
744+
description: |-
745+
CheckpointsPath configures path to file where logs checkpoints are stored.
746+
By default it's stored at host's /var/lib/vlagent_checkpoints/checkpoints.json.
747+
type: string
748+
decolorizeFields:
749+
description: DecolorizeFields defines fields to remove ANSI color
750+
codes across logs ingested from Kubernetes
751+
items:
752+
type: string
753+
type: array
754+
enabled:
755+
description: |-
756+
Enabled switches VLAgent to log collection mode.
757+
Note, for this purpose operator uses DaemonSet, while by default VLAgent uses StatefulSet.
758+
Switching this option will drop all persisted data.
759+
type: boolean
760+
extraFields:
761+
description: |-
762+
ExtraFields defines extra fields as JSON string which should be added to each collected log line
763+
Example: '{"env":"dev","cluster":"staging"}'
764+
type: string
765+
ignoreFields:
766+
description: IgnoreFields defines fields to ignore across logs
767+
ingested from Kubernetes
768+
items:
769+
type: string
770+
type: array
771+
logsPath:
772+
description: |-
773+
LogsPath configures root for logs path
774+
By default VLAgent collects logs from /var/log/containers
775+
type: string
776+
msgFields:
777+
description: MsgField defines fields that may contain the _msg
778+
field
779+
items:
780+
type: string
781+
type: array
782+
tenantID:
783+
description: 'TenantID defines default tenant ID to use for logs
784+
collected from pods in format: <accountID>:<projectID>'
785+
type: string
786+
timeFields:
787+
description: TimeFields defines fields that may contain the _time
788+
field
789+
items:
790+
type: string
791+
type: array
792+
type: object
739793
livenessProbe:
740794
description: LivenessProbe that will be added CRD pod
741795
type: object
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apiVersion: operator.victoriametrics.com/v1
2+
kind: VLAgent
3+
metadata:
4+
name: example
5+
spec:
6+
resources:
7+
requests:
8+
cpu: "50m"
9+
memory: "350Mi"
10+
limits:
11+
cpu: "500m"
12+
memory: "850Mi"
13+
k8sCollector:
14+
enabled: true
15+
extraFields: '{"env":"dev","cluster":"staging"}'
16+
msgFields:
17+
- msg
18+
- message
19+
- log.msg
20+
timeFields:
21+
- time
22+
- ts
23+
- timestamp
24+
remoteWrite:
25+
- url: "http://vlsingle-example-0.default.svc:9428/internal/insert"
26+
remoteWriteSettings:
27+
tmpDataPath: /opt/queue
28+
volumes:
29+
- name: persistent-queue
30+
hostPath:
31+
path: /opt/mnt/vmagent-queue
32+
volumeMounts:
33+
- mountPath: /opt/queue
34+
name: persistent-queue

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ aliases:
1414
## tip
1515

1616
* FEATURE: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): support `namespace` parameter in `attach_metadata` section for all scrape configurations. See [#1654](https://github.com/VictoriaMetrics/operator/issues/1654).
17+
* FEATURE: [vlagent](https://docs.victoriametrics.com/operator/resources/vlagent): support logs collection. See [#1501](https://github.com/VictoriaMetrics/operator/issues/1501).
1718

1819
## [v0.66.1](https://github.com/VictoriaMetrics/operator/releases/tag/v0.66.1)
1920

docs/api.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,27 @@ VLAgent - is a tiny but brave agent, which helps you collect logs from various s
170170
| spec<a href="#vlagent-spec" id="vlagent-spec">#</a><br/>_[VLAgentSpec](#vlagentspec)_ | _(Required)_<br/> |
171171

172172

173+
#### VLAgentK8sCollector
174+
175+
176+
177+
178+
179+
Appears in: [VLAgentSpec](#vlagentspec)
180+
181+
| Field | Description |
182+
| --- | --- |
183+
| checkpointsPath<a href="#vlagentk8scollector-checkpointspath" id="vlagentk8scollector-checkpointspath">#</a><br/>_string_ | _(Required)_<br/>CheckpointsPath configures path to file where logs checkpoints are stored.<br />By default it's stored at host's /var/lib/vlagent_checkpoints/checkpoints.json. |
184+
| decolorizeFields<a href="#vlagentk8scollector-decolorizefields" id="vlagentk8scollector-decolorizefields">#</a><br/>_string array_ | _(Required)_<br/>DecolorizeFields defines fields to remove ANSI color codes across logs ingested from Kubernetes |
185+
| enabled<a href="#vlagentk8scollector-enabled" id="vlagentk8scollector-enabled">#</a><br/>_boolean_ | _(Required)_<br/>Enabled switches VLAgent to log collection mode.<br />Note, for this purpose operator uses DaemonSet, while by default VLAgent uses StatefulSet.<br />Switching this option will drop all persisted data. |
186+
| extraFields<a href="#vlagentk8scollector-extrafields" id="vlagentk8scollector-extrafields">#</a><br/>_string_ | _(Required)_<br/>ExtraFields defines extra fields as JSON string which should be added to each collected log line<br />Example: '\{"env":"dev","cluster":"staging"\}' |
187+
| ignoreFields<a href="#vlagentk8scollector-ignorefields" id="vlagentk8scollector-ignorefields">#</a><br/>_string array_ | _(Required)_<br/>IgnoreFields defines fields to ignore across logs ingested from Kubernetes |
188+
| logsPath<a href="#vlagentk8scollector-logspath" id="vlagentk8scollector-logspath">#</a><br/>_string_ | _(Required)_<br/>LogsPath configures root for logs path<br />By default VLAgent collects logs from /var/log/containers |
189+
| msgFields<a href="#vlagentk8scollector-msgfields" id="vlagentk8scollector-msgfields">#</a><br/>_string array_ | _(Required)_<br/>MsgField defines fields that may contain the _msg field |
190+
| tenantID<a href="#vlagentk8scollector-tenantid" id="vlagentk8scollector-tenantid">#</a><br/>_string_ | _(Required)_<br/>TenantID defines default tenant ID to use for logs collected from pods in format: <accountID>:<projectID> |
191+
| timeFields<a href="#vlagentk8scollector-timefields" id="vlagentk8scollector-timefields">#</a><br/>_string array_ | _(Required)_<br/>TimeFields defines fields that may contain the _time field |
192+
193+
173194
#### VLAgentRemoteWriteSettings
174195

175196

@@ -236,6 +257,7 @@ Appears in: [VLAgent](#vlagent)
236257
| image<a href="#vlagentspec-image" id="vlagentspec-image">#</a><br/>_[Image](#image)_ | _(Optional)_<br/>Image - docker image settings<br />if no specified operator uses default version from operator config |
237258
| imagePullSecrets<a href="#vlagentspec-imagepullsecrets" id="vlagentspec-imagepullsecrets">#</a><br/>_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core) array_ | _(Optional)_<br/>ImagePullSecrets An optional list of references to secrets in the same namespace<br />to use for pulling images from registries<br />see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod |
238259
| initContainers<a href="#vlagentspec-initcontainers" id="vlagentspec-initcontainers">#</a><br/>_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#container-v1-core) array_ | _(Optional)_<br/>InitContainers allows adding initContainers to the pod definition.<br />Any errors during the execution of an initContainer will lead to a restart of the Pod.<br />More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ |
260+
| k8sCollector<a href="#vlagentspec-k8scollector" id="vlagentspec-k8scollector">#</a><br/>_[VLAgentK8sCollector](#vlagentk8scollector)_ | _(Required)_<br/>K8sCollector configures VLAgent logs collection from K8s pods |
239261
| logFormat<a href="#vlagentspec-logformat" id="vlagentspec-logformat">#</a><br/>_string_ | _(Optional)_<br/>LogFormat for VLAgent to be configured with. |
240262
| logLevel<a href="#vlagentspec-loglevel" id="vlagentspec-loglevel">#</a><br/>_string_ | _(Optional)_<br/>LogLevel for VLAgent to be configured with.<br />INFO, WARN, ERROR, FATAL, PANIC |
241263
| managedMetadata<a href="#vlagentspec-managedmetadata" id="vlagentspec-managedmetadata">#</a><br/>_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_<br/>ManagedMetadata defines metadata that will be added to the all objects<br />created by operator for the given CustomResource |

docs/env.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
| Environment variables |
22
| --- |
33
| VM_METRICS_VERSION: `v1.131.0` <a href="#variables-vm-metrics-version" id="variables-vm-metrics-version">#</a> |
4-
| VM_LOGS_VERSION: `v1.36.1` <a href="#variables-vm-logs-version" id="variables-vm-logs-version">#</a> |
4+
| VM_LOGS_VERSION: `v1.40.0` <a href="#variables-vm-logs-version" id="variables-vm-logs-version">#</a> |
55
| VM_ANOMALY_VERSION: `v1.26.1` <a href="#variables-vm-anomaly-version" id="variables-vm-anomaly-version">#</a> |
66
| VM_TRACES_VERSION: `v0.5.0` <a href="#variables-vm-traces-version" id="variables-vm-traces-version">#</a> |
77
| VM_OPERATOR_VERSION: `v0.65.0` <a href="#variables-vm-operator-version" id="variables-vm-operator-version">#</a> |

docs/resources/vlagent.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ then `VLAgent` pods will be created without resource requests and limits.
136136

137137
Also, you can specify requests without limits - in this case default values for limits will not be used.
138138

139+
## K8s logs collection
140+
141+
VLAgent supports collecting logs from K8s pods. To enable it it's required to set `spec.k8sCollector.enabled: true`.
142+
With this setting operator:
143+
- switches VLAgent to DaemonSet mode
144+
- mounts host paths, where k8s logs are stored
145+
- adds RBAC to be able to get pods, namespaces and nodes information using K8s API
146+
139147
## Storage management
140148

141149
In case of errors sending logs to `remoteWrite`,

0 commit comments

Comments
 (0)