diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9246e48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +k8s/ +test/ +Makefile diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..542ddd2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +test/test.sh -text eol=lf +start.sh -text eol=lf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12a74e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.k3d* +.helm-setup diff --git a/Dockerfile b/Dockerfile index ebe71b1..d856974 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,14 @@ -FROM centos:7 +FROM alpine:3.13.5 +ENV RSYSLOG_PORT=514 RSYSLOG_PROTOCOL=udp -RUN curl -s -L -o /etc/yum.repos.d/rsyslog.repo http://rpms.adiscon.com/v8-stable/rsyslog.repo -RUN yum -y install rsyslog gettext && yum clean all +RUN apk add --no-cache rsyslog gettext COPY rsyslog.conf.template /etc/rsyslog.conf.template COPY start.sh /start.sh +# So we can make the root file system read-only +RUN ln -sf /var/lib/rsyslog/rsyslog.conf /etc/rsyslog.conf + RUN chmod +x /start.sh CMD /start.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..218fd8f --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +IMAGE=localhost:5000/syslog-agent +DEFAULT_METRICS=kube-state-metrics +LOCAL_PORT=8081 + +## make build -- build the image +build: .k3d-image + +.k3d-image: Dockerfile start.sh rsyslog.conf.template + docker build -t $(IMAGE) . + @touch $@ + +## make push -- push the image to the local k3d registry +push: .k3d-registry .k3d-image + docker push $(IMAGE) + +## make test -- install all components and run a simple test by cURLing the NGINX container and looking for the access record +test: build up k8s ready + bash test/test.sh + +## run a Snyk scan on the image +scan: + docker scan $(IMAGE) + +## make k8s or make deploy -- install the components into the cluster +k8s deploy: push .k3d + kubectl apply -f k8s -f test + +## make up -- create and configure the testing cluster +k3d-up k3d cluster cluster-up up: .k3d + +## make down -- delete the testing cluster +k3d-down cluster-down down: + k3d cluster delete syslog + k3d registry delete local + rm -f .k3d-* + +## make ready -- wait for all components to be ready +ready: + @echo -n "Waiting for pod count..." + @while [ "$$(kubectl get pods -A | wc -l)" -lt 4 ] ; do sleep 2; echo -n .; done || true + @echo "DONE" + @echo -n "Waiting for pods ready..." + @while kubectl get pods -A | grep -q -E 'Pending|ContainerCreating'; do sleep 2; echo -n . ; done || true + @echo "READY" + +.k3d: .k3d-cluster .k3d-kube-state-metrics + +.k3d-cluster: .k3d-registry + k3d cluster create syslog -p "$(LOCAL_PORT):80@loadbalancer" --registry-use local +# @echo "Waiting for cluster to initialize" +# @while [ -n "kubectl get -n kube-system pods | grep ContainerCreating" ]; do echo -n "."; sleep 3; done +# @echo DONE + touch $@ + +.k3d-registry: + k3d registry create local -p 5000 + touch $@ + + +# Install the selected metrics server package +metrics: .k3d-$(DEFAULT_METRICS) + +.k3d-kube-state-metrics: .helm-setup + helm install -n kube-system metrics prometheus-community/kube-state-metrics + touch $@ + +.k3d-prometheus: + helm install -n kube-system prometheus prometheus-community/kube-prometheus-stack + touch $@ .k3d-kube-state-metrics + +# Run a local helm setup +helm: .helm-setup + +.helm-setup: + helm repo add prometheus-community https://prometheus-community.github.io/helm-charts + helm repo update + touch $@ + +clean: + -make -f $(lastword $(MAKEFILE_LIST)) down >/dev/null 2>/dev/null + rm -f .helm-setup + +real-clean: clean + docker image rm $(IMAGE) + +help: + awk '/^##/{print}' $(lastword $(MAKEFILE_LIST)) diff --git a/README.md b/README.md index c09beef..526ec62 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,33 @@ # Kubernetes Container Log Syslog Forwarder -This container is designed to run as a DaemonSet and forwards pod logs to a syslog -listener for all pods running on a node. Log forwarding is implemented with -[RSYSLOG](http://www.rsyslog.com/) and uses [omfwd](http://www.rsyslog.com/doc/v8-stable/configuration/modules/omfwd.html) module. +This container is designed to run as a DaemonSet and forwards pod logs to a syslog listener for all +pods running on a node. Log forwarding is implemented with +[RSYSLOG](http://www.rsyslog.com/) and +uses [omfwd](http://www.rsyslog.com/doc/v8-stable/configuration/modules/omfwd.html) module. ## Configuration Options + Configuration can be done with environment variables: * **RSYSLOG_TARGET** - Remote syslog listener * **RSYSLOG_PORT** - Remote syslog listener port * **RSYSLOG_PROTOCOL** - Remote syslog listener protocol (udp/tcp) -## Example DaemonSet -DaemonSet example requires a privileged SCC if using k8s v1.5+ or OpenShift and -probably needs rsyslog already installed in order to use /var/lib/rsyslog on the -host as the place for the file state directory +## DaemonSet + +A working example of a deployment daemonset can be found in the [k8s](./k8s) directory, along with +an example ConfigMap. You will need to adjust the configmap to suit your system. + +## Hacking + +The `Makefile` included here is set up to assist development *and* testing of the system, +using [`k3d`](https://k3d.io/). It will fully set up a test cluster, install all necessary +components, and run a simple test. + +Use `make test` to perform all of these + +**NOTE:** there is currently an occasional timing issue where the test will fail right after the +cluster is up. If this happens, wait 30 seconds and attempt `make test` again. + +Use `make help` to display information about available targets + diff --git a/k8s/agent.yaml b/k8s/agent.yaml new file mode 100644 index 0000000..0ac19da --- /dev/null +++ b/k8s/agent.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: syslog-agent + labels: + app: syslog-agent +spec: + selector: + matchLabels: + app: syslog-agent + template: + metadata: + labels: + app: syslog-agent + spec: + containers: + - name: syslog-agent + image: k3d-local:5000/syslog-agent + envFrom: + - configMapRef: + name: syslog-agent-config + volumeMounts: + - mountPath: /var/log + name: logs + readOnly: true + - mountPath: /var/lib/rsyslog + name: work + securityContext: + readOnlyRootFilesystem: true + volumes: + - name: logs + hostPath: + path: /var/log + - name: work + emptyDir: {} + + + + diff --git a/k8s/config.yaml b/k8s/config.yaml new file mode 100644 index 0000000..3a015a2 --- /dev/null +++ b/k8s/config.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: syslog-agent-config +data: + RSYSLOG_PORT: "514" + RSYSLOG_PROTOCOL: UDP + RSYSLOG_TARGET: rsyslog diff --git a/start.sh b/start.sh index 3d8d3d0..f201151 100644 --- a/start.sh +++ b/start.sh @@ -1,7 +1,5 @@ -#!/bin/bash +#!/bin/sh -rm -f /etc/rsyslog.conf +envsubst < /etc/rsyslog.conf.template > /var/lib/rsyslog/rsyslog.conf -envsubst < /etc/rsyslog.conf.template > /etc/rsyslog.conf - -exec /sbin/rsyslogd -n +exec /usr/sbin/rsyslogd -n diff --git a/test/nginx.yaml b/test/nginx.yaml new file mode 100644 index 0000000..d2d075c --- /dev/null +++ b/test/nginx.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app: nginx + name: nginx +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + strategy: {} + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 80 + resources: {} +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - name: 80-80 + port: 80 + protocol: TCP + targetPort: 80 + selector: + app: nginx + type: ClusterIP +--- +# apiVersion: networking.k8s.io/v1beta1 # for k3s < v1.19 +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: nginx + annotations: + ingress.kubernetes.io/ssl-redirect: "false" +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: nginx + port: + number: 80 diff --git a/test/rsyslog.yaml b/test/rsyslog.yaml new file mode 100644 index 0000000..ea63c12 --- /dev/null +++ b/test/rsyslog.yaml @@ -0,0 +1,59 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: rsyslog + labels: + app: rsyslog +spec: + selector: + app: rsyslog + ports: + - protocol: UDP + port: 514 + targetPort: 514 + name: syslog-udp + - protocol: TCP + port: 514 + targetPort: 514 + name: syslog-tcp +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: rsyslog + name: rsyslog +spec: + replicas: 1 + selector: + matchLabels: + app: rsyslog + template: + metadata: + labels: + app: rsyslog + spec: + containers: + - image: voxxit/rsyslog + name: rsyslog + ports: + - containerPort: 514 + protocol: UDP + name: syslog-udp + - containerPort: 514 + protocol: TCP + name: syslog-tcp + env: + - name: TZ + value: America/New_York + livenessProbe: + tcpSocket: + port: 514 + readinessProbe: + exec: + command: + - test + - -s + - /var/log/messages + diff --git a/test/test.sh b/test/test.sh new file mode 100644 index 0000000..c1271cf --- /dev/null +++ b/test/test.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# purpose: test the k8s cluster to see if a log message gets through + +DATE=$(date +%y%m%d%H%M%S) +curl -s "http://localhost:8081/testing/${DATE}" > /dev/null + +pod=$(kubectl get pods -l app=rsyslog | awk '!/NAME/{print $1}') +if kubectl exec "$pod" -- grep -q "testing/${DATE}" /var/log/messages +then + echo "PASS" +else + echo "FAIL" + exit 1 +fi