【IBM Application Gateway】Openshift上にRedisサーバー Replication構成（マスター/スレーブ）用意する

# はじめに
IBM Application Gatewayのセッション情報を保存できるRedisサーバー冗長化は、構成パターンがいくつかいくつかあります。

IBM Application GatewayがRedis Clusterをサポートしていないため、Standalone構成、Replication（マスター/スレーブ）構成、あるいは、Sentinel構成が候補となります。

Redisの冗長化についてはこちらを参考にさせて頂きました。
https://www.sraoss.co.jp/tech-blog/redis/redis-ha/

今回は、Kubernetesのガイドにあるyamlをベースにマスター/レプリカ構成を組んでみました。

例: Redisを使用したPHPのゲストブックアプリケーションのデプロイ
https://kubernetes.io/ja/docs/tutorials/stateless-application/guestbook/

# 悩みポイント1:Imageを変えると、レプリカ側もマスターとして動作してしまう。

KubernetesのガイドにあるYamlファイルは、imageが次のように指定されていました。
マスター：image: k8s.gcr.io/redis:e2e 
レプリカ：image: gcr.io/google_samples/gb-redisslave:v3

imageをDocker HubのRedisイメージに変更したところ、レプリカ側もマスターとして起動しました。
https://hub.docker.com/_/redis

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-slave
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: slave
      tier: backend
  replicas: 2
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: redis:latest
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 6379
```

以下の記事を拝見して、レプリカとして動かすためにはRedisの設定が必要な点がわかりました。

https://marsquai.com/7d9c0dd5-2fe1-4725-8cca-e766b4682aea/06e2238b-3fb0-452c-94f2-e52d2e0f68c0/93ddf6b6-6be1-4fed-a76e-4aeae502e442/


最終系のマスター/レプリカをデプロイするためのYAMLファイルはこちらです。

```redis-master-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-master
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: master
      tier: backend
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: redis:latest
        command:
            - "redis-server"
        args:
            - "--protected-mode"
            - "no"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 6379
```

スレーブとして動作させるため、slaveof <マスターのサービス名＞ <ポート番号＞ のコマンドを追加しています。
```redis-slave-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-slave
  labels:
    app: redis
spec:
  selector:
    matchLabels:
      app: redis
      role: slave
      tier: backend
  replicas: 2
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: redis:latest
        command:
            - "redis-server"
        args:
            - "--slaveof"
            - "redis-master"
            - "6379"
            - "--protected-mode"
            - "no"
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 6379
```

サービスの設定はKubernetesのガイドと同じです。

```redis-master-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    role: master
    tier: backend
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    role: master
    tier: backend
```

```redis-slave-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    role: slave
    tier: backend
spec:
  ports:
  - port: 6379
  selector:
    app: redis
    role: slave
    tier: backend
```



# 悩みポイント2:"Failed opening the RDB file dump.rdb (in server root dir /data) for saving: Permission denied"が出力されて、マスター/レプリカ間で同期できない。

マスター側のログに"Failed opening the RDB file dump.rdb (in server root dir /data) for saving: Permission denied"が出力されていました。

```
1:M 06 Apr 2022 04:14:02.271 * Replica 172.30.169.41:6379 asks for synchronization
1:M 06 Apr 2022 04:14:02.271 * Full resync requested by replica 172.30.169.41:6379
1:M 06 Apr 2022 04:14:02.271 * Replication backlog created, my new replication IDs are 'a5cd3c705f8a0b324f44971ca6fc7d8043ce482b' and '0000000000000000000000000000000000000000'
1:M 06 Apr 2022 04:14:02.271 * Starting BGSAVE for SYNC with target: disk
1:M 06 Apr 2022 04:14:02.272 * Background saving started by pid 11
11:C 06 Apr 2022 04:14:02.272 # Failed opening the RDB file dump.rdb (in server root dir /data) for saving: Permission denied
1:M 06 Apr 2022 04:14:02.369 # Background saving error
1:M 06 Apr 2022 04:14:02.369 # Connection with replica 172.30.169.41:6379 lost.
1:M 06 Apr 2022 04:14:02.369 # SYNC failed. BGSAVE child returned an error
```

パーミッションの問題にみえるため、必要な権限を確認したところ、anyuidのSSCが必要でした。
```
> oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-84f49d559d-q678p   1/1     Running   0          8m31s
redis-slave-5f7bc5fb7c-d26f5    1/1     Running   0          7m38s
redis-slave-5f7bc5fb7c-psh4q    1/1     Running   0          7m38s
> oc get pod/redis-master-84f49d559d-q678p -o yaml|oc adm policy scc-subject-review -f -
RESOURCE                            ALLOWED BY
Pod/redis-master-84f49d559d-q678p   anyuid
```

このため、anyuidのSSCを持つサービスアカウントを作成して、デプロイメントに設定します。

```
> oc create serviceaccount redis-sa
serviceaccount/redis-sa created

> oc adm policy add-scc-to-user anyuid -z redis-sa
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "redis-sa"

> oc describe serviceaccount redis-sa
Name:                redis-sa
Namespace:           test-redis
Labels:              <none>
Annotations:         <none>
Image pull secrets:  redis-sa-dockercfg-q8w5d
Mountable secrets:   redis-sa-token-sqcdj
                     redis-sa-dockercfg-q8w5d
Tokens:              redis-sa-token-4llgf
                     redis-sa-token-sqcdj
Events:              <none>

> oc set serviceaccount deployment/redis-master redis-sa
deployment.apps/redis-master serviceaccount updated

> oc describe deployment/redis-master |grep Service
  Service Account:  redis-sa

> oc set serviceaccount deployment/redis-slave redis-sa
deployment.apps/redis-slave serviceaccount updated

> oc describe deployment/redis-slave |grep Service
  Service Account:  redis-sa
```

設定することでエラー解消しました
```
1:C 06 Apr 2022 04:27:53.749 # Configuration loaded
1:M 06 Apr 2022 04:27:53.751 * monotonic clock: POSIX clock_gettime
1:M 06 Apr 2022 04:27:53.752 * Running mode=standalone, port=6379.
1:M 06 Apr 2022 04:27:53.752 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 06 Apr 2022 04:27:53.752 # Server initialized
1:M 06 Apr 2022 04:27:53.753 * Ready to accept connections
1:M 06 Apr 2022 04:27:55.130 * Replica 172.30.115.4:6379 asks for synchronization
1:M 06 Apr 2022 04:27:55.130 * Full resync requested by replica 172.30.115.4:6379
1:M 06 Apr 2022 04:27:55.130 * Replication backlog created, my new replication IDs are '9d966da84bc748f7df876a47339d13ff678d045d' and '0000000000000000000000000000000000000000'
1:M 06 Apr 2022 04:27:55.130 * Starting BGSAVE for SYNC with target: disk
1:M 06 Apr 2022 04:27:55.130 * Background saving started by pid 11
11:C 06 Apr 2022 04:27:55.136 * DB saved on disk
11:C 06 Apr 2022 04:27:55.137 * RDB: 0 MB of memory used by copy-on-write
1:M 06 Apr 2022 04:27:55.164 * Background saving terminated with success
1:M 06 Apr 2022 04:27:55.164 * Synchronization with replica 172.30.115.4:6379 succeeded
1:M 06 Apr 2022 04:27:55.650 * Replica 172.30.169.41:6379 asks for synchronization
1:M 06 Apr 2022 04:27:55.650 * Full resync requested by replica 172.30.169.41:6379
1:M 06 Apr 2022 04:27:55.650 * Starting BGSAVE for SYNC with target: disk
1:M 06 Apr 2022 04:27:55.651 * Background saving started by pid 12
12:C 06 Apr 2022 04:27:55.654 * DB saved on disk
```

マスター側のRedisサーバーに/data/dump.rdbファイルがあること、2つのスレーブと接続できていること（connected_slaves）を確認します。

```
> oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-5c69db88c4-g6glq   1/1     Running   0          4m1s
redis-slave-6dcb7cdcd5-8lrl6    1/1     Running   0          3m49s
redis-slave-6dcb7cdcd5-pzxbm    1/1     Running   0          3m55s
PS C:\temp-openshift\iag> oc rsh redis-master-5c69db88c4-g6glq

# ls /data
dump.rdb

# redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.30.169.42,port=6379,state=online,offset=350,lag=0
slave1:ip=172.30.115.19,port=6379,state=online,offset=350,lag=0
master_failover_state:no-failover
master_replid:9d966da84bc748f7df876a47339d13ff678d045d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:350
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:350
```

マスター側でテストデータを書き込みします。
```
> redis-cli set aaa 111
OK
> redis-cli get aaa
"111"
```

レプリカ側でも同様に確認します。
master_link_statusがupになっているため、マスター側とリンクできています。

```
> oc get pods
NAME                            READY   STATUS    RESTARTS   AGE
redis-master-5c69db88c4-g6glq   1/1     Running   0          7m40s
redis-slave-6dcb7cdcd5-8lrl6    1/1     Running   0          7m28s
redis-slave-6dcb7cdcd5-pzxbm    1/1     Running   0          7m34s
PS C:\temp-openshift\iag> oc rsh redis-slave-6dcb7cdcd5-8lrl6

# ls /data
dump.rdb

# redis-cli info replication
# Replication
role:slave
master_host:redis-master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_read_repl_offset:698
slave_repl_offset:698
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:9d966da84bc748f7df876a47339d13ff678d045d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:698
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:684
```

先ほどマスター側で登録したテストデータの同期が確認できました。
```
> redis-cli get aaa
"111"
```

# 最後に

今回は、Redisサーバーの冗長化構成を試しました。
今後IBM Applicatio Gatewayと組み合わせて動作確認していきたいと思います。