This commit is contained in:
450 changed files with 86070 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
Redis can be accessed via {{ if ne (int .Values.redis.port) 0 }}port {{ .Values.redis.port }}{{ end }} {{ if .Values.redis.tlsPort }} tls-port {{ .Values.redis.tlsPort }}{{ end }} and Sentinel can be accessed via {{ if ne (int .Values.sentinel.port) 0 }}port {{ .Values.sentinel.port }}{{ end }} {{ if .Values.sentinel.tlsPort }} tls-port {{ .Values.sentinel.tlsPort }}{{ end }} on the following DNS name from within your cluster:
{{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
To connect to your Redis server:
{{- if .Values.auth }}
1. To retrieve the redis password:
echo $(kubectl get secret {{ template "redis-ha.fullname" . }} -o "jsonpath={.data['auth']}" | base64 --decode)
2. Connect to the Redis master pod that you can use as a client. By default the {{ template "redis-ha.fullname" . }}-server-0 pod is configured as the master:
kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 -n {{ .Release.Namespace }} -c redis -- sh
3. Connect using the Redis CLI (inside container):
redis-cli -a <REDIS-PASS-FROM-SECRET>
{{- else }}
1. Run a Redis pod that you can use as a client:
kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 -n {{ .Release.Namespace }} -c redis -- sh
2. Connect using the Redis CLI:
redis-cli -h {{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
{{- end }}

View File

@@ -0,0 +1,730 @@
{{/* vim: set filetype=mustache: */}}
{{- define "config-redis.conf" }}
{{- if .Values.redis.customConfig }}
{{ tpl .Values.redis.customConfig . | indent 4 }}
{{- else }}
dir "/data"
port {{ .Values.redis.port }}
{{- if .Values.sentinel.tlsPort }}
tls-port {{ .Values.redis.tlsPort }}
tls-cert-file /tls-certs/{{ .Values.tls.certFile }}
tls-key-file /tls-certs/{{ .Values.tls.keyFile }}
{{- if .Values.tls.dhParamsFile }}
tls-dh-params-file /tls-certs/{{ .Values.tls.dhParamsFile }}
{{- end }}
{{- if .Values.tls.caCertFile }}
tls-ca-cert-file /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
{{- if eq (default "yes" .Values.redis.authClients) "no"}}
tls-auth-clients no
{{- end }}
tls-replication {{ if .Values.redis.tlsReplication }}yes{{ else }}no{{ end }}
{{- end }}
{{- if .Values.redis.disableCommands }}
{{- range .Values.redis.disableCommands }}
rename-command {{ . }} ""
{{- end }}
{{- end }}
{{- range $key, $value := .Values.redis.config }}
{{- if kindIs "slice" $value }}
{{- range $value }}
{{ $key }} {{ . }}
{{- end }}
{{- else }}
{{ $key }} {{ $value }}
{{- end }}
{{- end }}
{{- if .Values.auth }}
requirepass replace-default-auth
masterauth replace-default-auth
{{- end }}
{{- end }}
{{- end }}
{{- define "config-sentinel.conf" }}
{{- if .Values.sentinel.customConfig }}
{{ tpl .Values.sentinel.customConfig . | indent 4 }}
{{- else }}
dir "/data"
port {{ .Values.sentinel.port }}
{{- if .Values.sentinel.bind }}
bind {{ .Values.sentinel.bind }}
{{- end }}
{{- if .Values.sentinel.tlsPort }}
tls-port {{ .Values.sentinel.tlsPort }}
tls-cert-file /tls-certs/{{ .Values.tls.certFile }}
tls-key-file /tls-certs/{{ .Values.tls.keyFile }}
{{- if .Values.tls.dhParamsFile }}
tls-dh-params-file /tls-certs/{{ .Values.tls.dhParamsFile }}
{{- end }}
{{- if .Values.tls.caCertFile }}
tls-ca-cert-file /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
{{- if eq (default "yes" .Values.sentinel.authClients) "no"}}
tls-auth-clients no
{{- end }}
tls-replication {{ if .Values.sentinel.tlsReplication }}yes{{ else }}no{{ end }}
{{- end }}
{{- range $key, $value := .Values.sentinel.config }}
{{- if eq "maxclients" $key }}
{{ $key }} {{ $value }}
{{- else }}
sentinel {{ $key }} {{ template "redis-ha.masterGroupName" $ }} {{ $value }}
{{- end }}
{{- end }}
{{- if .Values.auth }}
sentinel auth-pass {{ template "redis-ha.masterGroupName" . }} replace-default-auth
{{- end }}
{{- if .Values.sentinel.auth }}
requirepass replace-default-sentinel-auth
{{- end }}
{{- end }}
{{- end }}
{{- define "lib.sh" }}
sentinel_get_master() {
set +e
if [ "$SENTINEL_PORT" -eq 0 ]; then
redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
grep -E '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?s*$))'
else
redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
grep -E '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?s*$))'
fi
set -e
}
sentinel_get_master_retry() {
master=''
retry=${1}
sleep=3
for i in $(seq 1 "${retry}"); do
master=$(sentinel_get_master)
if [ -n "${master}" ]; then
break
fi
sleep $((sleep + i))
done
echo "${master}"
}
identify_master() {
echo "Identifying redis master (get-master-addr-by-name).."
echo " using sentinel ({{ template "redis-ha.fullname" . }}), sentinel group name ({{ template "redis-ha.masterGroupName" . }})"
MASTER="$(sentinel_get_master_retry 3)"
if [ -n "${MASTER}" ]; then
echo " $(date) Found redis master (${MASTER})"
else
echo " $(date) Did not find redis master (${MASTER})"
fi
}
sentinel_update() {
echo "Updating sentinel config.."
echo " evaluating sentinel id (\${SENTINEL_ID_${INDEX}})"
eval MY_SENTINEL_ID="\$SENTINEL_ID_${INDEX}"
echo " sentinel id (${MY_SENTINEL_ID}), sentinel grp (${MASTER_GROUP}), quorum (${QUORUM})"
sed -i "1s/^/sentinel myid ${MY_SENTINEL_ID}\\n/" "${SENTINEL_CONF}"
if [ "$SENTINEL_TLS_REPLICATION_ENABLED" = true ]; then
echo " redis master (${1}:${REDIS_TLS_PORT})"
sed -i "2s/^/sentinel monitor ${MASTER_GROUP} ${1} ${REDIS_TLS_PORT} ${QUORUM} \\n/" "${SENTINEL_CONF}"
else
echo " redis master (${1}:${REDIS_PORT})"
sed -i "2s/^/sentinel monitor ${MASTER_GROUP} ${1} ${REDIS_PORT} ${QUORUM} \\n/" "${SENTINEL_CONF}"
fi
echo "sentinel announce-ip ${ANNOUNCE_IP}" >> ${SENTINEL_CONF}
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " announce (${ANNOUNCE_IP}:${SENTINEL_TLS_PORT})"
echo "sentinel announce-port ${SENTINEL_TLS_PORT}" >> ${SENTINEL_CONF}
else
echo " announce (${ANNOUNCE_IP}:${SENTINEL_PORT})"
echo "sentinel announce-port ${SENTINEL_PORT}" >> ${SENTINEL_CONF}
fi
}
redis_update() {
echo "Updating redis config.."
if [ "$REDIS_TLS_REPLICATION_ENABLED" = true ]; then
echo " we are slave of redis master (${1}:${REDIS_TLS_PORT})"
echo "slaveof ${1} ${REDIS_TLS_PORT}" >> "${REDIS_CONF}"
echo "slave-announce-port ${REDIS_TLS_PORT}" >> ${REDIS_CONF}
else
echo " we are slave of redis master (${1}:${REDIS_PORT})"
echo "slaveof ${1} ${REDIS_PORT}" >> "${REDIS_CONF}"
echo "slave-announce-port ${REDIS_PORT}" >> ${REDIS_CONF}
fi
echo "slave-announce-ip ${ANNOUNCE_IP}" >> ${REDIS_CONF}
}
copy_config() {
echo "Copying default redis config.."
echo " to '${REDIS_CONF}'"
cp /readonly-config/redis.conf "${REDIS_CONF}"
echo "Copying default sentinel config.."
echo " to '${SENTINEL_CONF}'"
cp /readonly-config/sentinel.conf "${SENTINEL_CONF}"
}
setup_defaults() {
echo "Setting up defaults.."
echo " using statefulset index (${INDEX})"
if [ "${INDEX}" = "0" ]; then
echo "Setting this pod as master for redis and sentinel.."
echo " using announce (${ANNOUNCE_IP})"
redis_update "${ANNOUNCE_IP}"
sentinel_update "${ANNOUNCE_IP}"
echo " make sure ${ANNOUNCE_IP} is not a slave (slaveof no one)"
sed -i "s/^.*slaveof.*//" "${REDIS_CONF}"
else
echo "Getting redis master ip.."
echo " blindly assuming (${SERVICE}-announce-0) or (${SERVICE}-server-0) are master"
DEFAULT_MASTER="$(getent_hosts 0 | awk '{ print $1 }')"
if [ -z "${DEFAULT_MASTER}" ]; then
echo "Error: Unable to resolve redis master (getent hosts)."
exit 1
fi
echo " identified redis (may be redis master) ip (${DEFAULT_MASTER})"
echo "Setting default slave config for redis and sentinel.."
echo " using master ip (${DEFAULT_MASTER})"
redis_update "${DEFAULT_MASTER}"
sentinel_update "${DEFAULT_MASTER}"
fi
}
redis_ping() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
redis-cli -h "${MASTER}"{{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} ping
else
redis-cli -h "${MASTER}"{{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" ping
fi
set -e
}
redis_ping_retry() {
ping=''
retry=${1}
sleep=3
for i in $(seq 1 "${retry}"); do
if [ "$(redis_ping)" = "PONG" ]; then
ping='PONG'
break
fi
sleep $((sleep + i))
MASTER=$(sentinel_get_master)
done
echo "${ping}"
}
find_master() {
echo "Verifying redis master.."
if [ "$REDIS_PORT" -eq 0 ]; then
echo " ping (${MASTER}:${REDIS_TLS_PORT})"
else
echo " ping (${MASTER}:${REDIS_PORT})"
fi
if [ "$(redis_ping_retry 3)" != "PONG" ]; then
echo " $(date) Can't ping redis master (${MASTER})"
echo "Attempting to force failover (sentinel failover).."
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " on sentinel (${SERVICE}:${SENTINEL_TLS_PORT}), sentinel grp (${MASTER_GROUP})"
if redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel failover "${MASTER_GROUP}" | grep -q 'NOGOODSLAVE' ; then
echo " $(date) Failover returned with 'NOGOODSLAVE'"
echo "Setting defaults for this pod.."
setup_defaults
return 0
fi
else
echo " on sentinel (${SERVICE}:${SENTINEL_PORT}), sentinel grp (${MASTER_GROUP})"
if redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel failover "${MASTER_GROUP}" | grep -q 'NOGOODSLAVE' ; then
echo " $(date) Failover returned with 'NOGOODSLAVE'"
echo "Setting defaults for this pod.."
setup_defaults
return 0
fi
fi
echo "Hold on for 10sec"
sleep 10
echo "We should get redis master's ip now. Asking (get-master-addr-by-name).."
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " sentinel (${SERVICE}:${SENTINEL_TLS_PORT}), sentinel grp (${MASTER_GROUP})"
else
echo " sentinel (${SERVICE}:${SENTINEL_PORT}), sentinel grp (${MASTER_GROUP})"
fi
MASTER="$(sentinel_get_master)"
if [ "${MASTER}" ]; then
echo " $(date) Found redis master (${MASTER})"
echo "Updating redis and sentinel config.."
sentinel_update "${MASTER}"
redis_update "${MASTER}"
else
echo "$(date) Error: Could not failover, exiting..."
exit 1
fi
else
echo " $(date) Found reachable redis master (${MASTER})"
echo "Updating redis and sentinel config.."
sentinel_update "${MASTER}"
redis_update "${MASTER}"
fi
}
redis_ro_update() {
echo "Updating read-only redis config.."
echo " redis.conf set 'replica-priority 0'"
echo "replica-priority 0" >> ${REDIS_CONF}
}
getent_hosts() {
index=${1:-${INDEX}}
service="${SERVICE}-announce-${index}"
host=$(getent hosts "${service}")
echo "${host}"
}
identify_announce_ip() {
echo "Identify announce ip for this pod.."
echo " using (${SERVICE}-announce-${INDEX}) or (${SERVICE}-server-${INDEX})"
ANNOUNCE_IP=$(getent_hosts | awk '{ print $1 }')
echo " identified announce (${ANNOUNCE_IP})"
}
{{- end }}
{{- define "vars.sh" }}
HOSTNAME="$(hostname)"
{{- if .Values.ro_replicas }}
RO_REPLICAS="{{ .Values.ro_replicas }}"
{{- end }}
INDEX="${HOSTNAME##*-}"
SENTINEL_PORT={{ .Values.sentinel.port }}
ANNOUNCE_IP=''
MASTER=''
MASTER_GROUP="{{ template "redis-ha.masterGroupName" . }}"
QUORUM="{{ .Values.sentinel.quorum }}"
REDIS_CONF=/data/conf/redis.conf
REDIS_PORT={{ .Values.redis.port }}
REDIS_TLS_PORT={{ .Values.redis.tlsPort }}
SENTINEL_CONF=/data/conf/sentinel.conf
SENTINEL_TLS_PORT={{ .Values.sentinel.tlsPort }}
SERVICE={{ template "redis-ha.fullname" . }}
SENTINEL_TLS_REPLICATION_ENABLED={{ default false .Values.sentinel.tlsReplication }}
REDIS_TLS_REPLICATION_ENABLED={{ default false .Values.redis.tlsReplication }}
{{- end }}
{{- define "config-init.sh" }}
echo "$(date) Start..."
{{- include "vars.sh" . }}
set -eu
{{- include "lib.sh" . }}
mkdir -p /data/conf/
echo "Initializing config.."
copy_config
# where is redis master
identify_master
identify_announce_ip
if [ -z "${ANNOUNCE_IP}" ]; then
"Error: Could not resolve the announce ip for this pod."
exit 1
elif [ "${MASTER}" ]; then
find_master
else
setup_defaults
fi
{{- if .Values.ro_replicas }}
# works only if index is less than 10
echo "Verifying redis read-only replica.."
echo " we have RO_REPLICAS='${RO_REPLICAS}' with INDEX='${INDEX}'"
if echo "${RO_REPLICAS}" | grep -q "${INDEX}" ; then
redis_ro_update
fi
{{- end }}
if [ "${AUTH:-}" ]; then
echo "Setting redis auth values.."
ESCAPED_AUTH=$(echo "${AUTH}" | sed -e 's/[\/&]/\\&/g');
sed -i "s/replace-default-auth/${ESCAPED_AUTH}/" "${REDIS_CONF}" "${SENTINEL_CONF}"
fi
if [ "${SENTINELAUTH:-}" ]; then
echo "Setting sentinel auth values"
ESCAPED_AUTH_SENTINEL=$(echo "$SENTINELAUTH" | sed -e 's/[\/&]/\\&/g');
sed -i "s/replace-default-sentinel-auth/${ESCAPED_AUTH_SENTINEL}/" "$SENTINEL_CONF"
fi
echo "$(date) Ready..."
{{- end }}
{{- define "trigger-failover-if-master.sh" }}
{{- if or (eq (int .Values.redis.port) 0) (eq (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
get_redis_role() {
is_master=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if (int .Values.redis.port) }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
info | grep -c 'role:master' || true
)
}
get_redis_role
if [[ "$is_master" -eq 1 ]]; then
echo "This node is currently master, we trigger a failover."
{{- $masterGroupName := include "redis-ha.masterGroupName" . }}
response=$(
redis-cli \
{{- if .Values.sentinel.auth }}
-a "${SENTINELAUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if (int .Values.sentinel.port) }}
-p {{ .Values.sentinel.port }} \
{{- else }}
-p {{ .Values.sentinel.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
SENTINEL failover {{ $masterGroupName }}
)
if [[ "$response" != "OK" ]] ; then
echo "$response"
exit 1
fi
timeout=30
while [[ "$is_master" -eq 1 && $timeout -gt 0 ]]; do
sleep 1
get_redis_role
timeout=$((timeout - 1))
done
echo "Failover successful"
fi
{{- end }}
{{- define "fix-split-brain.sh" }}
{{- include "vars.sh" . }}
ROLE=''
REDIS_MASTER=''
set -eu
{{- include "lib.sh" . }}
redis_role() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
ROLE=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} info | grep role | sed 's/role://' | sed 's/\r//')
else
ROLE=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" info | grep role | sed 's/role://' | sed 's/\r//')
fi
set -e
}
identify_redis_master() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
REDIS_MASTER=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} info | grep master_host | sed 's/master_host://' | sed 's/\r//')
else
REDIS_MASTER=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" info | grep master_host | sed 's/master_host://' | sed 's/\r//')
fi
set -e
}
reinit() {
set +e
sh /readonly-config/init.sh
if [ "$REDIS_PORT" -eq 0 ]; then
echo "shutdown" | redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }}
else
echo "shutdown" | redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}"
fi
set -e
}
identify_announce_ip
while [ -z "${ANNOUNCE_IP}" ]; do
echo "Error: Could not resolve the announce ip for this pod."
sleep 30
identify_announce_ip
done
trap "exit 0" TERM
while true; do
sleep {{ .Values.splitBrainDetection.interval }}
# where is redis master
identify_master
if [ "$MASTER" = "$ANNOUNCE_IP" ]; then
redis_role
if [ "$ROLE" != "master" ]; then
reinit
fi
elif [ "${MASTER}" ]; then
identify_redis_master
if [ "$REDIS_MASTER" != "$MASTER" ]; then
reinit
fi
fi
done
{{- end }}
{{- define "config-haproxy.cfg" }}
{{- if .Values.haproxy.customConfig }}
{{ tpl .Values.haproxy.customConfig . | indent 4 }}
{{- else }}
defaults REDIS
mode tcp
timeout connect {{ .Values.haproxy.timeout.connect }}
timeout server {{ .Values.haproxy.timeout.server }}
timeout client {{ .Values.haproxy.timeout.client }}
timeout check {{ .Values.haproxy.timeout.check }}
listen health_check_http_url
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:8888 {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
mode http
monitor-uri /healthz
option dontlognull
{{- $root := . }}
{{- $fullName := include "redis-ha.fullname" . }}
{{- $replicas := int (toString .Values.replicas) }}
{{- $masterGroupName := include "redis-ha.masterGroupName" . }}
{{- range $i := until $replicas }}
# Check Sentinel and whether they are nominated master
backend check_if_redis_is_master_{{ $i }}
mode tcp
option tcp-check
tcp-check connect
{{- if $root.Values.sentinel.auth }}
tcp-check send "AUTH ${SENTINELAUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send SENTINEL\ get-master-addr-by-name\ {{ $masterGroupName }}\r\n
tcp-check expect string REPLACE_ANNOUNCE{{ $i }}
tcp-check send QUIT\r\n
{{- range $i := until $replicas }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:26379 check inter {{ $root.Values.haproxy.checkInterval }}
{{- end }}
{{- end }}
# decide redis backend to use
#master
frontend ft_redis_master
{{- if .Values.haproxy.tls.enabled }}
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ $root.Values.haproxy.containerPort }} ssl crt {{ .Values.haproxy.tls.certMountPath }}{{ .Values.haproxy.tls.keyName }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
{{ else }}
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ $root.Values.redis.port }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
{{- end }}
use_backend bk_redis_master
{{- if .Values.haproxy.readOnly.enabled }}
#slave
frontend ft_redis_slave
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ .Values.haproxy.readOnly.port }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
use_backend bk_redis_slave
{{- end }}
# Check all redis servers to see if they think they are master
backend bk_redis_master
{{- if .Values.haproxy.stickyBalancing }}
balance source
hash-type consistent
{{- end }}
mode tcp
option tcp-check
tcp-check connect
{{- if .Values.auth }}
tcp-check send "AUTH ${AUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
{{- range $i := until $replicas }}
use-server R{{ $i }} if { srv_is_up(R{{ $i }}) } { nbsrv(check_if_redis_is_master_{{ $i }}) ge 2 }
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- end }}
{{- if .Values.haproxy.readOnly.enabled }}
backend bk_redis_slave
{{- if .Values.haproxy.stickyBalancing }}
balance source
hash-type consistent
{{- end }}
mode tcp
option tcp-check
tcp-check connect
{{- if .Values.auth }}
tcp-check send "AUTH ${AUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:slave
tcp-check send QUIT\r\n
tcp-check expect string +OK
{{- range $i := until $replicas }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- end }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
frontend stats
mode http
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ .Values.haproxy.metrics.port }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
http-request use-service prometheus-exporter if { path {{ .Values.haproxy.metrics.scrapePath }} }
stats enable
stats uri /stats
stats refresh 10s
{{- end }}
{{- if .Values.haproxy.extraConfig }}
# Additional configuration
{{ .Values.haproxy.extraConfig | indent 4 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "config-haproxy_init.sh" }}
HAPROXY_CONF=/data/haproxy.cfg
cp /readonly/haproxy.cfg "$HAPROXY_CONF"
{{- $fullName := include "redis-ha.fullname" . }}
{{- $replicas := int (toString .Values.replicas) }}
{{- range $i := until $replicas }}
for loop in $(seq 1 10); do
getent hosts {{ $fullName }}-announce-{{ $i }} && break
echo "Waiting for service {{ $fullName }}-announce-{{ $i }} to be ready ($loop) ..." && sleep 1
done
ANNOUNCE_IP{{ $i }}=$(getent hosts "{{ $fullName }}-announce-{{ $i }}" | awk '{ print $1 }')
if [ -z "$ANNOUNCE_IP{{ $i }}" ]; then
echo "Could not resolve the announce ip for {{ $fullName }}-announce-{{ $i }}"
exit 1
fi
sed -i "s/REPLACE_ANNOUNCE{{ $i }}/$ANNOUNCE_IP{{ $i }}/" "$HAPROXY_CONF"
{{- end }}
{{- end }}
{{- define "redis_liveness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
echo "response=$response"
case $response in
PONG|LOADING*) ;;
*) exit 1 ;;
esac
exit 0
{{- end }}
{{- define "redis_readiness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
if [ "$response" != "PONG" ] ; then
echo "ping=$response"
exit 1
fi
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
role
)
role=$( echo "$response" | sed "1!d" )
if [ "$role" = "master" ]; then
echo "role=$role"
exit 0
elif [ "$role" = "slave" ]; then
repl=$( echo "$response" | sed "4!d" )
echo "role=$role; repl=$repl"
if [ "$repl" = "connected" ]; then
exit 0
else
exit 1
fi
else
echo "role=$role"
exit 1
fi
{{- end }}
{{- define "sentinel_liveness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.sentinel.auth }}
-a "${SENTINELAUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.sentinel.port) 0 }}
-p {{ .Values.sentinel.port }} \
{{- else }}
-p {{ .Values.sentinel.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
if [ "$response" != "PONG" ]; then
echo "$response"
exit 1
fi
echo "response=$response"
{{- end }}

View File

@@ -0,0 +1,130 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "redis-ha.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "redis-ha.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 -}}
{{/*
Return sysctl image
*/}}
{{- define "redis.sysctl.image" -}}
{{- $registryName := default "docker.io" .Values.sysctlImage.registry -}}
{{- $tag := default "latest" .Values.sysctlImage.tag | toString -}}
{{- printf "%s/%s:%s" $registryName .Values.sysctlImage.repository $tag -}}
{{- end -}}
{{- /*
Credit: @technosophos
https://github.com/technosophos/common-chart/
labels.standard prints the standard Helm labels.
The standard labels are frequently used in metadata.
*/ -}}
{{- define "labels.standard" -}}
app: {{ template "redis-ha.name" . }}
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
chart: {{ template "chartref" . }}
{{- end -}}
{{- /*
Credit: @technosophos
https://github.com/technosophos/common-chart/
chartref prints a chart name and version.
It does minimal escaping for use in Kubernetes labels.
Example output:
zookeeper-1.2.3
wordpress-3.2.1_20170219
*/ -}}
{{- define "chartref" -}}
{{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "redis-ha.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "redis-ha.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{- define "redis-ha.masterGroupName" -}}
{{- $masterGroupName := tpl ( .Values.redis.masterGroupName | default "") . -}}
{{- $validMasterGroupName := regexMatch "^[\\w-\\.]+$" $masterGroupName -}}
{{- if $validMasterGroupName -}}
{{ $masterGroupName }}
{{- else -}}
{{ required "A valid .Values.redis.masterGroupName entry is required (matching ^[\\w-\\.]+$)" ""}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for poddisruptionbudget.
*/}}
{{- define "redis-ha.podDisruptionBudget.apiVersion" -}}
{{- if .Capabilities.APIVersions.Has "policy/v1" }}
{{- print "policy/v1" -}}
{{- else -}}
{{- print "policy/v1beta1" -}}
{{- end -}}
{{- end -}}
{{/*
Return true if the detected platform is Openshift
Usage:
{{- include "common.compatibility.isOpenshift" . -}}
*/}}
{{- define "compatibility.isOpenshift" -}}
{{- if .Capabilities.APIVersions.Has "security.openshift.io/v1" -}}
{{- true -}}
{{- end -}}
{{- end -}}
{{/*
Render a compatible securityContext depending on the platform. By default it is maintained as it is. In other platforms like Openshift we remove default user/group values that do not work out of the box with the restricted-v1 SCC
Usage:
{{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) -}}
*/}}
{{- define "compatibility.renderSecurityContext" -}}
{{- $adaptedContext := .secContext -}}
{{- if (((.context.Values.global).compatibility).openshift) -}}
{{- if or (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "force") (and (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "auto") (include "compatibility.isOpenshift" .context)) -}}
{{/* Remove incompatible user/group values that do not work in Openshift out of the box */}}
{{- $adaptedContext = omit $adaptedContext "fsGroup" "runAsUser" "runAsGroup" -}}
{{- if not .secContext.seLinuxOptions -}}
{{/* If it is an empty object, we remove it from the resulting context because it causes validation issues */}}
{{- $adaptedContext = omit $adaptedContext "seLinuxOptions" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Remove fields that are disregarded when running the container in privileged mode */}}
{{- if $adaptedContext.privileged -}}
{{- $adaptedContext = omit $adaptedContext "capabilities" "seLinuxOptions" -}}
{{- end -}}
{{- omit $adaptedContext "enabled" | toYaml -}}
{{- end -}}

View File

@@ -0,0 +1,15 @@
{{- if and .Values.auth (not .Values.existingSecret) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{ .Values.authKey }}: {{ .Values.redisPassword | b64enc | quote }}
{{- end -}}

View File

@@ -0,0 +1,64 @@
{{- $fullName := include "redis-ha.fullname" . }}
{{- $namespace := .Release.Namespace -}}
{{- $replicas := int (toString .Values.replicas) }}
{{- $root := . }}
{{- range $i := until $replicas }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}-announce-{{ $i }}
namespace: {{ $namespace | quote}}
labels:
{{ include "labels.standard" $root | indent 4 }}
{{- range $key, $value := $root.Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if (semverCompare "<=1.10-0" $.Capabilities.KubeVersion.GitVersion) }}
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
{{- end }}
{{- if $root.Values.serviceAnnotations }}
{{ toYaml $root.Values.serviceAnnotations | indent 4 }}
{{- end }}
spec:
{{- if (semverCompare ">=1.11-0" $.Capabilities.KubeVersion.GitVersion) }}
publishNotReadyAddresses: true
{{- end }}
type: ClusterIP
ports:
{{- if ne (int $root.Values.redis.port) 0 }}
- name: tcp-server
port: {{ $root.Values.redis.port }}
protocol: TCP
targetPort: redis
{{- end }}
{{- if $root.Values.redis.tlsPort }}
- name: server-tls
port: {{ $root.Values.redis.tlsPort }}
protocol: TCP
targetPort: redis-tls
{{- end }}
{{- if ne (int $root.Values.sentinel.port) 0 }}
- name: tcp-sentinel
port: {{ $root.Values.sentinel.port }}
protocol: TCP
targetPort: sentinel
{{- end }}
{{- if $root.Values.sentinel.tlsPort }}
- name: sentinel-tls
port: {{ $root.Values.sentinel.tlsPort }}
protocol: TCP
targetPort: sentinel-tls
{{- end }}
{{- if $root.Values.exporter.enabled }}
- name: http-exporter
port: {{ $root.Values.exporter.port }}
protocol: TCP
targetPort: {{ $root.Values.exporter.portName }}
{{- end }}
selector:
release: {{ $root.Release.Name }}
app: {{ include "redis-ha.name" $root }}
"statefulset.kubernetes.io/pod-name": {{ $fullName }}-server-{{ $i }}
{{- end }}

View File

@@ -0,0 +1,37 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.configmap.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
redis.conf: |
{{- include "config-redis.conf" . }}
sentinel.conf: |
{{- include "config-sentinel.conf" . }}
init.sh: |
{{- include "config-init.sh" . }}
fix-split-brain.sh: |
{{- include "fix-split-brain.sh" . }}
{{ if .Values.haproxy.enabled }}
haproxy.cfg: |
{{- include "config-haproxy.cfg" . }}
{{- end }}
haproxy_init.sh: |
{{- include "config-haproxy_init.sh" . }}
trigger-failover-if-master.sh: |
{{- include "trigger-failover-if-master.sh" . }}

View File

@@ -0,0 +1,14 @@
{{- if .Values.exporter.script }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-exporter-script-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
script: {{ toYaml .Values.exporter.script | indent 2 }}
{{- end }}

View File

@@ -0,0 +1,20 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-health-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
redis_liveness.sh: |
{{- include "redis_liveness.sh" . }}
redis_readiness.sh: |
{{- include "redis_readiness.sh" . }}
sentinel_liveness.sh: |
{{- include "sentinel_liveness.sh" . }}

View File

@@ -0,0 +1,80 @@
{{- if .Values.networkPolicy.enabled }}
{{- $root := . }}
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-network-policy
namespace: {{ .Release.Namespace | quote }}
{{- if .Values.networkPolicy.annotations }}
annotations:
{{- range $key, $value := .Values.networkPolicy.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.networkPolicy.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
policyTypes:
- Ingress
- Egress
egress:
- to:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
- port: {{ .Values.redis.port }}
protocol: TCP
- port: {{ .Values.sentinel.port }}
protocol: TCP
{{- range $rule := .Values.networkPolicy.egressRules }}
- to:
{{ (tpl (toYaml $rule.selectors) $) | indent 7 }}
ports:
{{ toYaml $rule.ports | indent 7 }}
{{- end }}
ingress:
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
- port: {{ .Values.redis.port }}
protocol: TCP
- port: {{ .Values.sentinel.port }}
protocol: TCP
{{- if .Values.haproxy.enabled }}
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
ports:
- port: {{ .Values.redis.port }}
protocol: TCP
- port: {{ .Values.sentinel.port }}
protocol: TCP
{{- end }}
{{- range $rule := .Values.networkPolicy.ingressRules }}
- from:
{{ (tpl (toYaml $rule.selectors) $) | indent 7 }}
ports:
{{- if $rule.ports }}
{{ toYaml $rule.ports | indent 7 }}
{{- else }}
- port: {{ $root.Values.redis.port }}
protocol: TCP
- port: {{ $root.Values.sentinel.port }}
protocol: TCP
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,21 @@
{{- if .Values.podDisruptionBudget -}}
apiVersion: {{ template "redis-ha.podDisruptionBudget.apiVersion" . }}
kind: PodDisruptionBudget
metadata:
name: {{ template "redis-ha.fullname" . }}-pdb
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
selector:
matchLabels:
# The replica label is set on StatefulSet pods but not the Test pods
# We want to avoid including the Test pods in the budget
{{ template "redis-ha.fullname" . }}: replica
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
{{ toYaml .Values.podDisruptionBudget | indent 2 }}
{{- end -}}

View File

@@ -0,0 +1,17 @@
{{- if .Values.prometheusRule.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ template "redis-ha.fullname" . }}
{{- if .Values.prometheusRule.namespace }}
namespace: {{ .Values.prometheusRule.namespace }}
{{- end }}
labels: {{- toYaml .Values.prometheusRule.additionalLabels | nindent 4 }}
spec:
groups:
- name: {{ template "redis-ha.fullname" . }}
{{- if .Values.prometheusRule.interval }}
interval: {{ .Values.prometheusRule.interval }}
{{- end }}
rules: {{- tpl (toYaml .Values.prometheusRule.rules) . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,19 @@
{{- if and .Values.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
{{- end }}

View File

@@ -0,0 +1,19 @@
{{- if and .Values.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
subjects:
- kind: ServiceAccount
name: {{ template "redis-ha.serviceAccountName" . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "redis-ha.fullname" . }}
{{- end }}

View File

@@ -0,0 +1,32 @@
{{- if not .Values.restore.existingSecret }}
{{- $regexRestoreS3 := "^s3://.+|^S3://.+" -}}
{{- $regexRestoreSSH := "^.+@.+:.+" -}}
{{- if or (regexFind $regexRestoreSSH (toString .Values.restore.ssh.source)) (regexFind $regexRestoreS3 (toString .Values.restore.s3.source)) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "redis-ha.fullname" . }}-secret
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{- if regexFind $regexRestoreSSH (toString .Values.restore.ssh.source) }}
SSH_KEY: "{{ .Values.restore.ssh.key | b64enc }}"
{{- end }}
{{- if regexFind $regexRestoreS3 (toString .Values.restore.s3.source) }}
AWS_SECRET_ACCESS_KEY: "{{ .Values.restore.s3.secret_key | b64enc }}"
AWS_ACCESS_KEY_ID: "{{ .Values.restore.s3.access_key | b64enc }}"
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,57 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- if .Values.exporter.enabled }}
exporter: enabled
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.serviceLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if .Values.serviceAnnotations }}
{{ toYaml .Values.serviceAnnotations | indent 4 }}
{{- end }}
spec:
type: ClusterIP
clusterIP: None
ports:
{{- if ne (int .Values.redis.port) 0 }}
- name: tcp-server
port: {{ .Values.redis.port }}
protocol: TCP
targetPort: redis
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: server-tls
port: {{ .Values.redis.tlsPort }}
protocol: TCP
targetPort: redis-tls
{{- end }}
{{- if ne (int .Values.sentinel.port) 0 }}
- name: tcp-sentinel
port: {{ .Values.sentinel.port }}
protocol: TCP
targetPort: sentinel
{{- end }}
{{- if .Values.sentinel.tlsPort }}
- name: sentinel-tls
port: {{ .Values.sentinel.tlsPort }}
protocol: TCP
targetPort: sentinel-tls
{{- end }}
{{- if .Values.exporter.enabled }}
- name: http-exporter-port
port: {{ .Values.exporter.port }}
protocol: TCP
targetPort: {{ .Values.exporter.portName }}
{{- end }}
selector:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}

View File

@@ -0,0 +1,31 @@
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "redis-ha.serviceAccountName" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- if .Values.serviceAccount.annotations }}
annotations:
{{ toYaml .Values.serviceAccount.annotations | indent 4 }}
{{- end }}
{{- if or .Values.auth .Values.sentinel.auth }}
secrets:
{{- end }}
{{- if .Values.auth }}
- name: {{ default (include "redis-ha.fullname" .) .Values.existingSecret }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: {{ default (printf "%s-sentinel" (include "redis-ha.fullname" .)) .Values.sentinel.existingSecret }}
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 0 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,39 @@
{{- if and ( or .Values.exporter.serviceMonitor.disableAPICheck ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ) ( .Values.exporter.serviceMonitor.enabled ) ( .Values.exporter.enabled ) }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Values.exporter.serviceMonitor.namespace | default .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.exporter.serviceMonitor.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
endpoints:
- targetPort: {{ .Values.exporter.port }}
{{- if .Values.exporter.serviceMonitor.interval }}
interval: {{ .Values.exporter.serviceMonitor.interval }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.telemetryPath }}
path: {{ .Values.exporter.serviceMonitor.telemetryPath }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.timeout }}
scrapeTimeout: {{ .Values.exporter.serviceMonitor.timeout }}
{{- end }}
{{- with .Values.exporter.serviceMonitor.endpointAdditionalProperties }}
{{- toYaml . | nindent 4 }}
{{- end }}
jobLabel: {{ template "redis-ha.fullname" . }}
namespaceSelector:
matchNames:
- {{ .Release.Namespace | quote }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
exporter: enabled
{{- end }}

View File

@@ -0,0 +1,639 @@
{{- $regexRestoreS3 := "^s3://.+|^S3://.+" -}}
{{- $regexRestoreSSH := "^.+@.+:.+" -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ template "redis-ha.fullname" . }}-server
namespace: {{ .Release.Namespace | quote }}
labels:
{{ template "redis-ha.fullname" . }}: replica
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{ include "labels.standard" . | indent 4 }}
annotations:
{{ toYaml .Values.redis.annotations | indent 4 }}
spec:
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
serviceName: {{ template "redis-ha.fullname" . }}
replicas: {{ .Values.replicas }}
podManagementPolicy: {{ .Values.podManagementPolicy }}
updateStrategy:
type: {{ .Values.redis.updateStrategy.type }}
{{- if .Values.redis.minReadySeconds }}
minReadySeconds: {{ .Values.redis.minReadySeconds }}
{{- end }}
template:
metadata:
annotations:
checksum/init-config: {{ print (include "config-redis.conf" .) (include "config-sentinel.conf" .) (include "config-init.sh" .) (include "fix-split-brain.sh" .) (include "redis_liveness.sh" .) (include "redis_readiness.sh" .) (include "sentinel_liveness.sh" .) (include "trigger-failover-if-master.sh" .)| sha256sum }}
{{- if .Values.podAnnotations }}
{{ toYaml .Values.podAnnotations | indent 8 }}
{{- end }}
{{- if and (.Values.exporter.enabled) (not .Values.exporter.serviceMonitor.enabled) }}
prometheus.io/port: "{{ .Values.exporter.port }}"
prometheus.io/scrape: "true"
prometheus.io/path: {{ .Values.exporter.scrapePath }}
{{- end }}
labels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
{{ template "redis-ha.fullname" . }}: replica
{{- range $key, $value := .Values.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
{{- if .Values.redis.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ .Values.redis.terminationGracePeriodSeconds }}
{{- end }}
{{- if .Values.schedulerName }}
schedulerName: "{{ .Values.schedulerName }}"
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
{{- end }}
affinity:
{{- if .Values.affinity }}
{{- with .Values.affinity }}
{{ tpl . $ | indent 8 }}
{{- end }}
{{- else }}
{{- if .Values.additionalAffinities }}
{{ toYaml .Values.additionalAffinities | indent 8 }}
{{- end }}
podAntiAffinity:
{{- if .Values.hardAntiAffinity }}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
topologyKey: kubernetes.io/hostname
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
topologyKey: kubernetes.io/hostname
{{- end }}
{{- end }}
{{- if .Values.topologySpreadConstraints.enabled }}
topologySpreadConstraints:
- maxSkew: {{ .Values.topologySpreadConstraints.maxSkew | default 1 }}
topologyKey: {{ .Values.topologySpreadConstraints.topologyKey | default "topology.kubernetes.io/zone" }}
whenUnsatisfiable: {{ .Values.topologySpreadConstraints.whenUnsatisfiable | default "ScheduleAnyway" }}
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.securityContext "context" $) | nindent 8 }}
serviceAccountName: {{ template "redis-ha.serviceAccountName" . }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountToken }}
initContainers:
{{- if .Values.sysctlImage.enabled }}
- name: init-sysctl
image: {{ template "redis.sysctl.image" . }}
imagePullPolicy: {{ .Values.sysctlImage.pullPolicy }}
resources: {{ toYaml .Values.sysctlImage.resources | nindent 10 }}
{{- if .Values.sysctlImage.mountHostSys }}
volumeMounts:
- name: host-sys
mountPath: /host-sys
{{- end }}
command: {{ toYaml .Values.sysctlImage.command | nindent 10 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- end }}
{{- if and .Values.hostPath.path .Values.hostPath.chown }}
- name: hostpath-chown
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
command:
- chown
- "{{ .Values.containerSecurityContext.runAsUser }}"
- /data
volumeMounts:
- name: data
mountPath: /data
{{- end }}
- name: config-init
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources:
{{ toYaml .Values.init.resources | indent 10 }}
command:
- sh
args:
- /readonly-config/init.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
{{- $replicas := int (toString .Values.replicas) -}}
{{- range $i := until $replicas }}
- name: SENTINEL_ID_{{ $i }}
value: {{ printf "%s\n%s\nindex: %d" (include "redis-ha.name" $) ($.Release.Name) $i | sha256sum | trunc 40 }}
{{- end }}
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if .Values.sentinel.existingSecret }}
name: {{ .Values.sentinel.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- name: data
mountPath: /data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end}}
{{ if regexFind $regexRestoreS3 (toString .Values.restore.s3.source) }}
- name: restore-s3
image: s3cmd/s3cmd:latest
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources:
{{ toYaml .Values.init.resources | indent 10 }}
command:
- sh
args:
- "-c"
- "timeout -t {{ .Values.restore.timeout }} \
s3cmd get {{ if .Values.restore.s3.region }}--region {{ .Values.restore.s3.region }} {{ end }}--force '{{ .Values.restore.s3.source }}' /data/dump.rdb_ \
&& test -s /data/dump.rdb_ \
&& if test -s /data/dump.rdb; \
then cp -v /data/dump.rdb /data/dump.rdb_orig; fi \
&& mv -v /data/dump.rdb_ /data/dump.rdb"
envFrom:
- secretRef:
{{- if .Values.restore.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ include "redis-ha.fullname" . }}-secret
{{- end }}
volumeMounts:
- name: data
mountPath: /data
{{- end }}
{{ if regexFind $regexRestoreSSH (toString .Values.restore.ssh.source) }}
- name: restore-ssh
image: lgatica/openssh-client:latest
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources:
{{ toYaml .Values.init.resources | indent 10 }}
command:
- sh
args:
- "-c"
- "rm -f key && echo -e \"${SSH_KEY}\" >key \
&& chmod 400 key \
&& timeout {{ .Values.restore.timeout }} \
scp -i key \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
'{{ .Values.restore.ssh.source }}' \
/data/dump.rdb_ \
&& test -s /data/dump.rdb_ \
&& if test -s /data/dump.rdb; \
then cp -v /data/dump.rdb /data/dump.rdb_orig; fi \
&& mv -v /data/dump.rdb_ /data/dump.rdb"
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
envFrom:
- secretRef:
{{- if .Values.restore.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ include "redis-ha.fullname" . }}-secret
{{- end }}
volumeMounts:
- name: data
mountPath: /data
{{- end }}
{{- if .Values.extraInitContainers }}
{{- toYaml .Values.extraInitContainers | nindent 6 }}
{{- end }}
containers:
- name: redis
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
{{- if .Values.redis.customCommand }}
{{ toYaml .Values.redis.customCommand | indent 10 }}
{{- else }}
- redis-server
{{- end }}
args:
{{- if .Values.redis.customArgs }}
{{ toYaml .Values.redis.customArgs | indent 10 }}
{{- else }}
- /data/conf/redis.conf
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- if .Values.auth }}
env:
- name: AUTH
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.redis.envFrom }}
envFrom:
{{ toYaml .Values.redis.envFrom | indent 10 }}
{{- end }}
{{- if .Values.redis.livenessProbe.enabled }}
livenessProbe:
initialDelaySeconds: {{ .Values.redis.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.redis.livenessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_liveness.sh
{{- end }}
{{- if .Values.redis.readinessProbe.enabled }}
readinessProbe:
initialDelaySeconds: {{ .Values.redis.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.redis.readinessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_readiness.sh
{{- end }}
{{- if .Values.redis.startupProbe.enabled }}
startupProbe:
initialDelaySeconds: {{ .Values.redis.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.startupProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.startupProbe.successThreshold }}
failureThreshold: {{ .Values.redis.startupProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_readiness.sh
{{- end }}
resources:
{{ toYaml .Values.redis.resources | indent 10 }}
ports:
{{- if ne (int .Values.redis.port) 0 }}
- name: redis
containerPort: {{ .Values.redis.port }}
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: redis-tls
containerPort: {{ .Values.redis.tlsPort }}
{{- end }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end}}
- mountPath: /health
name: health
{{- if .Values.redis.extraVolumeMounts }}
{{- toYaml .Values.redis.extraVolumeMounts | nindent 8 }}
{{- end }}
lifecycle:
{{ toYaml .Values.redis.lifecycle | indent 10 }}
- name: sentinel
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
{{- if .Values.sentinel.customCommand }}
{{ toYaml .Values.sentinel.customCommand | indent 10 }}
{{- else }}
- redis-sentinel
{{- end }}
args:
{{- if .Values.sentinel.customArgs }}
{{ toYaml .Values.sentinel.customArgs | indent 10 }}
{{- else }}
- /data/conf/sentinel.conf
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- if or .Values.auth .Values.sentinel.auth}}
env:
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if .Values.sentinel.existingSecret }}
name: {{ .Values.sentinel.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
{{- end }}
{{- if .Values.sentinel.livenessProbe.enabled }}
livenessProbe:
initialDelaySeconds: {{ .Values.sentinel.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.livenessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
{{- if .Values.sentinel.readinessProbe.enabled }}
readinessProbe:
initialDelaySeconds: {{ .Values.sentinel.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.readinessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
{{- if .Values.sentinel.startupProbe.enabled }}
startupProbe:
initialDelaySeconds: {{ .Values.sentinel.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.startupProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.startupProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.startupProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
resources:
{{ toYaml .Values.sentinel.resources | indent 10 }}
ports:
{{- if ne (int .Values.sentinel.port) 0 }}
- name: sentinel
containerPort: {{ .Values.sentinel.port }}
{{- end }}
{{- if .Values.sentinel.tlsPort }}
- name: sentinel-tls
containerPort: {{ .Values.sentinel.tlsPort }}
{{- end }}
volumeMounts:
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
- mountPath: /health
name: health
{{- if .Values.sentinel.extraVolumeMounts }}
{{- toYaml .Values.sentinel.extraVolumeMounts | nindent 8 }}
{{- end }}
lifecycle:
{{ toYaml .Values.sentinel.lifecycle | indent 10 }}
- name: split-brain-fix
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- sh
args:
- /readonly-config/fix-split-brain.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
{{- $replicas := int (toString .Values.replicas) -}}
{{- range $i := until $replicas }}
- name: SENTINEL_ID_{{ $i }}
value: {{ printf "%s\n%s\nindex: %d" (include "redis-ha.name" $) ($.Release.Name) $i | sha256sum | trunc 40 }}
{{- end }}
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if .Values.sentinel.existingSecret }}
name: {{ .Values.sentinel.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
resources:
{{- toYaml .Values.splitBrainDetection.resources | nindent 10 }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
{{- if .Values.exporter.enabled }}
- name: redis-exporter
image: "{{ .Values.exporter.image }}:{{ .Values.exporter.tag }}"
imagePullPolicy: {{ .Values.exporter.pullPolicy }}
args:
{{- range $key, $value := .Values.exporter.extraArgs }}
- --{{ $key }}={{ $value }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
- name: REDIS_ADDR
{{- if .Values.exporter.sslEnabled }}
value: rediss://{{ default "localhost" .Values.exporter.address }}:{{ .Values.redis.tlsPort }}
{{- else }}
value: redis://{{ default "localhost" .Values.exporter.address }}:{{ .Values.redis.port }}
{{- end }}
{{- if .Values.auth }}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.exporter.script }}
- name: REDIS_EXPORTER_SCRIPT
value: /script/script.lua
{{- end }}
{{- if .Values.exporter.sslEnabled }}
- name: REDIS_EXPORTER_TLS_CLIENT_KEY_FILE
value: /tls-certs/{{ .Values.tls.keyFile }}
- name: REDIS_EXPORTER_TLS_CLIENT_CERT_FILE
value: /tls-certs/{{ .Values.tls.certFile }}
- name: REDIS_EXPORTER_TLS_CA_CERT_FILE
value: /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
livenessProbe:
{{ toYaml .Values.exporter.livenessProbe | indent 10 }}
readinessProbe:
{{ toYaml .Values.exporter.readinessProbe | indent 10 }}
resources:
{{ toYaml .Values.exporter.resources | indent 10 }}
ports:
- name: {{ .Values.exporter.portName }}
containerPort: {{ .Values.exporter.port }}
volumeMounts:
{{- if .Values.exporter.script }}
- mountPath: /script
name: script-mount
{{- end }}
{{- if .Values.exporter.sslEnabled }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
{{- end }}
{{- if .Values.extraContainers }}
{{- toYaml .Values.extraContainers | nindent 6 }}
{{- end -}}
{{- with .Values.priorityClassName | default .Values.global.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
{{- if .Values.sysctlImage.mountHostSys }}
- name: host-sys
hostPath:
path: /sys
{{- end }}
{{- if .Values.exporter.script }}
- name: script-mount
configMap:
name: {{ template "redis-ha.fullname" . }}-exporter-script-configmap
items:
- key: script
path: script.lua
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: tls-certs
secret:
{{- if .Values.tls.secretName }}
secretName: {{ .Values.tls.secretName }}
{{- else }}
secretName: {{ template "redis-ha.fullname" . }}-tls-secret
{{- end }}
{{- end }}
- name: health
configMap:
name: {{ template "redis-ha.fullname" . }}-health-configmap
defaultMode: 0755
{{- if .Values.extraVolumes }}
{{- toYaml .Values.extraVolumes | nindent 6 }}
{{- end -}}
{{- if .Values.persistentVolume.enabled }}
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
annotations:
{{- range $key, $value := .Values.persistentVolume.annotations }}
{{ $key }}: {{ $value }}
{{- end }}
labels: {{- toYaml .Values.persistentVolume.labels | nindent 8 }}
spec:
accessModes:
{{- range .Values.persistentVolume.accessModes }}
- {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolume.size | quote }}
{{- if .Values.persistentVolume.storageClass }}
{{- if (eq "-" .Values.persistentVolume.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistentVolume.storageClass }}"
{{- end }}
{{- end }}
{{- else if .Values.hostPath.path }}
- name: data
hostPath:
path: {{ tpl .Values.hostPath.path .}}
{{- else }}
- name: data
emptyDir:
{{ toYaml .Values.emptyDir | indent 10 }}
{{- end }}

View File

@@ -0,0 +1,202 @@
{{- if .Values.haproxy.enabled }}
kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
{{- with .Values.haproxy.deploymentStrategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
revisionHistoryLimit: 1
replicas: {{ .Values.haproxy.replicas }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
template:
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
labels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
{{- range $key, $value := .Values.haproxy.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if and (.Values.haproxy.metrics.enabled) (not .Values.haproxy.metrics.serviceMonitor.enabled) }}
prometheus.io/port: "{{ .Values.haproxy.metrics.port }}"
prometheus.io/scrape: "true"
prometheus.io/path: "{{ .Values.haproxy.metrics.scrapePath }}"
{{- end }}
checksum/config: {{ print (include "config-haproxy.cfg" .) (include "config-haproxy_init.sh" .) | sha256sum }}
{{- if .Values.haproxy.annotations }}
{{ toYaml .Values.haproxy.annotations | indent 8 }}
{{- end }}
spec:
# Needed when using unmodified rbac-setup.yml
{{ if .Values.haproxy.serviceAccount.create }}
serviceAccountName: {{ template "redis-ha.serviceAccountName" . }}-haproxy
{{- else }}
serviceAccountName: {{ .Values.haproxy.serviceAccountName }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.securityContext "context" $) | nindent 8 }}
automountServiceAccountToken: {{ .Values.haproxy.serviceAccount.automountToken }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
affinity:
{{- if .Values.haproxy.affinity }}
{{- with .Values.haproxy.affinity }}
{{ tpl . $ | indent 8 }}
{{- end }}
{{- else }}
{{- if .Values.haproxy.additionalAffinities }}
{{ toYaml .Values.haproxy.additionalAffinities | indent 8 }}
{{- end }}
podAntiAffinity:
{{- if .Values.haproxy.hardAntiAffinity }}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
topologyKey: kubernetes.io/hostname
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
topologyKey: kubernetes.io/hostname
{{- end }}
{{- end }}
{{- if .Values.topologySpreadConstraints.enabled }}
topologySpreadConstraints:
- maxSkew: {{ .Values.topologySpreadConstraints.maxSkew | default 1 }}
topologyKey: {{ .Values.topologySpreadConstraints.topologyKey | default "topology.kubernetes.io/zone" }}
whenUnsatisfiable: {{ .Values.topologySpreadConstraints.whenUnsatisfiable | default "ScheduleAnyway" }}
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
{{- end }}
initContainers:
- name: config-init
image: {{ .Values.haproxy.image.repository }}:{{ .Values.haproxy.image.tag }}
imagePullPolicy: {{ .Values.haproxy.image.pullPolicy }}
resources:
{{ toYaml .Values.haproxy.init.resources | indent 10 }}
command:
- sh
args:
- /readonly/haproxy_init.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.containerSecurityContext "context" $) | nindent 10 }}
volumeMounts:
- name: config-volume
mountPath: /readonly
readOnly: true
- name: data
mountPath: /data
{{- if .Values.haproxy.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.haproxy.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: haproxy
image: {{ .Values.haproxy.image.repository }}:{{ .Values.haproxy.image.tag }}
imagePullPolicy: {{ .Values.haproxy.image.pullPolicy }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.containerSecurityContext "context" $) | nindent 10 }}
{{- if or .Values.auth .Values.sentinel.auth}}
env:
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if .Values.existingSecret }}
name: {{ .Values.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if .Values.sentinel.existingSecret }}
name: {{ .Values.sentinel.existingSecret }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
{{- end }}
livenessProbe:
httpGet:
path: /healthz
port: probe
initialDelaySeconds: 5
periodSeconds: 3
readinessProbe:
httpGet:
path: /healthz
port: probe
initialDelaySeconds: 5
periodSeconds: 3
ports:
- name: probe
containerPort: 8888
- name: redis
containerPort: {{ default "6379" .Values.haproxy.containerPort }}
{{- if .Values.haproxy.readOnly.enabled }}
- name: readonlyport
containerPort: {{ default "6380" .Values.haproxy.readOnly.port }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
- name: metrics-port
containerPort: {{ default "9101" .Values.haproxy.metrics.port }}
{{- end }}
resources:
{{ toYaml .Values.haproxy.resources | indent 10 }}
volumeMounts:
- name: data
mountPath: /usr/local/etc/haproxy
- name: shared-socket
mountPath: /run/haproxy
{{- if .Values.haproxy.tls.enabled }}
- name: pemfile
mountPath: {{ .Values.haproxy.tls.certMountPath }}
{{- end }}
lifecycle:
{{ toYaml .Values.haproxy.lifecycle | indent 10 }}
{{- with .Values.haproxy.priorityClassName | default .Values.global.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
volumes:
{{- if .Values.haproxy.tls.enabled }}
- name: pemfile
secret:
secretName: {{ .Values.haproxy.tls.secretName }}
{{- end }}
- name: config-volume
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
- name: shared-socket
emptyDir:
{{ toYaml .Values.haproxy.emptyDir | indent 10 }}
- name: data
emptyDir:
{{ toYaml .Values.haproxy.emptyDir | indent 10 }}
{{- end }}

View File

@@ -0,0 +1,74 @@
{{- if and .Values.haproxy.enabled .Values.haproxy.networkPolicy.enabled }}
{{- $root := . }}
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy-network-policy
namespace: {{ .Release.Namespace | quote }}
{{- if .Values.haproxy.networkPolicy.annotations }}
annotations:
{{- range $key, $value := .Values.haproxy.networkPolicy.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{- include "labels.standard" . | nindent 4 }}
{{- range $key, $value := .Values.haproxy.networkPolicy.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
policyTypes:
- Ingress
- Egress
egress:
- to:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
- port: {{ .Values.redis.port }}
protocol: TCP
- port: {{ .Values.sentinel.port }}
protocol: TCP
- to:
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
{{- range $rule := .Values.haproxy.networkPolicy.egressRules }}
- to:
{{- (tpl (toYaml $rule.selectors) $) | nindent 8 }}
ports:
{{- toYaml $rule.ports | nindent 8 }}
{{- end }}
ingress:
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
- port: {{ .Values.redis.port }}
protocol: TCP
- port: {{ .Values.sentinel.port }}
protocol: TCP
{{- range $rule := .Values.haproxy.networkPolicy.ingressRules }}
- from:
{{- (tpl (toYaml $rule.selectors) $) | nindent 8 }}
ports:
{{- if $rule.ports }}
{{- toYaml $rule.ports | nindent 8 }}
{{- end }}
- port: {{ $root.Values.redis.port }}
protocol: TCP
- port: {{ $root.Values.sentinel.port }}
protocol: TCP
{{- end }}
{{- end }}

View File

@@ -0,0 +1,18 @@
{{- if .Values.haproxy.podDisruptionBudget -}}
apiVersion: {{ template "redis-ha.podDisruptionBudget.apiVersion" . }}
kind: PodDisruptionBudget
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy-pdb
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
{{ toYaml .Values.haproxy.podDisruptionBudget | indent 2 }}
{{- end -}}

View File

@@ -0,0 +1,22 @@
{{- if .Values.haproxy.enabled }}
{{- if and .Values.haproxy.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: {{ template "redis-ha.fullname" . }}-haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
{{- end }}
{{- end }}

View File

@@ -0,0 +1,22 @@
{{- if .Values.haproxy.enabled }}
{{- if and .Values.haproxy.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: {{ template "redis-ha.fullname" . }}-haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
subjects:
- kind: ServiceAccount
name: {{ template "redis-ha.serviceAccountName" . }}-haproxy
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "redis-ha.fullname" . }}-haproxy
{{- end }}
{{- end }}

View File

@@ -0,0 +1,60 @@
{{- if .Values.haproxy.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: {{ template "redis-ha.fullname" . }}-haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.haproxy.service.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if .Values.haproxy.service.annotations }}
{{ toYaml .Values.haproxy.service.annotations | indent 4 }}
{{- end }}
spec:
type: {{ default "ClusterIP" .Values.haproxy.service.type }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.haproxy.service.loadBalancerIP }}
{{- end }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.externalTrafficPolicy }}
externalTrafficPolicy: {{ .Values.haproxy.service.externalTrafficPolicy }}
{{- end }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{ toYaml .Values.haproxy.service.loadBalancerSourceRanges | nindent 2 }}
{{- end }}
{{- if .Values.haproxy.service.externalIPs }}
externalIPs:
{{- range $key, $value := .Values.haproxy.service.externalIPs }}
- {{ $value }}
{{- end }}
{{- end }}
ports:
- name: tcp-haproxy
port: {{ .Values.haproxy.servicePort }}
protocol: TCP
targetPort: redis
{{- if and (eq .Values.haproxy.service.type "NodePort") .Values.haproxy.service.nodePort }}
nodePort: {{ .Values.haproxy.service.nodePort }}
{{- end }}
{{- if .Values.haproxy.readOnly.enabled }}
- name: tcp-haproxyreadonly
port: {{ .Values.haproxy.readOnly.port }}
protocol: TCP
targetPort: {{ .Values.haproxy.readOnly.port }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
- name: {{ .Values.haproxy.metrics.portName }}
port: {{ .Values.haproxy.metrics.port }}
protocol: TCP
targetPort: metrics-port
{{- end }}
selector:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
{{- end }}

View File

@@ -0,0 +1,15 @@
{{- if and .Values.haproxy.serviceAccount.create .Values.haproxy.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "redis-ha.serviceAccountName" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,39 @@
{{- if and ( or .Values.haproxy.metrics.serviceMonitor.disableAPICheck ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ) ( .Values.haproxy.metrics.serviceMonitor.enabled ) ( .Values.haproxy.metrics.enabled ) }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Values.haproxy.metrics.serviceMonitor.namespace | default .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.haproxy.metrics.serviceMonitor.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
endpoints:
- targetPort: {{ .Values.haproxy.metrics.port }}
{{- if .Values.haproxy.metrics.serviceMonitor.interval }}
interval: {{ .Values.haproxy.metrics.serviceMonitor.interval }}
{{- end }}
{{- if .Values.haproxy.metrics.serviceMonitor.telemetryPath }}
path: {{ .Values.haproxy.metrics.serviceMonitor.telemetryPath }}
{{- end }}
{{- if .Values.haproxy.metrics.serviceMonitor.timeout }}
scrapeTimeout: {{ .Values.haproxy.metrics.serviceMonitor.timeout }}
{{- end }}
{{- with .Values.haproxy.metrics.serviceMonitor.endpointAdditionalProperties }}
{{- toYaml . | nindent 4 }}
{{- end }}
jobLabel: {{ template "redis-ha.fullname" . }}-haproxy
namespaceSelector:
matchNames:
- {{ .Release.Namespace | quote }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
component: {{ template "redis-ha.fullname" . }}-haproxy
{{- end }}

View File

@@ -0,0 +1,27 @@
{{- if and .Values.redis.tlsPort (not .Values.tls.secretName) -}}
apiVersion: v1
kind: Secret
metadata:
metadata:
name: {{ template "redis-ha.fullname" . }}-tls-secret
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{- if .Values.tls.caCertFile }}
{{ .Values.tls.caCertFile }}: {{ .Files.Get "certs/ca.crt" | b64enc }}
{{- end }}
{{- if .Values.tls.certFile }}
{{ .Values.tls.certFile }}: {{ .Files.Get "certs/redis.crt" | b64enc }}
{{- end }}
{{- if .Values.tls.keyFile }}
{{ .Values.tls.keyFile }}: {{ .Files.Get "certs/redis.key" | b64enc }}
{{- end }}
{{- if .Values.tls.dhParamsFile }}
{{ .Values.tls.dhParamsFile }}: {{ .Files.Get "certs/redis.dh" | b64enc }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
{{- if and .Values.sentinel.auth (not .Values.sentinel.existingSecret) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "redis-ha.fullname" . }}-sentinel
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{ .Values.sentinel.authKey }}: {{ .Values.sentinel.password | b64enc | quote }}
{{- end -}}

View File

@@ -0,0 +1,32 @@
apiVersion: v1
kind: Pod
metadata:
name: {{ template "redis-ha.fullname" . }}-configmap-test
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
nodeSelector: {{ toYaml .Values.nodeSelector | nindent 4 }}
tolerations: {{ toYaml .Values.tolerations | nindent 4 }}
containers:
- name: check-init
image: {{ .Values.configmapTest.image.repository }}:{{ .Values.configmapTest.image.tag }}
args:
- --shell=sh
- /readonly-config/init.sh
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
resources: {{ toYaml .Values.configmapTest.resources | nindent 6 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 6 }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 4 }}
{{- end }}
restartPolicy: Never
volumes:
- name: config
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap

View File

@@ -0,0 +1,29 @@
{{- if .Values.haproxy.enabled }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "redis-ha.fullname" . }}-service-test
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 4 }}
tolerations:
{{ toYaml .Values.tolerations | indent 4 }}
containers:
- name: "{{ .Release.Name }}-service-test"
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
command:
- sh
- -c
- redis-cli -h {{ template "redis-ha.fullname" . }}-haproxy -p {{ .Values.redis.port }} info server
resources: {{ toYaml .Values.haproxy.tests.resources | nindent 6 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 6 }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 4 }}
{{- end }}
restartPolicy: Never
{{- end }}