LoginSignup
12
12

More than 5 years have passed since last update.

CentOS 6上でRedmine 2を動かすメモ

Last updated at Posted at 2012-12-03

[動作確認環境]
CentOS-6.3_x86_64

動作環境パターンは2つあります。
1.Apache + Passenger(mod_rails)
2.Nginx + Unicorn(Ruby Rack Application Server)

以下に2パターンそれぞれの構築方法を記載します。

Apache + Passenger編

yumリポジトリ設定にEPEL追加

rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-7.noarch.rpm

必要パッケージインストール

後々 git が必要になりますが、それは group install "Development Tools" で入ります。

yum groupinstall "Development Tools"
yum install openssl-devel readline-devel zlib-devel curl-devel libyaml-devel
yum install mysql-server mysql-devel
yum install httpd httpd-devel
yum install ImageMagick ImageMagick-devel

Ruby-1.9.3インストール

cd /usr/local/src
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.bz2
tar xjf ruby-1.9.3-p194.tar.bz2
cd ruby-1.9.3-p194
./configure
make
make install
ruby -v

# ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]

which ruby
# /usr/local/bin/ruby

MySQL設定

/etc/my.cnf [追記]

[mysqld]
~ (中略) ~
character-set-server=utf8

[mysql]
~ (中略) ~
default-character-set=utf8

MySQLユーザ登録

Redmine用のデータベース作成+専用ユーザ登録

MySQL起動

/etc/init.d/mysqld start

MySQL自動起動設定

chkconfig mysqld on
chkconfig --list mysql
# mysqld          0:off   1:off   2:on    3:on    4:on    5:on    6:off

Redmine用データベース作成

mysql> CREATE DATABASE redmine DEFAULT CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| redmine            |
| test               |
+--------------------+
4 rows in set (0.00 sec)

Redmine用ユーザ登録

mysql> GRANT ALL ON redmine.* TO redmine@'192.168.1.150' IDENTIFIED BY 'password';
  :
mysql> FLUSH PRIVILEGES;
  :
mysql>

Redmine (本体) インストール

cd /usr/local/src
wget http://rubyforge.org/frs/download.php/76495/redmine-2.1.2.tar.gz
cd /var/www/html # Apacheドキュメントルート
tar zxvf /usr/local/src/redmine-2.1.2.tar.gz
ln -s redmine-2.1.2 redmine
ls -l
  :
lrwxrwxrwx.  1 root    root     13 10月 12 20:22 2012 redmine -> redmine-2.1.2
drwxrwxr-x. 16    1000   1000 4096  9月 30 19:42 2012 redmine-2.0.2

データベース接続設定

[Redmineインストールディレクトリ]/config/database.ymlを作成します。

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: password
  encoding: utf8  

Redmineが仕様するgemパッケージインストール

bundlerを使って、RedmineおよびRuby on Railsが依存するgemパッケージをインストールします。
依存するgemパッケージは、Redmineを展開した中のGemfileに記述されています。

bundlerインストール

gem install bundler --no-rdoc --no-ri

# Fetching: bundler-1.2.1.gem (100%)
# Successfully installed bundler-1.2.1
# 1 gem installed

Redmineが使用するgemパッケージインストール

gemパッケージは、システム共通の場所にインストールするか、Railsアプリケーション(今回はRedmine)固有の場所にインストールするかを指定できます。同一マシンで異なるバージョンのRailsアプリケーションを動かすときは後者でないと競合が生じます。

そこで、今回はRedmine固有の場所にインストールすることにします。

bundleコマンドは、カレントディレクトリにあるGemfileを読むので、実行時はredmineにカレントディレクトリを移動します。

# cd /var/lib/redmine-2.1.2
# bundle install --path vendor/bundler --without development test postgresql sqlite
Fetching gem metadata from http://rubygems.org/.......
Installing rake (0.9.2.2)
Installing i18n (0.6.1)
Installing multi_json (1.3.6)
Installing activesupport (3.2.8)
Installing builder (3.0.0)
Installing activemodel (3.2.8)
Installing erubis (2.7.0)
Installing journey (1.0.4)
Installing rack (1.4.1)
Installing rack-cache (1.2)
Installing rack-test (0.6.2)
Installing hike (1.2.1)
Installing tilt (1.3.3)
Installing sprockets (2.1.3)
Installing actionpack (3.2.8)
Installing mime-types (1.19)
Installing polyglot (0.3.3)
Installing treetop (1.4.11)
Installing mail (2.4.4)
Installing actionmailer (3.2.8)
Installing arel (3.0.2)
Installing tzinfo (0.3.33)
Installing activerecord (3.2.8)
Installing activeresource (3.2.8)
Using bundler (1.2.1)
Installing coderay (1.0.8)
Installing rack-ssl (1.3.2)
Installing json (1.7.5) with native extensions
Installing rdoc (3.12)
Installing thor (0.16.0)
Installing railties (3.2.8)
Installing jquery-rails (2.0.3)
Installing mysql2 (0.3.11) with native extensions
Installing net-ldap (0.3.1)
Installing ruby-openid (2.1.8)
Installing rack-openid (1.3.1)
Installing rails (3.2.8)
Installing rmagick (2.13.1) with native extensions
Your bundle is complete! It was installed into ./vendor/bundler
Post-install message from rdoc:
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
 = 1.8.7 : gem install rdoc-data; rdoc-data --install
 = 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!

#

Redmine初期化

セッションデータ暗号化の鍵生成

cd /var/lib/redmine-2.1.2
bundle exec rake generate_secret_token

bundlerでインストールしたgemのコマンドは、bundle execで実行します。

データベース初期化

RAILS_ENV=production bundle exec rake db:migrate
# ==  Setup: migrating #=====================================================#=====
#-- create_table("attachments", {:force=>true})
#  :
#==  AddBoardsParentId: migrated (0.1603s) #=====================================

動作確認

WEBrickを実行し、Redmineが正常に起動するかを確認します。
(※WEBrickはRailsインストール時に一緒に入ってます)

WEBrick起動

cd /var/www/html/redmine
ruby script/rails server webrick -e production

=> Booting WEBrick
=> Rails 3.2.3 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
  :
[2012-05-24 22:49:39] INFO  WEBrick::HTTPServer#start: pid=16590 port=3000

同一マシン上のブラウザからアクセスして確認

Redmineの画面が表示されれば成功!


Passengerインストール

Passengerはgemパッケージで提供されていますが、RedmineのGemfileには記載されていないので、bundleではインストールされません。そこで、bundleでインストールできるよう追加の設定をします。

/Gemfile.local を新規に作成し以下を記述

gem "passenger"

で、bundle updateを実行します。

bundle update
  :
Installing daemon_controller (1.0.0)
Installing fastthread (1.0.7) with native extensions
  :
Installing passenger (3.0.17)
  :

※--path 指定を今回は省略することができます。bundleを実行したときのオプションが、/bundle/configファイルに保存されるためです。

Apache2に組み込むライブラリをビルドします。

bundle exec passenger-install-apache2-module

Welcome to the Phusion Passenger Apache 2 module installer, v3.0.17.
  :
Press Enter to continue, or Ctrl-C to abort.

Enterキーを押すとビルドが開始されます。しばらくすると、

-------------------------------------------

Checking for required software...

 * GNU C++ compiler... found at /usr/bin/g++
 * Curl development headers with SSL support... found
 * OpenSSL development headers... found
 * Zlib development headers... found
 * Ruby development headers... found
 * OpenSSL support for Ruby... found
 * RubyGems... found
 * Rake... found at /usr/local/bin/rake
 * rack... found
 * Apache 2... found at /usr/sbin/httpd
 * Apache 2 development headers... not found
 * Apache Portable Runtime (APR) development headers... not found
 * Apache Portable Runtime Utility (APU) development headers... not found

Some required software is not installed.
But don't worry, this installer will tell you how to install them.

Press Enter to continue, or Ctrl-C to abort.

Apache関連で not found がでます。
必要なパッケージをインストールします。

yum install httpd-devel apr-devel

再度、bundleを実行すると今度はすべてfoundとなり成功します。
そしてコンパイル後にApacheの設定内容が出力されるのでコピーして控えておきます。

--------------------------------------------
The Apache 2 module was successfully installed.

Please edit your Apache configuration file, and add these lines:

   LoadModule passenger_module /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so
   PassengerRoot /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18
   PassengerRuby /usr/local/bin/ruby

After you restart Apache, you are ready to deploy any number of Ruby on Rails
applications on Apache, without any further Ruby on Rails-specific
configuration!

Press ENTER to continue.


--------------------------------------------
Deploying a Ruby on Rails application: an example

Suppose you have a Rails application in /somewhere. Add a virtual host to your
Apache configuration file and set its DocumentRoot to /somewhere/public:

   <VirtualHost *:80>
      ServerName www.yourhost.com
      # !!! Be sure to point DocumentRoot to 'public'!
      DocumentRoot /somewhere/public    
      <Directory /somewhere/public>
         # This relaxes Apache security settings.
         AllowOverride all
         # MultiViews must be turned off.
         Options -MultiViews
      </Directory>
   </VirtualHost>

And that's it! You may also want to check the Users Guide for security and
optimization tips, troubleshooting and other useful information:

  /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/doc/Users guide Apache.html

Enjoy Phusion Passenger, a product of Phusion (www.phusion.nl) :-)
https://www.phusionpassenger.com

Phusion Passenger is a trademark of Hongli Lai & Ninh Bui.

Apache設定

/etc/httpd/conf.d/passenger.conf を新規作成して記述します。

LoadModule passenger_module /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so
PassengerRoot /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18
PassengerRuby /usr/local/bin/ruby

<VirtualHost *:80>
  ServerName 192.168.1.150
  # !!! Be sure to point DocumentRoot to 'public'!
  DocumentRoot /var/www/html/redmine/public
  <Directory /var/www/html/redmine/public>
    # This relaxes Apache security settings.
    AllowOverride all
    # MultiViews must be turned off.
    Options -MultiViews
  </Directory>
</VirtualHost>

**ApacheからRedmineをアクセスできるように所有者変更

chown -R apache.apache /var/www/html/redmine-2.1.2

Apache起動+自動起動設定

/etc/init.d/httpd start
chkconfig httpd on

Redmineへアクセスして動作確認
http://192.168.1.150/


Nginx + Unicorn編

※作業前にApacheは停止しておいてください。

Unicorn


RailsサーバーのUnicornを設定します。
Unicornは、Rackアプリケーションを実行するためのHTTPサーバーで、クライアントへの効率的な応答(速さ、低遅延)を目的として設計されています。


インストール

Unicornは、gem形式で提供されています。そこで、RedmineのGemfile.localに記述し、Redmine固有にインストールします。

[redmineインストールディレクトリ]/Gemfile.local を作成

Gemfile.local
gem "unicorn"

bundlerの更新実施

# Redmine インストールディレクトリへ移動
cd /var/www/html/redmine

bundle update

Installing kgio (2.7.4) with native extensions
    :
Installing raindrops (0.10.0) with native extensions
    :
Installing unicorn (4.4.0) with native extensions
    :
動作確認:フォアグランドプロセスとして起動
# Redmine インストールディレクトリへ移動
cd /var/www/html/redmine

bundle exec unicorn_rails -l 8081 -E production

I, [2012-10-23T16:28:22.687949 #3783]  INFO -- : listening on addr=0.0.0.0:8081 fd=9
I, [2012-10-23T16:28:22.688150 #3783]  INFO -- : worker=0 spawning...
I, [2012-10-23T16:28:22.689226 #3783]  INFO -- : master process ready
I, [2012-10-23T16:28:22.689970 #3786]  INFO -- : worker=0 spawned pid=3786
I, [2012-10-23T16:28:22.690106 #3786]  INFO -- : Refreshing Gem list
/var/lib/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/activesupport-3.2.8/lib/active
_support/dependencies.rb:251:in `block in require': iconv will be deprecated in the 
future, use String#encode instead.
I, [2012-10-23T16:28:29.639910 #3786]  INFO -- : worker=0 ready

コマンドラインオプションは以下のとおり。

--listen -l [アドレス:]ポート
ソケットのエンドポイントを指定

--config-file -c ファイル
設定ファイルを指定

-D
デーモンプロセス起動指定

-E [RAILS_ENV]
production等を指定

フォアグラウンドプロセスとして起動した場合、Ctrl-Cで停止します。
デーモンプロセスとして起動した場合、シグナルINT(強制終了)またはQUIT(グレースフル停止)をマスタープロセスに送ります。
デーモンプロセスに対する設定ファイル再読み込みトリガーはシグナルHUP、プログラム(Ruby)をデプロイしたらシグナルUSR2を送ります。

マスタープロセスのプロセスIDは、次のように調べます。

pgreg -f 'unicorn_rails master'
設定ファイル

[redmineインストールディレクトリ]/config/unicorn.rb を作成

unicorn.rb
# ワーカープロセスの数。1ワーカーで1つのリクエストを処理する。
# ワーカー数が上限に達すると、先行するリクエストが完了するまで待ちとなる。
worker_processes 2

# リクエスト待ち受け口、TCPとUNIXドメインとが指定可能。
# CASE1: TCP
# listen 8282, :tcp_nopush => true
# CASE2: Socket
listen "/tmp/unicorn_redmine.sock", :backlog => 64

# 稼働中のプロセスのPIDを書いておくファイル。相対パス指定OKか?
pid "tmp/pids/unicorn.pid"
# デーモンで起動すると標準出力/標準エラー出力が/dev/nullになるので、
# それぞれログファイルに出力する。
stderr_path "log/unicorn.stderr.log"
stdout_path 'log/unicorn.stdout.log'

# マスタープロセス起動時にアプリケーションをロードする(true時)。
# ワーカープロセス側でロードをしないのでメモリ消費、応答性良好になる。
# ただし、ソケットはfork後に開きなおす必要あり。
# HUPシグナルでアプリケーションはロードされない。
preload_app true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
  begin
    uid, gid = Process.euid, Process.egid
    user, group = "redmine", "redmine"
    target_uid = Etc.getpwnam(user).uid
    target_gid = Etc.getgrnam(group).gid
    worker.tmp.chown(target_uid, target_gid)
    if uid != target_uid or gid != target.gid
      Process.initgroups(user, target_gid)
      Process::GID.change_privilege(target_gid)
      Process::UID.change_privilege(target_uid)
    end
  rescue
    if RAILS_ENV = "development"
      STDERR.puts "could not change user, oh well"
    else
      STDERR.puts "could not change user, oh well"
      raise e
    end
  end
end
起動スクリプト

新規作成

/etc/init.d/unicorn
# chkconfig: 345 83 17
# description: unicorn is a HTTP rack application server.

# set -e => 実行したコマンドが0以外の戻り値を返した場合、シェルスクリプト終了
set -e

sig () {
  test -s "$PID" && kill -$1 `cat "$PID"`
}

oldsig () {
  test -s "$OLD_PID" && kill -$1 `cat "$OLD_PID"`
}

cmd () {

  case $1 in
    start)
      sig 0 && echo >&2 "Already running" && exit 0
      echo "Starting"
      $CMD
      ;;
    stop)
      sig QUIT && echo "Stopping" && exit 0
      echo >&2 "Not running"
      ;;
    force-stop)
      sig TERM && echo "Forcing a stop" && exit 0
      echo >&2 "Not running"
      ;;
    restart|reload)
      sig USR2 && sleep 5 && oldsig QUIT && echo "Killing old master" `cat $OLD_PID` && exit 0
      echo >&2 "Couldn't reload, starting '$CMD' instead"
      $CMD
      ;;
    upgrade)
      sig USR2 && echo Upgraded && exit 0
      echo >&2 "Couldn't upgrade, starting '$CMD' instead"
      $CMD
      ;;
    rotate)
            sig USR1 && echo rotated logs OK && exit 0
            echo >&2 "Couldn't rotate logs" && exit 1
            ;;
    *)
      echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
      exit 1
      ;;
    esac
}

setup () {

  echo -n "$RAILS_ROOT: "
  cd $RAILS_ROOT || exit 1
  export PID=$RAILS_ROOT/tmp/pids/unicorn.pid
  export OLD_PID="$PID.oldbin"
  CMD="bundle exec unicorn_rails -c config/unicorn.rb -E $RAILS_ENV -D"
}

start_stop () {

  # either run the start/stop/reload/etc command for every config under /etc/unicorn
  # or just do it for a specific one

  # $1 contains the start/stop/etc command
 # $2 if it exists, should be the specific config we want to act on
  if [ $2 ]; then
    . $2
    setup
    cmd $1
  else
    for CONFIG in /etc/unicorn/*.conf; do
      # import the variables
      . $CONFIG
      setup

      # run the start/stop/etc command
      cmd $1
    done
   fi
}

ARGS="$1 $2"
start_stop $ARGS
/etc/unicorn/redmine-2.1.2.conf
RAILS_ENV=production
RAILS_ROOT=/var/lib/redmine-2.1.2
自動起動設定:chkconfig
chkconfig --add unicorn
chkconfig unicorn on
chkconfig --list unicorn
unicorn         0:off   1:off   2:on    3:on    4:on    5:on    6:off

Nginx


/etc/nginx/conf.d/redmine.conf

server_nameなど適宜変更してください。

proxy_pass
upstreamブロック使用の場合
http://[upstream-block-name];

/etc/nginx/conf.d/redmine.conf
upstream unicorn-redmine {
    server unix:/tmp/unicorn_redmine.sock;
}
server {
    listen 80;
    server_name redmine.php-server;
    root /var/www/html/redmine/public;
    location / {
        if (-f $request_filename) { break; }
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://unicorn-redmine;
    }
}
Nginx+Unicorn起動
/etc/init.d/unicorn start
/etc/init.d/nginx start

[root@centos6 redmine]# netstat -tapon
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name    Timer
tcp        0      0 127.0.0.1:9000              0.0.0.0:*                   LISTEN      1650/php-fpm        off (0.00/0/0)
tcp        0      0 0.0.0.0:3306                0.0.0.0:*                   LISTEN      18107/mysqld        off (0.00/0/0)
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      28357/nginx         off (0.00/0/0)
tcp        0      0 0.0.0.0:8282                0.0.0.0:*                   LISTEN      28341/unicorn_rails off (0.00/0/0)

[Apache+Passenger] VS [Nginx+Unicorn]

どちらもノーチューンでベンチマーク取ってみました。

■ Apache+Passenger
[root@centos6 redmine]# ab -c 100 -n 10000 http://redmine.php-server/projects/test-project
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking redmine.php-server (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache/2.2.15
Server Hostname:        redmine.php-server
Server Port:            80

Document Path:          /projects/test-project
Document Length:        3905 bytes

Concurrency Level:      100
Time taken for tests:   200.904 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      46370000 bytes
HTML transferred:       39050000 bytes
Requests per second:    49.78 [#/sec] (mean)
Time per request:       2009.040 [ms] (mean)
Time per request:       20.090 [ms] (mean, across all concurrent requests)
Transfer rate:          225.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   3.6      0      45
Processing:    59 2000 2055.3   1346   17687
Waiting:       59 2000 2055.3   1346   17686
Total:         59 2000 2055.3   1348   17687

Percentage of the requests served within a certain time (ms)
  50%   1348
  66%   2158
  75%   2770
  80%   3221
  90%   4660
  95%   6134
  98%   7963
  99%   9799
 100%  17687 (longest request)

■ Nginx+Unicorn
[root@centos6 ~]# ab -c 100 -n 10000 http://redmine.php-server/projects/test-project
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking redmine.php-server (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx/1.2.5
Server Hostname:        redmine.php-server
Server Port:            80

Document Path:          /projects/test-project
Document Length:        172 bytes

Concurrency Level:      100
Time taken for tests:   3.225 seconds
Complete requests:      10000
Failed requests:        98
   (Connect: 0, Receive: 0, Length: 98, Exceptions: 0)
Write errors:           0
Non-2xx responses:      9902
Total transferred:      3643854 bytes
HTML transferred:       2085834 bytes
Requests per second:    3100.79 [#/sec] (mean)
Time per request:       32.250 [ms] (mean)
Time per request:       0.322 [ms] (mean, across all concurrent requests)
Transfer rate:          1103.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   1.1      2      17
Processing:     1   19 165.7      3    2328
Waiting:        1   18 165.7      2    2328
Total:          2   20 165.9      5    2332

Percentage of the requests served within a certain time (ms)
  50%      5
  66%      5
  75%      5
  80%      6
  90%      7
  95%      9
  98%     15
  99%     21
 100%   2332 (longest request)


結果(感想)

Nginx+Unicorn 爆速!!
非同期ソケット(処理)ということでリソース消費がめちゃエコ仕様っす。

Apache+Unicorn 死亡フラグw
接続数分の子プロセス(スレッド?)生成するもんだからリソース食いまくり。そりゃあWEBサーバ台数追加したくもなります。abコマンド実行中はLoadAverageが上がる上がるw(※ベンチマーク見ればわかると思いますが。)

総評
Nginx+Unicorn圧勝
Apacheの方はチューニングである程度の改善は見込めると思いますが、Nginx+Unicornがノーチューンでここまでのパフォーマンスを発揮してしまう現実を目の前にするとApacheのチューニングなんて「メンドクセー」になってしまいますw。
フツーに考えて今回のケースだと勝負になりませんね。

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12