light log

学んだこととか

Linux(CentOS)でlocaleのエラーが出た

出たので調べた。

環境

OS X 上の Vagrant/VirtualBox 上の CentOS

$ cat /etc/redhat-release 
CentOS release 6.5 (Final)
$ arch
x86_64
$ uname -a
Linux vagrant-centos65.vagrantup.com 2.6.32-431.3.1.el6.x86_64 #1 SMP Fri Jan 3 21:39:27 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

参考:CentOSのバージョン確認コマンドとアーキテクチャ確認コマンド | mawatari.jp

事象

yumコマンドとかlocaleコマンドとかいろいろなコマンドの実行時に以下のようなエラーが出ていた。

$ locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=ja_JP.UTF-8
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=

原因

localeのLC_*に設定されているja_JP.UTF-8がシステムに無い。

Macではデフォルトでssh時にMac上のLC_*の設定を接続先に送るようになってる(/etc/ssh_config内のSendEnv LANG LC_*)ため、Mac上でja_JP.UTF-8になってると上記のようになる。

参考:Mac から Ubuntu に ssh ログインするとなんかロケール云々で怒られるやつ - 休刊 ボルシチは食べ物です。

対策

以下で解決した。

$ sudo yum update glibc-common

参考:開発機の仮想マシンで locale が怒られる - @Konboi memo

上のエラー文字列で調べるといろいろヒットして、

  • Macssh_configの内容をいじってLC_*を送信しないようにする
  • CentOS上のLC_ALLに"C"や"en_US.UTF-8"を設定する($ export LC_ALL="C")

とかいくつか解決法が見つかって、確かにこれらでも解決する(下だけ試した)んだけど、なんとなく本来的な解決法じゃないような気がした。(好みの問題かもしれない)

で、どうしよっかなーと思ってたら上のglibc-commonをアップデートしたらいいというページを見つけたのでこれだ!って思った。

要は最初からsudo yum update -yしとけばよかったんだな。

そもそもlocaleって?

ちゃんと調べたことなかったので、この際に調べてみた。

ロケールとは、ソフトウェアに内蔵される、言語や国・地域ごとに異なる単位、記号、日付、通貨などの表記規則の集合。または単に、利用する言語や国・地域の指定。多くのソフトウェアやプログラミング言語は、使用する言語とともにロケールを設定し、ロケールで定められた方式に基づいてデータの表記や処理を行う。

ロケール - IT用語辞典 e-Words

LC_ALLを設定するとLC_CTYPEなどの個別の設定を全部上書きする。LC_ALLも個別のLC_CTYPEなども設定されていない場合、LANGの値が使用される。

LC_CTYPEとかLC_NUMERICとかのそれぞれ意味は以下のリンクを参照。

Linux Hacks:環境変数によるロケールの管理 (1/2) - ITmedia エンタープライズ

ロケール名は以下の形式になる。

language(_territory)(.encoding)(@modifier)

ja_JP.UTF-8だと、languageがja、territoryがJP、encodingがUTF-8になる。@modifierが何かとかは上のリンク参考。

"C"ロケールは、

Cロケールとしてもよく知られているPOSIXロケールは、伝統的なUNIX環境を設定するもので、ASCIIエンコーディングPOSIX文字クラス、米国英語の日時、数、通貨の書式を用いる。

とのこと。

localeコマンドの簡単な使い方は以下の通り。

ほか

今回自分はやらなかったけど、manの内容とかも日本語化したければ以下のページが参考になりそう。

VagrantのCentOSを日本語化してみる - yk5656 diary

Linuxシステム[実践]入門 (Software Design plus)

Linuxシステム[実践]入門 (Software Design plus)

新Linux/UNIX入門 第3版 (林晴比古実用マスターシリーズ)

新Linux/UNIX入門 第3版 (林晴比古実用マスターシリーズ)

Unicornの設定をする

昨日↓に引き続き、

yamacent.hatenablog.com

一昨日↓の設定ファイルの見直しをする。

yamacent.hatenablog.com

今日はUnicorn

完成形

以下のようにした。

ほぼ、以下二つのページのコピペ。

アプリのルート/config/unicorn.rb

worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

listen '/tmp/unicorn.sock'
pid '/tmp/unicorn.pid'

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

設定内容

上に挙げた二つのリンクだけど、一つ目のリンク先も二つ目を参考にしているとのことなので、結局この設定のベースは二つ目のページになる。

それで二つ目のページは、Heroku Dev Centerの日本語訳なので、Herokuにデプロイする前提の設定例になっているんだけど、基本的にはHerokuじゃなくても使えるだろうと思ってそのまま流用している。

以下では個々の設定内容について自分の理解を簡単に書くけれど、上記Heroku Dev Centerの劣化コピー的な内容でしかないので詳細はそっちを参照することを推奨。

worker_processes

Unicornは、リクエストを振り分ける一つのmasterプロセスと、実際にリクエストを処理する複数のworkerプロセスで構成されるアーキテクチャになっている。

worker_processesはworkerプロセスの数を指定する。

実際のアプリのメモリ使用量などを見ながらworker数を調節できるように、環境変数を指定できるようにしてあるとのこと。

timeout

timeoutの秒数。

preload_app

trueを指定するとアプリを事前に読み込む。こうすることでworkerプロセスの起動時間を短縮できる。

Signal.trap 'TERM'

UnicornはQUITシグナルを受け取ったときにグレースフルシャットダウンするので、TERMを受け取ったときに適切なアクションに切り替える設定をしている。

QUIT 時には、現在のリクエスト処理が終わるのを待ってからmasterが各workerをシャットダウンし、最後に自身をシャットダウンする。そのため、適切なアクションとは、masterの場合は自身にQUITを送ってシャットダウン処理を開始すること、workerの場合は(masterからシャットダウン指令が来るので)TERMは無視すること、になっている。

defined?(ActiveRecord::Base)らへん

あんまりよくわかっていない。

ActiveRecordを使うなら必要そうだからつけてる。。

まとめ

とりあえずこれで良い…のかな。

Unicorn用のNginxの設定をする

nginx.confを昨日いい加減に書いたので、

yamacent.hatenablog.com

設定を見直す。

昨日の書いたのは以下。

nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
  include mime.types;
  default_type application/octet-stream;

  sendfile on;
  keepalive_timeout 65;

  upstream unicorn_app {
    server unix:/tmp/unicorn.sock;
  }

  server {
    listen 80;
    server_name _;
    location / {
      proxy_pass http://unicorn_app;
    }
  }
}

以下を参考にした。

serverブロックを別ファイルにする

まず、初期状態のnginx.confのhttpブロックはこうなってる。

http {
  ...

  include /etc/nginx/conf.d/*.conf;
}

各サーバの設定はconf.d以下にサーバごとにファイルを分けて格納するのがより良い方法なのだろう。

ということで、新たにconf.d/rails_app.confを作って、upstreamブロックとserverブロックをそっちに移動する。

rails_app.conf

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name _;
  location / {
    proxy_pass http://unicorn_app;
  }
}

nginx.confは初期状態の、conf.d以下をincludeする形に戻す。

ここまでで一旦動作確認する。本質的には設定内容は変わってないはずなので、問題なく動くはず。

$ unicorn_rails -c config/unicorn.rb 

conf.d以下に最初からあるファイル(default.conf、example_ssl.conf)はファイル名末尾に.bkとかを付けてincludeしないようにしておいた。

設定内容の意味を知る

だいたい見た目から予想はつくものの、昨日の時点ではちゃんと調べずに書いてたので、ちょっと調べた。

upstream

Wikiにはこう書いてある。

Defines a group of servers. Servers can listen on different ports.

Module ngx_http_upstream_module

サーバグループを定義するやつみたい。

今回のように一つだけ書く場合には、ただ単に後ろで参照するための名前を付けているのと同じだと思う。

試しにupstreamの内容を後方のproxy_passに直接書いてみる。

rails_app.conf

#upstream unicorn_app {
#  server unix:/tmp/unicorn.sock;
#}

server {
  listen 80;
  server_name _;
  location / {
    #proxy_pass http://unicorn_app;
    proxy_pass http://unix:/tmp/unicorn.sock;
  }
}

リロード。

$ sudo nginx -s reload
$ unicorn_rails -c config/unicorn.rb 

問題なく表示された。元に戻しておく。

listen

これは見た目通りリッスンするポート番号だろう。

Sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests.

Module ngx_http_core_module

サーバが受け入れるリクエストのアドレスとポート、またはUNIXドメインソケットのパス。

それはまあそうなんだけど、気になるのはデフォルト。

If the directive is not present then either :80 is used if nginx runs with the superuser privileges, or :8000 otherwise.

とのことなので、superuser権限で実行してたらデフォルトで*:80が使われる。

ということはlisten 80;の行は要らないから消しちゃおう。

リロード。問題なし。

server_name

これも見た目通り。

Server names are defined using the server_name directive and determine which server block is used for a given request.

Server names

nginxはこのリクエストを受け取ると、このホスト名がserver_nameディレクティブに設定したサーバ名に一致するserverディレクティブのバーチャルサーバを選び、そのserverディレクティブ内の設定が適応されます。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

指定してあるアンダースコア(_)は全部に一致するやつ。

In catch-all server examples the strange name “_” can be seen:

Server names

最初はこれもアンダースコアがデフォルトかと思ったけど、デフォルトは""だった。

Default: server_name "";

It allows this server to process requests without the “Host” header field

Module ngx_http_core_module

空を意味する""を設定すると、Host:ヘッダ フィールドがない場合に一致します。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

""の場合にはHostヘッダフィールドがない場合に一致となっているので、server_name _;の行を削除すると動かないかと思ったけど、動いた。server_name hoge;とかにしてみても動いた。

なんでだろうと思ったけど、

複数のバーチャルサーバがあるときには、リクエストのHost:ヘッダ フィールドのホスト名がserver_nameディレクティブのサーバ名に一致するserverディレクティブのバーチャルサーバが適応されます。しかし、一致しない場合にはデフォルトサーバとして選ばれたバーチャルサーバが適応されます。

デフォルトサーバはIPアドレスとポート番号の条件が一致したlistenディレクティブを持つserverディレクティブの中で次のものが選ばれます。

  • 一致したlistenディレクティブの中でdefault_serverオプションを持つ
  • 設定ファイル内で最初に一致したlistenディレクティブを持つ

listenディレクティブにdefault_serverパラメータを付けると、そのserverディレクティブがデフォルトサーバになります。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

これの二個目に合致して、デフォルトサーバになってるっぽい。

結果的に動くけど、意図した動作じゃないので、やっぱりアンダースコアは指定した状態にしておく。

location

これも直感的。 指定したパスに一致したリクエストを受けたときの動作をブロック内で設定する。

Module ngx_http_core_module

proxy_pass

プロキシ先のURIやパスを指定する。

Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped.

Module ngx_http_proxy_module

できあがり

以上を踏まえると以下のような感じになった。

nginx.conf(初期設定のまま)

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


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

    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;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

conf.d/rails_app.conf

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  server_name _;
  location / {
    proxy_pass http://unicorn_app;
  }
}

まとめ

超シンプルな設定でNginxのパワーを全然活かしてないんだろうけど、今のところこれで全然問題なさそう。

2015/5/28 追記

上記の設定だと、アプリでリダイレクトしたとき(scaffoldでできるcreateとかupdateとか)のときに、ポート番号が引き継がれなくてページが読み込めなかった。

なので、プロキシ時にヘッダ情報を引き継ぐ処理を追加する。

参考:Rails 4.2 + Unicorn + Nginx でアプリケーションサーバの構築 - Qiita

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  server_name _;

  location / {
    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_app;
  }
}

proxy_set_header Host $http_host;だけで一応上記の問題は解消したんだけど、参考にしたページに従って他の二行も追加しておいた。

続き書いた

yamacent.hatenablog.com

Nginx + Unicornをつかう(手抜き記事)

今日はサザンのライブだった!!!

幸せな時間だったなあ。。

サザン最高。桑田さん最高。

ということで、今日はあんまり作業できなかったので、Unicornを使ってみた感じをさっくりと書く。

Vagrantfile書いてNginxインストールしてrbenvでRubyインストールして、みたいなのは今度まとめる。

今日は一応更新しましたというだけの手抜き記事。本当に自分のメモ書き&備忘録的内容なので、他の方にはあんまり参考にならないと思う。

...

ここまで書いてから結局結構時間かかった。。ねむい。。すぐハマる。。

Unicorn

まずrails newで生成されるGemfileの、unicornの行のコメントを解除。

gem 'unicorn'

でインストール。

$ bundle install

unicornの設定ファイルを用意する。

config/unicorn.rbに以下を保存。

listen '/tmp/unicorn.sock'
pid '/tmp/unicorn.pid'

unicornを起動してみる。

$ unicorn_rails -c config/unicorn.rb 

JavaScriptランタイムが無いっていうエラーが出た。

therubyracerの行のコメントを解除。

$ gem 'therubyracer', platforms: :ruby

インストール。

$ bundle install

unicornを起動してみる。

こんなエラーが出た。

Sorry, you can't use byebug without Readline. To solve this, you need to rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get install libreadline-dev` and then reinstall your Ruby.

確認してみるとreadlineは既に入ってるように見える。なぞ。

ググったら以下の記事が見つかった。

CentOSにruby on railsをインストールする

以下を追加すればいいとのこと。神。

gem 'rb-readline'

言われた通り上記をGemfileに追加してインストール。

$ bundle install

unicornを起動してみる。

$ unicorn_rails -c config/unicorn.rb 

今度はうまくいった。

Nginx

/etc/nginx/nginx.confを以下の通り編集した。(httpブロック内)

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
  include mime.types;
  default_type application/octet-stream;

  sendfile on;
  keepalive_timeout 65;

  upstream unicorn_app {
    server unix:/tmp/unicorn.sock;
  }

  server {
    listen 80;
    server_name _;
    location / {
      proxy_pass http://unicorn_app;
    }
  }
}

確認

unicorn起動して、nginx起動して、nginxのポートにアクセスしてRailsのデフォルトページが表示されればok。

まとめ

サザン最高!

(内容はちゃんとまとめ直します。。)

続き書いた

yamacent.hatenablog.com

yamacent.hatenablog.com

参考

Vagrant使ってCentOSにNginx入れた

至る所に情報は転がってるんだけど、一応備忘録としてメモ。

経緯

昨日はさくらのVPSを設定して遊んでたんだけど、ある程度設定に確信が持てるまでの試行錯誤は手元で気軽な環境でやった方が効率がよさそうだったので、前にインストールしてあったVagrantでやってみることにした。

Vagrant

インストールは以前に済んでたので、

yamacent.hatenablog.com

今日は以下とかを読みながらいじくってみた。

Vagrant日本語ドキュメント

Vagrant楽しい。いつでもぶっ壊してやり直せるという安心感がすごい。

Nginx

NginxのBeginner’s Guideを読みながら初期設定してみた。

/etc/nginx/nginx.conf にデフォルトで書いてあるhttp contextをコメントアウトして、代わりに以下を追加する。

http {
  server {
    location / {
      root /data/www;
    }
    location /images/ {
      root /data;
    }
  }
}

んで /data を /vagrant へのシンボリックリンクとしておく。

sudo ln -s /vagrant /data

ゲストOSの/vagrantとホストOSのVagrantfileを置いてるディレクトリがデフォルトで同期されてるので、ホストOSで普段のエディタを使ってnginx.confで指定したディレクトリにhtmlファイルを作成することができる。

あとVagrantfileにポートフォワードの設定をしておく。(最初から書いてあるのをコメントアウトする)

agrant.configure(2) do |config|

  ...
  
  config.vm.network "forwarded_port", guest: 80, host: 8080

  ...
  
end

これで http://localhost:8080/ で保存したページが見れた。

/images/も同様に、同期されてるディレクトリに適当に画像を置いておくとブラウザで表示できる。大島優子の画像を使った。(関係ない)

まとめ

サーバいじり超楽しい。

Ansible使ってみたい。

さくらのVPSを借りた

昨日書いたHerokuでのClearDB利用は制限がきつくて、すぐにmax_questionエラーになるので、さくらのVPSを借りてみた。ClearDBのプランを有料のやつにすれば多少は制限がゆるくなるんだろうけど、いずれHerokuじゃないサーバも触ってみたかったのでこれを機にやってみることにした。

とはいえ今日はあんまりやってない。

やったのは以下くらい。

  • ドットインストールを見る(見つつ以下をやる)
  • さくらのVPSの契約
  • CentOSのセキュリティとかの設定
  • 各種ソフトウェアのインストール
    • Apache
    • Git (最初から入ってたっけな)
    • Ruby (rbenv)
    • Bundler, Rails

ドットインストール

何か始めるときにはいつもお世話になるドットインストール。

今回も例に洩れずお世話になった。

さくらのVPS入門 (全21回) - プログラミングならドットインストール

これの「#12 Webページを設定してみよう」までを見ながら、動画だけではわからない詳細とかは適宜ググりながら、設定した。#12以降は必要になったときに随時見ればいいかな、という感じ。

契約

プランは1GB 900円のやつにした。

以前にレンサバを借りたときのIDがあったので、それを使って契約。

ずっと使ってなかったのでIDがわからなくて苦労した。メールを大捜索した。

設定

ドットインストール師匠にならって、以下のような設定をした。

ソフトのインストール

上に書いた通りのソフトを入れた。

まだいろいろ追加する必要がある。

あとドットインストールの流れでApacheを入れたけど、Apacheは前に使ったことがあるので今度はNginxを使ってみたい。

まとめ

今日はこれだけ。

サーバいじり超たのしい。

HerokuでMySQLを使うためにいろいろした

いろいろしたのでメモしておく。

そこら中の有益な記事を参考にした。

ClearDBアドオンの追加

HerokuのデフォルトのDBはPostgreSQLなので、MySQLを使うためにClearDBアドオンを入れる。

$ heroku addons:create cleardb:ignite

環境変数を設定する。

$ heroku config | grep CLEARDB_DATABASE_URL
CLEARDB_DATABASE_URL:       mysql://<username>:<password>@<host>/<databse>?reconnect=true
$ heroku config:set DATABASE_URL='mysql2://<username>:<password>@<host>/<database>?reconnect=true'

何をやってるかというと、ClearDBアドオンを入れると追加されるCLEARDB_DATABASE_URLの値を、DATABASE_URLの値にセットしている。その際、スキームをmysql://からmysql2://に変える。

できたらマイグレートとかする。

参考

Rails外からDBにアクセスする

以上の設定でRailsアプリからはMySQLにアクセスできた。(たしか)

しかし今回はDBにレコードを定期的に追加するスクリプトをHeroku Schedulerで走らせていて、そいつからもアクセスできるようにする必要がある。(スクリプトではActiveRecordを使ってる)

そのスクリプトに設定をハードコーディングしたり独自の設定ファイルを別に用意したくないので、Railsのdatabase.ymlを読み込んで使うことにする。

最初に自動生成されたdatabase.ymlにこう書いてある。

# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>

ので、こうする。

production:
  <<: *default
  url: <%= ENV['DATABASE_URL'] %>

その上で、DBにアクセスしたいスクリプト側はこうした。

require 'active_record'
require 'yaml'
require 'erb'

...

env = ENV['RAILS_ENV'] ||= 'development'

CONFIG = YAML.load(
  ERB.new(File.read(
    File.expand_path('path/to/database.yml', __FILE__)
  )).result)[env]

ActiveRecord::Base.establish_connection(CONFIG)

参考

文字コード

上で動かしはじめると、

Mysql2::Error: Incorrect string value:...

というエラーが出た。

絵文字を格納するには、MySQLエンコードをutf8mb4にする必要があるとのこと。(デフォルトはutf8)

参考:ClearDB に文字コード utf8mb4 で保存する - xykのブログ

この記事に従って、まず環境変数の修正を行う。

$ heroku config:set DATABASE_URL="mysql2://<username>:<password>@<host>/<database>?reconnect=true&encoding=utf8mb4"

DATABASE_URLの末尾に&encoding=utf8mb4を付加。

次に、マイグレーションファイルを以下の通り修正する。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users, options: 'DEFAULT CHARSET=utf8mb4' do |t|
        
      ...

    end
  end
end

create_tableoptions: 'DEFAULT CHARSET=utf8mb4'を付加。

で、変更をコミット、プッシュ、マイグレートする。

まとめ

一挙手一投足ハマって大変。

yamacent.hatenablog.com