initial commit
This commit is contained in:
commit
3e748f9303
20
LICENSE
Normal file
20
LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 Shane Wadleigh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# ansible-role-redis-k8s
|
||||
|
||||
Ansible role for deploying a statefulset for redis and sentinel
|
||||
|
||||
- https://redis.io/
|
||||
- https://github.com/redis
|
||||
98
defaults/main.yml
Normal file
98
defaults/main.yml
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
redis_deployment_name: redis
|
||||
redis_namespace: "{{ redis_deployment_name }}"
|
||||
|
||||
redis_image: docker.io/redis
|
||||
redis_image_tag: latest
|
||||
|
||||
# KUBERNETES SETTINGS
|
||||
redis_labels:
|
||||
app.kubernetes.io/name: "{{ redis_deployment_name }}"
|
||||
app.kubernetes.io/managed-by: ansible
|
||||
|
||||
redis_selector:
|
||||
app.kubernetes.io/name: "{{ redis_deployment_name }}"
|
||||
|
||||
# requiredDuringSchedulingIgnoredDuringExecution,preferredDuringSchedulingIgnoredDuringExecution
|
||||
redis_anti_affinity_type: requiredDuringSchedulingIgnoredDuringExecution
|
||||
|
||||
# DoNotSchedule, ScheduleAnyway
|
||||
redis_topology_constraint: DoNotSchedule
|
||||
|
||||
redis_replicas: 3
|
||||
redis_min_available: 2
|
||||
redis_max_skew: 1
|
||||
redis_max_unavailable: 1
|
||||
|
||||
redis_strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: "{{ redis_max_unavailable }}"
|
||||
|
||||
# security and resource limits
|
||||
# redis_pod_security_context:
|
||||
# runAsNonRoot: true
|
||||
# fsGroup: 1000
|
||||
# seccompProfile: { type: RuntimeDefault }
|
||||
|
||||
# redis_container_security_context:
|
||||
# allowPrivilegeEscalation: false
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# seccompProfile: { type: RuntimeDefault }
|
||||
|
||||
# REDIS SETTINGS
|
||||
redis_port: 6379
|
||||
redis_parallel_syncs: 1
|
||||
redis_protected_mode: "no"
|
||||
redis_append_only: "yes"
|
||||
redis_max_memory: 256mb
|
||||
redis_max_memory_policy: allkeys-lru
|
||||
redis_storage_class: local-path
|
||||
redis_storage_size: "1Gi"
|
||||
|
||||
redis_container_resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
|
||||
redis_command_off: |
|
||||
/bin/sh -lc '
|
||||
cp /config/redis.conf /conf/redis.conf
|
||||
if hostname | grep -q -- "-0$"; then
|
||||
sed -i "/^replicaof /d" /conf/redis.conf
|
||||
fi
|
||||
exec redis-server /conf/redis.conf
|
||||
'
|
||||
|
||||
redis_command: >
|
||||
/bin/sh -lc |
|
||||
cp /config/redis.conf /conf/redis.conf
|
||||
if hostname | grep -q -- '-0$'; then
|
||||
sed -i '/^replicaof /d' /conf/redis.conf
|
||||
fi
|
||||
exec redis-server /conf/redis.conf
|
||||
|
||||
# redis_server_env:
|
||||
# OAUTH2_PROVIDER_APPLICATION_MODEL: "{{ redis_oauth_application }}"
|
||||
|
||||
# SENTINEL SETTINGS
|
||||
redis_sentinel_port: 26379
|
||||
redis_sentinel_quorum: 2
|
||||
redis_sentinel_down_after_ms: 10000
|
||||
redis_sentinel_failover_timeout: 10000
|
||||
redis_sentinel_master: "mymaster"
|
||||
|
||||
redis_sentinel_container_resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
|
||||
redis_sentinel_command: >
|
||||
/bin/sh -lc exec redis-sentinel /config/sentinel.conf
|
||||
28
handlers/main.yml
Normal file
28
handlers/main.yml
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
- name: REDIS HANDLER check for existing deployment {{ redis_deployment_name }}
|
||||
listen: redis-restart
|
||||
kubernetes.core.k8s_info:
|
||||
api_version: apps/v1
|
||||
kind: StatefulSet
|
||||
name: "{{ redis_deployment_name }}"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
register: redis_deploy_info
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: REDIS HANDLER restart deployment {{ redis_deployment_name }}
|
||||
listen: redis-restart
|
||||
kubernetes.core.k8s:
|
||||
api_version: apps/v1
|
||||
kind: StatefulSet
|
||||
name: "{{ redis_deployment_name }}"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
state: patched
|
||||
definition:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/restartedAt: "{{ lookup('pipe', 'date -u +%Y-%m-%dT%H:%M:%SZ') }}"
|
||||
when:
|
||||
- redis_deploy_info.resources | length > 0
|
||||
40
meta/main.yml
Normal file
40
meta/main.yml
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
dependencies: []
|
||||
|
||||
galaxy_info:
|
||||
role_name: redis_k8s
|
||||
author: srw
|
||||
description: Ansible role for deploying redis and sentinel to kubernetes
|
||||
company: "NMD, LLC"
|
||||
license: "license (BSD, MIT)"
|
||||
min_ansible_version: "2.10"
|
||||
platforms:
|
||||
- name: Fedora
|
||||
versions:
|
||||
- all
|
||||
- name: Debian
|
||||
versions:
|
||||
- buster
|
||||
- bullseye
|
||||
- bookworm
|
||||
- trixie
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- bionic
|
||||
- focal
|
||||
- jammy
|
||||
- name: Alpine
|
||||
version:
|
||||
- all
|
||||
- name: ArchLinux
|
||||
versions:
|
||||
- all
|
||||
galaxy_tags:
|
||||
- server
|
||||
- system
|
||||
- containers
|
||||
- kubernetes
|
||||
- k8s
|
||||
- k3s
|
||||
- rke2
|
||||
- redis
|
||||
178
tasks/deploy.yml
Normal file
178
tasks/deploy.yml
Normal file
@ -0,0 +1,178 @@
|
||||
---
|
||||
- name: REDIS DEPLOY
|
||||
tags:
|
||||
- redis
|
||||
- redis-deploy
|
||||
block:
|
||||
|
||||
- name: REDIS DEPLOY PodDisruptionBudget
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
definition:
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: "{{ redis_deployment_name }}"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
spec:
|
||||
minAvailable: "{{ redis_min_available }}"
|
||||
selector:
|
||||
matchLabels: "{{ redis_selector }}"
|
||||
|
||||
- name: REDIS DEPLOY Create Redis StatefulSet with Sentinel Sidecar
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
server_side_apply:
|
||||
field_manager: ansible
|
||||
force_conflicts: true
|
||||
wait: true
|
||||
wait_timeout: 600
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: "{{ redis_deployment_name }}"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
spec:
|
||||
serviceName: redis
|
||||
replicas: "{{ redis_replicas }}"
|
||||
strategy: "{{ redis_strategy }}"
|
||||
selector:
|
||||
matchLabels: "{{ redis_selector }}"
|
||||
template:
|
||||
metadata:
|
||||
labels: "{{ redis_labels }}"
|
||||
annotations:
|
||||
rollout.redis/token: "{{ redis_rollout_token | d(omit) }}"
|
||||
spec:
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: "{{ redis_max_skew }}"
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: "{{ redis_topology_constraint }}"
|
||||
labelSelector:
|
||||
matchLabels: "{{ redis_selector }}"
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
"{{ redis_anti_affinity_type }}":
|
||||
- labelSelector:
|
||||
matchLabels: "{{ redis_selector }}"
|
||||
topologyKey: kubernetes.io/hostname
|
||||
securityContext: "{{ redis_pod_security_context | d(omit) }}"
|
||||
initContainers:
|
||||
- name: copy-sentinel-config
|
||||
image: "{{ redis_image }}:{{ redis_image_tag }}"
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
cp /conf-ro/sentinel.conf /conf-rw/sentinel.conf
|
||||
volumeMounts:
|
||||
- name: conf-ro
|
||||
mountPath: /conf-ro
|
||||
readOnly: true
|
||||
- name: conf-rw
|
||||
mountPath: /conf-rw
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
containers:
|
||||
# REDIS
|
||||
- name: redis
|
||||
image: "{{ redis_image }}:{{ redis_image_tag }}"
|
||||
securityContext: "{{ redis_container_security_context | d(omit) }}"
|
||||
resources: "{{ redis_container_resources | d(omit) }}"
|
||||
args:
|
||||
- redis-server
|
||||
- /conf/redis.conf
|
||||
ports:
|
||||
- name: redis
|
||||
containerPort: "{{ redis_port }}"
|
||||
volumeMounts:
|
||||
- name: conf-ro
|
||||
mountPath: /conf/redis.conf
|
||||
subPath: redis.conf
|
||||
readOnly: true
|
||||
- name: data
|
||||
mountPath: /data
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: redis
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- redis-cli
|
||||
- -p
|
||||
- "{{ redis_port | string }}"
|
||||
- ping
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ redis_deployment_name }}-secrets"
|
||||
|
||||
# SENTINEL
|
||||
- name: redis-sentinel
|
||||
image: "{{ redis_image }}:{{ redis_image_tag }}"
|
||||
securityContext: "{{ redis_container_security_context | d(omit) }}"
|
||||
resources: "{{ redis_sentinel_container_resources | d(omit) }}"
|
||||
args:
|
||||
- redis-sentinel
|
||||
- /conf/sentinel.conf
|
||||
ports:
|
||||
- name: redis-sentinel
|
||||
containerPort: "{{ redis_sentinel_port }}"
|
||||
volumeMounts:
|
||||
- name: conf-rw
|
||||
mountPath: /conf
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: redis-sentinel
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- redis-cli
|
||||
- -p
|
||||
- "{{ redis_sentinel_port | string }}"
|
||||
- ping
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: "{{ redis_deployment_name }}-secrets"
|
||||
|
||||
volumes:
|
||||
- name: conf-ro
|
||||
configMap:
|
||||
name: "{{ redis_deployment_name }}-configs"
|
||||
items:
|
||||
- key: redis.conf
|
||||
path: redis.conf
|
||||
- key: sentinel.conf
|
||||
path: sentinel.conf
|
||||
- name: conf-rw
|
||||
emptyDir: {}
|
||||
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
labels: "{{ elasticsearch_labels }}"
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: "{{ redis_storage_class }}"
|
||||
resources:
|
||||
requests:
|
||||
storage: "{{ redis_storage_size }}"
|
||||
91
tasks/environment.yml
Normal file
91
tasks/environment.yml
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
- name: REDIS ENVIRONMENT SETUP
|
||||
tags:
|
||||
- redis
|
||||
- redis-env
|
||||
block:
|
||||
|
||||
- name: REDIS ENVIRONMENT create configmaps
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
server_side_apply:
|
||||
field_manager: ansible
|
||||
force_conflicts: true
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: "{{ redis_deployment_name }}-configs"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
data:
|
||||
redis.conf: |
|
||||
dir /data
|
||||
bind 0.0.0.0
|
||||
port {{ redis_port }}
|
||||
protected-mode {{ redis_protected_mode }}
|
||||
appendonly {{ redis_append_only }}
|
||||
maxmemory {{ redis_max_memory }}
|
||||
maxmemory-policy {{ redis_max_memory_policy }}
|
||||
|
||||
# --- Auth (server password) ---
|
||||
requirepass ${REDIS_PASSWORD}
|
||||
# Replicas authenticate to the master with the same password:
|
||||
masterauth ${REDIS_PASSWORD}
|
||||
|
||||
# Optional replication safety knobs (recommended if you want write safety):
|
||||
# min-replicas-to-write 1
|
||||
# min-replicas-max-lag 5
|
||||
|
||||
# Optional announcements (usually not required inside K8s):
|
||||
# replica-read-only yes
|
||||
# replica-announce-ip ${POD_IP}
|
||||
# replica-announce-port {{ redis_port }}
|
||||
|
||||
sentinel.conf: |
|
||||
port {{ redis_sentinel_port }}
|
||||
protected-mode {{ redis_protected_mode }}
|
||||
sentinel resolve-hostnames yes
|
||||
sentinel monitor {{ redis_sentinel_master }} redis-0.redis.{{ redis_namespace }}.svc.cluster.local {{ redis_port }} 2
|
||||
sentinel down-after-milliseconds {{ redis_sentinel_master }} {{ redis_sentinel_down_after_ms }}
|
||||
sentinel failover-timeout {{ redis_sentinel_master }} {{ redis_sentinel_failover_timeout }}
|
||||
sentinel parallel-syncs {{ redis_sentinel_master }} {{ redis_parallel_syncs }}
|
||||
|
||||
# Auth for Sentinel -> Redis (master/replicas)
|
||||
sentinel auth-pass {{ redis_sentinel_master }} ${REDIS_PASSWORD}
|
||||
|
||||
# If you want to protect the sentinel port itself, uncomment and make clients AUTH:
|
||||
# requirepass ${REDIS_PASSWORD}
|
||||
|
||||
- name: REDIS ENVIRONMENT base64 encode secrets
|
||||
no_log: true
|
||||
ansible.builtin.set_fact:
|
||||
redis_secrets_b64: "{{ redis_secrets_b64 | default({}) | combine({item.key: (item.value | b64encode)}) }}"
|
||||
loop: "{{ redis_secrets | dict2items }}"
|
||||
|
||||
# - name: REDIS ENVIRONMENT verify secrets
|
||||
# debug:
|
||||
# msg:
|
||||
# - "{{ redis_secrets }}"
|
||||
# - "{{ redis_secrets_b64 }}"
|
||||
|
||||
- name: REDIS ENVIRONMENT create secrets
|
||||
no_log: true
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
server_side_apply:
|
||||
field_manager: ansible
|
||||
force_conflicts: true
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: "{{ redis_deployment_name }}-secrets"
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
type: Opaque
|
||||
data: "{{ redis_secrets_b64 }}"
|
||||
notify:
|
||||
- redis-restart
|
||||
when:
|
||||
- redis_secrets is defined
|
||||
34
tasks/main.yml
Normal file
34
tasks/main.yml
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
- name: REDIS KUBERNETES DEPLOYMENT
|
||||
delegate_to: localhost
|
||||
connection: local
|
||||
become: false
|
||||
tags: never
|
||||
block:
|
||||
- name: REDIS create namespace {{ redis_namespace }}
|
||||
tags: [redis, redis-env, redis-services, redis-deploy]
|
||||
kubernetes.core.k8s:
|
||||
state: present
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: "{{ redis_namespace }}"
|
||||
|
||||
- name: REDIS setup environment
|
||||
ansible.builtin.include_tasks: environment.yml
|
||||
tags:
|
||||
- redis
|
||||
- redis-env
|
||||
|
||||
- name: REDIS create services
|
||||
ansible.builtin.include_tasks: services.yml
|
||||
tags:
|
||||
- redis
|
||||
- redis-services
|
||||
|
||||
- name: REDIS create statefulset
|
||||
ansible.builtin.include_tasks: deploy.yml
|
||||
tags:
|
||||
- redis
|
||||
- redis-deploy
|
||||
44
tasks/services.yml
Normal file
44
tasks/services.yml
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
- name: REDIS SERVICES
|
||||
tags:
|
||||
- redis
|
||||
- redis-services
|
||||
block:
|
||||
|
||||
- name: REDIS SERVICES create headless service
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
spec:
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: true
|
||||
selector: "{{ redis_selector }}"
|
||||
ports:
|
||||
- name: redis
|
||||
port: "{{ redis_port }}"
|
||||
targetPort: "{{ redis_port }}"
|
||||
|
||||
- name: REDIS SERVICES create sentinel headless service
|
||||
kubernetes.core.k8s:
|
||||
state: "{{ redis_state | d('present') }}"
|
||||
definition:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-sentinel
|
||||
namespace: "{{ redis_namespace }}"
|
||||
labels: "{{ redis_labels }}"
|
||||
spec:
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: true
|
||||
selector: "{{ redis_selector }}"
|
||||
ports:
|
||||
- name: redis-sentinel
|
||||
port: "{{ redis_sentinel_port }}"
|
||||
targetPort: "{{ redis_sentinel_port }}"
|
||||
Loading…
Reference in New Issue
Block a user