Redisのインストール
Redisをインストールしてみる。
環境
- CentOS 8.0.1905
- Redis 5.0.7
インストール
事前に必要モジュールのインストール。
$ 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.
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