とーますメモ

Ruby on Rails / Goなどの学習メモ

【Rails】Mac上でImagemagick + RMagickをインストール

Imagemagickはコマンドラインから画像の操作や表示ができるツール。
多くのOS及び言語から使用できるため人気がある。

RMagickはRuby用のImagemagickインターフェース。
RubyからImagemagickを使用する場合は、このgemをインストールする。

Imagemagickのインストール

RMagickはimagemagickのバージョン6にしか対応していないので
バージョン6をインストール。

$ brew install imagemagick@6

パスを.bash_profileに設定

export PATH="/usr/local/opt/imagemagick@6/bin:$PATH"
$ source ~/.bash_profile

以下のコマンドでバージョン情報が表示されれば、インストール完了

$ convert --version

RMagickのインストール

インストール

gem 'carrierwave'
gem 'rmagick'

このままインストールすると、以下のエラーが出てインストールできない。

Package MagickCore was not found in the pkg-config search path.
Perhaps you should add the directory containing `MagickCore.pc'
to the PKG_CONFIG_PATH environment variable
No package 'MagickCore' found

MagickCore.pcが含まれるパスであるPKG_CONFIG_PATHを
環境変数に設定する必要がある。

.bash_profile

export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig:$PATH"
$ source ./bash_profile

インストール

$ bundle install

[参考]
Macにrmagickをインストールする - Qiita
RMagickのインストールでエラー - Qiita

【Ansible】Macのbrew経由でAnsibleのバージョンを上げたら、ansible.cfgを読み込まなくなったっぽい。

自分用メモ。

以前はansible.cfgを「/usr/local/bin/ansible/ansible.cfg」に置いており、
「/usr/local/bin/ansible/」内に「apps」ディレクトリを作成し、
さらにその中に各プロジェクトのディレクトリを作成し、プレイブックをその中に置いていた。

こんなイメージ

├── apps
│   └── project1
│       ├── hosts
│       │   ├── production
│       │   └── staging
│       ├── log
│       │   └── ansible.log
│       └── playbook
│           ├── dbservers.yml
│           ├── group_vars
│           │   ├── production.yml
│           │   └── staging.yml
│           ├── roles
├── ansible.cfg

実行するときは、各プロジェクト内に移動し、その後にプレイブックをのコマンドを叩いていた。

例)

$ cd apps/project1 && ansible-playbook -i hosts/staging playbook/site.yml

以前(バージョンは忘れた・・・2.2とかだった気がする)は、上記の構成とコマンドでansible.cfg内の設定を
読み込んでくれていたが、バージョンを2.5.3に上げたら、読み込まなくなりansible.cfg内で設定していたremote_userが効かなくなったため
サーバにSSHログインできなくなった。

公式ドキュメントを見ると、以下の順でansible.cfgは読み込まれる。

① ANSIBLE_CONFIG (environment variable if set)
② ansible.cfg (in the current directory)
③ ~/.ansible.cfg (in the home directory)
④ /etc/ansible/ansible.cfg

Ansible Configuration Settings — Ansible Documentation

このドキュメントを見ると、そもそもプロジェクトのフォルダに移動して、コマンドを実行してもansible.cfgは読み込まれないように見えるが
以前のバージョンでは動作していた。。。

なので、ansible.cfgを各プロジェクト内直下に置き、実行してみたところ動作した。
以下がその構成。

├── apps
│   └── project1
│       ├── ansible.cfg
│       ├── hosts
│       │   ├── production
│       │   └── staging
│       ├── log
│       │   └── ansible.log
│       └── playbook
│           ├── dbservers.yml
│           ├── group_vars
│           │   ├── production.yml
│           │   └── staging.yml
│           ├── roles

[参考]
Ansible: path to ansible.cfg - Stack Overflow

【Ansible】Macのbrew経由でPythonをインストールしたら、Ansibleが動作しなくなった。

表題のとおりだが、
今まで動いていたAnsibleが以下のエラーを吐くようになった。

dyld: Library not loaded: @executable_path/../.Python
 Referenced from: /usr/local/Cellar/ansible/2.5.2/libexec/bin/python2.7
 Reason: image not found

いろんなサイトを見ると、よくわからないがリンクが切れたどうだとかの
書いてあったので、ansibleをアンインストールし

$ brew uninstall ansible

再インストールしたら直った。

$ brew install ansible


[参考]
ansible --version dyld: Library not loaded: @executable_path/../.Python · Issue #39435 · ansible/ansible · GitHub

【FTP】FTPのパッシブモードについて

自分用メモ

Clientからパッシブモードで「FTPサーバ」に接続する場合は以下のようになるっぽい。

When Passive mode is used the Client first connects to the server on port 21. Then the client issues a PASV or EPSV command. The server then replies with an IP address and port number. The client finally uses that IP Address and port number to establish the data connection.

By Active and Passive FTP Transfers Defined

In step 1, the client contacts the server on the command port and issues the PASV command. The server then replies in step 2 with PORT 2024, telling the client which port it is listening to for the data connection. In step 3 the client then initiates the data connection from its data port to the specified server data port. Finally, the server sends back an ACK in step 4 to the client's data port.

By Active FTP vs. Passive FTP, a Definitive Explanation


1)[Client => FTP Server] 21番で接続し、ハンドシェイクし、コントロールコネクションを張る。

2)[FTP Server => Client] 自ら(FTP Server)のIP及びデータ転送に使用するポート番号をクライアントに返す

3)[Client => FTP Server] 2)で返ってきたIPとポート番号を元にデータコネクションを作る。

またパッシブモードは、コントロールコネクションもデータコネクションも「クライアントから」リクエストを送るため
クライアントサイドのFirewallの設定が楽になる。

「FTPS」の場合は、よくわからない・・・
FTP/SSL in passive mode with portrange, which ports has to be open on the firewall? - Server Fault


[参考]
FTPのアクティブモードとパッシブモードの違いは、データ転送の接続方向
How to Enable FTP Passive Mode - cPanel Knowledge Base - cPanel Documentation
アクティブFTPとパッシブFTP
centos - How to configure vsftpd to work with passive mode - Server Fault
Setting Up An FTPS Server Behind A Firewall or NAT For PASV Mode Data Transfers

【初心者用】Nginxが何なのか、一から調べてみた。

そのそもよくNginxがわかっていないので自分用に内容を整理する。

1)Nginxの用途

①「静的なコンテンツのWebサーバ」として利用

・静的ファイルを配信するWebサーバとして利用

②「動的なコンテンツのWebサーバ」として利用

・Webアプリとの接続方法は、FastCGIやWSGI、TCPソケットなどがある。

③「ロードバランサ、リバースプロキシ」として利用

・キャッシュサーバとしても利用可

2)Nginxを使うと何がいいの?

・設定ファイルの構造がシンプル
・少ないリソースで動作する

3)Nginxの重要ディレクトリ

「/etc/nginx/」・・・nginxの設定ファイルの場所
「/var/cache/nginx」・・・キャッシュの場所
「/var/log/nginx」・・・ログの場所

4)Nginxの重要設定ファイル

以下「/etc/nginx/」より

「nginx.conf」・・・最初に読み込まれるファイル。他の設定ファイルはこのファイルから読み込まれる。
※includeによって読み込める。

「conf.d/default.conf」・・・ポート番号やドキュメントルートの設定など、基本的なWebサーバの設定
※インストール直後。

5)コメントの付け方

「#」をつければ良い。「#」から行末がコメント対象になる。

6)設定ファイルの見方

設定ファイルは、やろうと思えば、ひとつのファイル(/etc/nginx/nginx.conf)内で全て完結できる。
ただそれだと管理上わかりづらくなるので、別ファイルにしている。
例)http情報は/etc/nginx/nginx.confに、server情報は/etc/nginx/conf.d/*.confに。

nginxはディレクティブと呼ばれる設定項目を書いていく。
ディレクティブには「;(セミコロン)」で終わるものと、「{}(中かっこ)」を使って囲ったブロックで
コンテキストを作成できるものがある。
もっとの外側のブロックをmainコンテキスト。
ディレクティブより作成されたコンテキストは、「<ディレクティブ名>コンテキスト」と呼ぶ。例)httpコンテキスト、serverコンテキスト

ディレクティブには、それぞれ記述できるコンテキストが決まっている。
httpディレクティブは、mainコンテキスト、serverディレクティブは、httpコンテキストの中にしか書けない。
またディレクティブには、複数のコンテキスト内に書けるものもある。
例)access_logディレクティブ
ちなみに複数のディレクティブが存在する場合、もっとの内側のディレクティブが有効になる。

また同じ名前のディレクティブだが、使用されるコンテキストによって、意味が変わる場合がある。
例)serverディレクティブ
httpコンテキスト内だと、バーチャルホストを設定するディレクティブを意味するが、upstreamコンテキスト内だとロードバランサの振り分け先を設定する意味のディレクティブになる。

7)mainコンテキスト

ユーザ名(user)

デフォルトだと「nginx」になっているので、nginxというユーザを作成か、違うユーザを作成しこの値を変更する必要がある。このユーザを作成する場合は、システムログインの必要が無いため、ログインシェルを/bash/falseに変更しログイン権限を外す。

$ sudo groupadd nginx
$ sudo useradd -g nginx nginx
$ sudo usermod -s /bin/false nginx

「/bin/false」は、
1.「/bin/false」はシェエルを通じて一切の操作ができない偽シェルである
2.「/bin/false」は通常、人間以外のユーザーに割り当てられるシェルである
3.人間以外のユーザーとはデーモンとして起動する Apache や MySQL のことを指す
4.デーモンは定められた機能を果たすのが目的で、シェルを通じて操作をするための役割は担わない(担わせない)
5.「/bin/false」を割り当てられたデーモンが、ログイン・ログアウトの処理をすることは通常ありえない(ログインさせない)
という特徴があります。

...

説明が分かりづらかったかもしれませんが、端的に言うとプログラムに割り当てているシェルを、人間が使うシェルの設定に登録すると、予期せぬときに予期せぬ影響がでる可能性があるので、その可能性を排除したいということです。
そういうリスクの芽を摘むのがセキュリティだと私は考えています。

by /bin/false と /sbin/nologin と /etc/shells について - make world

workerプロセス数

workerプロセス数の設定値。CPUのコア数と同じ数を設定する。

サーバのCore数の確認方法

$ cat /proc/cpuinfo | grep "cpu cores" | uniq
cpu cores	: 1

以下のように書くと、コア数と同数に設定される。

worker_processes auto;

エラーログ

エラーログの設定例。以下ではwarnのログレベルを指定。

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

注意するポイントは、
エラーログには、httpコンテキスト箇所で説明するアクセスログのようにフォーマットを指定することができない。

プロセスID

説明略

pid        /var/run/nginx.pid;

8)eventsコンテキスト

worker_connections

worker_connectionsは、1つのworkerプロセスが同時に受け付けられるプロセス数。

events {
    worker_connections  1024;
}

9)httpコンテキスト

Webサーバ全体の設定を書く。httpコンテキスト内には複数のserverコンテキストを記述でき、
その共通設定を書くことで、各serverコンテキストの設定をまとめて書くことができる。
もちろん、各serverコンテキストでは、httpコンテキストの設定を上書きすることができる。

mime.types

拡張子とContent-Typeの紐付けに使用される。ここに定義されていない拡張子でアクセスされると
「application/octet-stream」としてContent-Typeヘッダが設定される。

アクセスログ

アクセスログの設定例。以下ではmainというフォーマットを指定。

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "gzip=$gzip_ratio"';

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

keepalive_timeout

続けざまに色んなページアクセスするクライアントが多い場合などに有用な設定。
クライアントとの接続をすぐに切るのではなく、応答が終わってもしばらく待機しておくことで
次回アクセスが来たときに、早くクライアントに処理を返すことができる。

サーバ側のタイムアウトを65秒に設定している例

keepalive_timeout  65;

serverコンテキスト設定ファイルの読込

以下のように書いて、conf.d以下の*.confにマッチするファイルを読み込む設定でも良いが

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

Apacheの設定ファイルのやり方に沿って

「/etc/nginx/sites-available/」内に設定ファイルを追加し
「/etc/nginx/sites-enabled/」内から「/etc/nginx/sites-available/」の設定ファイルにシンボリックリンクを貼る方式をとるほうが
柔軟に設定を変更できる。

以下の設定で、「/etc/nginx/sites-enabled/」内のファイルを全て読み込む

include /etc/nginx/sites-enabled/*;

[参考]
Nginxのバーチャルホスト設定 - Qiita
UbuntuのApache設定ファイル、どうやるんだっけ? - Qiita

10)serverコンテキスト

このコンテキストの設定は、1)で説明した①〜③の中の何を選ぶかで、違ってくる。
色んなサイトの設定例を見ると、どのタイプで設定しているのか言及していないケースが多いので非常にわかりずらい。

見分け方は大まかに説明すると以下。

①のタイプ(静的なコンテンツのWebサーバ)

基本的なディレクティブとrootやindexディレクティブで構成されている。

例)

server {
  listen 80;
  server_name static.example.com;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  location / {
    root /var/www/html/static.example.com;
    index index.html index.htm;
  }
}

②のタイプ(動的なコンテンツのWebサーバ)

「fastcgi」、「uwsgi_params」や「passenger・・・」などの設定が入っている。

③のタイプ(ロードバランサ、リバースプロキシ)

「proxy_pass」の設定が入っている。

11)自分用の設定

リバースプロキシでサーバ内のRailsのRackサーバであるPumaのソケットに接続。
SSLはLet's scriptを使用。SSL系の設定は別記事にて紹介している。
thoames.hatenadiary.jp

/etc/nginx/nginx.conf

user  nginx;
worker_processes  auto;

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;

    server_tokens off;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "gzip=$gzip_ratio"';

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

    keepalive_timeout  65;

    gzip              on;
    gzip_http_version 1.0;
    gzip_types        text/plain
                      text/xml
                      text/css
                      application/xml
                      application/xhtml+xml
                      application/rss+xml
                      application/atom_xml
                      application/javascript
                      application/x-javascript
    gzip_disable      "MSIE [1-6]\.";
    gzip_disable      "Mozilla/4";
    gzip_comp_level   1;
    gzip_proxied        any;
    gzip_vary           on;


    # If we need to upload large files.
    # ex)
    # client_max_body_size 100m;
    # location ~ ^/upload/ {
    #   client_max_body_size 2g;
    # }

    # include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

「server_tokens off;」と設定しておけば、ディストリビューションのバージョンがレスポンスヘッダーから削除される。

/etc/nginx/sites-available/default

# @see https://mozilla.github.io/server-side-tls/ssl-config-generator/

upstream app {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response
  server unix:///var/www/example.com/shared/tmp/sockets/example-puma.sock fail_timeout=0;
}

geo $allow_ip {
  default 0;
  # xxx.xxx.xxx.xxx 1;
}

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
  return 301 https://$host$request_uri;
}

server {

  #########################################
  #   Maintenance page settings [START]   #
  #########################################

  # [How to setup maintenance mode]
  # 1. make maintenance.html in [/usr/share/nginx/html].
  # 2. if you want to see site even thought current mode is maintenance mode, you can add allow_ip.

  set $maintenance false;
  error_page 503 @maintenance_page;

  if (-e /usr/share/nginx/html/maintenance.html) {
    set $maintenance true;
  }

  if ($allow_ip) {
    set $maintenance false;
  }

  location @maintenance_page {
    root /usr/share/nginx/html;
    expires 0;
    rewrite ^(.*)$ /maintenance.html break;
  }

  #######################################
  #   Maintenance page settings [END]   #
  #######################################

  listen 443 ssl http2;

  server_name example.com;

  location / {

    if ($maintenance = true) {
      return 503;
    }

    # proxy_pass http://localhost:3000;
    # proxy_pass http://unix:/var/www/example.com/shared/tmp/sockets/example-puma.sock:/;
    proxy_pass http://app;

    proxy_set_header Host                  $host;
    proxy_set_header X-Real-IP             $remote_addr;
    proxy_set_header X-Forwarded-Host      $host;
    proxy_set_header X-Forwarded-Server    $host;
    proxy_set_header X-Forwarded-For       $proxy_add_x_forwarded_for;

    proxy_redirect                         http:// https://;
    proxy_set_header X-Forwarded-Proto     https;
  }

  ssl on;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_session_tickets off;

  # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
  ssl_dhparam /etc/nginx/conf.d/ssl/dhparam.pem;

  # @see http://nginx.org/en/docs/http/configuring_https_servers.html
  ssl_protocols     TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
  ssl_prefer_server_ciphers on;

  # HSTS (31536000 seconds = 12 months)
  add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';

  # Enable OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

}

12)今後の対応

・キャッシュ対応
ngx_cache_purgeを使い、更新が発生するたびに内側からのみアクセスできるURLを通し、キャッシュを削除する方法がある。
nginxでリバースプロキシキャッシュして、キャッシュを削除する機能を付ける - Qiita

RedisでもMemcachedでもNginxに対応したモジュールがある。
Webサーバが複数構成になる際は、導入を検討する。
画像サーバの負荷対策:プロキシーキャッシュサーバ導入 - Qiita
nginxでプロキシ&キャッシュサーバー « chibiegg日誌
nginx+memcachedで画像キャッシュ - Webエンジニアがアフィリエイトで稼ぐ

【Sidekiq】Capistranoで独自タスクを書いてみた。

capistrano-sidekiqが微妙だったので、
自分でタスクを書いた。

append :linked_files, "config/database.yml", "config/secrets.yml", "config/sidekiq.yml"

set :sidekiq_pid_path, -> { File.join(shared_path, 'tmp', 'pids', 'sidekiq.pid') }
set :sidekiq_config, ->   { File.join(shared_path, 'config', 'sidekiq.yml') }

namespace :sidekiq do
  task :kill do
    on roles(:app) do
      execute "kill -TERM `cat #{fetch :sidekiq_pid_path}`; true"
    end
  end

  task :restart do
    on roles(:app) do
      invoke 'sidekiq:kill'
      within current_path do
        execute "su - USER_NAME -c 'cd /var/www/shopify_data_manager/current && $HOME/.rbenv/bin/rbenv exec bundle exec sidekiq -C #{fetch :sidekiq_config} --daemon'"
      end
    end
  end
  after  'deploy:finishing', 'sidekiq:restart'
end

namespace :deploy do

  desc 'upload linked_files'
  task :upload do
    on roles(:app) do |host|
      execute :mkdir, '-p', "#{shared_path}/config"
      upload!('config/database.yml',"#{shared_path}/config/database.yml")
      upload!('config/secrets.yml',"#{shared_path}/config/secrets.yml")
      upload!('config/sidekiq.yml',"#{shared_path}/config/sidekiq.yml")
    end
  end

  ...

end


最初は、Sidekiqを起動するコマンドを以下のように書いていた。

execute :bundle, :exec, "sidekiq -e development -C #{fetch :sidekiq_config}"

しかし、これだと何故か動かなくて、
いろいろ調べているうちに、以下のページからヒントを貰い、

monitoring - monitでsidekiqの停止を感知できるが、sidekiqを自動起動できない - スタック・オーバーフロー

ユーザを変更してみたら、動作した。


[参考]
Capistranoでunicornとsidekiqをシンプルにrestartする - Qiita
capistrano-sidekiqを使うのをやめる - Qiita