exlar's IT note

ITやスマートデバイスを中心とした趣味情報の寄せ集め

DockerでmacOSファイル共有サーバをお手軽構築

自宅のサーバーが古くなり、いつ故障してもおかしくない状況なので少しずつ移行をはじめています。新サーバーでは原則ルールとして Docker コンテナでの再構築に取り組んでいるため、それぞれの機能をメモしていこうと思います。

今回は Netatalk について。

Netatalk とは

Netatalk - Wikipedia

netatalkはUnix系OS上でMac OSやOS Xに対してAFPによるファイルサーバの機能を提供するオープンソースのソフトウェアである。 かつてはAppleTalkも実装したが、この機能は削除された。

MacのローカルファイルシステムであるHFSやHFS+は、リソースフォークやFinder情報といったメタデータをもつ。更に最近のOS Xでは拡張属性 (EA)も扱う。netatalkは、こうしたApple独自のメタデータを完全に保存することを目的として設計されたファイルサーバソフトウェアである。 メタデータは可能な限りサーバ側のファイルシステムの拡張属性に保存する。これが出来ない場合は隠しファイルを作って保存する。 また、バックアップソフトウェアであるTime Machineの保存先として利用することもできる。

引用の通り、macOS 向けのファイル共有プロトコルです。Mac は賢いので Windows の共有フォルダ(SMB)も普通に使えるのですが、.DS_Store といったゴミファイルが見えてしまったりと煩わしい点も若干あるので、より純粋なファイル共有プロトコルを採用します。

余談になりますが、自宅には一応Windowsもあるので、結局は Samba も構築することになります。

Docker Image の選定

ここでは Docker そのものの利用について触れません。docker rundocker-compose up のコマンドを用いたことがある方を想定しています。

Image を Dockerfile で新規構築してもよいのですが、先人たちの優れたコンテナが登録されていることも多々あるため、まずは DockerHub で検索します。環境変数での設定可能箇所が多いほど、柔軟で使いやすいコンテナであることが多いです。

❯ docker search netatalk
INDEX       NAME                                            DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/odarriba/timemachine                  Lastest Netatalk with configurable user/pa...   22                   [OK]
docker.io   docker.io/cptactionhank/netatalk                                                                13                   [OK]
docker.io   docker.io/cilix/netatalk                        Docker image designed to make it easy to h...   1                    [OK]
docker.io   docker.io/ironicbadger/build-netatalk3-debian   Builds v3 of netatalk for Debian from source    1                    [OK]
docker.io   docker.io/r1kelsey/netatalk                                                                     1                    [OK]
docker.io   docker.io/chattaway/netatalk                    netatalk                                        0                    [OK]

(2016/09/17 18:00現在)

Stars の数が一定数あるものを選びます。必ずしも Star が多からよいというわけではなく、少ないものでも日が浅いだけで高品質なものが埋もれている可能性はありますが、品質を測る一定の目安にはなるはずです。

一番多い odarriba/timemachine は timemachine 目的に特化しているみたいなので、今回選定対象からは外しました。そして選択したのがこちら。

cptactionhank/netatalk

下記の通り docker-compose.yml を作成したところ、正常に動作しました。

docker-compose.yml:

version: '2'

services:
  netatalk:
    image: cptactionhank/netatalk:latest
    container_name: netatalk
    network_mode: host
    env_file: credential.env #パスワード系のファイルは外部に
    environment:
      - AVAHI=1
    ports:
      - "548:548"
    volumes:
      - /your/media/path:/media/share
    restart: always

credential.env (docker-compose.yml の中に書いても構いません):

AFP_USER=username
AFP_PASSWORD=password
AFP_UID=1000
AFP_GID=1000

Netatalk 動作仕様の簡単なメモ

  • デフォルト設定 (afp.conf) では、 /media/share のみが共有されるようになっています
  • 1組(のみ?)のユーザーID/パスワードを環境変数で設定しなければ動作しません
  • 標準設定では不足する場合、/etc/afp.conf を volume mount (-v) で置換することで自由に設定可能です

凝った使い方をしなければこれだけで十分な気がします。

コンテナ設定のポイント

  • 環境変数にて AVAHI=1 を設定すると Auto Discovery が有効になります。Finderの左メニューに「共有」として自動検出されるものです。
  • network_mode: host オプションにて、ホストマシンの Interface に bind します。これをせずに通常の起動でコンテナの仮想NICに bind した場合、avahi による Auto Discovery が機能しません。
    • この時、Finderから「サーバへ接続(Command + K)」を選択してホストマシンのIPアドレス指定 (afp://IP_Address/) すれば接続することはできますが不便です。

まとめ

DockerHub のイメージを利用して簡単に Mac ファイル共有サーバーを作成しました。なお、この方法で作成したネットワークパスを TimeMacahine 保存場所としても利用できるはずです。

参考リンク

GitHub - cptactionhank/docker-netatalk: Docker container running Netatalk

Mac OS X に Rails & Redmine 環境を構築して Plugin 開発をする準備

はじめに

仕事で Redmine を利用しており、あれこれカスタマイズ・活用していると欲がでてくるもので、求める Plugin が見つからないと自ら開発したくなってきます。しかし、Rails 環境をまともに構築したことがないので、開発環境の準備方法すら分からない... ということで、何も考えずに Rails と Redmine が動く ローカル環境を準備することに特化した作業手順を記載します。

完全新規からのセットアップではないため若干端折ってしまっていますが、そこは前提を丁寧に記載することでフォローしたいと思います。

本エントリ記載手順の前提環境

  • Mac OS X El Capitan - version 10.11.4
  • rbenv (ローカル ruby 実行バージョンを簡単に切り替えできる支援ツール) インストール済み
  • ruby -v
    ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin15]
  • brew -v
    Homebrew 0.9.9 (git revision 2f20; last commit 2016-05-15)
    Homebrew/homebrew-core (git revision 0a6d; last commit 2016-05-16)

今回用いた Mac では、既に rbenv と brew をインストール済みでした。ruby の2.2系がどういう位置づけなのかサッパリ把握していませんが、今回の作業実施にあたり参照したブログ記事で2.2系をいくつか見たという理由と、仕事で用いている RedmineのDockerコンテナ では最新版で 2.1.7-p400 (2015-08-18) が使われており、まあ近いでしょう、というあまり根拠になっていない理由で良しとしました。

rbenv と brew (Homebrew) の準備は下記リンクを参考にしてください。

余談その1:
Redmine をサクッと立ち上げたい場合はこちらのDockerコンテナが非常に強力です。docker-compose が動く環境さえあれば(もちろん、普通に docker run してもよいのですが) 、新規環境をサクサクと作成することができます。仕事では、事業部門毎にひとつ Redmine の動作環境を与えるイメージで切り出しをしています。今回のメインテーマではないので紹介はこの程度にします。
sameersbn/docker-redmine: Dockerized redmine app server

Rails 実行環境の構築

では早速、環境作りを勧めます。以下コマンドを何も考えずに打ち込みます。

$ gem install powder
$ rbenv rehash
$ powder install

pwder とは

powは手軽に使えるRackサーバです。 Rackアプリ、Railsアプリの開発に便利です。

Mac用のRackサーバ「pow」が便利 - Qiita

ow + rbenvで手軽なRack環境構築 MacOSXでRack環境をlocalに手軽に準備するものとして pow があるのですが

pow + rbenvで手軽なRack環境構築 - Qiita

正直何者かよく理解していないわけですが、先人たちが OS X での Rack 環境構築には便利なものと推されているので素直に従います。

そういえば、上記引用リンクの先には node が必要という記載もありますが…今回の環境では既に node (-v 4.1) が入ってしまっており、依存関係を確認できていません。

余談その2:
ここで、Rackとは? という疑問が発生しますが、Webサーバーですね。私の理解が正しければ、他言語と横並びで見た時の分類は以下のとおり。

Lang Web Server I/F仕様 Webサーバーの実装(一例)
Ruby Rack protocol Rack
Python WSGI *1
Perl PSGI Plack

"Webサーバーの実装" 列には、I/F仕様の策定と同時に行われた実装という意味で記載しています。無論、各I/F仕様に対応したフレームワークは無数にあります。(*1)部分については歴史を熟知していませんが、PythonはI/F仕様の策定のみ中心に行われ、実装については周辺のフレームワークに任せたという理解でよいのだろうか??

話を戻します。

$ gem install bundler
$ gem install rails
    or
  gem install rails --version="~> 4.2.3"

rails のバージョンはできるだけ Redmine に合わせておきたいと思いつつバージョン指定無しでインストールしてしまいましたが、2016/05/17時点でインストールされるバージョンは 4.2.6 だったためセーフです。

バージョンを指定したい場合は最終行のように指定するようです。

$ rbenv rehash
$ rails ­-v
Rails 4.2.6
$ bundle -v
Bundler version 1.12.3

それぞれインストールが完了し、バージョンが表示されることを確認します。

ここまできたら、Railsが期待したとおりに実行できるかを確認します。

# 作業ディレクトリ。どこでもよい
$ cd ~/tmp

# 1個ずつ段取りを確認するために、最初は bundler でのモジュール一括インストールをスキップする
$ rails new foo --skip-bundle

# その後手動で bundle をインストールする
# --path を付与すべきか正確に確認していないが、 vendor/bundle に
# インストールするケースが一般的なのは認識があるので名に付与しておく
$ bundle install
   or
 bundle install --path vendor/bundle # <--こっちがいい?

続いて、Rails登場初期によくデモ映像で見かけた scaffold でテスト。

# ユーザー管理機能の作成
$ rails g scaffold user name:string email:string
$ rake db:migrate
$ rails s

# 上手く起動したように見えたら、ページを開く
open http://localhost:3000/users 

Rails のサンプルサイトが表示されたら、ここまでは正常完了です。

Redmineを起動してみる

bundle install と エラー

$ cd ~/tmp

# redmine を入手します
$ git clone https://github.com/redmine/redmine.git

$ cd ./redmine
$ bundle install --path vendor/bundle

すると、 rmagic のインストールに失敗し Failed となってしまいました。

$ ~/tmp/redmine ❯❯❯ gem install rmagick -v '2.15.4'
Building native extensions.  This could take a while...
ERROR:  Error installing rmagick:
        ERROR: Failed to build gem native extension.

    /Users/me/.rbenv/versions/2.2.4/bin/ruby -r ./siteconf20160516-94505-17jpn7a.rb extconf.rb
checking for clang... yes
checking for Magick-config... no
checking for pkg-config... yes
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
checking for outdated ImageMagick version (<= 6.4.9)... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/me/.rbenv/versions/2.2.4/bin/$(RUBY_BASE_NAME)

extconf failed, exit code 1

Gem files will remain installed in /Users/me/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rmagick-2.15.4 for inspection.
Results logged to /Users/me/.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/extensions/x86_64-darwin-15/2.2.0-static/rmagick-2.15.4/gem_make.out

いくつか調べてみて、PKG_CONFIG_PATH の記載がないからなどの情報がありましたが、ImageMagic をインストールしていないという報告もあり。確かに本環境ではインストールしていませんということで、brew を用いてインストールします。

# update していない場合は事前にやっておきましょう
$ brew update

$ brew install imagemagick

ImageMagickをインストールすると、以下コマンドが通過するようになりました。

$ gem install rmagick -v '2.15.4'

Building native extensions.  This could take a while...
Successfully installed rmagick-2.15.4
1 gem installed

database の設定

今回の環境はただの Plugin 開発ですので、シンプルにSQLITEの環境で凌ぎます。事前にコンフィグ指定をしておかないと以下に示す Gemfile の部分で警告が出てしまい、generate も行えませんでした。

警告の発生元:

database_file = File.join(File.dirname(__FILE__), "config/database.yml")
if File.exist?(database_file)
  database_config = YAML::load(ERB.new(IO.read(database_file)).result)
  adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
  if adapters.any?
    adapters.each do |adapter|
      case adapter
      when 'mysql2'
        gem "mysql2", "~> 0.3.11", :platforms => [:mri, :mingw, :x64_mingw]
        gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
      when 'mysql'
        gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
      when /postgresql/
        gem "pg", "~> 0.18.1", :platforms => [:mri, :mingw, :x64_mingw]
        gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
      when /sqlite3/
        gem "sqlite3", :platforms => [:mri, :mingw, :x64_mingw]
        gem "jdbc-sqlite3", ">= 3.8.10.1", :platforms => :jruby
        gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
      when /sqlserver/
        gem "tiny_tds", "~> 0.6.2", :platforms => [:mri, :mingw, :x64_mingw]
        gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
      else
        warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
      end
    end
  else
    warn("No adapter found in config/database.yml, please configure it first")
  end
else
  warn("Please configure your config/database.yml first")
end

database の設定:

$ cp config/database.yml.example config/database.yml
$ vi config/database.yml

~~~ ↓ここから #sqliteファイル名は適当に設定しています
production:
  adapter: sqlite3
  database: db/redmine_production.sqlite3

development:
  adapter: sqlite3
  database: db/redmine_development.sqlite3

test:
  adapter: sqlite3
  database: db/redmine_test.sqlite3

DBの作成(マイグレーション):

$ bundle exec rake db:create
$ bundle exec rake db:migrate

Redmine (Rack) を起動してみる

そろそろ終わりかとおもいきや、 secret key の作成が必要とのこと。Rails 4.1 からのセキュリティ関連の制約のようです。

$ rails s

warning: duplicated key at line 466 ignored: "inodot"
=> Booting WEBrick
=> Rails 4.2.6 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2016-05-16 23:39:41] INFO  WEBrick 1.3.1
[2016-05-16 23:39:41] INFO  ruby 2.2.4 (2015-12-16) [x86_64-darwin15]
[2016-05-16 23:39:41] INFO  WEBrick::HTTPServer#start: pid=43973 port=3000
DEPRECATION WARNING: You didn't set `secret_key_base`. Read the upgrade documentation to learn more about this new config option. (called from service at /Users/me/.rbenv/versions/2.2.4/lib/ruby/2.2.0/webrick/httpserver.rb:138)
DEPRECATION WARNING: You didn't set `secret_key_base`. Read the upgrade documentation to learn more about this new config option. (called from service at /Users/me/.rbenv/versions/2.2.4/lib/ruby/2.2.0/webrick/httpserver.rb:138)
[2016-05-16 23:39:48] ERROR RuntimeError: Missing `secret_token` and `secret_key_base` for 'development' environment, set these values in `config/secrets.yml`

なので、以下のとおり secret key を生成のうえ、config/secrets.yml に設定を行います。

# secret key の生成。pbcopy は MacOSのクリップボードにコピーする
# コマンドなので、非Mac環境の場合は生テキストをコピ&ペーストします
$ bundle exec rake secret RAILS_ENV=development | pbcopy

vi config/secrets.yml

~~~ ↓ここから
development:
  secret_key_base: 83f2xxxxxxxxxxx <-ここに記載

そして起動成功へ

$ rails s

/Users/me/tmp/redmine/vendor/bundle/ruby/2.2.0/gems/htmlentities-4.3.1/lib/htmlentities/mappings/expanded.rb:465: warning: duplicated key at line 466 ignored: "inodot"
=> Booting WEBrick
=> Rails 4.2.6 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2016-05-17 00:49:42] INFO  WEBrick 1.3.1
[2016-05-17 00:49:42] INFO  ruby 2.2.4 (2015-12-16) [x86_64-darwin15]
[2016-05-17 00:49:42] INFO  WEBrick::HTTPServer#start: pid=xxxx port=3000

htmlentities モジュールにて何やら警告が発生していますが、直近の開発には問題なさそうなので無視します。

Redmine Plugin の作成

今回は Plugin の generate ができることまで確認して終了。

$ rails g redmine_plugin redmine_your_plugin_name

# redmine_your_plugin_name: 作成したいプラグイン名を記載

Rails & Redmine 環境構築のまとめ

Rails & Redmine 環境を El Capitan に構築しました。その手順は以下のとおりです。

  1. Rails サポート対象の Ruby 実行環境の準備
  2. rbenv (←とにかく期待するバージョンの ruby があればいいので必須ではない)
  3. brew のインストール
  4. gem モジュールのインストール
    • gem install powder
    • gem install bundler
    • gem install rails
  5. ImageMagick のインストール (brew install imagemagick)
  6. Rails実行環境の設定
    • config/database.yml の設定
    • config/secrets.yml の設定

最初は普段運用している Docker の Redmineコンテナを用いればよいかなとも思っていたのですが、docker-compose コマンドに依存しすぎると中ではどのような構成でどう動いているのか理解が弱くなってしまうため、あえて新規構築を行いました。

今回の作業を経て、bundle がどのタイミングで何をしてくれるだとか、どのタイミングでモジュールをインストールするのかといったことの理解が深まったように思います。

本題のPlugin開発着手については、またそのうち。

参考資料

Let's Encrypt 証明書をサーバー無停止で新規発行する(Nginx編)

はじめに - 本記事のゴール

先日、WordPress環境向けに Let's Encrypt 証明書の適用を試みました。

そこでは Standalone プラグイン(現在稼働中のHTTPサービスを一度停止して証明書を発行する方法)を主に試しましたが、ここでは Webroot プラグイン(現在稼働中のHTTPサービスは継続したまま新規に発行する)方法を試みます。

前回の記事: WordPressを無料のSSL証明書 Let's Encrypt でHTTPS化する - exlar's note

Let's Encrypt 証明書作成の準備をする (Nginx環境)

前回記事でも "webrootの仕組み" として少し記載しました。

/path/to/doc_root/ で設定した部分に、スクリプトが /.well-known/acme-challenge/xxxxxx(←ランダム文字) という構造でファイルを作成し認証しているようです。従って、WordPressをリバースプロキシしているnginxに対しては、以下のコンフィグを適用する必要があります。

ですので、Let's Encrypt スクリプトがアクセスするためのパスを以下の通り定義します。

    server_name exlair.hatenablog.com;

    ...(中略…各自のコンフィグ)...

    location ^~ /.well-known/acme-challenge {
        root /path/to/doc_root/;
    }
  • 新規発行したいドメインの VirtualHost 設定に適用します。ここでは、exlair.hatenablog.com に適用するイメージを記載しています。
  • /path/to/doc/root/ 部分は何でも構いません。用途が限定的なので、現在稼働しているサービスに依存しない場所へ新規作成したほうが運用しやすいと個人的には思います。別の新規ドメイン作成時にも同じものが流用できるので。
  • パーミッションは、Webページが表示できる最低限のレベルで問題ありません(スクリプト実行時には結局、rootでランダム文字のディレクトリが生成されるため)。

Let's Encrypt 証明書を作成する

証明書の作成コマンドを実行する

  • 作成したいドメイン名「YOUR_DOMAIN」部分に置き換えて記載してください
  • /path/to/doc_root 部分は、Let's Encrypt 証明書作成の準備をする で設定したスクリプト用の公開パスです
$ ./letsencrypt-auto certonly --webroot -w /path/to/doc_root -d YOUR_DOMAIN

Checking for new version...
Requesting root privileges to run letsencrypt...
[sudo] password for username_xxxxx:

認証実行時の Let's Encrypt サーバーからのアクセス(1リクエスト)

66.133.109.36 "GET /.well-known/acme-challenge/xxYYzzLSkLeS4sSin_KLhyyyyyyyyyyo7xxxxxxxxxx HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

なお、今回実行時のIPアドレスは上記のとおりでしたが、Let's Encrypt 総合ポータル よくある質問にある通りIPアドレスのリストは随時変更される可能性があるため公開されていません。保証できませんということですね。

Let's Encrypt が認証時に使用するIPアドレスは何ですか?

Let's Encrypt では、サーバの認証(ドメイン・サブドメイン使用権者の認証のためのWebサーバへの接続)に使用するIPアドレスのリストを公開していません。これは、使用されるIPアドレスが随時変更される可能性があるためです。 また、将来的には、一回の認証プロセスにおいて、複数のIPアドレスから接続するようになるかもしれません。

証明書作結果(成功)

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem. Your cert will
   expire on 2016-08-06. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
  • YOUR_DOMAIN 部分は、-d オプションで指定したドメインが記載されます。
  • expire 部分には、証明書の有効期間=発行日から3ヶ月後の月日が記載されます。

証明書の作成ディレクトリを確認してみます。

$ sudo ls -al /etc/letsencrypt/live/

drwx------ 4 root root 4096 May  8 15:30 .
drwxr-xr-x 8 root root 4096 Apr 13 22:31 ..
drwxr-xr-x 2 root root 4096 Apr 17 15:56 DOMAIN1     (←以前作成したもの)
drwxr-xr-x 2 root root 4096 May  8 15:30 YOUR_DOMAIN (←今回作成したもの)

無事作成されました。

自動更新の設定

Let's Encrypt 証明書の有効期間は発行日から3ヶ月後で短いため、cron による自動更新が推奨されています。

letsencrypt-auto renew コマンドを実行するだけで、新規発行時に作成されたコンフィグを用いてうまいこと更新してくれるようで、既にスクリプトも仕込んであるのですが、まだ更新時期に突入したことがないので成否の確証がとれていません…。

ですので結果はおって更新したいと思います。

まとめ

以上、サーバーのダウンタイム無しで特定 VirtualHost 向けの証明書を作成する手順でした。

なお、今回 Let's Encrypt スクリプト群を git pull したところ、わずか数週間でいろいろファイル郡が更新されていました。nginx 向けの更新簡易化スクリプトがもしかしたら安定利用できる状態になっているかもしれませんが、その辺の最新状況をウォッチできていないため、本記事では汎用的な方法で行いました。

(最初に Let's Encrypt を試した時は、Nginx向けの簡易化プラグイン?は非推奨になっていました)