diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e6e9dde9430b89b4e9117a60ce9ee1a23c87b913..07c6893d5b8e215aaa3a34d2fb07055ec76a30e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,8 +33,6 @@ variables: CACHE: auto build-push: - tags: - - staging extends: .kaniko-build before_script: - > diff --git a/.gitlab/lint.yaml b/.gitlab/lint.yaml index 1fdaaaf300b10ff597f4fc358fdd23ce4fca0c1e..998269c005e1a22cb2f3a920d75b41d7ceccb0a1 100644 --- a/.gitlab/lint.yaml +++ b/.gitlab/lint.yaml @@ -1,4 +1,7 @@ --- +include: + - remote: https://gitlab.com/ethz-hpc/pipelines/-/raw/main/scripts/chart/kube-linter.yaml + yamllint: stage: lint image: registry.gitlab.com/pipeline-components/yamllint:0.31.2@sha256:82f082414bad17ec04f4d271262a6dcaf64883bb4f1ce73923380125af8b94ee @@ -21,7 +24,7 @@ markdownlint: stage: lint image: registry.gitlab.com/pipeline-components/markdownlint:0.13.3@sha256:05e98b078e72c637e90a15094d012ed63108d101a941c2526833717ae50eb802 script: - - mdl --style all --warnings . + - mdl --warnings . rules: - if: >- $CI_PIPELINE_SOURCE !~ /^(?:push|merge_request_event|schedule|pipeline)$/ && @@ -34,6 +37,8 @@ markdownlint: - "*.MD" - "**/*.md" - "**/*.MD" + - ".mdlrc" + - ".markdown.style.rb" hadolint: stage: lint @@ -50,3 +55,27 @@ hadolint: - Dockerfile* - "**/Dockerfile*" - .gitlab-ci.yaml + - .gitlab/lint.yaml + +iperf-kube-lint: + extends: .kube-linter + image: registry.gitlab.com/ethz-hpc/pipelines/kube-linter:latest + stage: lint + script: + - set -eo pipefail + - cd charts/iperf + - >- + helm template iperf-server . | + awk -v o=/dev/stderr '/^(apiVersion:|---)/ { o="/dev/stdout" } { print >o }' | + tee /dev/stderr | + kube-linter lint --fail-if-no-objects-found --fail-on-invalid-resource - + rules: + - if: >- + $CI_PIPELINE_SOURCE !~ /^(?:push|merge_request_event|schedule|pipeline)$/ && + $RENOVATE == "true" + when: never + - changes: + paths: + - .gitlab-ci.yaml + - .gitlab/lint.yaml + - charts/**/*.yaml diff --git a/.markdown.style.rb b/.markdown.style.rb new file mode 100644 index 0000000000000000000000000000000000000000..12c1d99b743ade6cf1b61f575bf147bf46bb45f0 --- /dev/null +++ b/.markdown.style.rb @@ -0,0 +1,2 @@ +all +rule 'MD013', :line_length => 150, :code_block_line_length => 150 diff --git a/.mdlrc b/.mdlrc new file mode 100644 index 0000000000000000000000000000000000000000..14d936c396099028ff6be84b8ccbb8abad90c1f5 --- /dev/null +++ b/.mdlrc @@ -0,0 +1 @@ +style "./.markdown.style.rb" diff --git a/.yamllint b/.yamllint index 7c7ea0b2ce4a2b934a9babf851ff2935b2250931..0c0dc62228358511aaa69055cb6d7c6741bf0489 100644 --- a/.yamllint +++ b/.yamllint @@ -12,4 +12,9 @@ rules: max: 150 # due to digest pinning level: warning +ignore: + - charts/iperf/templates/deployment.yaml + - charts/iperf/templates/networkpolicy.yaml + - charts/iperf/templates/service.yaml + # ex: ft=yaml et ts=2 sw=2 : diff --git a/Dockerfile b/Dockerfile index 046ab7cdd958cd2ca382cc484b98bd3018f46fe0..ad2503248d925a2e7d8e634811dee4b09cfdda10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,18 @@ ARG CURL_VERSION=8.5.0-2ubuntu10.1 ARG NMAP_VERSION=7.94+git20230807.3be01efb1+dfsg-3build2 # renovate: datasource=repology depName=ubuntu_24_04/tini versioning=semver-coerced ARG TINI_VERSION=0.19.0-1 +# renovate: datasource=repology depName=ubuntu_24_04/traceroute versioning=semver-coerced +ARG TRACEROUTE_VERSION=1:2.1.5-1 +# renovate: datasource=repology depName=ubuntu_24_04/inetutils-ping versioning=semver-coerced +ARG INETUTILS_PING_VERSION=2:2.5-3ubuntu4 +# renovate: datasource=repology depName=ubuntu_24_04/iftop versioning=semver-coerced +ARG IFTOP=1.0~pre4-9build2 +# renovate: datasource=repology depName=ubuntu_24_04/tcpdump versioning=semver-coerced +ARG TCPDUMP_VERSION=4.99.4-3ubuntu4 +# renovate: datasource=repology depName=ubuntu_24_04/mtr-tiny versioning=semver-coerced +ARG MTR_TINY_VERSION=0.95-1.1build2 +# renovate: datasource=repology depName=ubuntu_24_04/iputils-tracepath versioning=semver-coerced +ARG IPUTILS_TRACEPATH=3:20240117-1build1 # renovate: datasource=docker ARG UBUNTU_IMAGE_TAG=24.04 @@ -25,12 +37,17 @@ ARG REVISION="" # hadolint ignore=DL3015 RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y \ - iproute2="${IPROUTE2_VERSION}" \ - bind9-utils="${BIND9_UTILS_VERSION}" \ bind9-dnsutils="${BIND9_UTILS_VERSION}" \ - tini="${TINI_VERSION}" \ + bind9-utils="${BIND9_UTILS_VERSION}" \ curl="${CURL_VERSION}" \ + inetutils-ping="${INETUTILS_PING_VERSION}" \ + iproute2="${IPROUTE2_VERSION}" \ + iputils-tracepath="${IPUTILS_TRACEPATH}" \ + mtr-tiny="${MTR_TINY_VERSION}" \ nmap="${NMAP_VERSION}" \ + tcpdump="${TCPDUMP_VERSION}" \ + tini="${TINI_VERSION}" \ + traceroute="${TRACEROUTE_VERSION}" \ && if [ "${WITH_IPERF:-0}" = "1" ]; then apt-get install -y iperf3="${IPERF3_VERSION}"; fi \ && rm -rf /var/cache/apt/* diff --git a/README.md b/README.md index deb3c0f41bc3a0b65b2e406b25969708c5452d28..8db58b3bc704ccbfd09791c116af649dff188ffc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,71 @@ # Network utilities container image Useful for debugging network issues. + +## Usage + +Create an ad-hoc "net-debug" pod in the default namespace on the pod network: + +```sh +$ kubectl run -it --rm -n default \ + --image=registry.ethz.ch/hpc-registry/netutils net-debug +root@debug:/# nslookup kubernetes.default +Server: 10.205.209.10 +Address: 10.205.209.10#53 + +Name: kubernetes.default.svc.cluster.local +Address: 10.205.209.1 +``` + +The same on the host network: + +```sh +$ kubectl run -it --rm -n default --overrides='{"spec":{"hostNetwork":true}}' \ + --image=registry.ethz.ch/hpc-registry/netutils net-debug +root@eu-k8s-dev-10:/# ip a show access +8: access: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 9000 qdisc noqueue state UP group default qlen 1000 + link/ether d6:6a:36:07:83:02 brd ff:ff:ff:ff:ff:ff + inet 10.205.160.40/20 brd 10.205.175.255 scope global access + valid_lft forever preferred_lft forever +``` + +### Iperf server-client + +An ad-hoc pod on a specific node running iperf server: + +```sh +$ kubectl run --attach --rm -n default \ + --overrides='{"spec":{"nodeName":"eu-k8s-dev-09.euler.ethz.ch"}}' \ + --image=registry.ethz.ch/hpc-registry/netutils \ + iperf-server --command -- iperf3 --server -4 --one-off +``` + +*Note*: The pod will terminate and will be deleted after the first handled +connection thanks to `--one-off`. + +Determine its IP: + +```sh +$ kubectl get pod -o wide -n default iperf-server +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +iperf-server 1/1 Running 0 4m2s 10.205.244.143 eu-k8s-dev-09.euler.ethz.ch <none> <none> +``` + +And run a client on a different node (on pod network): + +```sh +$ kubectl run --attach --rm -n default \ + --overrides='{"spec":{"nodeName":"eu-k8s-dev-10.euler.ethz.ch"}}' \ + --image registry.ethz.ch/hpc-registry/netutils \ + iperf-client --command -- iperf3 --bidir -P 4 -4 --client 10.205.244.143 +... +[ 17][RX-C] 0.00-10.00 sec 3.79 GBytes 3.25 Gbits/sec 2725 sender +[ 17][RX-C] 0.00-10.00 sec 3.79 GBytes 3.25 Gbits/sec receiver +[ 19][RX-C] 0.00-10.00 sec 3.81 GBytes 3.27 Gbits/sec 3012 sender +[ 19][RX-C] 0.00-10.00 sec 3.80 GBytes 3.27 Gbits/sec receiver +[SUM][RX-C] 0.00-10.00 sec 22.2 GBytes 19.1 Gbits/sec 11059 sender +[SUM][RX-C] 0.00-10.00 sec 22.2 GBytes 19.0 Gbits/sec receiver + +iperf Done. +pod "iperf-client" deleted +``` diff --git a/charts/iperf/.kube-linter.yaml b/charts/iperf/.kube-linter.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d7327abf548da58758f93d3da21319134ca1913c --- /dev/null +++ b/charts/iperf/.kube-linter.yaml @@ -0,0 +1,5 @@ +--- +checks: + exclude: + - latest-tag + - drop-net-raw-capability diff --git a/charts/iperf/Chart.yaml b/charts/iperf/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..88b7185070a268623330879b3a2eda54303722fc --- /dev/null +++ b/charts/iperf/Chart.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: v2 +name: iperf +description: Iperf3 server for network bandwidth testing + +type: application +version: 0.1.0 +appVersion: 0.1.0 + +dependencies: [] diff --git a/charts/iperf/Makefile b/charts/iperf/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fcf704f3009edcc957a2530bf3c57ea733fcdb6b --- /dev/null +++ b/charts/iperf/Makefile @@ -0,0 +1,31 @@ +NAMESPACE ?= default +CHART_NAME ?= iperf-server +HELM_ARGS := $(CHART_NAME) . -n $(NAMESPACE) -f values.yaml $(HELM_ARGS) + +.PHONY: lint +lint: + make render | awk -v o=/dev/stderr '/^(apiVersion:|---)/ { o="/dev/stdout" } { print >o }' | \ + kube-linter lint --fail-if-no-objects-found --fail-on-invalid-resource - + +.PHONY: render +render: + helm template $(HELM_ARGS) + +.PHONY: install +install: + helm install --create-namespace $(HELM_ARGS) + +upgrade: + helm upgrade $(HELM_ARGS) + +.PHONY: diff +# requires helm diff plugin +diff: + helm diff upgrade $(HELM_ARGS) + +.PHONY: kdiff +kdiff: + make render | awk -v o=/dev/stderr '/^(apiVersion:|---)/ { o="/dev/stdout" } { print >o }' | \ + kubectl diff -n "$(NAMESPACE)" -f - ||: + +# ex: ft=make noet ts=4 sw=4 : diff --git a/charts/iperf/templates/deployment.yaml b/charts/iperf/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..00f577bc0eb52cc3160e9e05ba6d0e74679b6ace --- /dev/null +++ b/charts/iperf/templates/deployment.yaml @@ -0,0 +1,82 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + labels: + app.kubernetes.io/name: iperf-server + app.kubernetes.io/part-of: iperf-server + name: iperf-server + namespace: "{{ .Release.Namespace }}" +spec: + replicas: {{ .Values.replicas | default 1 }} + selector: + matchLabels: + app.kubernetes.io/name: iperf-server + template: + metadata: + labels: + app.kubernetes.io/name: iperf-server + app.kubernetes.io/part-of: iperf-server + spec: + containers: + - command: + - /usr/bin/iperf3 + args: + - --server +{{- range $_, $arg := (index (default (list) .Values.server) "args") }} + - {{ $arg }} +{{- end }} + image: {{ .Values.image.name | + default "registry.ethz.ch/hpc-registry/netutils" }}:{{ + .Values.image.tag | default "latest" }} + imagePullPolicy: Always + name: iperf-server + ports: + - containerPort: {{ .Values.server.port | default 5201 }} + name: iperf-server + protocol: TCP + resources: + limits: + cpu: 2 + ephemeral-storage: 64Mi + memory: 512Mi + requests: + cpu: 200m + ephemeral-storage: 2Mi + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + add: + - NET_ADMIN + volumeMounts: + - name: tmp + mountPath: /tmp +{{- if .Values.mountHost | default false }} + - name: host + mountPath: /host +{{- end }} +{{- if .Values.nodeSelector }} + nodeSelector: +{{ .Values.nodeSelector | indent 10 }} +{{- end }} + restartPolicy: Always + hostNetwork: {{ .Values.hostNetwork | default false }} + securityContext: + runAsNonRoot: true + serviceAccountName: iperf +{{- if .Values.tolerations }} + tolerations: +{{ .Values.tolerations | indent 10 }} +{{- end }} + volumes: + - name: tmp + emptyDir: + sizeLimit: 56Mi +{{- if .Values.mountHost | default false }} + - hostPath: + path: / + name: host +{{- end }} diff --git a/charts/iperf/templates/networkpolicy.yaml b/charts/iperf/templates/networkpolicy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..86c4ee764cc240e8ad3728d08d54dd9a78a6f2f0 --- /dev/null +++ b/charts/iperf/templates/networkpolicy.yaml @@ -0,0 +1,27 @@ +{{- if index (default (dict) .Values.networkPolicy) "enabled" | default false }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: iperf-server + app.kubernetes.io/part-of: iperf-server + name: iperf-server + namespace: "{{ .Release.Namespace }}" +spec: + ingress: + - from: +{{- range $_, $cidr := .Values.networkPolicy.ingress.CIDRs }} + - ipBlock: + cidr: "{{ $cidr }}" +{{- end }} + ports: + - port: {{ .Values.server.port | default 5201 }} + protocol: TCP + podSelector: + matchLabels: + app.kubernetes.io/name: iperf-server + app.kubernetes.io/part-of: iperf-server + policyTypes: + - Ingress +{{- end }} diff --git a/charts/iperf/templates/sa.yaml b/charts/iperf/templates/sa.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fbaa719251fe685e988d44cd7a8664d3add54d95 --- /dev/null +++ b/charts/iperf/templates/sa.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: iperf + namespace: "{{ .Release.Namespace }}" + labels: + app.kubernetes.io/part-of: iperf-server diff --git a/charts/iperf/templates/service.yaml b/charts/iperf/templates/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b8a20d99bd0c0f6e4c6ff9a75067b55d19539ec6 --- /dev/null +++ b/charts/iperf/templates/service.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: v1 +kind: Service +metadata: +{{- if index (default (dict) .Values.service) "annotations" }} + annotations: +{{ .Values.service.annotations | toYaml | indent 4 }} +{{- end }} + labels: + app.kubernetes.io/name: iperf-server + app.kubernetes.io/part-of: iperf-server + name: iperf-server + namespace: "{{ .Release.Namespace }}" +spec: + externalTrafficPolicy: "{{ .Values.service.externalTrafficPolicy | default "Local" }}" + ports: + - name: iperf-server + port: {{ .Values.server.port | default 5201 }} + targetPort: iperf-server + protocol: TCP + selector: + app.kubernetes.io/name: iperf-server + type: LoadBalancer diff --git a/charts/iperf/values.yaml b/charts/iperf/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..605b9039c8f35647f7a7f06c324cb7199a7cf5fb --- /dev/null +++ b/charts/iperf/values.yaml @@ -0,0 +1,22 @@ +--- +image: + name: registry.ethz.ch/hpc-registry/netutils + tag: latest +replicas: 1 +## mount / of the host at /host in the container +mountHost: false +hostNetwork: false +# nodeSelector: {} +service: + annotations: + metallb.universe.tf/address-pool: public + metallb.universe.tf/ip-allocated-from-pool: public + metallb.universe.tf/loadBalancerIPs: CHANGEME + externalTrafficPolicy: Local +server: + args: [] + port: 5201 +networkPolicy: + enabled: false + ingress: + CIDRs: []