Redisのインストール・セットアップ

Redis 冗長化 ＆ フェールオーバー環境を CentOS 6.x 上に構築する手順です
手順は３部構成となっており、本稿は **Redis のインストール方法**となります

　**（その１）：Redisのインストール**
　[（その２）：Redisの冗長化](http://qiita.com/KurosawaTsuyoshi/items/936c32bb8947fb907ef5)
　[（その３）：Redis-Sentinelによるフェールオーバー環境の構築](http://qiita.com/KurosawaTsuyoshi/items/e6a4def57c9a12f1d752)

# 環境
・CentOS release 6.x
・Redis 3.0.7
・Redis-Sentinel 3.0.7

# 構成の概要
まずは以下構成で Redis を構築します

| 起動ファイル | ポート | 設定ファイル |説明 |
| --- | --- | --- | --- |
| redis | 6379 | 6379.conf | Redis 本体です |

# Redis のインストール
パッケージから導入する方法と、ソースからインストールする２種類の方法があります
各自の環境制約を踏まえて導入手段を検討してください

## パッケージから導入する場合
Redis は標準パッケージに含まれていません（2015/11/15時点）
サードパーティのリポジトリをインストールするので、標準リポジトリしか利用できない環境では後述の「ソースからインストール」にスキップしてください

サードパーティのリポジトリをセットアップします
remi と EPEL リポジトリが必要です[導入手順はこちら](http://qiita.com/KurosawaTsuyoshi/items/9a2b61af6e13dfa5641b)
メモリ管理に tcmalloc を利用しているため gperftools-libs もインストールします

```bash:CentOS5/6
$ sudo yum install --enablerepo=epel gperftools
$ yum info --enablerepo=remi redis | egrep "Version|バージョン"
Version : 3.0.7
$ sudo yum install --enablerepo=remi redis
```
自動起動スクリプトがインストールされるので、実行して正常起動することを確認します

```bash:Redisサーバ起動
$ sudo service redis start
redis-server を起動中:                                     [  OK  ]
```
```bash:プロセス確認
$ ps -ef | grep redis
redis 10995 1 0 21:57 ? 00:00:00 /usr/bin/redis-server 127.0.0.1:6379
```
Redis に接続してバージョンを確認します

```bash:接続確認
$ redis-cli info | grep redis_version
redis_version:3.0.7
```
### Redis のワークディレクトリ作成
パッケージ版では設定ファイルの配置先が /etc/ 直下となります
Redis 冗長化にあたり設定ファイルは複数作成するので、煩雑回避のため Redis 用のワークディレクトリを作成して集約させます
なお redis ユーザが書き込めるよう権限を付与しておきます

　*/etc/redis：設定ファイルの格納先*

```bash:Redisワークディレクトリの作成
$ sudo mkdir /etc/redis
$ sudo chmod 755 /etc/redis
$ sudo chown redis:redis /etc/redis
```

### 自動起動スクリプトの修正
設定ファイルの配置先を変更したので REDIS_CONFIG を変更します
複数プロセス立ち上げるので pidfile もポート番号を付加しておきます

```bash:/etc/init.d/redis
$ sudo vi /etc/init.d/redis
：
#pidfile="/var/run/redis/redis.pid"
#REDIS_CONFIG="/etc/redis.conf"
REDISPORT=6379
pidfile="/var/run/redis/redis_${REDISPORT}.pid"
REDIS_CONFIG="/etc/redis/${REDISPORT}.conf"
```

### Redis 設定ファイルのコピー
ポートごとの設定ファイルにするので元の設定ファイルをコピーします

```bash:Redis設定ファイル雛形のコピー
$ sudo cp -p /etc/redis.conf /etc/redis/6379.conf
```

**ここまで実施したら「Redis 設定ファイルの編集」までスキップしてください**

## ソースからインストールする場合
[Redis](http://download.redis.io/) のホームページからソースをダウンロードしてインストールします
2016/2/3時点の最新版は、3.0.7 のようです

Redis の冗長構成には redis 2.4.16 または 2.6.0-rc6 以降のバージョンが必要です
こちらで適切なバージョンを確認して、以下コマンドのファイル名を変更してください
[Downlod 一覧](http://download.redis.io/releases/)

```bash:redisのソースダウンロード
$ cd
$ cd Downlods
$ wget http://download.redis.io/releases/redis-3.0.7.tar.gz
```
```bash:redisのインストール
$ tar xzf redis-3.0.*.tar.gz
$ cd redis-3.0.*
$ make
$ sudo make install
```
make でエラーが発生する場合は、開発ツールをインストールしてください

```bash
make[3]: gcc: コマンドが見つかりませんでした
make[3]: *** [net.o] エラー 127

$ sudo yum groupinstall 'Development Tools'
```
デフォルトのインストール先は、/usr/local/bin/ です
変更したい場合は、redis-3.0.*/src の配下の Makefile を手動で変更します


```mf:Makefile
PREFIX?=インストール先ディレクトリ名
```

### Redis ユーザ及びグループの作成
Redis インスタンスを起動するユーザとグループを作成します
ログインは不要なので nologin を指定します

```bash
$ sudo groupadd redis
$ sudo useradd -s /sbin/nologin -M -g redis redis
```

### Redis のワークディレクトリ作成
Redis 用のワークディレクトリを作成し、 redis ユーザの書き込み権限を付与します
必要なワークディレクトリは以下となります

　*/etc/redis：設定ファイルの格納先*
　*/var/run/redis：データファイルや起動プロセスIDファイルの格納先*
　*/var/log/redis：ログ出力先*

```bash:Redisワークディレクトリの作成
$ sudo mkdir /etc/redis /var/run/redis /var/log/redis
$ sudo chmod 755 /etc/redis /var/run/redis /var/log/redis
$ sudo chown redis:redis /etc/redis /var/run/redis /var/log/redis
```

### Redis 設定ファイルのコピー
ソースインストールでは Redis 設定ファイルの雛形が用意されているので格納先ディレクトリにコピーします
パッケージ導入では既に配置されています。この章はスキップしてください

```bash:Redis設定ファイル雛形のコピー
$ sudo cp -p ~/Downloads/redis-3.0.*/redis.conf /etc/redis/6379.conf
```

### Redis サーバの起動と停止
Redis 起動シェルスクリプトを引数に設定ファイルを指定し redis ユーザで起動します

```bash:redisの起動
$ sudo -u redis sh -c "redis-server /etc/redis/6379.conf --daemonize yes --dir /var/run/redis"
```

Redis が正常に起動すれば以下のメッセージが画面に出力されます

```text:
6251:M 03 Feb 04:46:23.166 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
6251:M 03 Feb 04:46:23.166 # Redis can't set maximum open files to 10032 because of OS error: Operation not permitted.
6251:M 03 Feb 04:46:23.166 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 6251
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               
```

別のターミナルを立ち上げて Redis クライアントから接続してみます
重要：
　初期状態では Redis はサービスとして起動していないので、プロンプトは戻ってきません
　よって別のターミナルを立ち上げて操作する必要があります

```bash:Redis接続
$ redis-cli -p 6379
```

Redisサーバから PING コマンドで PONG が返却されることを確認します

```text
127.0.0.1:6379> ping
PONG
```

接続確認ができたら Redis サーバを終了させます
切断後は PING コマンドで PONG が返却されないことを確認してください

　**重要**
　デフォルトの設定では データファイル書き出し先に権限に無いため shutdown コマンドで単純に終了できません
　必ず、config set コマンドで dir を指定してから shutdown してください
　これは後ほど、設定ファイルで デーモン化 することで対処します

```text:Redisサーバの終了
127.0.0.1:6379> config set dir /var/run/redis
127.0.0.1:6379> shutdown
not connected>
not connected> ping
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> exit
```

ps コマンドでも redis プロセスが出力されないことを確認しましょう

```bash
$ ps -e | grep redis
★ 何も表示されないこと
```

### 自動起動スクリプトのコピー
ソースからインストールした場合は、自動起動のスクリプトを設定する必要があります
サーバの再起動でも自動で Redis を起動させるため、用意された自動起動スクリプトをコピーして登録します

```bash:自動起動スクリプトのコピー
$ sudo cp -p ~/Downloads/redis-3.0.*/utils/redis_init_script /etc/init.d/redis
```

自動起動スクリプトを修正します
ヘッダの５行目に `chkconfig: 345 70 15` を追記します（下記の最終行です）
プロセスIDファイルの配置場所も変更します
もしパスワードを設定する場合は、CLIEXEC も変更しておきます

　**重要**
　　Redisでは、接続時にセキュリティを高めるためパスワード設定ができます
　　もしパスワードを設定したら起動スクリプトでもセットが必要です
　　これをやらないと Redisサーバプロセスを スクリプトから終了させることができません...
　　対応としては CLIEXEC に -a の引数でパスワードを指定します

```bash:/etc/init.d/redis
$ sudo vi /etc/init.d/redis
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
# chkconfig: 345 70 15
：
#CLIEXEC=/usr/local/bin/redis-cli  ★　ここはパスワード設定した場合のみ変更します！！
CLIEXEC="/usr/local/bin/redis-cli -a hoge"
#PIDFILE=/var/run/redis_${REDISPORT}.pid
PIDFILE=/var/run/redis/redis_${REDISPORT}.pid
```

# Redis 設定ファイルの編集
ここからはパッケージ導入でもソース導入でも必要な作業となります
Redis サーバの環境設定をおこないます

```bash:Redis設定ファイルの編集
$ sudo vi /etc/redis/6379.conf
```
```text:6379.conf
daemonize yes
pidfile redis_6379.pid
port 6379
bind 127.0.0.1
loglevel notice
logfile /var/log/redis/6379.log
dbfilename 6379.rdb
dir /var/run/redis
requirepass hoge
maxclients 1024
```
各項目の詳細は以下となります
　1. daemonize：Redisをデーモンで起動させます
　2. pidfile：Redisのプロセスファイルの名前を指定します（ポート番号にあわせてます）
　3. port：起動するポート番号を指定します
　4. bind：接続可能なサーバのIPアドレスを指定します
　　**重要**
　　　コメントアウトまたは'0.0.0.0'で制限解除となります
　　　ただしセキュリティの確保のためにも接続するサーバのみへ制限しておきましょう
　　　本稿では同一サーバから接続するので 127.0.0.1 としています
　　　なお複数のサーバから接続する場合は、スペース区切りでIPアドレスを列挙します
　　　あわせて iptables で接続するサーバのIPとポートを解放する必要があります
　5. ログ出力レベルを指定します
　　検証中は debug または info 、サービス稼働では notice としておきましょう
　6. ログファイルの名前を指定します（ポート番号にあわせてます）
　7. RDB（永続性担保のスナップショット）のファイル名を指定します（ポート番号にあわせてます）
　　　Redisの魅力としてデータの永続性担保が挙げられます
　　　メモリ内のデータをあるタイミングでファイルに書き出します
　8. redis実行時のベースディレクトリを指定します
　9. パスワードを指定します
　　**重要**
　　　サンプルとして hoge としてますが、わかりにくい適切なパスワードに変更してください
　10. 接続できる最大コネクション数を指定します
　　**重要**
　　　複数のHTTPサーバから利用する場合は、サーバ台数 × MaxClient などの考慮が必要です
　　　場合によっては最大接続数がデフォルトの 1024 では足りないケースもあります
　　　ただTCP接続ではファイルディスクリプタが利用されるため、OSのファイルオープン数の
　　　制限値を考慮する必要があり、単純に設定値を拡張できません
　　　1024 で足りない場合は後述の Redis 自動起動ファイル内でlimit制限を変更します


# Redisの起動設定
Redisサーバの設定後、起動スクリプトで Redis サーバの起動と停止ができることを確認します

```bash:Redisサーバの起動
$ sudo /etc/init.d/redis start
Starting Redis server...
$ ps -ef | grep redis
root       3328      1  0 05:27 ?        00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379      
```

Redisに接続します
設定ファイルの requirepass で指定したパスワードを auth コマンドで入力し認証をとおしてから PING コマンドで PONG が返却されることを確認します

```text
$ redis-cli -p 6379
127.0.0.1:6379> auth hoge
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> quit
```

自動起動スクリプトで終了できることを確認します

```bash:Redisサーバの停止
$ sudo /etc/init.d/redis stop
Stopping ...
Redis stopped
$ ps -e | grep redis
$ /usr/local/bin/redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused
```

Redisの起動と終了は、以下コマンドでもOKです

```bash
$ sudo service redis start
$ sudo service redis restart
$ sudo service redis stop
```

## redis の自動起動（サービス）登録
起動スクリプトで正常に起動と終了ができることを確認してからサービスとして登録します  

```bash:
$ sudo chkconfig redis on
$ chkconfig --list redis
redis  0:off  1:off  2:on  3:on  4:on  5:on  6:off
```

サーバを再起動させて Redis が自動起動されることを確認します

```bash:サーバ再起動
$ reboot
```

サーバが再起動したら

```bash:Redis自動起動の確認
$ ps -ef | grep redis
redis      2042      1  0 06:26 ?        00:00:00 /usr/bin/redis-server 127.0.0.1:6379                                        
```

## Redis の冗長化
複数の Redis でレプリケートさせる方法は[Redisのフェールオーバー環境を構築する（その２）](http://qiita.com/KurosawaTsuyoshi/items/936c32bb8947fb907ef5)で説明します

# Appendix

## iptablesの変更
外部のサーバから Redis に接続する場合は、iptables の設定が必要です。
IPアドレス（帯域指定可）と redis のポートを許可し、iptables サービスを再起動させて反映します

```bash:iptables
$ sudo vi /etc/sysconfig/iptables
-A INPUT -p tcp -m tcp -s 128.0.0.0/24 --dport 6379 -j ACCEPT
```
```bash:iptablesの再起動
$ sudo /etc/init.d/iptables restart
```

## メモリオーバーコミットの設定
redis の起動時に以下メッセージがログに表示される場合があります

```text:/var/log/redis/redis.log
# WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
    [15050] 16 Jan 22:17:28.303 * DB loaded from disk: 0.000 seconds
```
Linux では実メモリが足りなくなり、溢れたスワップ領域も使い切ってしまうとメモリを
確保しているプロセスを強制終了させる仕組みがあります（OOMKillerと呼ばれてます）

```text
0：空き容量がなければ実行中のプロセスを強制終了する（デフォルト）
1：制限ギリギリまでメモリがあるように振る舞い、確保できなければ実行中のプロセスを強制終了する
2：空き容量がない場合はエラーを発生させる
```

redis はデータ永続化（スナップショットやAOFログ保存）の際、格納データの2倍のメモリを利用するので
もしメモリを使い切ってしまい、確保（allocate）に失敗した場合は エラーが発生します
この時、OOMKiller の設定によっては redis プロセスが強制終了されます

ワーニングメッセージでは vm.overcommit_memory = 1 をセットと表示されていますが
本来ならエラーを誘発させアプリ側でエラートラップすべきなので、"2"をセットします
（ここ解釈を間違えてるかもしれません。詳しい方教えてくださいませ）

なおシステム設定ファイルはバックアップを取ってから変更します
また変更後はサーバの再起動が必要です

```bash
$ sudo cp -p /etc/sysctl.conf /etc/sysctl.conf_`date '+%Y%m%d'`
$ sudo vi /etc/sysctl.conf
vm.overcommit_memory = 2

$ sudo shutdown -r now
```

## crackit/crack@redis.io
redis コマンドの “config set dbfilename” 機能を悪用した、redis 起動ユーザ権限で任意のファイルが作成できる攻撃手法が公開されています。
（＝ sshの公開鍵を配置してリモートログイン用の穴を開けてしまう）

redis著者による攻撃考察は[こちら](http://antirez.com/news/96)

原因は bind の設定忘れで、もし攻撃を受けると、crackit というキーと、バリューに sshキーとcrack@redis.io が登録されます。（見ればすぐヤバイと感じます）

他サーバとの接続検証などで、bind設定（IPアドレス制限）を解放した場合、その後閉じることを忘れずに実施することが重要です

対処方法は
1. redis.conf の bind でIPを制限する（閉じる）
　接続できるサーバのIPアドレスを指定し、redisを再起動する

```bash
$ sudo vi /etc/redis/redis.conf
bind  127.0.0.1
$ sudo /etc/init.d/redis restart
```

2. iptables でredisのポートを閉じる  
redisのポートにアクセスできるIPアドレスのみ指定し、iptabelsを再起動する  

```bash
$ sudo vi /etc/sysconfig/iptables

　[IP帯域で許可する場合]
 　-A INPUT -p tcp -m tcp -s [接続させるIP帯域] –dport 6379 -j ACCEPT

　[ポート閉塞する場合]
　　6397の行をコメントアウト（もしくは6379関連は記載しない）

$ sudo /etc/init.d/iptables restart
```  

以上です