Edited at

privateなgemサーバー構築のお話

More than 1 year has passed since last update.


目的

privateなgemサーバーを構築したい。

geminaboxやgemirroがよく使われているらしい。

ここではgeminaboxを使う。


構築環境


  • CentOS 7.2(vagrant bento)

  • geminabox

  • puma

  • nginx(yum package)


設置手順

rubyが設置されてない場合はyumやrbenvで設置する。この説明は割愛する。

bundlerの設置

gem install bundler

アプリケーションのディレクトリを生成する。

mkdir gembox

cd gembox
bundle init

アプリケーションサーバーとしてpumaを使う。

bundle initで生成されたGemfileを下記のように編集する。

# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem "geminabox"
gem "puma"

別にglobalのruby環境やsystemのruby環境で設置しても構わない。

共通的なものだけglobalのruby環境に設置するのが自己ルールであるだけだ。

bundle install --path vendor/bundle

config.ruを生成する。

require "rubygems"

require "geminabox"

Geminabox.data = "./data" # ... or wherever
run Geminabox::Server

data、config、tmpディレクトリを生成する。

dataはgemファイルの保管箱である。

unixソケットを使うので、socketsディレクトリも作ろう。

mkdir data

mkdir config
mkdir -p tmp/pids
mkdir tmp/sockets

puma.rbを生成する。

mkdir config

vi config/puma.rb

app_root = "#{File.expand_path("../..", __FILE__)}"
pidfile "#{app_root}/tmp/pids/puma.pid"
state_path "#{app_root}/tmp/pids/puma.state"
bind "unix://#{app_root}/tmp/sockets/puma.sock"
directory app_root
threads 5,5
workers 2
environment 'production'
daemonize true

daemon化したいと思い、daemonize trueにしたがこのせいでsystemdで動けなくなり、とても苦労した。

あとで説明する。今は必要なので、そのままおいて。

bundle execは面倒だからpumaコマンドは生成しておく。

bundle binstubs puma

binディレクトリが生成されpumaやpumactlコマンドが生成されているはずだ。

ここまでするとプロジェクトの中身はこうなる。

.

├── Gemfile
├── Gemfile.lock
├── bin
│   ├── puma
│   ├── pumactl
│   └── rackup
├── config
│   └── puma.rb
├── config.ru
├── data
├── tmp
│   ├── pids
│   └── sockets
└── vendor
└── bundle

pumaの起動・終了

bin/pumactl -F config/puma.rb start

bin/pumactl -F config/puma.rb stop

nginxの設置

epelが設置されていない場合は先に設置する。

yumリポジトリを登録ではなくyumパッケージで設置する。

sudo yum install epel-release -y

sudo yum install nginx -y

nginx.confファイルを下記のように編集する。

sudo vi /etc/nginx/nginx.conf

# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/

user vagrant;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;

server {
listen 80;
listen [::]:80;
server_name gembox;
client_max_body_size 100M;

# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
}
}

vagrantユーザでpumaが実行されるので、nginxのworkerプロセスもvagrantで実行される必要がある。

そうしないと502 Bad Gatewayエラーが発生する。

そのときのエラーログはこうだ。

cat /var/log/nginx/error.log

...
2017/04/07 01:43:53 [crit] 13203#0: *1 connect() to unix:/home/vagrant/gembox/tmp/sockets/puma.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.33.1, server: _, request: "GET / HTTP/1.1", upstream: "http://unix:/home/vagrant/gembox/tmp/sockets/puma.sock:/", host: "gembox"
...


  • serverの設定


    • include /etc/nginx/conf.d/gembox_server.conf



upstream gembox {

server unix:/home/vagrant/gembox/tmp/sockets/puma.sock;
}


  • locationの設定


    • include /etc/nginx/default.d/gembox_server.conf



location / {

proxy_pass http://gembox;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

nginxの起動・終了・状態・リロード

sudo systemctl start nginx.service

sudo systemctl stop nginx.service
sudo systemctl status nginx.service
sudo systemctl reload nginx.service

おまけにsystemdについて

ここまでするとちゃんとgeminaboxのページが表示されるはずだが、もし表示されなかった場合は80ポートLISTENされているのか、確認する。

firewalld含めて確認する必要がある。

geminaboxページが表示されたら、gemファイルを1個選んでアップロードしてみよう。

必ずアップロードするんだ。アップロードするとgem保管箱の仕組みが作られるみたい。

空いていたdataディレクトリにファイルやディレクトリが生成される。

data

├── _cache
├── _temp
├── gems
├── latest_specs.4.8
├── latest_specs.4.8.gz
├── prerelease_specs.4.8
├── prerelease_specs.4.8.gz
├── quick
├── specs.4.8
└── specs.4.8.gz

gemファイル1個だけアップロードする場合はこれでも悪くないが、複数のgemをアップロードするにはコマンドが便利。

mac(ホスト) - vagrant(ゲスト、geminaboxサーバー)

vi /private/etc/hosts
192.168.33.10 gembox

ホストからゲストへ複数のgemファイルをアップロードしてみよう。

gem inabox *.gem -g http://gembox

500エラーが発生した。

Pushing yard-0.9.8.gem to http://gembox/...

ERROR: Error (500 received)

<html>
<head><title>500 Internal Server Error</title></head>
<body bgcolor="white">
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx/1.10.2</center>
</body>
</html>

nginxのworkerプロセスはvagrantなので、「/var/lib/nginx」をownerがvagrantではないとgemファイルのアップロードができない。

そのときのエラーログはこうだ。

2017/04/07 10:58:21 [crit] 4488#0: *15 open() "/var/lib/nginx/tmp/client_body/0000000001" failed (13: Permission denied), client: 192.168.33.1, server: gembox, request: "POST /upload HTTP/1.1", host: "gembox"

ownerをvagrantに変更する。(vagrant、ゲスト側)

sudo chown -R vagrant /var/lib/nginx

もう一度やってみる。

アップロードファイルのサイズが大きい場合はエラーが発生する。

Pushing nokogiri-1.7.0.1.gem to http://gembox/...

ERROR: Error (413 received)

<html>
<head><title>413 Request Entity Too Large</title></head>
<body bgcolor="white">
<center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx/1.10.2</center>
</body>
</html>

そのときのエラーログはこうだ。(vagrant、ゲスト側)

2017/04/07 11:50:52 [error] 4488#0: *70 client intended to send too large body: 9148319 bytes, client: 192.168.33.1, server: gembox, request: "POST /upload HTTP/1.1", host: "gembox"

このようなときは、nginx.confファイルの中でclient_max_body_sizeを適当に大きいサイズで変更しよう。

もう一度やってみる。

アップロードしたgemファイルが表示されるはずだ。

http://gembox

これでおわり!

ではない。

pumaをsystemdのサービスに登録して使う。

sudo vi /etc/systemd/system/puma.service

[Unit]
Description=Puma HTTP Server
After=network.target

[Service]
Type=simple
User=vagrant
Group=vagrant
PIDFile=/home/vagrant/gembox/tmp/pids/puma.pid
WorkingDirectory=/home/vagrant/gembox
ExecStart=/usr/bin/bash -lc 'bin/pumactl -F config/puma.rb start'
ExecReload=/bin/kill -s USR1 $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutSec=300
Restart=always

[Install]
WantedBy=multi-user.target

systemdの詳細は割愛する。

Software Design 2015年2月号のLinux systemd入門を参考

おまけにpuma関連signal

ExecStartがサービスをstartするとき実行されるコマンドだが、「/usr/bin/bash -lc 'puma起動コマンド'」で実行しないといけない。

そうしないとvagrantではないrootユーザーのログインシェルで実行される。

puma.rbファイルからdaemonize trueの設定を消す!

systemdのサービスがdaemon化してくれることだから要らないし、むしろこれのせいでサービスがずっとfailedになってしまう。

おしまい。