エンコードされた国際化ドメイン名をデコードする

こんにちは。masm11 です。

国際化ドメイン名をご存知でしょうか? インゲージ.jp みたいなやつのことですね。 しかし、時々、xn--eck2as8usb.jp と表示されていることがあって、「これ、一体どこ??」 と思うことがあります。今回はこの文字列を元に戻す方法をご紹介します。

国際化ドメイン名とは

ドメイン名には日本語は使えません。 しかし、一部には「日本語を使いたいんじゃああ!!」という人もいて、 対応する必要がありました。 ですが、やっぱりドメイン名には日本語は使えないので、 英数字に変換して使うことにしたのです。

例えば、

インゲージ.jpxn--eck2as8usb.jp

といった感じです。xn-- が付いていれば、国際化ドメイン名を変換したものです。

ブラウザなどでは自動で変換してアクセスしてくれて、便利に使えます。 ですが、時々 xn--eck2as8usb.jp だけを見せられて、「これ何?」と判断する必要に迫られる場面もあります。

変換方法

国際化ドメイン名を英数字に変換するには、Punycode と呼ばれる変換方法を使っています。

Linux では、ライブラリとしては libidn、ツールとしては idn というコマンドが 存在します。

実際に使ってみましょう。

[~]$ echo 'インゲージ.jp' | idn
xn--eck2as8usb.jp
[~]$ 

このように変換してくれます。逆に変換したい時には、-u を付けて、

[~]$ echo 'xn--eck2as8usb.jp' | idn -u
インゲージ.jp
[~]$ 

というように使います。また、

[~]$ echo 'xn--6wyu77cyid.xn--eck2as8usb.jp' | idn -u
開発部.インゲージ.jp
[~]$ 

というように、サブドメインにも対応しています。

まとめ

変換された国際化ドメイン名を元に戻す方法をご紹介しました。 これで、国際化ドメイン名で困ることが一つ減りました。

ですが、国際化ドメイン名には他にも問題があります。

[~]$ echo 'インゲ−ジ.jp' | idn
xn--t9gx68chbza60a.jp
[~]$ 

変換すると上に挙げた例と結果が異なるのですが、何故でしょうか? 実は「−」が長音記号でなくハイフンなのです。 英数字なら見間違えることは滅多にないのですが(それでも l1 は区別しづらいですが)、日本語や、もっと広く世界の言語を相手にすると、見間違えることが増えます。以前、Google.com の "G" の文字がよく似た別の文字の詐欺サイトがあったらしいですね。怖い怖い。

以上、国際化ドメイン名が嫌いな masm11 でした。

SFTP で chroot する方法

こんにちは、masm11 です。

実はつい先日まで SFTP で chroot ができることを知りませんでした。 そこで今回は、SFTP での chroot の設定方法についてまとめてみたいと思います。

FTP, FTPS, SFTP...?

その前に、FTP にもいろいろ種類があって、混乱している方もいらっしゃるかもしれませんので、 一度ここで整理してみます。

  • FTP

    古き良き時代から存在する FTP です。当然のように、 ユーザ名やパスワードは生のままインターネットを流れます。

  • FTPS

    HTTP に HTTPS があるように、FTP にも FTPS があります。 HTTPS が TLS トンネルの中で HTTP 通信するのと同じように、 FTPS は TLS トンネルの中で FTP 通信を行います。 従って、ユーザ名やパスワードはしっかり暗号化されます。

    FTPS と SFTP で混乱してしまう方は、HTTPSFTPS も末尾が S で同類、と覚えると良いでしょう。

  • SFTP

    こちらは、SSH 接続の中で独自に FTP っぽいことをします。 FTP, FTPS とはかなり毛色が異なります。

    SSH 接続を使うので、認証にはパスワードでなく公開鍵認証を使う ことができます。もちろん暗号化はバッチリです。

chroot する意義

FTP, FTPS, SFTP は、ログインしてしまえば、そのサーバにあるファイルは、 どのファイルであろうとアクセスし放題です。しかし、これではセキュリティ的に 問題がある場合があります。そういう場合、特定のディレクトリ以下のみにアクセスを 限定したくなります。

そんな時に便利なのが chroot 機能です。この機能を使うと、 ユーザは、サーバが指定したディレクトリより上の階層にはアクセスできなくなります。

SFTP で chroot の設定をする

  1. ~/.ssh/authorized_keys に SSH 公開鍵を追加する

    chroot とは関係ありませんが、パスワードなしでログインできるようにします。

  2. /etc/ssh/sshd_config に以下を追記する

    Match User someone
    ChrootDirectory /var/www/somedir
    ForceCommand internal-sftp -u 002
    

    someone は SFTP を使うユーザ、/var/www/somedir が chroot 先です。

    -u 002 は umask の設定です。umask については、今回は説明を省略します。

    設定を変更したら sshd を再起動します。

    sudo systemctl restart sshd
    

  3. /var/www/somedir のパーミッションを修正する

    sudo chown root:root /var/www/somedir
    sudo chmod 755 /var/www/somedir
    

    chroot 先は root:root で 755 でなければならない仕様だそうです。

確認

以上で、sftp すると chroot しているはずです。

$ sftp サーバ名
Connected to サーバ名.
sftp> pwd
Remote working directory: /
sftp> ls
(省略)

もし、接続した直後に切断されてしまう場合は、何か設定が間違っています。サーバで、

sudo journalctl -f

を実行した状態でもう一度接続してみると、何かメッセージが出力されるかもしれませんので、確認してみてください。

まとめ

つい先日まで、SFTP で chroot できることを知らず、FTP は嫌だし FTPS は面倒いなー、と思っていました。 今回、SFTP で chroot できることを知ったので、これからはこの方法で気軽に設定していきたいと思います!

Mix Leap Study 特別編 - レガシーをぶっつぶせ。現場でDDD! コラボカンファレンスに参加しました -- Ruby での開発に活かせるのか?

yahoo-osaka.connpass.com

どうも永田(@kizashi1122)です。 このイベントに参加してきました。

参加のモチベーション

「DDD」という言葉は当然知っている。ただあんまりよくわかっていない。エリック・エヴァンスの本も読んでいない。

こんな状態で、「DDD とは何なのか?」を少しでも理解できればなというモチベーションで挑みました。

聞いた発表

  • ドメイン駆動設計という設計スタイル by @masuda220
  • DDDのモデリングとは何なのか、そしてどうコードに落とすのか by @little_hand_s
  • 現場でドメイン駆動設計を広げるには何をすれば良いか? by @tsuyok
  • 抽象的な教えを試行錯誤しながら解釈した DDD の実践レポート by @suzuki_hoge
  • 過去の失敗例から再考するモデル駆動設計 by @j5ik2o

全部メイン会場やないかい。

正直、増田さん(@masuda220)の発表は概念的な内容も多く、しっかり理解できたかどうかは不安でした。 ただ後半はコードに落ちる部分の説明もあり、なんとなく「こういうことかな」という理解をしました。

私自身は、今は Ruby で開発しているものの Java の経験者でもあり、特に松岡さん(@little_hand_s)とかとじゅんさん(@j5ik2o)の発表はとても腑に落ちるもので、増田さんの発表の後半の続きのような、よりコード側の説明がありDDD初心者にはとても理解しやすかったです。

ただ、これってDDDと言うかこれって・・・では?と思うところがあり、ちょっとモヤっとしていました(・・・については後述)。

懇親会

登壇者との懇親会の参加させていただきました。 ちょうど隣に、登壇者の松岡さん(@little_hand_s)向かいに Yahoo! の辻さん(@crossroad0201)が座られていたので、思い切って上述の疑問をぶちまけました。

私:

「私はDDD初心者で、今日、みなさんの発表を聞いていて感じたのですが、これって純粋にオブジェクト指向をするってことじゃないんですか?

松岡さん、辻さん:

「そのとおりだと思います」

このときの嬉しさ。我が意を得たり。

さらに補足してくれるスマートな松岡さん。

Web アプリケーションなのでややこしくなるんですよ。スタンドアローンアプリなら自然とDDDになるんです。

なるほど。めちゃくちゃわかりやすい解説。さらに松岡さんはこう続けます。

要は、高凝集で疎結合を目指していればいいんですよ。

インゲージでは今、自社サービスを Ruby(Rails)で開発しているので質問。

「松岡さん、やはり DDD と Ruby は相性よくないんですかね?

松岡さん

「よくないと思いますね」

なるほど。 確かに今日の話を聞く限りは、DDD は型安全でないと実装できない・・・とは言ってないものの、型安全であるほうが望ましいだろうなと思っていました。 この1点だけでもRubyなどのLLは不向きなのかなと思います。

また、Ruby on Rails はそもそも DDD が必要ではないようなサービスに向いているのでは?という意見もありました。 適材適所ということでしょうか?

ただ、私は「Ruby on Rails にだって、DDD の考え方はとても重要ではないか。何か活用できるはず」と思っていました。

Ruby on Rails の構成

DDD と Ruby on Rails が相性が悪いということについて、私の今の理解はこうです。

Webアプリケーションというのは、結局のところ、画面からの入力(ほとんどの場合テキスト)をデータベース(SQLレベルではテキスト)に渡す作業ばかりです。

Rails では、画面からの入力は Controller クラスが行い、Controller クラスはデータベースのテーブルの1対1に対応した Model(Rails の文脈の Model)クラスを通じてデータベースの書き込みや読み込みを行うわけです。

つまり、

画面→ Controller → Model → RDBMS

ということになります。ここでは、View はちょっと割愛します。 つまりこうなると、ビジネスロジックを書くのは、Controller か Model になり、Fat Controller になるか、Fat Model になるかの道しかない。 これはDDD的にうまくない。HTTPリクエストに依存している Controller層とデータベースの永続化を担う Model のどちらからがビジネスロジック(ドメインロジック?)も担うのは確かにおかしい。

ただ、なんとなく、ビジネスロジックをこの間に持たせることがうまくいけば、DDD 的なやり方に近づくのではないかなと思っていました。 (上記の理解は間違っているかもしれないです)

まさにこれを考えていたときに、このブログが公開されました。

engineer.crowdworks.jp

すごい。タイムリー。ちょうどこういうことを考えていました。

最後に

この懇親会のあと、終電をなくし、かとじゅんさん、田中さん、辻さんと3時まで色々なお話ができたのはいい思い出です。

DDD 本ちゃんと読まないと。

Mix Leap 運営の方々、特に、Yahoo! の辻さん(@crossroad0201)、ありがとうございました。

自前 Redis から AWS Elasticache に移行した

http://download.redis.io/logocontest/82.png

どうも永田です。

弊社サービスの「リレーション」では、セッション管理やジョブキュー、一時的なデータ置き場として Redis を使っています。 最近、EC2 上に自前で構築していた Redis から AWS Elasticache に移行しました。

動機

自前で構築していた Redis は Sentinel を使ってクラスタを構築していました。特に問題はなかったのですが、やはり管理対象を減らしてマネージドサービスに移行していきたいという思いがあり、移行することにしました。

データの移行

実は自前 Redis は3系のバージョンを使っていました。AWS Elasticache では現時点で最新の5.0.4を使っています。気になるのはデータ(rdbファイル)を移行する際の互換性。

github.com

こちらには

Redis dump file is 100% backwards compatible. An older dump file format will always work with a newer version of Redis.

とあり、バージョンによる互換性は大丈夫だと言えそうです。

ただし、AWS Elasticache はクラスタを起動するタイミングでしか rdb ファイルをインポートできないため、移行は多少ややこしくなります(後述)

Cluster Mode はどちらにすべきか?

AWS Elasticache はそもそもがクラスタなのですが、作成する際に Cluster Mode のチェックボックスがあります。当然オンでしょ?と思ったらハマりました。弊社サービスでは非同期ジョブに Sidekiq を使っており、ジョブキューには Redis を使っているのですが、その Sidekiq が Redis Cluster には対応していないようなのです。

github.com

ここに、Cluster is NOT appropriate for Sidekiq と太字で書かれています。

実際のエラーメッセージは

ERR CROSSSLOT Keys in request don't hash to the same slot.

というもので、https://github.com/mperham/sidekiq/blob/3d622887c81b28cf0ffc5c8c1f7dd626c463f6a1/lib/sidekiq/api.rb#L205-L206 ここに TODO で誰かクラスターサポートしてよとコメントに書いていますね。

では実際の移行手順はどうなるのか?

上述のとおり、rdb ファイルは、Elasticache のクラスタ作成時でないと指定できません。 そのため、多少時間はかかるのですが、以下の手順を取ることにしました。

  1. AWS で Elasticache のクラスタ作成の入力項目を全部埋めておく(rdb の s3 のロケーションも)
  2. アプリケーションをメンテナンスモードをオンにする
  3. バッチや常駐プログラムの起動を停止し、自前 Redis へのコネクションがないことを確認する(コマンドは後述)
  4. 旧 Redis の rdb ファイルを 1 で指定した s3 のロケーションにアップロードする
  5. アップロードしたファイルのS3上でのパーミッションを変更する(詳細は後述)
  6. クラスタ作成の実施する(大体7分から8分くらいか)
  7. ステータスが creating から modifying に変わり、primary endpoint が確定したら、アプリケーションが参照するRedisのホストをその primary endpoint に変更し、設定内容を反映させる
  8. メンテナンスモードをオフにする
  9. セッションが引き継げているかなど動作確認をする

といった流れとなりました。

Redis のコネクションの確認は

$ redis-cli -h <host> CLIENT LIST 

で確認可能です。

また s3 にアップロードした rdb ファイルのパーミッションは

docs.aws.amazon.com

にあるとおり、 [Add Account] に 540804c33a284a299d2547575ce1010f2312ef3da9b3a053c8bc45bf233e4353 を入力し、読み取り権限を与える必要があります。

移行後

残念ながら、AWS Elasticache にしたことによってパフォーマンスがよくなったとかそういった感触はありません。 メモリ空間は今までより増えたので、さらに活用していこうかと思います。

では!

エンジニアは引き続き募集中だよ!

bash スクリプトの罠

こんにちは、masm11 です。お久しぶりです。 今回は bash スクリプトの罠を一つご紹介したいと思います。

症状

以下のような bash スクリプトを作成します。

#!/bin/bash

sleep 5

5秒間 sleep して終了するだけのプログラムです。

実行すると、

$ ./test.sh
$ 

5秒後にプロンプトに戻ります。

次は、端末をもう一枚開き、test.sh を実行中に、以下のように test.sh を書き換えてみます。

$ echo echo kita >> test.sh
$ 

するとどうでしょう。

$ ./test.sh
kita
$ 

後から追加したコードも実行されてしまっています。

解明

いったい何が起きているのでしょうか?

こういう時にはシステムコールをトレースすると便利です。

以下のように実行します。

$ strace -f -o log ./test.sh
kita
$ 

こうすると、どんなシステムコールが実行され、どんな返り値だったかが、log ファイルに 出力されます。

この log ファイルから重要そうな箇所だけを抜き出してみました。

24863 openat(AT_FDCWD, "./test.sh", O_RDONLY) = 3
24863 read(3, "#!/bin/bash\n\nsleep 5\n", 80) = 21
24863 lseek(3, 0, SEEK_SET)             = 0
24863 dup2(3, 255)                      = 255
24863 fstat(255, {st_mode=S_IFREG|0755, st_size=21, ...}) = 0
24863 lseek(255, 0, SEEK_CUR)           = 0
24863 read(255, "#!/bin/bash\n\nsleep 5\n", 21) = 21
24863 wait4(-1,  <unfinished ...>
24863 <... wait4 resumed>[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 24864
24863 read(255, "echo kita\n", 21)      = 10
24863 read(255, "", 21)                 = 0

最初の 24863 はプロセスIDで、その右に実行したシステムコールの内容が出力されています。

wait4 と書いてあるところが、sleep コマンドの終了を待っているところです。その後に、

24863 read(255, "echo kita\n", 21)      = 10

と続いています。なんと、ここでスクリプトの続きを読んでいました。

まとめ

bash スクリプトは実行中にもスクリプトを read していることがわかりました。 実行中に書き換えると、場合によってはとんでもない目に遭うことが想像できます。

bash スクリプトは実行中に書き換えないようにしましょう!

Ruby で Excel ファイルを作る

お久しぶりです。masm11 です。随分時間があいてしまいました。

以前、Python で Excel ファイルを作成する方法について書きました。 しかし弊社はやはり Ruby がメインなので、今回は Ruby で作成する方法について 紹介したいと思います。

Ruby で Excel ファイルを扱える gem にはいくつかあるようです。

  • Win32OLE
  • WrapExcel
  • Spreadsheet
  • Roo
  • Axlsx

(https://qiita.com/damassima/items/1b791ba90459ef0534fe より)

たくさんありますね。しかし今回は .xlsx ファイルを Linux で(かつ無料で)作成したいので、

  • rubyXL

を使うことにします。

gem のインストール

Rails で使いたいので、Gemfile に以下のように書きます。

gem 'rubyXL', '~> 3.4.3'

そして、いつもどおり、

bundle

です。

Rails でなくただの Ruby プログラムで使いたい場合は、

gem install rubyXL -v '~> 3.4.3'

ですね。

require する

rubyXL にはいろいろな便利メソッドがありますが、 rubyXL 3.4.0 以降では、メモリ使用量を削減するため、これらの便利なメソッドを使うには明示的に require してやる必要があります。

あとで set_number_formatchange_contents メソッドを使いたいので、 ここでは以下のようにします。

require 'rubyXL/convenience_methods/cell'
require 'rubyXL/convenience_methods/workbook'

Rails の場合でも必要です。

その他に require できるものについては、本家に書いてあります。

使う

require したら、コード本体を書いてみましょう。

まずは、Workbook を作成し、その中のシートを取得します。

book = RubyXL::Workbook.new
sheet = book[0]

ではこのシートに値をセットしていきます。

A1 セルに foo を入力するには以下のようにします。

sheet.add_cell(0, 0, 'foo')

簡単ですね。

A2 セルに数値の 2 を入力するには以下のようにします。

sheet.add_cell(1, 0, 2)

'2' と文字列にしてしまうと、勝手に '2 になってしまうので、型には注意してください。 Excel のいつものお約束ですね。

なお、add_cell の引数は、1つめが縦の位置、2つめが横の位置で、どちらも 0 から始まります。

日付の場合は少し特殊です。add_cell メソッドが Date 型に対応していないのです。

A3 セルに日付の 2019/06/10 を入力するには、以下のようにします。

c = sheet.add_cell(2, 0, 0)
c.change_contents(Date.parse('2019/06/10'))
c.set_number_format('yyyy/mm/dd')

1行めでセルに数値型の 0 をセットしています。これでこのセルは数値型になります。 そして2行めで日付をセットしています。change_contents メソッドは Date 型に対応しています。 最後に3行めで日付の表示形式をセットします。

ここで、上に書いた require をしていない場合、以下のエラーになります。

./test.rb:14:in `<main>': undefined method `change_contents' for #<RubyXL::Cell(2,0): 0, datatype=nil, style_index=0> (NoMethodError)

保存する

ファイルに保存します。

book.write('foo.xlsx')

この foo.xlsx を開くと、以下のようになります。

f:id:masm11:20190610224818p:plain

Rails を使った Web サービスなどでは、小さいファイルなら、ファイルに保存するよりメモリ中に作ってくれた方が便利なこともよくあります。 その場合は、以下のようにします。

data = book.stream.read

まとめ

以上、Ruby で簡単に Excel ファイルを作ってみました。

Python で作れることを知った時には、「きっと Ruby でもできるはず!」と思いました。 本当にあるんですね… 便利な世の中です。

ではまた。

Rails Developers Meetup 2019 に参加してきましたー

こんにちは。Re:lation のエンジニアの石田です。

先日行われた Rails Developers Meetup 2019 に行ってきました。

2日間で70近いセッションがあって、どれを選ぶかかなり迷いました。 ホント3日間にしてほしいです。。。 今回は、来月行われる Ruby Kaigi 2019 で発表される方も多くいらっしゃったので、同じような発表については優先度を下げる形で選んで回りました。

ちなみに、参加費は会社が全額負担してくれました。(ありがたい!)

railsdm.github.io

どのセッションも良かったのですが、特に印象に残った発表だけに絞って、書いていきます。

[Day 1: KEYNOTE] "Ask Me Anything" by DHH

www.youtube.com

急遽、翻訳なしでゆっくり喋るという形式で KeyNote が始まって、正直半分も聞き取れなかったです。。。

それでも、Rails6 の受信メールの処理を扱う Action Mailbox の話や Rails 7 での構想についての話は何とかついていけました。 JavaScript Framework には否定的な感じなので、このあたりは今後も自分で環境を構築していく必要性を感じました。

ともあれ、ちゃんと英語を聞き取れるようにならないといかんなぁ。。。

[A-1] アプリケーションを作るときに考える25のこと

www.slideshare.net

自分が Rails に初めて触ったのが Re:lation でその頃に色々とgemを調べたりしたのですが、今のrailsのデファクトスタンダードをキチンと追えているのか自信がなかったので聞いてきました。

知らないgemも多く、その知見も聞いてよかったのですが、github の Dependency graph で追うという発想はまったくなかったです。 技術を追うための1つの方法が知れてラッキーという感じでした。

[B-9] Railsアップグレード百景

speakerdeck.com

Re:lation もアップグレードしていく必要があり、その知見があまりないので聞いてきました。

色々なハマりどころを聞くことができて非常にありがたかったです。 特に Rails 4 と Rails 5 に互換性のない gem (devise など) があることを初めて知ったので、このあたりの対策も考えていかないとキツそうに感じました。

[A-10] 毎日の開発に役立つRailsプラグインづくりの秘訣

speakerdeck.com

現場の問題を解決するためにgemを作るんだという松田さんの話は非常にうなずけました。

ただ、「E2Eテストが書くのがメンドイ」とか「ER図みたい」とかやることの規模が大きすぎてスゲーの一言。 考えついても、この規模のgemを作ろうという気合が出ない自分はダメなのだろうかとちょっと凹みました。

[C-17] Ruby on Railsの正体と向き合い方

speakerdeck.com

DHHの考え方や思想など、あまりインタビュー記事などもを追って見ないので非常にタメになりました。

設計の話などはまだ消化できていない部分もあって、自分の中で消化できるように今後も考えていきたいです。

[A-19] 巨大なモノリシック Rails アプリケーションのマイクロサービス化戦略

speakerdeck.com

クッ社では、モノリシックなRailsアプリケーションをマイクロサービス化していってます。という話。

今回の railsdm のテーマとして、この話題が一番多かったように思います。 次点で CDN Cache とか BFF の話。

マイクロサービス化は難しいとよく聞いてはいましたが、改めて難しいんだなぁと感じました。

あと、ガラケー向けのサイトが未だに一定数のアクセスがあるのは驚きました。 こういうシステムを運用していかなければならないというツラミもあるという話と思いきや、システムを切り出した際に最新技術で動くシステムになった話で非常に面白かったです。

全体的な感想

railsdmは初参加+勉強会参加が久々だったのもあり、話についていけるか不安だったのですが、色々な話を聞くことができて非常に楽しかったです。

自分の勉強会参加したいというモチベーションがまた上がったので、行ってよかったと思いました!

次は RubyKaigi に行きます!