Redisのインストール

Redisをインストールしてみる。

環境

インストール

事前に必要モジュールのインストール。

$ sudo yum -y install epel-release
$ sudo yum -y install gcc jemalloc tcl

Redisはepelリポジトリの中にありますが、公式ドキュメントを見るとソースコードよりインストールしているので、その手順に習ってソースよりインストール。

$ cd /usr/local/src/
$ wget http://download.redis.io/releases/redis-5.0.7.tar.gz
$ sudo tar xzf redis-5.0.7.tar.gz
$ cd redis-5.0.7
$ sudo make test
$ sudo make
$ sudo make install

Redis Quick Start - Installing Redis

初期設定

下記あたりを参考にして、初期設定を実施。

Redis Quick Start - Installing Redis more properly

OSグループ・ユーザーの作成

Redisを起動するOSユーザーを作成。

$ sudo groupadd redis
$ sudo useradd -s /sbin/nologin -g redis redis

ディレクトリの作成

コンフィグ・ファイル配置用のディレクトリを作成。

$ sudo mkdir /etc/redis
$ sudo chmod 755 /etc/redis
$ sudo chown redis:redis /etc/redis

スナップショットが作成されるディレクトリを作成。

$ sudo mkdir /var/redis/6379
$ sudo chmod 755 /var/redis/6379
$ sudo chown redis:redis /var/redis/6379

ログ用ディレクトリを作成。

$ sudo mkdir /var/log/redis
$ sudo chmod 755 /var/log/redis
$ sudo chown redis:redis /var/log/redis

コンフィグの修正

オリジナルのコンフィグ・ファイルを、上記で作成したコンフィグ用ディレクトリにコピー。

$ sudo cp /usr/local/src/redis-5.0.7/redis.conf /etc/redis/6379.conf

とりあえず、下記を変更。

parameter value
daemonize yes
port 6379
supervised systemd
loglevel notice
logfile /var/log/redis/redis_6379.log
rdbcompression yes
rdbchecksum yes
dir /var/redis/6379
requirepass (任意のパスワード文字列)
maxclients 10000
maxmemory (実メモリの半分くらいに設定するのがいいぽい。AWS ElastiCache Redisでは実メモリの75%だった)
maxmemory-policy volatile-ttl (メモリがmax値に到達した時の動作を指定)※1
maxmemory-samples 5 ( maxmemory-policy で利用されるサンプル値を指定)※2

(※1) maxmemory-policyの一覧

  • volatile-lru
    • Evict using approximated LRU among the keys with an expire set.
  • allkeys-lru
    • Evict any key using approximated LRU.
  • volatile-lfu
    • Evict using approximated LFU among the keys with an expire set.
  • allkeys-lfu
    • Evict any key using approximated LFU.
  • volatile-random
    • Remove a random key among the ones with an expire set.
  • allkeys-random
    • Remove a random key, any key.
  • volatile-ttl
    • Remove the key with the nearest expire time (minor TTL)
  • noeviction
    • Don't evict anything, just return an error on write operations.

(※2) maxmemory-samplesの説明

LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated algorithms (in order to save memory), so you can tune it for speed or accuracy. For default Redis will check five keys and pick the one that was used less recently, you can change the sample size using the following configuration directive.

maxmemory-policy で動作するアルゴリズムのスピードと正確さを、利用するサンプル値により変更できるよ、ということみたいです。

Redis configuration file example

overcommit_memoryの値を変更する

起動時に以下のワーニングログが出力されていたので対応。

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.

vm.overcommit_memory て何?という話ですが、RedisのFAQサイトに以下の説明がありました。

Redis background saving schema relies on the copy-on-write semantic of fork in modern operating systems: Redis forks (creates a child process) that is an exact copy of the parent. The child process dumps the DB on disk and finally exits. In theory the child should use as much memory as the parent being a copy, but actually thanks to the copy-on-write semantic implemented by most modern operating systems the parent and child process will share the common memory pages. A page will be duplicated only when it changes in the child or in the parent. Since in theory all the pages may change while the child process is saving, Linux can't tell in advance how much memory the child will take, so if the overcommit_memory setting is set to zero fork will fail unless there is as much free RAM as required to really duplicate all the parent memory pages, with the result that if you have a Redis dataset of 3 GB and just 2 GB of free memory it will fail.

Setting overcommit_memory to 1 tells Linux to relax and perform the fork in a more optimistic allocation fashion, and this is indeed what you want for Redis.

Background saving fails with a fork() error under Linux even if I have a lot of free RAM!

vm.overcommit_memory のパラメータの説明は下記です。

  • 0
    • Heuristic overcommit handling. Obvious overcommits of address space are refused. Used for a typical system. It ensures a seriously wild allocation fails while allowing overcommit to reduce swap usage. root is allowed to allocate slightly more memory in this mode. This is the default.
  • 1
    • Always overcommit. Appropriate for some scientific applications. Classic example is code using sparse arrays and just relying on the virtual memory consisting almost entirely of zero pages.
  • 2
    • Don't overcommit. The total address space commit for the system is not permitted to exceed swap + a configurable amount (default is 50%) of physical RAM. Depending on the amount you use, in most situations this means a process will not be killed while accessing pages but will receive errors on memory allocation as appropriate.
    • Useful for applications that want to guarantee their memory allocations will be available in the future without having to initialize every page.

Overcommit Accounting

Redis saveの処理は、子プロセスとして起動して、親プロセスと同等のメモリを必要となる。モダンなOSでは、子プロセスは親プロセスと同じメモリ領域を共有して、変更が発生した際に、実メモリを確保する動きをする。Linuxでは子プロセスがどれだけメモリ確保するのか事前に把握できないので、vm.overcommit_memoryのパラメータが1だと、forkに失敗するよ。0はヒューリスティクスにハンドリングするモードだから、子プロセスが実メモリをいきなり使うと思って、メモリ不足になっちゃうんだね。1なら、実際にメモリがなくなるまでオーバーコミットしてくれるよ。という事なんでしょうか。

ので、パラメータを1に変更します。 /etc/sysctl.d/90-redis-sysctl.conf というファイルを作成して、以下を記載します。

$ cat /etc/sysctl.d/90-redis-sysctl.conf
vm.overcommit_memory = 1

実際の反映処理は後述。

TCP backlog settingを変更する

同じく起動時に、以下のワーニングログが出力されるので対応。

WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

TCP Backlog て何?という話ですが、Linuxのmanでは以下の説明がありました。

The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.

Linux Programmer's Manual - LISTEN

pending connectionのqueueという訳ですね。 /proc/sys/net/core/somaxconn を変えろと言っている somaxconn とは何?ですが、これは恐らくSocket Max Connectionのことですね。キューが511でも、作成できるソケット数がそれより少ないから、怒られているんでしょう。

そのため設定変更。/etc/sysctl.d/90-redis-sysctl.conf に、以下も追記します。

$ sudo cat /etc/sysctl.d/90-redis-sysctl.conf
vm.overcommit_memory = 1
net.core.somaxconn = 512

ファイルのパーミッションを整えておきます。

$ chown root:root /etc/sysctl.d/90-redis-sysctl.conf
$ chmod 644 /etc/sysctl.d/90-redis-sysctl.conf

以下コマンドで設定変更。

$ sudo /sbin/sysctl --system

透過的Huge Pagesの無効化

THPを有効のままRedisを起動すると、以下のメッセージがログに出力されているので、THPを無効化します。

WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.

CentOS8でも、THPはデフォルトで有効化(always)になっています。

$ sudo cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

GRUBの設定ファイル /etc/sysconfig/grub 内にある GRUB_CMDLINE_LINUX パラメーターに、 transparent_hugepage=never という値を追加。

$ sudo cat /etc/sysconfig/grub
...
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 crashkernel=auto resume=/dev/mapper/cl-swap transparent_hugepage=never rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet"
...

以下コマンドにて、grub設定ファイルの再作成を実施。

$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg

OSを再起動すると、THPが無効化されています。

$ sudo cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]

自動起動設定

Redis起動用のSystemdユニットファイルを作成します。

/etc/systemd/system/redis.service

[Unit]
Description=redis
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/redis-server /etc/redis/6379.conf
ExecStop=/usr/local/bin/redis-cli -p 6379 shutdown
Restart=always
User=redis
Group=redis
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

普通に起動させると、以下のfile descriptorに関するログが出力されていたので、 LimitNOFILE=65536 を設定しています。

You requested maxclients of 10000 requiring at least 10032 max file descriptors.

Server can't set maximum open files to 10032 because of OS error: Operation not permitted.

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'.

ファイルの権限を変更。

$ sudo chown root:root /etc/systemd/system/redis.service
$ sudo chmod 644 /etc/systemd/system/redis.service

起動します。

$ sudo systemctl daemon-reload
$ sudo systemctl start redis
$ sudo systemctl enable redis

接続確認

cliから接続して、動作を確認します。

$ redis-cli
127.0.0.1:6379> auth *******
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> del hello
(integer) 1