mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #3640 from akhilmhdh/feat/redis-sentinel-support
Feat/redis sentinel support
This commit is contained in:
@@ -15,8 +15,8 @@ import { mockSmtpServer } from "./mocks/smtp";
|
||||
import { initDbConnection } from "@app/db";
|
||||
import { queueServiceFactory } from "@app/queue";
|
||||
import { keyStoreFactory } from "@app/keystore/keystore";
|
||||
import { Redis } from "ioredis";
|
||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||
export default {
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
dbRootCert: envConfig.DB_ROOT_CERT
|
||||
});
|
||||
|
||||
const redis = new Redis(envConfig.REDIS_URL);
|
||||
const redis = buildRedisFromConfig(envConfig);
|
||||
await redis.flushdb("SYNC");
|
||||
|
||||
try {
|
||||
@@ -55,8 +55,8 @@ export default {
|
||||
});
|
||||
|
||||
const smtp = mockSmtpServer();
|
||||
const queue = queueServiceFactory(envConfig.REDIS_URL, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
||||
const keyStore = keyStoreFactory(envConfig.REDIS_URL);
|
||||
const queue = queueServiceFactory(envConfig, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
||||
const keyStore = keyStoreFactory(envConfig);
|
||||
|
||||
const hsmModule = initializeHsmModule(envConfig);
|
||||
hsmModule.initialize();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
||||
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
||||
import { applyJitter } from "@app/lib/dates";
|
||||
import { delay as delayMs } from "@app/lib/delay";
|
||||
@@ -68,8 +67,8 @@ type TWaitTillReady = {
|
||||
jitter?: number;
|
||||
};
|
||||
|
||||
export const keyStoreFactory = (redisUrl: string) => {
|
||||
const redis = new Redis(redisUrl);
|
||||
export const keyStoreFactory = (redisConfigKeys: TRedisConfigKeys) => {
|
||||
const redis = buildRedisFromConfig(redisConfigKeys);
|
||||
const redisLock = new Redlock([redis], { retryCount: 2, retryDelay: 200 });
|
||||
|
||||
const setItem = async (key: string, value: string | number | Buffer, prefix?: string) =>
|
||||
|
||||
@@ -30,7 +30,19 @@ const envSchema = z
|
||||
.enum(["true", "false"])
|
||||
.default("false")
|
||||
.transform((el) => el === "true"),
|
||||
REDIS_URL: zpStr(z.string()),
|
||||
REDIS_URL: zpStr(z.string().optional()),
|
||||
REDIS_SENTINEL_HOSTS: zpStr(
|
||||
z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Comma-separated list of Sentinel host:port pairs. Eg: 192.168.65.254:26379,192.168.65.254:26380")
|
||||
),
|
||||
REDIS_SENTINEL_MASTER_NAME: zpStr(
|
||||
z.string().optional().default("mymaster").describe("The name of the Redis master set monitored by Sentinel")
|
||||
),
|
||||
REDIS_SENTINEL_ENABLE_TLS: zodStrBool.optional().describe("Whether to use TLS/SSL for Redis Sentinel connection"),
|
||||
REDIS_SENTINEL_USERNAME: zpStr(z.string().optional().describe("Authentication username for Redis Sentinel")),
|
||||
REDIS_SENTINEL_PASSWORD: zpStr(z.string().optional().describe("Authentication password for Redis Sentinel")),
|
||||
HOST: zpStr(z.string().default("localhost")),
|
||||
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")).default(
|
||||
`postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`
|
||||
@@ -259,26 +271,34 @@ const envSchema = z
|
||||
(data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY),
|
||||
"Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined."
|
||||
)
|
||||
.refine(
|
||||
(data) => Boolean(data.REDIS_URL) || Boolean(data.REDIS_SENTINEL_HOSTS),
|
||||
"Either REDIS_URL or REDIS_SENTINEL_HOSTS must be defined."
|
||||
)
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
|
||||
DB_READ_REPLICAS: data.DB_READ_REPLICAS
|
||||
? databaseReadReplicaSchema.parse(JSON.parse(data.DB_READ_REPLICAS))
|
||||
: undefined,
|
||||
isCloud: Boolean(data.LICENSE_SERVER_KEY),
|
||||
isSmtpConfigured: Boolean(data.SMTP_HOST),
|
||||
isRedisConfigured: Boolean(data.REDIS_URL),
|
||||
isRedisConfigured: Boolean(data.REDIS_URL || data.REDIS_SENTINEL_HOSTS),
|
||||
isDevelopmentMode: data.NODE_ENV === "development",
|
||||
isRotationDevelopmentMode: data.NODE_ENV === "development" && data.ROTATION_DEVELOPMENT_MODE,
|
||||
isProductionMode: data.NODE_ENV === "production" || IS_PACKAGED,
|
||||
|
||||
isRedisSentinelMode: Boolean(data.REDIS_SENTINEL_HOSTS),
|
||||
REDIS_SENTINEL_HOSTS: data.REDIS_SENTINEL_HOSTS?.trim()
|
||||
?.split(",")
|
||||
.map((el) => {
|
||||
const [host, port] = el.trim().split(":");
|
||||
return { host: host.trim(), port: Number(port.trim()) };
|
||||
}),
|
||||
isSecretScanningConfigured:
|
||||
Boolean(data.SECRET_SCANNING_GIT_APP_ID) &&
|
||||
Boolean(data.SECRET_SCANNING_PRIVATE_KEY) &&
|
||||
Boolean(data.SECRET_SCANNING_WEBHOOK_SECRET),
|
||||
isHsmConfigured:
|
||||
Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined,
|
||||
|
||||
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG,
|
||||
SECRET_SCANNING_ORG_WHITELIST: data.SECRET_SCANNING_ORG_WHITELIST?.split(",")
|
||||
}));
|
||||
|
||||
24
backend/src/lib/config/redis.ts
Normal file
24
backend/src/lib/config/redis.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
export type TRedisConfigKeys = Partial<{
|
||||
REDIS_URL: string;
|
||||
REDIS_SENTINEL_HOSTS: { host: string; port: number }[];
|
||||
REDIS_SENTINEL_MASTER_NAME: string;
|
||||
REDIS_SENTINEL_ENABLE_TLS: boolean;
|
||||
REDIS_SENTINEL_USERNAME: string;
|
||||
REDIS_SENTINEL_PASSWORD: string;
|
||||
}>;
|
||||
|
||||
export const buildRedisFromConfig = (cfg: TRedisConfigKeys) => {
|
||||
if (cfg.REDIS_URL) return new Redis(cfg.REDIS_URL, { maxRetriesPerRequest: null });
|
||||
|
||||
return new Redis({
|
||||
// refine at tope will catch this case
|
||||
sentinels: cfg.REDIS_SENTINEL_HOSTS!,
|
||||
name: cfg.REDIS_SENTINEL_MASTER_NAME!,
|
||||
maxRetriesPerRequest: null,
|
||||
sentinelUsername: cfg.REDIS_SENTINEL_USERNAME,
|
||||
sentinelPassword: cfg.REDIS_SENTINEL_PASSWORD,
|
||||
enableTLSForSentinelMode: cfg.REDIS_SENTINEL_ENABLE_TLS
|
||||
});
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import "./lib/telemetry/instrumentation";
|
||||
|
||||
import dotenv from "dotenv";
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||
|
||||
@@ -9,6 +8,7 @@ import { runMigrations } from "./auto-start-migrations";
|
||||
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
||||
import { keyStoreFactory } from "./keystore/keystore";
|
||||
import { formatSmtpConfig, initEnvConfig } from "./lib/config/env";
|
||||
import { buildRedisFromConfig } from "./lib/config/redis";
|
||||
import { removeTemporaryBaseDirectory } from "./lib/files";
|
||||
import { initLogger } from "./lib/logger";
|
||||
import { queueServiceFactory } from "./queue";
|
||||
@@ -44,15 +44,15 @@ const run = async () => {
|
||||
|
||||
const smtp = smtpServiceFactory(formatSmtpConfig());
|
||||
|
||||
const queue = queueServiceFactory(envConfig.REDIS_URL, {
|
||||
const queue = queueServiceFactory(envConfig, {
|
||||
dbConnectionUrl: envConfig.DB_CONNECTION_URI,
|
||||
dbRootCert: envConfig.DB_ROOT_CERT
|
||||
});
|
||||
|
||||
await queue.initialize();
|
||||
|
||||
const keyStore = keyStoreFactory(envConfig.REDIS_URL);
|
||||
const redis = new Redis(envConfig.REDIS_URL);
|
||||
const keyStore = keyStoreFactory(envConfig);
|
||||
const redis = buildRedisFromConfig(envConfig);
|
||||
|
||||
const hsmModule = initializeHsmModule(envConfig);
|
||||
hsmModule.initialize();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Job, JobsOptions, Queue, QueueOptions, RepeatOptions, Worker, WorkerListener } from "bullmq";
|
||||
import Redis from "ioredis";
|
||||
import PgBoss, { WorkOptions } from "pg-boss";
|
||||
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
TScanPushEventPayload
|
||||
} from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||
import {
|
||||
@@ -265,10 +265,10 @@ export type TQueueJobTypes = {
|
||||
|
||||
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
||||
export const queueServiceFactory = (
|
||||
redisUrl: string,
|
||||
redisCfg: TRedisConfigKeys,
|
||||
{ dbConnectionUrl, dbRootCert }: { dbConnectionUrl: string; dbRootCert?: string }
|
||||
) => {
|
||||
const connection = new Redis(redisUrl, { maxRetriesPerRequest: null });
|
||||
const connection = buildRedisFromConfig(redisCfg);
|
||||
const queueContainer = {} as Record<
|
||||
QueueName,
|
||||
Queue<TQueueJobTypes[QueueName]["payload"], void, TQueueJobTypes[QueueName]["name"]>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Redis } from "ioredis";
|
||||
import { Knex } from "knex";
|
||||
import { createTransport } from "nodemailer";
|
||||
|
||||
import { formatSmtpConfig, getConfig } from "@app/lib/config/env";
|
||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
@@ -65,12 +65,15 @@ export const bootstrapCheck = async ({ db }: BootstrapOpt) => {
|
||||
});
|
||||
|
||||
console.log("Testing redis connection");
|
||||
const redis = new Redis(appCfg.REDIS_URL);
|
||||
const redis = buildRedisFromConfig(appCfg);
|
||||
const redisPing = await redis?.ping();
|
||||
if (!redisPing) {
|
||||
console.error("Redis - Failed to connect");
|
||||
} else {
|
||||
console.error("Redis successfully connected");
|
||||
console.log("Redis successfully connected");
|
||||
if (appCfg.isRedisSentinelMode) {
|
||||
console.log("Redis Sentinel Mode");
|
||||
}
|
||||
redis.disconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import type { RateLimitOptions, RateLimitPluginOptions } from "@fastify/rate-limit";
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||
import { RateLimitError } from "@app/lib/errors";
|
||||
|
||||
export const globalRateLimiterCfg = (): RateLimitPluginOptions => {
|
||||
const appCfg = getConfig();
|
||||
const redis = appCfg.isRedisConfigured
|
||||
? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 })
|
||||
: null;
|
||||
const redis = appCfg.isRedisConfigured ? buildRedisFromConfig(appCfg) : null;
|
||||
|
||||
return {
|
||||
errorResponseBuilder: (_, context) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "Configurations"
|
||||
title: "Environment Variables"
|
||||
description: "Read how to configure environment variables for self-hosted Infisical."
|
||||
---
|
||||
|
||||
@@ -34,12 +34,14 @@ Used to configure platform-specific security and operational settings
|
||||
|
||||
By default, Infisical binds to `localhost`, which restricts access to connections from the same machine.
|
||||
|
||||
To make the application accessible externally (e.g., for self-hosted deployments), set this to `0.0.0.0`, which tells the server to listen on all network interfaces.
|
||||
To make the application accessible externally (e.g., for self-hosted deployments), set this to `0.0.0.0`, which tells the server to listen on all network interfaces.
|
||||
|
||||
Example values:
|
||||
|
||||
- `localhost` (default, same as `127.0.0.1`)
|
||||
- `0.0.0.0` (all interfaces, accessible externally)
|
||||
- `192.168.1.100` (specific interface IP)
|
||||
|
||||
Example values:
|
||||
- `localhost` (default, same as `127.0.0.1`)
|
||||
- `0.0.0.0` (all interfaces, accessible externally)
|
||||
- `192.168.1.100` (specific interface IP)
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="TELEMETRY_ENABLED" type="string" default="true" optional>
|
||||
@@ -86,8 +88,9 @@ The platform utilizes Postgres to persist all of its data and Redis for caching
|
||||
### PostgreSQL
|
||||
|
||||
<Info>
|
||||
Please note that the database user you create must be granted all privileges on the Infisical database.
|
||||
This includes the ability to create new schemas, create, update, delete, modify tables and indexes, etc.
|
||||
Please note that the database user you create must be granted all privileges
|
||||
on the Infisical database. This includes the ability to create new schemas,
|
||||
create, update, delete, modify tables and indexes, etc.
|
||||
</Info>
|
||||
|
||||
<ParamField query="DB_CONNECTION_URI" type="string" default="" required>
|
||||
@@ -119,10 +122,42 @@ DB_READ_REPLICAS=[{"DB_CONNECTION_URI":""}]
|
||||
</ParamField>
|
||||
|
||||
### Redis
|
||||
Redis is used for caching and background tasks. You can use either a standalone Redis instance or a Redis Sentinel setup.
|
||||
|
||||
<ParamField query="REDIS_URL" type="string" default="none" required>
|
||||
Redis connection string.
|
||||
</ParamField>
|
||||
<Tabs>
|
||||
<Tab title="Redis standalone">
|
||||
<ParamField query="REDIS_URL" type="string" default="none" required>
|
||||
Redis connection string.
|
||||
</ParamField>
|
||||
</Tab>
|
||||
<Tab title="Redis Sentinel">
|
||||
<ParamField
|
||||
query="REDIS_SENTINEL_HOSTS"
|
||||
type="string"
|
||||
default="none"
|
||||
required
|
||||
>
|
||||
Comma-separated list of Sentinel host:port pairs. ```
|
||||
192.168.65.254:26379,192.168.65.254:26380 ```
|
||||
</ParamField>
|
||||
<ParamField
|
||||
query="REDIS_SENTINEL_MASTER_NAME"
|
||||
type="string"
|
||||
default="mymaster"
|
||||
>
|
||||
The name of the Redis master set monitored by Sentinel
|
||||
</ParamField>
|
||||
<ParamField query="REDIS_SENTINEL_ENABLE_TLS" type="bool" default="false">
|
||||
Whether to use TLS/SSL for Redis Sentinel connection
|
||||
</ParamField>
|
||||
<ParamField query="REDIS_SENTINEL_USERNAME" type="string" default="none">
|
||||
Authentication username for Redis Sentinel
|
||||
</ParamField>
|
||||
<ParamField query="REDIS_SENTINEL_PASSWORD" type="string" default="none">
|
||||
Authentication password for Redis Sentinel
|
||||
</ParamField>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Email Service
|
||||
|
||||
@@ -640,13 +675,27 @@ To help you sync secrets from Infisical to services such as Github and Gitlab, I
|
||||
The App ID of your GitHub App.
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="SECRET_SCANNING_GIT_APP_SLUG" type="string" default="none" optional>
|
||||
The slug of your GitHub App.
|
||||
</ParamField>
|
||||
{" "}
|
||||
|
||||
<ParamField query="SECRET_SCANNING_PRIVATE_KEY" type="string" default="none" optional>
|
||||
A private key for your GitHub App.
|
||||
</ParamField>
|
||||
<ParamField
|
||||
query="SECRET_SCANNING_GIT_APP_SLUG"
|
||||
type="string"
|
||||
default="none"
|
||||
optional
|
||||
>
|
||||
The slug of your GitHub App.
|
||||
</ParamField>
|
||||
|
||||
{" "}
|
||||
|
||||
<ParamField
|
||||
query="SECRET_SCANNING_PRIVATE_KEY"
|
||||
type="string"
|
||||
default="none"
|
||||
optional
|
||||
>
|
||||
A private key for your GitHub App.
|
||||
</ParamField>
|
||||
|
||||
<ParamField query="SECRET_SCANNING_WEBHOOK_SECRET" type="string" default="none" optional>
|
||||
The webhook secret of your GitHub App.
|
||||
|
||||
1
sink/redis-sentinel/.env.example
Normal file
1
sink/redis-sentinel/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
HOST_IP=
|
||||
20
sink/redis-sentinel/README.md
Normal file
20
sink/redis-sentinel/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Sink for Redis Sentinel
|
||||
|
||||
1. Create a `.env` from `.env.example`
|
||||
2. The HOST_IP value must be your host ip, so that redis inside the containers can connect to it and switch over as needed.
|
||||
|
||||
To test Sentinel is working correctly
|
||||
|
||||
1. Run
|
||||
|
||||
```
|
||||
docker exec -it sentinel-2 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
|
||||
```
|
||||
|
||||
2. Run
|
||||
|
||||
```
|
||||
docker-compose -f docker-compose.sentinel.yml stop redis-master
|
||||
```
|
||||
|
||||
3. Again running step 1 should show the other replica port.
|
||||
166
sink/redis-sentinel/docker-compose.sentinel.yml
Normal file
166
sink/redis-sentinel/docker-compose.sentinel.yml
Normal file
@@ -0,0 +1,166 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
redis-master:
|
||||
image: redis:latest
|
||||
container_name: redis-master
|
||||
hostname: redis-master
|
||||
ports:
|
||||
- "6380:6379"
|
||||
volumes:
|
||||
- ./data/master:/data
|
||||
command:
|
||||
[
|
||||
"redis-server",
|
||||
"--appendonly",
|
||||
"yes",
|
||||
"--repl-diskless-load",
|
||||
"on-empty-db",
|
||||
"--replica-announce-ip",
|
||||
"${HOST_IP}",
|
||||
"--replica-announce-port",
|
||||
"6380",
|
||||
"--protected-mode",
|
||||
"no",
|
||||
]
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.3
|
||||
|
||||
redis-slave-1:
|
||||
image: redis:latest
|
||||
container_name: redis-slave-1
|
||||
hostname: redis-slave-1
|
||||
depends_on:
|
||||
- redis-master
|
||||
ports:
|
||||
- "6381:6379"
|
||||
volumes:
|
||||
- ./data/slave1:/data
|
||||
command:
|
||||
[
|
||||
"redis-server",
|
||||
"--appendonly",
|
||||
"yes",
|
||||
"--replicaof",
|
||||
"redis-master",
|
||||
"6379",
|
||||
"--repl-diskless-load",
|
||||
"on-empty-db",
|
||||
"--replica-announce-ip",
|
||||
"${HOST_IP}",
|
||||
"--replica-announce-port",
|
||||
"6381",
|
||||
"--protected-mode",
|
||||
"no",
|
||||
]
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.4
|
||||
|
||||
redis-slave-2:
|
||||
image: redis:latest
|
||||
container_name: redis-slave-2
|
||||
hostname: redis-slave-2
|
||||
depends_on:
|
||||
- redis-master
|
||||
ports:
|
||||
- "6382:6379"
|
||||
volumes:
|
||||
- ./data/slave2:/data
|
||||
command:
|
||||
[
|
||||
"redis-server",
|
||||
"--appendonly",
|
||||
"yes",
|
||||
"--replicaof",
|
||||
"redis-master",
|
||||
"6379",
|
||||
"--repl-diskless-load",
|
||||
"on-empty-db",
|
||||
"--replica-announce-ip",
|
||||
"${HOST_IP}",
|
||||
"--replica-announce-port",
|
||||
"6382",
|
||||
"--protected-mode",
|
||||
"no",
|
||||
]
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.5
|
||||
|
||||
sentinel-1:
|
||||
image: redis:latest
|
||||
container_name: sentinel-1
|
||||
hostname: sentinel-1
|
||||
depends_on:
|
||||
- redis-master
|
||||
ports:
|
||||
- "26379:26379"
|
||||
command: >
|
||||
sh -c 'echo "bind 0.0.0.0" > /etc/sentinel.conf &&
|
||||
echo "sentinel monitor mymaster ${HOST_IP} 6380 2" >> /etc/sentinel.conf &&
|
||||
echo "sentinel resolve-hostnames yes" >> /etc/sentinel.conf &&
|
||||
echo "sentinel down-after-milliseconds mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel failover-timeout mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel parallel-syncs mymaster 1" >> /etc/sentinel.conf &&
|
||||
redis-sentinel /etc/sentinel.conf'
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.6
|
||||
|
||||
sentinel-2:
|
||||
image: redis:latest
|
||||
container_name: sentinel-2
|
||||
hostname: sentinel-2
|
||||
depends_on:
|
||||
- redis-master
|
||||
ports:
|
||||
- "26380:26379"
|
||||
command: >
|
||||
sh -c 'echo "bind 0.0.0.0" > /etc/sentinel.conf &&
|
||||
echo "sentinel monitor mymaster ${HOST_IP} 6380 2" >> /etc/sentinel.conf &&
|
||||
echo "sentinel resolve-hostnames yes" >> /etc/sentinel.conf &&
|
||||
echo "sentinel down-after-milliseconds mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel failover-timeout mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel parallel-syncs mymaster 1" >> /etc/sentinel.conf &&
|
||||
redis-sentinel /etc/sentinel.conf'
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.7
|
||||
|
||||
sentinel-3:
|
||||
image: redis:latest
|
||||
container_name: sentinel-3
|
||||
hostname: sentinel-3
|
||||
depends_on:
|
||||
- redis-master
|
||||
ports:
|
||||
- "26381:26379"
|
||||
command: >
|
||||
sh -c 'echo "bind 0.0.0.0" > /etc/sentinel.conf &&
|
||||
echo "sentinel monitor mymaster ${HOST_IP} 6380 2" >> /etc/sentinel.conf &&
|
||||
echo "sentinel resolve-hostnames yes" >> /etc/sentinel.conf &&
|
||||
echo "sentinel down-after-milliseconds mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel failover-timeout mymaster 10000" >> /etc/sentinel.conf &&
|
||||
echo "sentinel parallel-syncs mymaster 1" >> /etc/sentinel.conf &&
|
||||
redis-sentinel /etc/sentinel.conf'
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.8
|
||||
|
||||
redisinsight:
|
||||
image: redis/redisinsight:latest
|
||||
container_name: redisinsight
|
||||
ports:
|
||||
- "5540:5540"
|
||||
networks:
|
||||
redis-net:
|
||||
ipv4_address: 172.21.0.9
|
||||
|
||||
networks:
|
||||
redis-net:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.21.0.0/16
|
||||
Reference in New Issue
Block a user