diff --git a/activemq-helm-chart/Chart.yaml b/activemq-helm-chart/Chart.yaml new file mode 100644 index 0000000000..b3c364e030 --- /dev/null +++ b/activemq-helm-chart/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: activemq +description: A Helm chart for Apache ActiveMQ +type: application +version: 1.0.0 +appVersion: "latest" +keywords: + - activemq + - message queue + - messaging +home: https://activemq.apache.org +sources: + - https://github.com/apache/activemq +maintainers: + - name: activemq \ No newline at end of file diff --git a/activemq-helm-chart/README.md b/activemq-helm-chart/README.md new file mode 100644 index 0000000000..a764417277 --- /dev/null +++ b/activemq-helm-chart/README.md @@ -0,0 +1,138 @@ +# ActiveMQ Helm Chart + +A Helm chart for deploying Apache ActiveMQ on Kubernetes. + +## Introduction + +This chart bootstraps an ActiveMQ deployment on a Kubernetes cluster using the Helm package manager. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.0+ +- PV provisioner support in the underlying infrastructure (if persistence is enabled) + +## Installing the Chart + +To install the chart with the release name `my-activemq`: + +```bash +helm install my-activemq . +``` + +## Uninstalling the Chart + +To uninstall/delete the `my-activemq` deployment: + +```bash +helm uninstall my-activemq +``` + +## Configuration + +The following table lists the configurable parameters of the ActiveMQ chart and their default values. + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `replicaCount` | Number of replicas | `1` | +| `image.repository` | ActiveMQ image repository | `apache/activemq-classic` | +| `image.tag` | ActiveMQ image tag | `latest` | +| `image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `auth.username` | ActiveMQ admin username | `admin` | +| `auth.password` | ActiveMQ admin password | `changeme` | +| `service.type` | Kubernetes service type | `ClusterIP` | +| `service.port` | OpenWire service port | `61616` | +| `service.webConsolePort` | Web Console port | `8161` | +| `persistence.enabled` | Enable persistence using PVC | `false` | +| `persistence.storageClass` | PVC Storage Class | `""` | +| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` | +| `persistence.size` | PVC Storage Request | `8Gi` | +| `resources.limits.cpu` | CPU limit | `1000m` | +| `resources.limits.memory` | Memory limit | `2Gi` | +| `resources.requests.cpu` | CPU request | `500m` | +| `resources.requests.memory` | Memory request | `1Gi` | +| `livenessProbe.enabled` | Enable liveness probe | `true` | +| `readinessProbe.enabled` | Enable readiness probe | `true` | +| `ingress.enabled` | Enable ingress | `false` | +| `config.activemqXmlPath` | Path to custom activemq.xml file | `""` | +| `config.jettyXmlPath` | Path to custom jetty.xml file | `""` | +| `tls.enabled` | Enable TLS/SSL | `false` | +| `tls.keystoreSecret` | Kubernetes secret containing broker.ks | `""` | +| `tls.truststoreSecret` | Kubernetes secret containing broker.ts | `""` | +| `tls.sslPort` | OpenWire SSL port | `61617` | +| `tls.webConsoleSslPort` | Web Console SSL port | `8162` | + +## Accessing ActiveMQ + +### Web Console + +The ActiveMQ Web Console is accessible on port 8161. To access it locally: + +```bash +kubectl port-forward svc/my-activemq 8161:8161 +``` + +Then open http://localhost:8161 in your browser and login with the configured credentials. + +### OpenWire Connection + +Applications can connect to ActiveMQ on port 61616 using the service name: + +``` +tcp://my-activemq:61616 +``` + +## Custom Configuration + +To use custom configuration files, place your files in the chart directory and specify the paths: + +```yaml +config: + activemqXmlPath: "my-activemq.xml" + jettyXmlPath: "my-jetty.xml" +``` + +## Persistence + +The ActiveMQ image stores data at the `/opt/apache-activemq/data` path of the container. + +By default, the chart mounts a Persistent Volume at this location. The volume is created using dynamic volume provisioning. + +## TLS Configuration + +To enable TLS/SSL connections, you need to create Kubernetes secrets containing your keystore and truststore files. + +### Generate Test Certificates (for development) + +```bash +# Generate keystore +keytool -genkey -alias broker -keyalg RSA -keystore broker.ks \ + -storepass changeit -keypass changeit \ + -dname "CN=localhost, OU=Test, O=Test, L=Test, ST=Test, C=US" + +# Export certificate +keytool -export -alias broker -keystore broker.ks \ + -file broker.cert -storepass changeit + +# Create truststore +keytool -import -alias broker -keystore broker.ts \ + -file broker.cert -storepass changeit +``` + +### Create Kubernetes Secrets + +```bash +kubectl create secret generic activemq-keystore --from-file=broker.ks +kubectl create secret generic activemq-truststore --from-file=broker.ts +``` + +### Install with TLS Enabled + +```bash +helm install my-activemq . \ + --set tls.enabled=true \ + --set tls.keystoreSecret=activemq-keystore \ + --set tls.truststoreSecret=activemq-truststore +``` + +**Note:** When TLS is enabled, only SSL/TLS ports are exposed. Non-TLS ports are not available. \ No newline at end of file diff --git a/activemq-helm-chart/activemq_sample.xml b/activemq-helm-chart/activemq_sample.xml new file mode 100644 index 0000000000..9f32bd90f5 --- /dev/null +++ b/activemq-helm-chart/activemq_sample.xml @@ -0,0 +1,130 @@ + + + + + + + + file:${activemq.conf}/credentials.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-helm-chart/templates/NOTES.txt b/activemq-helm-chart/templates/NOTES.txt new file mode 100644 index 0000000000..cf84715dd2 --- /dev/null +++ b/activemq-helm-chart/templates/NOTES.txt @@ -0,0 +1,39 @@ +1. Get the ActiveMQ Web Console URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "activemq.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo {{ if .Values.tls.enabled }}https{{ else }}http{{ end }}://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "activemq.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "activemq.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo {{ if .Values.tls.enabled }}https{{ else }}http{{ end }}://$SERVICE_IP:{{ if .Values.tls.enabled }}{{ .Values.tls.webConsoleSslPort }}{{ else }}{{ .Values.service.webConsolePort }}{{ end }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "activemq.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") + echo "Visit {{ if .Values.tls.enabled }}https://127.0.0.1:{{ .Values.tls.webConsoleSslPort }}{{ else }}http://127.0.0.1:{{ .Values.service.webConsolePort }}{{ end }} to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME {{ if .Values.tls.enabled }}{{ .Values.tls.webConsoleSslPort }}{{ else }}{{ .Values.service.webConsolePort }}{{ end }}:$CONTAINER_PORT +{{- end }} + +2. Login credentials: + Username: {{ .Values.auth.username }} + Password: {{ .Values.auth.password }} + +3. To connect to ActiveMQ from applications: +{{- if .Values.tls.enabled }} + Connection URL: ssl://{{ include "activemq.fullname" . }}:{{ .Values.tls.sslPort }} +{{- else }} + Connection URL: tcp://{{ include "activemq.fullname" . }}:{{ .Values.service.port }} +{{- end }} + +{{- if .Values.tls.enabled }} +4. TLS is enabled. Ensure you have created the required secrets: + - Keystore secret: {{ .Values.tls.keystoreSecret }} + - Truststore secret: {{ .Values.tls.truststoreSecret }} +{{- end }} \ No newline at end of file diff --git a/activemq-helm-chart/templates/_helpers.tpl b/activemq-helm-chart/templates/_helpers.tpl new file mode 100644 index 0000000000..8427364191 --- /dev/null +++ b/activemq-helm-chart/templates/_helpers.tpl @@ -0,0 +1,49 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "activemq.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "activemq.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "activemq.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "activemq.labels" -}} +helm.sh/chart: {{ include "activemq.chart" . }} +{{ include "activemq.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "activemq.selectorLabels" -}} +app.kubernetes.io/name: {{ include "activemq.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} \ No newline at end of file diff --git a/activemq-helm-chart/templates/configmap.yaml b/activemq-helm-chart/templates/configmap.yaml new file mode 100644 index 0000000000..4d44407b38 --- /dev/null +++ b/activemq-helm-chart/templates/configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "activemq.fullname" . }}-config + labels: + {{- include "activemq.labels" . | nindent 4 }} +data: + users.properties: | + {{ .Values.auth.username }}={{ .Values.auth.password }} + groups.properties: | + admins={{ .Values.auth.username }} + {{- if .Values.config.activemqXmlPath }} + activemq.xml: | +{{ .Files.Get .Values.config.activemqXmlPath | indent 4 }} + {{- end }} + {{- if .Values.config.jettyXmlPath }} + jetty.xml: | +{{ .Files.Get .Values.config.jettyXmlPath | indent 4 }} + {{- end }} \ No newline at end of file diff --git a/activemq-helm-chart/templates/service.yaml b/activemq-helm-chart/templates/service.yaml new file mode 100644 index 0000000000..2e421e3657 --- /dev/null +++ b/activemq-helm-chart/templates/service.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "activemq.fullname" . }} + labels: + {{- include "activemq.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + {{- if not .Values.tls.enabled }} + - name: openwire + port: {{ .Values.service.port }} + targetPort: openwire + protocol: TCP + - name: webconsole + port: {{ .Values.service.webConsolePort }} + targetPort: webconsole + protocol: TCP + {{- else }} + - name: openwire-ssl + port: {{ .Values.tls.sslPort }} + targetPort: openwire-ssl + protocol: TCP + - name: webconsole-ssl + port: {{ .Values.tls.webConsoleSslPort }} + targetPort: webconsole-ssl + protocol: TCP + {{- end }} + selector: + {{- include "activemq.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/activemq-helm-chart/templates/statefulset.yaml b/activemq-helm-chart/templates/statefulset.yaml new file mode 100644 index 0000000000..60d2d292bb --- /dev/null +++ b/activemq-helm-chart/templates/statefulset.yaml @@ -0,0 +1,136 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "activemq.fullname" . }} + labels: + {{- include "activemq.labels" . | nindent 4 }} +spec: + serviceName: {{ include "activemq.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "activemq.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "activemq.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: activemq + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + {{- if not .Values.tls.enabled }} + - name: openwire + containerPort: 61616 + protocol: TCP + - name: webconsole + containerPort: 8161 + protocol: TCP + {{- else }} + - name: openwire-ssl + containerPort: {{ .Values.tls.sslPort }} + protocol: TCP + - name: webconsole-ssl + containerPort: {{ .Values.tls.webConsoleSslPort }} + protocol: TCP + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + tcpSocket: + port: {{ if .Values.tls.enabled }}openwire-ssl{{ else }}openwire{{ end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + tcpSocket: + port: {{ if .Values.tls.enabled }}openwire-ssl{{ else }}openwire{{ end }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + {{- end }} + volumeMounts: + - name: data + mountPath: /opt/apache-activemq/data + - name: config + mountPath: /opt/apache-activemq/conf/users.properties + subPath: users.properties + - name: config + mountPath: /opt/apache-activemq/conf/groups.properties + subPath: groups.properties + {{- if .Values.config.activemqXmlPath }} + - name: config + mountPath: /opt/apache-activemq/conf/activemq.xml + subPath: activemq.xml + {{- end }} + {{- if .Values.config.jettyXmlPath }} + - name: config + mountPath: /opt/apache-activemq/conf/jetty.xml + subPath: jetty.xml + {{- end }} + {{- if .Values.tls.enabled }} + - name: keystore + mountPath: /opt/apache-activemq/conf/broker.ks + subPath: broker.ks + readOnly: true + - name: truststore + mountPath: /opt/apache-activemq/conf/broker.ts + subPath: broker.ts + readOnly: true + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ include "activemq.fullname" . }}-config + {{- if .Values.tls.enabled }} + - name: keystore + secret: + secretName: {{ .Values.tls.keystoreSecret }} + - name: truststore + secret: + secretName: {{ .Values.tls.truststoreSecret }} + {{- end }} + {{- if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- end }} \ No newline at end of file diff --git a/activemq-helm-chart/values.yaml b/activemq-helm-chart/values.yaml new file mode 100644 index 0000000000..5b95b600e1 --- /dev/null +++ b/activemq-helm-chart/values.yaml @@ -0,0 +1,78 @@ +replicaCount: 1 + +image: + repository: apache/activemq-classic + tag: "latest" + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: "" + +auth: + username: admin + password: changeme + +config: + activemqXmlPath: "" + jettyXmlPath: "" + +service: + type: ClusterIP + port: 61616 + webConsolePort: 8161 + +persistence: + enabled: false + storageClass: "" + accessMode: ReadWriteOnce + size: 8Gi + +resources: + limits: + cpu: 1000m + memory: 2Gi + requests: + cpu: 500m + memory: 1Gi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +podAnnotations: {} + +livenessProbe: + enabled: true + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 20 + failureThreshold: 6 + successThreshold: 1 + +readinessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 20 + failureThreshold: 3 + successThreshold: 1 + +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: activemq.local + paths: + - path: / + pathType: Prefix + tls: [] + +tls: + enabled: false + keystoreSecret: "activemq-keystore" # kubectl create secret generic activemq-keystore --from-file=broker.ks= + truststoreSecret: "activemq-truststore" # kubectl create secret generic activemq-truststore --from-file=broker.ts= + sslPort: 61617 + webConsoleSslPort: 8162 \ No newline at end of file