rabbitmq on k8s

This commit is contained in:
marcel-dempers 2020-07-31 15:04:07 +10:00
parent ac1743d0d4
commit e8c05bbe4e
13 changed files with 440 additions and 49 deletions

View File

@ -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
}

View File

@ -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

View File

@ -12,7 +12,6 @@ COPY publisher.go /src
RUN go build publisher.go
FROM alpine as runtime
COPY --from=build /src/publisher /app/publisher

View File

@ -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!")
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: rabbit-secret
type: Opaque
data:
# echo -n "cookie-value" | base64
RABBITMQ_ERLANG_COOKIE: V0lXVkhDRFRDSVVBV0FOTE1RQVc=

View File

@ -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

View File

@ -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 <br/>
Username: `guest` <br/>
Password: `guest` <br/>
# 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
```

View File

@ -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)