クライアントを使用して Tair (Redis OSS-compatible) インスタンスに接続する際に、TLS (SSL) 暗号化を有効にすることで、データリンクのセキュリティを向上させ、データ整合性を確保できます。Redis プロトコルと互換性のある任意のクライアントを使用できます。このトピックでは、一般的なクライアントを使用してインスタンスに接続する方法を示すサンプルコードを提供します。
前提条件
Tair インスタンスで TLS (SSL) 暗号化が有効になっていること。詳細については、「TLS 暗号化を有効にする」をご参照ください。
クライアントは、Tair インスタンスと同じ Virtual Private Cloud (VPC) 内にある Elastic Compute Service (ECS) インスタンスでホストされていること。
注意
インスタンスで VPC 経由のパスワードなしのアクセスが有効になっている場合、同じ VPC 内のクライアントはパスワードなしでインスタンスに接続できます。
準備
クライアントをホストする ECS インスタンスの内部 IP アドレスを Tair インスタンスのホワイトリストに追加します。詳細については、「ホワイトリストを設定する」をご参照ください。
クライアントプログラムのコードで設定するために、次の情報を取得します。
取得する情報
取得方法
インスタンスのエンドポイント
インスタンス ページに移動し、上部のナビゲーションバーでリージョンを選択し、ターゲットインスタンスの ID をクリックします。[接続情報] セクションで、さまざまな接続タイプのエンドポイントとポートを表示できます。
説明インスタンスは複数のエンドポイントタイプをサポートしています。セキュリティを強化し、ネットワーク遅延を低減するために、VPC エンドポイントを使用することをお勧めします。詳細については、「エンドポイントの表示」をご参照ください。
ポート
デフォルトのポートは 6379 です。ポートをカスタマイズすることもできます。詳細については、「エンドポイントまたはポートの変更」をご参照ください。
インスタンスのアカウント (一部のクライアントでは不要)
デフォルトでは、インスタンスには r-bp10noxlhcoim2**** のようにインスタンス ID にちなんで名付けられたアカウントがあります。新しいアカウントを作成して権限を付与することもできます。詳細については、「アカウントの作成と管理」をご参照ください。
アカウントのパスワード
パスワードの形式は、選択したアカウントによって異なります。
デフォルトアカウント (インスタンス ID にちなんで名付けられたアカウント): パスワードのみを入力します。
新しく作成されたアカウント: パスワードの形式は
<user>:<password>です。たとえば、カスタムアカウントがtestaccountで、パスワードがRp829dlwaの場合、testaccount:Rp829dlwaと入力する必要があります。
説明RDM などのサードパーティのデータベース管理ツールを使用してインスタンスに接続する場合、パスワードフィールドに
user:password形式でパスワードを入力する必要があります。パスワードを忘れた場合は、リセットできます。詳細については、「パスワードの変更またはリセット」をご参照ください。
認証局 (CA) 証明書をダウンロードします。詳細については、「TLS 暗号化を有効にする」をご参照ください。
プロキシ接続モード
このモードは、標準アーキテクチャ、プロキシモードのクラスターアーキテクチャ、または読み書き分離アーキテクチャを使用するインスタンスに適用されます。次のセクションを展開して、コード例を表示します。
redis-cli
Redis をコンパイルするときは、redis-cli で TLS 接続を有効にするために BUILD_TLS=yes を指定する必要があります。
ECS インスタンスにログインし、次のコマンドを実行して redis-cli をダウンロード、インストール、コンパイルします。
sudo yum -y install openssl-devel gcc # gcc 依存関係をインストールします。 wget https://download.redis.io/releases/redis-7.2.0.tar.gz tar xzf redis-7.2.0.tar.gz cd redis-7.2.0&&make BUILD_TLS=yesこのトピックでは Redis 7.2.0 を例として使用します。他のバージョンをインストールすることもできます。コンパイルとインストールのプロセスには通常 2〜3 分かかります。
コマンドラインウィンドウで次のコマンドを実行して、インスタンスに接続します。
./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 --tls --cacert ./ApsaraDB-CA-Chain.pemcacert パラメーターの後に、CA 証明書へのパスを指定します。
次のコマンドを実行して、パスワード認証を完了します。
AUTH passwordOK 応答は、インスタンスに接続されていることを示します。
Java
この例では、Jedis バージョン 3.6.0 を使用します。最新バージョンを使用してください。
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisSSLTest {
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
public static void main(String[] args) throws Exception {
// ApsaraDB-CA-Chain.jks は証明書ファイル名です。
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory("ApsaraDB-CA-Chain.jks");
// 接続プールの設定は、インスタンスのエンドポイント、ポート番号、タイムアウト設定、およびパスワードです。
JedisPool pool = new JedisPool(new GenericObjectPoolConfig(), "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com",
6379, 2000, "redistest:Pas***23", 0, true, sslSocketFactory, null, null);
try (Jedis jedis = pool.getResource()) {
jedis.set("key", "value");
System.out.println(jedis.get("key"));
}
}
}Python
この例では、redis-py クライアントを使用します。最新バージョンを使用してください。
接続プール
#!/bin/python
import redis
# 接続プールを設定します。host、port、password の値をインスタンスのエンドポイント、ポート番号、パスワードに置き換えます。
# ApsaraDB-CA-Chain.pem は証明書ファイル名です。
pool = redis.ConnectionPool(connection_class=redis.connection.SSLConnection, max_connections=100,
host="r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com", port=6379, password="redistest:Pas***23",
ssl_cert_reqs=True, ssl_ca_certs="ApsaraDB-CA-Chain.pem")
client = redis.Redis(connection_pool=pool)
client.set("hi", "redis")
print client.get("hi")標準接続
#!/bin/python
import redis
# 接続情報を設定します。host、port、password の値をインスタンスのエンドポイント、ポート番号、パスワードに置き換えます。
# ApsaraDB-CA-Chain.pem は証明書ファイル名です。
client = redis.Redis(host="r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com", port=6379,
password="redistest:Test1234", ssl=True,
ssl_cert_reqs="required", ssl_ca_certs="ApsaraDB-CA-Chain.pem")
client.set("hello", "world")
print client.get("hello")PHP
この例では、predis クライアントを使用します。最新バージョンを使用してください。phpredis クライアントを使用する場合は、接続例について「phpredis/phpredis#1600」をご参照ください。
<?php
require __DIR__.'/predis/autoload.php';
/* 接続情報を設定します。host、port、password の値をインスタンスのエンドポイント、ポート番号、パスワードに置き換えます。
ApsaraDB-CA-Chain.pem は証明書ファイル名です。*/
$client = new Predis\Client([
'scheme' => 'tls',
'host' => 'r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com',
'port' => 6379,
'password' => 'redistest:Pas***23',
'ssl' => ['cafile' => 'ApsaraDB-CA-Chain.pem', 'verify_peer' => true],
]);
/* 次のコードのエンドポイントとポートを置き換えます。*/
//$client = new Predis\Client('tls://r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com:6379?ssl[cafile]=ApsaraDB-CA-Chain.pem&ssl[verify_peer]=1');
$client->set("hello", "world");
print $client->get("hello")."\n";
?>C#
この例では、StackExchange.Redis クライアントを使用します。最新バージョンを使用してください。
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using StackExchange.Redis;
namespace SSLTest
{
class Program
{
private static bool CheckServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var ca = new X509Certificate2(
"/your path/ApsaraDB-CA-Chain/ApsaraDB-CA-Chain.pem");
return chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint == ca.Thumbprint);
}
static void Main(string[] args)
{
// 接続情報を設定します。host、port、password の値をインスタンスのエンドポイント、ポート番号、パスワードに置き換えます。
// ApsaraDB-CA-Chain.pem は証明書ファイル名です。
ConfigurationOptions config = new ConfigurationOptions()
{
EndPoints = {"r-bp10q23zyfriodu*****.redis.rds.aliyuncs.com:6379"},
Password = "redistest:Pas***23",
Ssl = true,
};
config.CertificateValidation += CheckServerCertificate;
using (var conn = ConnectionMultiplexer.Connect(config))
{
Console.WriteLine("connected");
var db = conn.GetDatabase();
db.StringSet("hello", "world");
Console.WriteLine(db.StringGet("hello"));
}
}
}
}Spring Data Redis
この例では、Spring Data Redis バージョン 2.7.12 (Java 1.8 用) を使用します。最新バージョンを使用してください。
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// TLS 証明書の設定をプロパティファイルに保存します。
String host = "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/path/to/ApsaraDB-CA-Chain.jks";
ClientOptions clientOptions = ClientOptions.builder().sslOptions(
SslOptions.builder().jdkSslProvider().truststore(new File(trustStoreFilePath)).build()).build();
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
config.setPassword(password);
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.useSsl().build();
return new LettuceConnectionFactory(config, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
}
}Lettuce
この例では、Lettuce バージョン 6.2.4.RELEASE を使用します。最新バージョンを使用してください。
public class SSLExample {
public static void main(String[] args) throws Exception {
String host = "r-bp1zxszhcgatnx****.redis.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/path/to/ApsaraDB-CA-Chain.jks";
RedisURI uri = RedisURI.builder()
.withHost(host)
.withPort(port)
.withPassword(password.toCharArray())
.withSsl(true).build();
SslOptions sslOptions = SslOptions.builder()
.jdkSslProvider()
.truststore(new File(trustStoreFilePath)).build();
ClientOptions clientOptions = ClientOptions.builder()
.sslOptions(sslOptions).build();
RedisClient client = RedisClient.create(uri);
client.setOptions(clientOptions);
RedisCommands<String, String> sync = client.connect().sync();
System.out.println(sync.set("key", "value"));
System.out.println(sync.get("key"));
}
}Go
この例では、go-redis クライアント v9.5.1 を使用します。v9.0 以降を使用してください。
package main
import (
"context"
"fmt"
"io/ioutil"
"crypto/tls"
"crypto/x509"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
caCert, err := ioutil.ReadFile("/root/ApsaraDB-CA-Chain.pem")
if err != nil {
fmt.Println("Error loading CA certificate:", err)
return
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: true, // 実際にはスキップせず、VerifyPeerCertificate で証明書をチェックします
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// https://github.com/golang/go/blob/81555cb4f3521b53f9de4ce15f64b77cc9df61b9/src/crypto/tls/handshake_client.go#L327-L344 からコピーして適合させたコードですが、ホスト名の検証をスキップするように適合させています。
// https://github.com/golang/go/issues/21971#issuecomment-412836078 を参照してください。
// これが接続での最初のハンドシェイクである場合、サーバーの証明書を処理し、(オプションで) 検証します。
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
panic(err)
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: caCertPool,
DNSName: "", // <- ホスト名の検証をスキップ
Intermediates: x509.NewCertPool(),
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
},
}
rdb := redis.NewClient(&redis.Options{
Addr: "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379",
Username: "default",
Password: "Pas***23",
TLSConfig: tlsConfig,
})
err = rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
}直接接続モード
このモードは、直接接続モードのクラスターアーキテクチャを使用するインスタンスにのみ適用されます。次のセクションを展開して、コード例を表示します。
redis-cli
Redis をコンパイルするときは、redis-cli で TLS 接続を有効にするために BUILD_TLS=yes を指定する必要があります。
ECS インスタンスにログインし、次のコマンドを実行して redis-cli をダウンロード、インストール、コンパイルします。
sudo yum -y install openssl-devel gcc # gcc 依存関係をインストールします。 wget https://download.redis.io/releases/redis-7.2.0.tar.gz tar xzf redis-7.2.0.tar.gz cd redis-7.2.0&&make BUILD_TLS=yesこのトピックでは Redis 7.2.0 を例として使用します。他のバージョンをインストールすることもできます。コンパイルとインストールのプロセスには通常 2〜3 分かかります。
コマンドラインウィンドウで次のコマンドを実行して、インスタンスに接続します。
./src/redis-cli -h r-bp14joyeihew30****.redis.rds.aliyuncs.com -p 6379 -c --tls --cacert ./ApsaraDB-CA-Chain.pemcacert パラメーターの後に、CA 証明書へのパスを指定します。
次のコマンドを実行して、パスワード認証を完了します。
AUTH passwordOK 応答は、インスタンスに接続されていることを示します。
Java
この例では、Jedis バージョン 4.3.0 を使用します。最新バージョンを使用してください。
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.HashSet;
import java.util.Set;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import redis.clients.jedis.ConnectionPoolConfig;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisClusterTSL {
private static final int DEFAULT_TIMEOUT = 2000;
private static final int DEFAULT_REDIRECTIONS = 5;
private static final ConnectionPoolConfig jedisPoolConfig = new ConnectionPoolConfig();
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
public static void main(String args[]) throws Exception{
// アイドル接続の最大数。直接接続モードでは、クライアントはデータベースシャードに直接接続します。(ビジネス マシンの数 × MaxTotal) の値が、単一のデータベースシャードの最大接続数未満であることを確認してください。
jedisPoolConfig.setMaxTotal(30);
// アイドル接続の最大数。必要に応じてこの値を設定します。
jedisPoolConfig.setMaxIdle(30);
jedisPoolConfig.setMinIdle(15);
// 直接接続のエンドポイント。
int port = 6379;
String host = "r-2zee50zxi5iiq****.redis.rds-aliyun.rds.aliyuncs.com";
String user = "default";
String password = "Pas***23";
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory("/root/ApsaraDB-CA-Chain.jks");
DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder().connectionTimeoutMillis(DEFAULT_TIMEOUT)
.socketTimeoutMillis(DEFAULT_TIMEOUT)
.user(user).password(password)
.ssl(true)
.sslSocketFactory(sslSocketFactory).build();
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort(host, port));
JedisCluster jc = new JedisCluster(jedisClusterNode, jedisClientConfig, DEFAULT_REDIRECTIONS, jedisPoolConfig);
System.out.println(jc.set("key", "value"));
System.out.println(jc.get("key"));
jc.close(); // アプリケーションが終了し、リソースを破棄する必要がある場合は、このメソッドを呼び出します。このメソッドはクライアントを切断し、リソースを解放します。
}
}Python
この例では、redis-py クライアント 4.3.6 (Python 3.6 用) を使用します。最新バージョンを使用してください。
#!/usr/bin/env python
from redis.cluster import RedisCluster
# host と port の値をインスタンスのエンドポイントとポート番号に置き換えます。
host = 'r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com'
port = 6379
# user と pwd の値をインスタンスのアカウントとパスワードに置き換えます。
user = 'default'
pwd = 'Pas***23'
rc = RedisCluster(host=host, port=port, username=user, password=pwd, ssl=True, ssl_ca_certs="/root/ApsaraDB-CA-Chain.pem")
# 接続が確立された後、データベース操作を実行できます。次のコードは、SET と GET の使用方法の例を示しています。
rc.set('foo', 'bar')
print(rc.get('foo'))
PHP
この例では、phpredis クライアント 5.3.7 を使用します。最新バージョンを使用してください。
<?php
// 直接接続のエンドポイントとポート。
$array = ['r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379'];
// 接続パスワード。
$pwd = "Pas***23";
// TLS 接続情報。
$tls = ["verify_peer" => false, "verify_peer_name" => false];
// パスワードを使用してクラスターに接続します。
$obj_cluster = new RedisCluster(NULL, $array, 1.5, 1.5, true, $pwd, $tls);
// 接続結果を出力します。
var_dump($obj_cluster);
if ($obj_cluster->set("foo", "bar") == false) {
die($obj_cluster->getLastError());
}
$value = $obj_cluster->get("foo");
echo $value;
echo "\n";
?>C#
この例では、StackExchange.Redis クライアントを使用します。最新バージョンを使用してください。
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace TairClient
{
class Program
{
static void Main()
{
// 直接接続のエンドポイント。
const string Host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
const int Port = 6379;
Console.WriteLine("connecting...");
var config = new ConfigurationOptions
{
EndPoints = { { Host, Port } },
Ssl = true,
Password = "Pas***23",
};
config.CertificateValidation += (sender, cert, chain, errors) =>
{
if (errors == SslPolicyErrors.RemoteCertificateChainErrors || errors == SslPolicyErrors.RemoteCertificateNameMismatch)
{
return true;
}
var caCert = LoadCertificateFromPem("/root/ApsaraDB-CA-Chain.pem");
var isCertIssuedByTrustedCA = chain.ChainElements
.Cast<X509ChainElement>()
.Any(x => x.Certificate.Thumbprint.Equals(caCert.Thumbprint, StringComparison.OrdinalIgnoreCase));
// 他の検証ロジックをカスタマイズ...
return isCertIssuedByTrustedCA;
};
using (var conn = ConnectionMultiplexer.Connect(config))
{
Console.WriteLine("connected");
var db = conn.GetDatabase();
db.StringSet("hello", "world");
Console.WriteLine(db.StringGet("hello")); // 書き込み: world
}
}
private static X509Certificate2 LoadCertificateFromPem(string pemFilePath)
{
// X509Certificate2 の静的メソッドを使用して、PEM コンテンツから証明書を直接ロードします。
X509Certificate2 cert = X509Certificate2.CreateFromPem(File.ReadAllText(pemFilePath));
return cert;
}
}
}Spring Data Redis
この例では、Spring Data Redis バージョン 2.7.5 (Java 1.8 用) を使用します。最新バージョンを使用してください。
Jedis を使用 (推奨)
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisConfigJedis {
private static SSLSocketFactory createTrustStoreSSLSocketFactory(String jksFile) throws Exception {
KeyStore trustStore = KeyStore.getInstance("jks");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(jksFile);
trustStore.load(inputStream, null);
} finally {
inputStream.close();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext.getSocketFactory();
}
@Bean
public RedisConnectionFactory redisConnectionFactory() throws Exception {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379";
String user = "default";
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
List<String> clusterNodes = Arrays.asList(host);
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes);
redisClusterConfiguration.setUsername(user);
redisClusterConfiguration.setPassword(password);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// アイドル接続の最大数。直接接続モードでは、クライアントはデータベースシャードに直接接続します。(ビジネス マシンの数 × MaxTotal) の値が、単一のデータベースシャードの最大接続数未満であることを確認してください。
jedisPoolConfig.setMaxTotal(30);
jedisPoolConfig.setMaxIdle(20);
jedisPoolConfig.setMinIdle(20);
final SSLSocketFactory sslSocketFactory = createTrustStoreSSLSocketFactory(trustStoreFilePath);
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().useSsl()
.sslSocketFactory(sslSocketFactory).and().usePooling().poolConfig(jedisPoolConfig).build();
return new JedisConnectionFactory(redisClusterConfiguration, jedisClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}Lettuce を使用
import java.io.File;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.SslOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
int port = 6379;
String user = "default";
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
ClientOptions clientOptions = ClientOptions.builder().sslOptions(
SslOptions.builder().jdkSslProvider().truststore(new File(trustStoreFilePath)).build()).build();
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
clusterConfiguration.clusterNode(host, port);
clusterConfiguration.setUsername(user);
clusterConfiguration.setPassword(password);
LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.useSsl()
.disablePeerVerification()
.build();
return new LettuceConnectionFactory(clusterConfiguration, lettuceClientConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}Lettuce
この例では、Lettuce バージョン 6.3.0.RELEASE を使用します。最新バージョンを使用してください。
import java.io.File;
import java.time.Duration;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.SocketOptions.KeepAliveOptions;
import io.lettuce.core.SocketOptions.TcpUserTimeoutOptions;
import io.lettuce.core.SslOptions;
import io.lettuce.core.SslVerifyMode;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
public class SSLClusterExample {
/**
* TCP_KEEPALIVE が有効になっており、3 つのパラメーターは次のように設定されています:
* TCP_KEEPIDLE = 30
* TCP_KEEPINTVL = 10
* TCP_KEEPCNT = 3
*/
private static final int TCP_KEEPALIVE_IDLE = 30;
/**
* TCP_USER_TIMEOUT は、故障時に Lettuce が継続的にタイムアウトするのを防ぐことができます。
* 参照: https://github.com/lettuce-io/lettuce-core/issues/2082
*/
private static final int TCP_USER_TIMEOUT = 30;
public static void main(String[] args) throws Exception {
String host = "r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com";
int port = 6379;
String password = "Pas***23";
String trustStoreFilePath = "/root/ApsaraDB-CA-Chain.jks";
RedisURI uri = RedisURI.builder()
.withHost(host)
.withPort(port)
.withPassword(password.toCharArray())
.withSsl(true)
.withVerifyPeer(SslVerifyMode.CA) // 直接クラスター接続の性質上、SslVerifyMode.FULL は使用できません。ホスト名の検証をスキップする必要があります。
.build();
SslOptions sslOptions = SslOptions.builder()
.jdkSslProvider()
.truststore(new File(trustStoreFilePath)).build();
ClusterTopologyRefreshOptions refreshOptions = ClusterTopologyRefreshOptions.builder()
.enablePeriodicRefresh(Duration.ofSeconds(15))
.dynamicRefreshSources(false)
.enableAllAdaptiveRefreshTriggers()
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build();
// TCP KeepAlive の設定
SocketOptions socketOptions = SocketOptions.builder()
.keepAlive(KeepAliveOptions.builder()
.enable()
.idle(Duration.ofSeconds(TCP_KEEPALIVE_IDLE))
.interval(Duration.ofSeconds(TCP_KEEPALIVE_IDLE / 3))
.count(3)
.build())
.tcpUserTimeout(TcpUserTimeoutOptions.builder()
.enable()
.tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
.build())
.build();
RedisClusterClient redisClient = RedisClusterClient.create(uri);
redisClient.setOptions(ClusterClientOptions.builder()
.socketOptions(socketOptions)
.sslOptions(sslOptions)
.validateClusterNodeMembership(false)
.topologyRefreshOptions(refreshOptions).build());
StatefulRedisClusterConnection<String, String> connection = redisClient.connect();
connection.sync().set("key", "value");
System.out.println(connection.sync().get("key"));
}
}Go
この例では、go-redis クライアント v9.5.1 を使用します。v9.0 以降を使用してください。
package main
import (
"context"
"fmt"
"io/ioutil"
"crypto/tls"
"crypto/x509"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
caCert, err := ioutil.ReadFile("/root/ApsaraDB-CA-Chain.pem")
if err != nil {
fmt.Println("Error loading CA certificate:", err)
return
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: true, // 実際にはスキップせず、VerifyPeerCertificate で証明書をチェックします
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// https://github.com/golang/go/blob/81555cb4f3521b53f9de4ce15f64b77cc9df61b9/src/crypto/tls/handshake_client.go#L327-L344 からコピーして適合させたコードですが、ホスト名の検証をスキップするように適合させています。
// https://github.com/golang/go/issues/21971#issuecomment-412836078 を参照してください。
// これが接続での最初のハンドシェイクである場合、サーバーの証明書を処理し、(オプションで) 検証します。
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
panic(err)
}
certs[i] = cert
}
opts := x509.VerifyOptions{
Roots: caCertPool,
DNSName: "", // <- ホスト名の検証をスキップ
Intermediates: x509.NewCertPool(),
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
},
}
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"r-2zee50zxi5iiqm****.redis.rds-aliyun.rds.aliyuncs.com:6379"},
Username: "default",
Password: "Pas***23",
TLSConfig: tlsConfig,
})
err = rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
}よくある質問
No subject alternative DNS name matching xxx foundエラーが表示されるのはなぜですか?このエラーは、TLS を有効にした後にインスタンスのエンドポイントまたはポート番号を変更した場合に発生します。この問題を解決するには、コンソールで TLS 証明書を更新してから、再度接続してみてください。