From f62a79be33e387161fd6c16fd2872efed9e7e756 Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Fri, 24 Jul 2020 15:59:33 +1000 Subject: [PATCH 1/3] publish+consumer --- .../applications/consumer/consumer.go | 83 +++++++++++++++++++ .../rabbitmq/applications/consumer/dockerfile | 19 +++++ .../applications/publisher/dockerfile | 20 +++++ .../applications/publisher/publisher.go | 76 +++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 messaging/rabbitmq/applications/consumer/consumer.go create mode 100644 messaging/rabbitmq/applications/consumer/dockerfile create mode 100644 messaging/rabbitmq/applications/publisher/dockerfile create mode 100644 messaging/rabbitmq/applications/publisher/publisher.go diff --git a/messaging/rabbitmq/applications/consumer/consumer.go b/messaging/rabbitmq/applications/consumer/consumer.go new file mode 100644 index 0000000..98cdd7e --- /dev/null +++ b/messaging/rabbitmq/applications/consumer/consumer.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + log "github.com/sirupsen/logrus" + "github.com/streadway/amqp" + "os" +) + +var rabbit_host = os.Getenv("RABBIT_HOST") +var rabbit_port = os.Getenv("RABBIT_PORT") +var rabbit_user = os.Getenv("RABBIT_USERNAME") +var rabbit_password = os.Getenv("RABBIT_PASSWORD") + +func main() { + + consume() +} + + +func consume() { + + conn, err := amqp.Dial("amqp://" + rabbit_user + ":" +rabbit_password + "@" + rabbit_host + ":" + rabbit_port +"/") + + if err != nil { + log.Fatalf("%s: %s", "Failed to connect to RabbitMQ", err) + } + + ch, err := conn.Channel() + + if err != nil { + log.Fatalf("%s: %s", "Failed to open a channel", err) + } + + q, err := ch.QueueDeclare( + "publisher", // name + false, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + + if err != nil { + log.Fatalf("%s: %s", "Failed to declare a queue", err) + } + + fmt.Println("Channel and Queue established") + + defer conn.Close() + defer ch.Close() + + msgs, err := ch.Consume( + q.Name, // queue + "", // consumer + false, // auto-ack + false, // exclusive + false, // no-local + false, // no-wait + nil, // args + ) + + if err != nil { + log.Fatalf("%s: %s", "Failed to register consumer", err) + } + + forever := make(chan bool) + + go func() { + for d := range msgs { + log.Printf("Received a message: %s", d.Body) + + d.Ack(false) + } + }() + + fmt.Println("Running...") + <-forever + + + + +} \ No newline at end of file diff --git a/messaging/rabbitmq/applications/consumer/dockerfile b/messaging/rabbitmq/applications/consumer/dockerfile new file mode 100644 index 0000000..b63b85d --- /dev/null +++ b/messaging/rabbitmq/applications/consumer/dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.14-alpine as build + +RUN apk add --no-cache git + +WORKDIR /src + +RUN go get github.com/sirupsen/logrus +RUN go get github.com/streadway/amqp + +COPY consumer.go /src + +RUN go build consumer.go + + +FROM alpine as runtime + +COPY --from=build /src/consumer /app/consumer + +CMD [ "/app/consumer" ] \ No newline at end of file diff --git a/messaging/rabbitmq/applications/publisher/dockerfile b/messaging/rabbitmq/applications/publisher/dockerfile new file mode 100644 index 0000000..258fef4 --- /dev/null +++ b/messaging/rabbitmq/applications/publisher/dockerfile @@ -0,0 +1,20 @@ +FROM golang:1.14-alpine as build + +RUN apk add --no-cache git + +WORKDIR /src + +RUN go get github.com/julienschmidt/httprouter +RUN go get github.com/sirupsen/logrus +RUN go get github.com/streadway/amqp + +COPY publisher.go /src + +RUN go build publisher.go + + +FROM alpine as runtime + +COPY --from=build /src/publisher /app/publisher + +CMD [ "/app/publisher" ] \ No newline at end of file diff --git a/messaging/rabbitmq/applications/publisher/publisher.go b/messaging/rabbitmq/applications/publisher/publisher.go new file mode 100644 index 0000000..514699e --- /dev/null +++ b/messaging/rabbitmq/applications/publisher/publisher.go @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "net/http" + "github.com/julienschmidt/httprouter" + log "github.com/sirupsen/logrus" + "github.com/streadway/amqp" + "os" +) + +var rabbit_host = os.Getenv("RABBIT_HOST") +var rabbit_port = os.Getenv("RABBIT_PORT") +var rabbit_user = os.Getenv("RABBIT_USERNAME") +var rabbit_password = os.Getenv("RABBIT_PASSWORD") + +func main() { + router := httprouter.New() + + router.POST("/publish/:message", func(w http.ResponseWriter, r *http.Request, p httprouter.Params){ + submit(w,r,p) + }) + + fmt.Println("Running...") + log.Fatal(http.ListenAndServe(":80", router)) +} + + +func submit(writer http.ResponseWriter, request *http.Request, p httprouter.Params) { + message := p.ByName("message") + + fmt.Println("Received message: " + message) + conn, err := amqp.Dial("amqp://" + rabbit_user + ":" +rabbit_password + "@" + rabbit_host + ":" + rabbit_port +"/") + + if err != nil { + log.Fatalf("%s: %s", "Failed to connect to RabbitMQ", err) + } + + defer conn.Close() + ch, err := conn.Channel() + + if err != nil { + log.Fatalf("%s: %s", "Failed to open a channel", err) + } + defer ch.Close() + + q, err := ch.QueueDeclare( + "publisher", // name + false, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + + if err != nil { + log.Fatalf("%s: %s", "Failed to declare a queue", err) + } + + err = ch.Publish( + "", // exchange + q.Name, // routing key + false, // mandatory + false, // immediate + amqp.Publishing { + ContentType: "text/plain", + Body: []byte(message), + }) + + if err != nil { + log.Fatalf("%s: %s", "Failed to publish a message", err) + } + + fmt.Println("publish success!") + +} \ No newline at end of file From ac1743d0d441c56a35644066fad36321bd010ab2 Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Sun, 26 Jul 2020 06:58:32 +1000 Subject: [PATCH 2/3] add readme --- messaging/rabbitmq/readme.md | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 messaging/rabbitmq/readme.md diff --git a/messaging/rabbitmq/readme.md b/messaging/rabbitmq/readme.md new file mode 100644 index 0000000..bb56e73 --- /dev/null +++ b/messaging/rabbitmq/readme.md @@ -0,0 +1,73 @@ +# RabbitMQ + +Docker image over [here](https://hub.docker.com/_/rabbitmq) +``` +docker network create rabbits +docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-1 --name rabbit-1 rabbitmq:3.8 +docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-2 --name rabbit-2 rabbitmq:3.8 +docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-3 --name rabbit-3 rabbitmq:3.8 + +# how to grab existing erlang cookie +docker exec -it rabbit-1 cat /var/lib/rabbitmq/.erlang.cookie +DSHEVCXBBETJJVJWTOWT + +docker rm -f rabbit-1 +docker rm -f rabbit-2 +docker rm -f rabbit-3 +``` + +``` +docker exec -it rabbit-1 rabbitmqctl cluster_status + +#join node 2 + +docker exec -it rabbit-2 rabbitmqctl stop_app +docker exec -it rabbit-2 rabbitmqctl reset +docker exec -it rabbit-2 rabbitmqctl join_cluster rabbit@rabbit-1 +docker exec -it rabbit-2 rabbitmqctl start_app +docker exec -it rabbit-2 rabbitmqctl cluster_status + + +#join node 3 +docker exec -it rabbit-3 rabbitmqctl stop_app +docker exec -it rabbit-3 rabbitmqctl reset +docker exec -it rabbit-3 rabbitmqctl join_cluster rabbit@rabbit-1 +docker exec -it rabbit-3 rabbitmqctl start_app +docker exec -it rabbit-3 rabbitmqctl cluster_status + +``` + + + + +# Management + +``` +docker run -d --rm --net rabbits -p 8080:15672 -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-manager --name rabbit-manager rabbitmq:3.8-management + +#join the manager + +docker exec -it rabbit-manager rabbitmqctl stop_app +docker exec -it rabbit-manager rabbitmqctl reset +docker exec -it rabbit-manager rabbitmqctl join_cluster rabbit@rabbit-1 +docker exec -it rabbit-manager rabbitmqctl start_app +docker exec -it rabbit-manager rabbitmqctl cluster_status +``` + +# Enable Statistics + +docker exec -it rabbit-1 rabbitmq-plugins enable rabbitmq_management +docker exec -it rabbit-2 rabbitmq-plugins enable rabbitmq_management +docker exec -it rabbit-3 rabbitmq-plugins enable rabbitmq_management + +# Message Publisher + +``` +docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest -p 80:80 publisher +``` + +# Message Consumer + +``` +docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest consumer +``` From e8c05bbe4e6ac2301b8720a79ebf46c7b6ea0ee1 Mon Sep 17 00:00:00 2001 From: marcel-dempers Date: Fri, 31 Jul 2020 15:04:07 +1000 Subject: [PATCH 3/3] rabbitmq on k8s --- .../applications/consumer/consumer.go | 15 +- .../applications/publisher/deployment.yaml | 62 +++++++ .../applications/publisher/dockerfile | 1 - .../applications/publisher/publisher.go | 14 +- .../rabbitmq/config/rabbit-1/rabbitmq.conf | 7 + .../rabbitmq/config/rabbit-2/rabbitmq.conf | 7 + .../rabbitmq/config/rabbit-3/rabbitmq.conf | 7 + .../rabbitmq/kubernetes/rabbit-configmap.yaml | 19 +++ .../rabbitmq/kubernetes/rabbit-rbac.yaml | 32 ++++ .../rabbitmq/kubernetes/rabbit-secret.yaml | 8 + .../kubernetes/rabbit-statefulset.yaml | 101 ++++++++++++ messaging/rabbitmq/kubernetes/readme.md | 60 +++++++ messaging/rabbitmq/readme.md | 156 ++++++++++++++---- 13 files changed, 440 insertions(+), 49 deletions(-) create mode 100644 messaging/rabbitmq/applications/publisher/deployment.yaml create mode 100644 messaging/rabbitmq/config/rabbit-1/rabbitmq.conf create mode 100644 messaging/rabbitmq/config/rabbit-2/rabbitmq.conf create mode 100644 messaging/rabbitmq/config/rabbit-3/rabbitmq.conf create mode 100644 messaging/rabbitmq/kubernetes/rabbit-configmap.yaml create mode 100644 messaging/rabbitmq/kubernetes/rabbit-rbac.yaml create mode 100644 messaging/rabbitmq/kubernetes/rabbit-secret.yaml create mode 100644 messaging/rabbitmq/kubernetes/rabbit-statefulset.yaml create mode 100644 messaging/rabbitmq/kubernetes/readme.md diff --git a/messaging/rabbitmq/applications/consumer/consumer.go b/messaging/rabbitmq/applications/consumer/consumer.go index 98cdd7e..1273862 100644 --- a/messaging/rabbitmq/applications/consumer/consumer.go +++ b/messaging/rabbitmq/applications/consumer/consumer.go @@ -1,5 +1,6 @@ package main + import ( "fmt" log "github.com/sirupsen/logrus" @@ -13,13 +14,11 @@ var rabbit_user = os.Getenv("RABBIT_USERNAME") var rabbit_password = os.Getenv("RABBIT_PASSWORD") func main() { - consume() } - func consume() { - + conn, err := amqp.Dial("amqp://" + rabbit_user + ":" +rabbit_password + "@" + rabbit_host + ":" + rabbit_port +"/") if err != nil { @@ -27,11 +26,11 @@ func consume() { } ch, err := conn.Channel() - + if err != nil { log.Fatalf("%s: %s", "Failed to open a channel", err) } - + q, err := ch.QueueDeclare( "publisher", // name false, // durable @@ -59,7 +58,7 @@ func consume() { false, // no-wait nil, // args ) - + if err != nil { log.Fatalf("%s: %s", "Failed to register consumer", err) } @@ -76,8 +75,4 @@ func consume() { fmt.Println("Running...") <-forever - - - - } \ No newline at end of file diff --git a/messaging/rabbitmq/applications/publisher/deployment.yaml b/messaging/rabbitmq/applications/publisher/deployment.yaml new file mode 100644 index 0000000..aa4a940 --- /dev/null +++ b/messaging/rabbitmq/applications/publisher/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: v1 +kind: Secret +metadata: + name: rabbitmq-publisher +type: Opaque +data: + RABBIT_USERNAME: Z3Vlc3Q= + RABBIT_PASSWORD: Z3Vlc3Q= +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rabbitmq-publisher + labels: + app: rabbitmq-publisher +spec: + selector: + matchLabels: + app: rabbitmq-publisher + replicas: 1 + template: + metadata: + labels: + app: rabbitmq-publisher + spec: + containers: + - name: rabbitmq-publisher + image: aimvector/rabbitmq-publisher:v1.0.0 + imagePullPolicy: Always + ports: + - containerPort: 80 + env: + - name: RABBIT_HOST + value: "rabbitmq-0.rabbitmq.rabbits.svc.cluster.local" + - name: RABBIT_PORT + value: "5672" + - name: RABBIT_USERNAME + valueFrom: + secretKeyRef: + name: rabbitmq-publisher + key: RABBIT_USERNAME + - name: RABBIT_PASSWORD + valueFrom: + secretKeyRef: + name: rabbitmq-publisher + key: RABBIT_PASSWORD +--- +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq-publisher + labels: + app: rabbitmq-publisher +spec: + type: LoadBalancer + selector: + app: rabbitmq-publisher + ports: + - protocol: TCP + name: http + port: 80 + targetPort: 80 diff --git a/messaging/rabbitmq/applications/publisher/dockerfile b/messaging/rabbitmq/applications/publisher/dockerfile index 258fef4..6d7a0b6 100644 --- a/messaging/rabbitmq/applications/publisher/dockerfile +++ b/messaging/rabbitmq/applications/publisher/dockerfile @@ -12,7 +12,6 @@ COPY publisher.go /src RUN go build publisher.go - FROM alpine as runtime COPY --from=build /src/publisher /app/publisher diff --git a/messaging/rabbitmq/applications/publisher/publisher.go b/messaging/rabbitmq/applications/publisher/publisher.go index 514699e..897fec6 100644 --- a/messaging/rabbitmq/applications/publisher/publisher.go +++ b/messaging/rabbitmq/applications/publisher/publisher.go @@ -15,6 +15,7 @@ var rabbit_user = os.Getenv("RABBIT_USERNAME") var rabbit_password = os.Getenv("RABBIT_PASSWORD") func main() { + router := httprouter.New() router.POST("/publish/:message", func(w http.ResponseWriter, r *http.Request, p httprouter.Params){ @@ -25,11 +26,11 @@ func main() { log.Fatal(http.ListenAndServe(":80", router)) } - func submit(writer http.ResponseWriter, request *http.Request, p httprouter.Params) { message := p.ByName("message") - + fmt.Println("Received message: " + message) + conn, err := amqp.Dial("amqp://" + rabbit_user + ":" +rabbit_password + "@" + rabbit_host + ":" + rabbit_port +"/") if err != nil { @@ -37,16 +38,18 @@ func submit(writer http.ResponseWriter, request *http.Request, p httprouter.Para } defer conn.Close() + ch, err := conn.Channel() - + if err != nil { log.Fatalf("%s: %s", "Failed to open a channel", err) } + defer ch.Close() q, err := ch.QueueDeclare( "publisher", // name - false, // durable + true, // durable false, // delete when unused false, // exclusive false, // no-wait @@ -66,11 +69,10 @@ func submit(writer http.ResponseWriter, request *http.Request, p httprouter.Para ContentType: "text/plain", Body: []byte(message), }) - + if err != nil { log.Fatalf("%s: %s", "Failed to publish a message", err) } fmt.Println("publish success!") - } \ No newline at end of file diff --git a/messaging/rabbitmq/config/rabbit-1/rabbitmq.conf b/messaging/rabbitmq/config/rabbit-1/rabbitmq.conf new file mode 100644 index 0000000..960d3ab --- /dev/null +++ b/messaging/rabbitmq/config/rabbit-1/rabbitmq.conf @@ -0,0 +1,7 @@ +loopback_users.guest = false +listeners.tcp.default = 5672 + +cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config +cluster_formation.classic_config.nodes.1 = rabbit@rabbit-1 +cluster_formation.classic_config.nodes.2 = rabbit@rabbit-2 +cluster_formation.classic_config.nodes.3 = rabbit@rabbit-3 \ No newline at end of file diff --git a/messaging/rabbitmq/config/rabbit-2/rabbitmq.conf b/messaging/rabbitmq/config/rabbit-2/rabbitmq.conf new file mode 100644 index 0000000..960d3ab --- /dev/null +++ b/messaging/rabbitmq/config/rabbit-2/rabbitmq.conf @@ -0,0 +1,7 @@ +loopback_users.guest = false +listeners.tcp.default = 5672 + +cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config +cluster_formation.classic_config.nodes.1 = rabbit@rabbit-1 +cluster_formation.classic_config.nodes.2 = rabbit@rabbit-2 +cluster_formation.classic_config.nodes.3 = rabbit@rabbit-3 \ No newline at end of file diff --git a/messaging/rabbitmq/config/rabbit-3/rabbitmq.conf b/messaging/rabbitmq/config/rabbit-3/rabbitmq.conf new file mode 100644 index 0000000..960d3ab --- /dev/null +++ b/messaging/rabbitmq/config/rabbit-3/rabbitmq.conf @@ -0,0 +1,7 @@ +loopback_users.guest = false +listeners.tcp.default = 5672 + +cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config +cluster_formation.classic_config.nodes.1 = rabbit@rabbit-1 +cluster_formation.classic_config.nodes.2 = rabbit@rabbit-2 +cluster_formation.classic_config.nodes.3 = rabbit@rabbit-3 \ No newline at end of file diff --git a/messaging/rabbitmq/kubernetes/rabbit-configmap.yaml b/messaging/rabbitmq/kubernetes/rabbit-configmap.yaml new file mode 100644 index 0000000..3352d2d --- /dev/null +++ b/messaging/rabbitmq/kubernetes/rabbit-configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: rabbitmq-config +data: + enabled_plugins: | + [rabbitmq_federation,rabbitmq_management,rabbitmq_peer_discovery_k8s]. + rabbitmq.conf: | + loopback_users.guest = false + listeners.tcp.default = 5672 + + cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s + cluster_formation.k8s.host = kubernetes.default.svc.cluster.local + cluster_formation.k8s.address_type = hostname + cluster_formation.node_cleanup.only_log_warning = true + ##cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config + ##cluster_formation.classic_config.nodes.1 = rabbit@rabbitmq-0.rabbitmq.rabbits.svc.cluster.local + ##cluster_formation.classic_config.nodes.2 = rabbit@rabbitmq-1.rabbitmq.rabbits.svc.cluster.local + ##cluster_formation.classic_config.nodes.3 = rabbit@rabbitmq-2.rabbitmq.rabbits.svc.cluster.local \ No newline at end of file diff --git a/messaging/rabbitmq/kubernetes/rabbit-rbac.yaml b/messaging/rabbitmq/kubernetes/rabbit-rbac.yaml new file mode 100644 index 0000000..fda2bf6 --- /dev/null +++ b/messaging/rabbitmq/kubernetes/rabbit-rbac.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: rabbitmq +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: rabbitmq +rules: +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get + - list + - watch +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: rabbitmq + namespace: rabbits +subjects: +- kind: ServiceAccount + name: rabbitmq + namespace: rabbits +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: rabbitmq \ No newline at end of file diff --git a/messaging/rabbitmq/kubernetes/rabbit-secret.yaml b/messaging/rabbitmq/kubernetes/rabbit-secret.yaml new file mode 100644 index 0000000..0011c73 --- /dev/null +++ b/messaging/rabbitmq/kubernetes/rabbit-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: rabbit-secret +type: Opaque +data: + # echo -n "cookie-value" | base64 + RABBITMQ_ERLANG_COOKIE: V0lXVkhDRFRDSVVBV0FOTE1RQVc= \ No newline at end of file diff --git a/messaging/rabbitmq/kubernetes/rabbit-statefulset.yaml b/messaging/rabbitmq/kubernetes/rabbit-statefulset.yaml new file mode 100644 index 0000000..988745e --- /dev/null +++ b/messaging/rabbitmq/kubernetes/rabbit-statefulset.yaml @@ -0,0 +1,101 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: rabbitmq +spec: + serviceName: rabbitmq + replicas: 4 + selector: + matchLabels: + app: rabbitmq + template: + metadata: + labels: + app: rabbitmq + spec: + serviceAccountName: rabbitmq + initContainers: + - name: config + image: busybox + command: ['/bin/sh', '-c', 'cp /tmp/config/rabbitmq.conf /config/rabbitmq.conf && ls -l /config/ && cp /tmp/config/enabled_plugins /etc/rabbitmq/enabled_plugins'] + volumeMounts: + - name: config + mountPath: /tmp/config/ + readOnly: false + - name: config-file + mountPath: /config/ + - name: plugins-file + mountPath: /etc/rabbitmq/ + containers: + - name: rabbitmq + image: rabbitmq:3.8-management + ports: + - containerPort: 4369 + name: discovery + - containerPort: 5672 + name: amqp + env: + - name: RABBIT_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: RABBIT_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: RABBITMQ_NODENAME + value: rabbit@$(RABBIT_POD_NAME).rabbitmq.$(RABBIT_POD_NAMESPACE).svc.cluster.local + - name: RABBITMQ_USE_LONGNAME + value: "true" + - name: RABBITMQ_CONFIG_FILE + value: "/config/rabbitmq" + - name: RABBITMQ_ERLANG_COOKIE + valueFrom: + secretKeyRef: + name: rabbit-secret + key: RABBITMQ_ERLANG_COOKIE + - name: K8S_HOSTNAME_SUFFIX + value: .rabbitmq.$(RABBIT_POD_NAMESPACE).svc.cluster.local + volumeMounts: + - name: data + mountPath: /var/lib/rabbitmq + readOnly: false + - name: config-file + mountPath: /config/ + - name: plugins-file + mountPath: /etc/rabbitmq/ + volumes: + - name: config-file + emptyDir: {} + - name: plugins-file + emptyDir: {} + - name: config + configMap: + name: rabbitmq-config + defaultMode: 0755 + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: "standard" + resources: + requests: + storage: 50Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq +spec: + clusterIP: None + ports: + - port: 4369 + targetPort: 4369 + name: discovery + - port: 5672 + targetPort: 5672 + name: amqp + selector: + app: rabbitmq \ No newline at end of file diff --git a/messaging/rabbitmq/kubernetes/readme.md b/messaging/rabbitmq/kubernetes/readme.md new file mode 100644 index 0000000..5cc5507 --- /dev/null +++ b/messaging/rabbitmq/kubernetes/readme.md @@ -0,0 +1,60 @@ +# RabbitMQ on Kubernetes + +Create a cluster with [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) + +``` +kind create cluster --name rabbit --image kindest/node:v1.18.4 +``` + +## Namespace + +``` +kubectl create ns rabbits +``` + +## Storage Class + +``` +kubectl get storageclass +NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE +standard (default) rancher.io/local-path Delete WaitForFirstConsumer false 84s +``` + +## Deployment + +``` +kubectl apply -n rabbits -f .\kubernetes\rabbit-rbac.yaml +kubectl apply -n rabbits -f .\kubernetes\rabbit-configmap.yaml +kubectl apply -n rabbits -f .\kubernetes\rabbit-secret.yaml +kubectl apply -n rabbits -f .\kubernetes\rabbit-statefulset.yaml +``` + +## Access the UI + +``` +kubectl -n rabbits port-forward rabbitmq-0 8080:15672 +``` +Go to htttp://localhost:8080
+Username: `guest`
+Password: `guest`
+ +# Message Publisher + +``` + +cd messaging\rabbitmq\applications\publisher +docker build . -t aimvector/rabbitmq-publisher:v1.0.0 + +kubectl apply -f rabbits deployment.yaml +``` + +# Automatic Synchronization + +https://www.rabbitmq.com/ha.html#unsynchronised-mirrors + +``` +rabbitmqctl set_policy ha-fed \ + ".*" '{"federation-upstream-set":"all", "ha-sync-mode":"automatic", "ha-mode":"nodes", "ha-params":["rabbit@rabbitmq-0.rabbitmq.rabbits.svc.cluster.local","rabbit@rabbitmq-1.rabbitmq.rabbits.svc.cluster.local","rabbit@rabbitmq-2.rabbitmq.rabbits.svc.cluster.local"]}' \ + --priority 1 \ + --apply-to queues +``` \ No newline at end of file diff --git a/messaging/rabbitmq/readme.md b/messaging/rabbitmq/readme.md index bb56e73..4d1a081 100644 --- a/messaging/rabbitmq/readme.md +++ b/messaging/rabbitmq/readme.md @@ -2,44 +2,17 @@ Docker image over [here](https://hub.docker.com/_/rabbitmq) ``` +# run a standalone instance docker network create rabbits -docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-1 --name rabbit-1 rabbitmq:3.8 -docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-2 --name rabbit-2 rabbitmq:3.8 -docker run -d --rm --net rabbits -e RABBITMQ_ERLANG_COOKIE=DSHEVCXBBETJJVJWTOWT --hostname rabbit-3 --name rabbit-3 rabbitmq:3.8 +docker run -d --rm --net rabbits --hostname rabbit-1 --name rabbit-1 rabbitmq:3.8 # how to grab existing erlang cookie docker exec -it rabbit-1 cat /var/lib/rabbitmq/.erlang.cookie -DSHEVCXBBETJJVJWTOWT +# clean up docker rm -f rabbit-1 -docker rm -f rabbit-2 -docker rm -f rabbit-3 ``` -``` -docker exec -it rabbit-1 rabbitmqctl cluster_status - -#join node 2 - -docker exec -it rabbit-2 rabbitmqctl stop_app -docker exec -it rabbit-2 rabbitmqctl reset -docker exec -it rabbit-2 rabbitmqctl join_cluster rabbit@rabbit-1 -docker exec -it rabbit-2 rabbitmqctl start_app -docker exec -it rabbit-2 rabbitmqctl cluster_status - - -#join node 3 -docker exec -it rabbit-3 rabbitmqctl stop_app -docker exec -it rabbit-3 rabbitmqctl reset -docker exec -it rabbit-3 rabbitmqctl join_cluster rabbit@rabbit-1 -docker exec -it rabbit-3 rabbitmqctl start_app -docker exec -it rabbit-3 rabbitmqctl cluster_status - -``` - - - - # Management ``` @@ -63,11 +36,130 @@ docker exec -it rabbit-3 rabbitmq-plugins enable rabbitmq_management # Message Publisher ``` -docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest -p 80:80 publisher + +cd messaging\rabbitmq\applications\publisher +docker build . -t aimvector/rabbitmq-publisher:v1.0.0 + +docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest -p 80:80 aimvector/rabbitmq-publisher:v1.0.0 ``` # Message Consumer ``` -docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest consumer + +docker build . -t aimvector/rabbitmq-consumer:v1.0.0 +docker run -it --rm --net rabbits -e RABBIT_HOST=rabbit-1 -e RABBIT_PORT=5672 -e RABBIT_USERNAME=guest -e RABBIT_PASSWORD=guest aimvector/rabbitmq-consumer:v1.0.0 ``` + +# Clustering + +https://www.rabbitmq.com/cluster-formation.html + +## Note + +Remember we will need the Erlang Cookie to allow instances to authenticate with each other. + +# Manual Clustering + +``` + +docker exec -it rabbit-1 rabbitmqctl cluster_status + +#join node 2 + +docker exec -it rabbit-2 rabbitmqctl stop_app +docker exec -it rabbit-2 rabbitmqctl reset +docker exec -it rabbit-2 rabbitmqctl join_cluster rabbit@rabbit-1 +docker exec -it rabbit-2 rabbitmqctl start_app +docker exec -it rabbit-2 rabbitmqctl cluster_status + +#join node 3 +docker exec -it rabbit-3 rabbitmqctl stop_app +docker exec -it rabbit-3 rabbitmqctl reset +docker exec -it rabbit-3 rabbitmqctl join_cluster rabbit@rabbit-1 +docker exec -it rabbit-3 rabbitmqctl start_app +docker exec -it rabbit-3 rabbitmqctl cluster_status + +``` + +# Automated Clustering + +``` +docker run -d --rm --net rabbits ` +-v ${PWD}/config/rabbit-1/:/config/ ` +-e RABBITMQ_CONFIG_FILE=/config/rabbitmq ` +-e RABBITMQ_ERLANG_COOKIE=WIWVHCDTCIUAWANLMQAW ` +--hostname rabbit-1 ` +--name rabbit-1 ` +-p 8081:15672 ` +rabbitmq:3.8-management + +docker run -d --rm --net rabbits ` +-v ${PWD}/config/rabbit-2/:/config/ ` +-e RABBITMQ_CONFIG_FILE=/config/rabbitmq ` +-e RABBITMQ_ERLANG_COOKIE=WIWVHCDTCIUAWANLMQAW ` +--hostname rabbit-2 ` +--name rabbit-2 ` +-p 8082:15672 ` +rabbitmq:3.8-management + +docker run -d --rm --net rabbits ` +-v ${PWD}/config/rabbit-3/:/config/ ` +-e RABBITMQ_CONFIG_FILE=/config/rabbitmq ` +-e RABBITMQ_ERLANG_COOKIE=WIWVHCDTCIUAWANLMQAW ` +--hostname rabbit-3 ` +--name rabbit-3 ` +-p 8083:15672 ` +rabbitmq:3.8-management + +#NODE 1 : MANAGEMENT http://localhost:8081 +#NODE 2 : MANAGEMENT http://localhost:8082 +#NODE 3 : MANAGEMENT http://localhost:8083 + +# enable federation plugin +docker exec -it rabbit-1 rabbitmq-plugins enable rabbitmq_federation +docker exec -it rabbit-2 rabbitmq-plugins enable rabbitmq_federation +docker exec -it rabbit-3 rabbitmq-plugins enable rabbitmq_federation + +``` + +# Basic Queue Mirroring + +``` +docker exec -it rabbit-1 bash + +# https://www.rabbitmq.com/ha.html#mirroring-arguments + +rabbitmqctl set_policy ha-fed \ + ".*" '{"federation-upstream-set":"all", "ha-mode":"nodes", "ha-params":["rabbit@rabbit-1","rabbit@rabbit-2","rabbit@rabbit-3"]}' \ + --priority 1 \ + --apply-to queues +``` + +# Automatic Synchronization + +https://www.rabbitmq.com/ha.html#unsynchronised-mirrors + +``` +rabbitmqctl set_policy ha-fed \ + ".*" '{"federation-upstream-set":"all", "ha-sync-mode":"automatic", "ha-mode":"nodes", "ha-params":["rabbit@rabbit-1","rabbit@rabbit-2","rabbit@rabbit-3"]}' \ + --priority 1 \ + --apply-to queues +``` + +# Further Reading + +https://www.rabbitmq.com/ha.html + + +# Clean up + +``` +docker rm -f rabbit-1 +docker rm -f rabbit-2 +docker rm -f rabbit-3 +``` + +# RabbitMQ on Kubernetes + +Checkout the Kubernetes walkthrough [here](./kubernetes/readme.md) \ No newline at end of file