Ruby で カウントダウン Twitter BOT 作って Scaleway の Docker で動かす- 後半(完成まで)

大事な日までのカウントダウンを毎日 tweet してくれる BOTRuby で作成して Scaleway サーバで動かそうと思います。こんな感じでつぶやきます。

前半のおさらい

このエントリは、先日書いた以下の記事の続きです。

ngzm.hateblo.jp

前半は下記のことを行ないました。

  1. カウントダウン Twitter BOTRuby で作成しました
  2. Scaleway に Docker コンテナが動く仮想サーバを立てました
  3. 上記仮装サーバの Docker 上で Ruby:latest コンテナが動くことを確認しました。

今回はその続きで、実際に Scaleway の Docker で動かすまでの話です。

カウントダウン Twitter BOT の Docker コンテナを作成

1) Docker Ruby イメージについて

まず最初は、今回の Docker コンテナのベースとなる Ruby イメージについて、その基本的な使い方や構成などを確認しておく必要があります。これらは、 DockerHub の Ruby ページ で確認できます。

どうも Ruby アプリケーションは /usr/src/app/ にデプロイして動かせば良いようです。

2) Dockerfile などコンテナ環境の準備

さて、Twitter BOT アプリケーション用の Docker イメージを作成するための Dockerfile を作成します。

今回、この Docker ファイルは、Twitter BOT アプリケーション(countdown_bot.rb など)と同じディレクトリに作成しました。

FROM ruby:latest
MAINTAINER ngzm<nk.ngzm@gmail.com>
ADD . /usr/src/app
RUN cd /usr/src/app && bundle install
RUN cd /usr/src/app && chmod u+x countdown_bot.sh
CMD ["/usr/src/app/countdown_bot.sh"]

countdown_bot.sh というのはこのアプリを起動するだけの簡単なシェルスクリプトです。次のような感じです。これもアプリケーションと同じディレクトリに保存します。

#!/bin/bash
cd /usr/src/app
if [ $? -eq 0 ]; then
    ruby countdown_bot.rb
fi

なお、作成した Docker ファイルや、起動スクリプト countdown_bot.sh は、アプリケーションコードと同様、github のページ に置いてありますので適宜参照してください。

3) Docker コンテナを作成

準備が完了したので、docker build で Twitter BOT アプリケーション用の Docker イメージを作成します。

# docker build -t countdown .

Sending build context to Docker daemon 68.61 kB
Step 1 : FROM ruby:latest
 ---> d2cee8adb148
Step 2 : MAINTAINER ngzm<nk.ngzm@gmail.com>
 ---> Using cache
 ---> 276ca2d970a3
Step 3 : ADD . /usr/src/app
 ---> f7a2c4909d42
Removing intermediate container 8730da1e725f
Step 4 : RUN cd /usr/src/app && bundle install
 ---> Running in 7895f7170efc
Fetching gem metadata from https://rubygems.org/........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Installing public_suffix 2.0.5
Installing buftok 0.2.0
Installing unf_ext 0.0.7.4 with native extensions
Installing equalizer 0.0.11
Installing multipart-post 2.0.0
Installing http-form_data 1.0.1
Installing http_parser.rb 0.6.0 with native extensions
Installing thread_safe 0.3.6
Installing naught 1.1.0
Installing simple_oauth 0.3.1
Using bundler 1.14.6
Installing addressable 2.5.1
Installing unf 0.1.4
Installing faraday 0.11.0
Installing memoizable 0.4.2
Installing domain_name 0.5.20170404
Installing http-cookie 1.0.3
Installing http 2.2.1
Installing twitter 6.1.0
Bundle complete! 1 Gemfile dependency, 19 gems now installed.
Bundled gems are installed into /usr/local/bundle.
 ---> 6842c8ee3be4
Removing intermediate container 7895f7170efc
Step 5 : RUN cd /usr/src/app && chmod u+x countdown_bot.sh
 ---> Running in b14e152beca3
 ---> b581ef596c71
Removing intermediate container b14e152beca3
Step 6 : CMD /usr/src/app/countdown_bot.sh
 ---> Running in 28768d88b292
 ---> 40c6fe50a43f
Removing intermediate container 28768d88b292
Successfully built 40c6fe50a43f

成功です。Twitter BOT アプリケーションファイルをコピーして、bundle install もうまく行っていますね。docker images で確認すると、きちんとDocker イメージが生成されていました。

続いて、 Docker イメージから、Docker コンテナの作成 + 起動を行います。

# docker run --name countdown_bot countdown

エラーなく終了しました。ここで Twitter を見てみると、無事につぶやきが投稿されていました。OKです。カウントダウン Twitter BOT の Docker コンテナができました。

cron で定期的に実行させる

ここまできたら、後は、カウントダウン Twitter BOT コンテナを、定期的に自動実行できれば良い訳です。とりあえず、今回は、cron に仕込んで実行させることにします。

# crontab -e

0 9 * * * docker start countdown_bot

私の生活リズム都合で、毎朝 9時につぶやいてもらうように設定しました。

その後追記

と言う訳で、翌日の朝9時になりました。が、その時間になっても一向につぶやきませんでした。むむむ。

どこか不具合があったかぁ、、と考えたら、はっと問題点に気づきました。私がセットしたのは確かに 9:00AM ですが、それは日本時間ではありません。そうです Scaleway サーバは Paris にあるのです。早速サーバにログインして、タイムゾーンを確認すると UTC でした。なるほど。

ということで対策を行います。今回は、せっかくUTCで動いているので、タイムゾーンはそのままで、Twitter BOT アプリケーションの起動時間を -9時間するように cron を設定し直しました。

# crontab -e

3 0 * * * docker start countdown_bot

なお、ジャスト0時に起動するのもなんなので*1、0時3分に起動するようにして完了です。翌日 9時3分に無事につぶやきを確認してとりあえずおしまいデス。

まとめ

  • Scaleway の VC1s + Docker で Ruby アプリケーションが割と簡単に動かせる
  • 海外のサーバは JST じゃない(当然ですが)ので気をつけろ
  • 毎朝 9時3分にこのつぶやきを見ながら、その日に向かってしっかり頑張ろう!!

*1:経験的にこのような境界値に思わぬ問題が潜んでいることが多いので、今回は簡単に少しだけずらしてリスクを回避します

Ruby で カウントダウン Twitter BOT 作って Scaleway の Docker で動かす- 前半(ただしWIP)

大事な日までのカウントダウンを毎日 tweet してくれる BOTRuby で作成して Scaleway サーバで動かそうと思います。こんな感じでつぶやきます。

モチベーション

どうしてそんなものを作る気になったのか?とか「大事な日」とは何の日なの?とかは、いろいろあって、ここでは公にできないのですが、とりあえず、その日までの過ごし方が重要なので「その日に向かって日々頑張ろう!!」的な気持ちを継続するために、本 BOT に毎日つぶやいてもらいます。

また Scaleway とはフランスの格安サーバ会社で、ベアメタルなサーバが 日本円で 400円/月くらいからレンタルできるのが特徴です。2年ほど前に kawasaki.rb の LT で教えてもらって気になっていたやつです。

今回はサーバ上の Docker コンテナにアプリを入れて動かすつもりです。

www.scaleway.com

Twitter Bot の作成

Ruby では twitter という Gem があって、これを利用すればあっさりと実現できます。以下、簡単に実装の流れを説明します。

1)Twitter API 利用するアプリケーション登録

Twitter に投稿するためには Twitter API を利用する必要がありますが、そのためには、アプリケーション登録をする必要があります。ということで、さっそく Twitter developers へログインしてアプリケーション情報を登録します。

Welcome — Twitter Developers

注意点として、今回はTwitter 投稿するので、Access Level を “Read and write” にしておく必要があります。

アプリの情報が登録できたら、アプリが利用する Twitter API の認証と認可に必要な Consumer の情報と Access Token の情報を取得できます。
具体的には、下記の4つの情報をコピーします。

  1. Consumer Key (API Key)
  2. Consumer Secret (API Secret)
  3. Access Token
  4. Access Token Secret

2)Twitter Bot のコーディング

twitter Gem を使用した Twitter 投稿処理は、次のコードが基本となります。

require 'twitter'

client = Twitter::REST::Client.new(
    consumer_key:        'YOUR-CONSUMER-KEY',
    consumer_secret:     'YOUR-CONSUMER-SECRET',
    access_token:        'YOUR-ACCESS-TOKEN',
    access_token_secret: 'YOUR-ACCESS_TOKEN_SECRET'
)
client.update 'Some Tweet Message ...'

consumer_key、consumer_secret、access_token、access_token_secret の各値は、Twitter developers で登録したアプリケーションの情報をセットしてください。

私の場合は、大事な日までのカウントダウンを呟いてもらわなくてはなりませんので、その辺りも含めて(現在)以下のコードになってます。

require 'twitter'
require 'date'
require 'yaml'

# SayTwitter
class Tweeter
  def initialize(config)
    @config = config
  end

  def say(message)
    client = Twitter::REST::Client.new(
      consumer_key:     @config['tweet']['consumer_key'],
      consumer_secret:  @config['tweet']['consumer_secret'],
      access_token:     @config['tweet']['access_token'],
      access_token_secret: @config['tweet']['access_token_secret']
    )
    client.update message
  end
end

# Countdown Class
class CountDown
  def initialize(config)
    @config = config
  end

  def post
    the_day = Date.new(@config['theday']['year'],
                       @config['theday']['month'],
                       @config['theday']['day'])
    boring_days = (the_day - Date.today).to_i

    # Say to Twitter
    message = "Hi! just #{boring_days} days left until the Special Day."
    yield message
  end
end

# Load CONFIG FILE
CONFIG_FILE = 'countdown_bot.yml'.freeze
config = YAML.load_file(CONFIG_FILE)

countdown = CountDown.new(config)
tweet = Tweeter.new(config)

# Tweet Count Down Message
countdown.post do |message|
  tweet.say message
end

consumer_key、consumer_secret、access_token、access_token_secret などの情報は、外部 YAML ファイルで定義するようにしています。コード全体は、github のページ を参照してください。

Scaleway サーバの作成

アプリは作成できたので、これを動作させるサーバを Scaleway 上に準備します。前提として、今回も 400円/月 の範囲で活用したいと思います。

1)C1 サーバ + docker イメージで試してみる

まずは、Scaleway の C1 という ARM CORE のベアメタルサーバで一番チープな構成(400円/月)で試してみます。

f:id:ngzm:20170426183844p:plain

サーバで稼働するOSイメージは、ubuntu などメジャーなデストリビューションから選択できます。

f:id:ngzm:20170426183909p:plain

また、あらかじめミドルウェアまで入ったOSイメージも用意されています。こうなると PaaS みたいです。

f:id:ngzm:20170426183913p:plain

今回は、Docker コンテナでアプリを動かすので、OSのイメージは Dokcer を選択してオーケストレーションします。こうして、しばらくするとマシンが立ち上がってきました。起動できたので、次は Docker コンテナを作成しますが、とりあえず Docker -v でバージョンを確認すると Version 1.12 でした。現在の最新版が 1.17 系なので、ちょっと古いですね。

続いて、ruby:latest イメージを pull して、docker run してみます。するとナゾの go-lang エラーがばんばん出ました。

# docker run --rm -it ubuntu:trusty

panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
    panic: standard_init_linux.go:175: exec user process caused "exec format error"

goroutine 1 [running, locked to thread]:
panic(0x3339c8, 0x10747800)
    /usr/local/go/src/runtime/panic.go:481 +0x330
github.com/urfave/cli.HandleAction.func1(0x107c5958)
    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x328
...
... 以下省略
...

よく見ると、Docker コンテナのフォーマットエラーとか何とか書いてあります。とにかくうまく動きません。

2)C1 サーバ + ubuntu イメージで試してみる

どうもダメなので、一旦サーバを破棄して、C1 サーバ + ubuntu xenial (16.04 LTS) イメージ でサーバを作って、そこに Docker を入れて試してみます。無事に Docker のインストールまで完了したので、 そのバージョンを確認すると Version 1.17 最新版です。

まずは、Docker の動作確認用コンテナ “Hello World” で動作確認してみます、すると、さっきと同様に go-lang のエラーが発生!それでも、あきらめずに、ruby:latest イメージで docker run してみますと、やっぱりエラーとなりうまく動きませんでした。

3)C1 サーバ の問題

どうしてもダメなので、C1 サーバのドキュメントをちゃんとみて見ると、その CPU は「ARM v7」であると書いてありました。

f:id:ngzm:20170426183855p:plain

「ARM v7」って?さらに調べたところ、どうやら 32bit CPU らしき情報を発見!なるほど、32 bit なら docker は動かないですね。この事実が分かるまで2〜3時間かかってしまいました。

せっかくのベアメタルサーバなのに、これでは目的が達成できなません。ちなみに、これより上位の構成(当然お金も高くなる)であれば、64bit のCPUを採用しているので、あとは金で解決するか・・いや止めよう

4)VC1s サーバ + docker イメージで試してみる

Scaleway はベアメタルなサーバ以外にも、一般的な仮装サーバの提供も行なっており、それであれば、約400円/月 かつ 64bit の環境が使えます。

f:id:ngzm:20170426183905p:plain

ということで、ベアメタルサーバは諦め、仮装サーバ VC1s での実現を目指します。OSイメージはとりあえず Docker で行きます。

と、無事にマシンが起動できました。そこで、先ほどと同様 “Hello World” コンテナで動作確認をしてみると無事に成功しました。今度は大丈夫そうです。 ということで、ruby:latest イメージを pull して docker run で Ruby コンテナを実行させると、きちんと irb が起動しました。

# docker run --rm -it ruby:latest
irb(main):001:0>
irb(main):002:0* puts "Hello!!"
Hello!!
=> nil

とりあえず、現在はここまでの状況です。明日以降で Ruby で作成した Twitter BOT を Scaleway のVC1s サーバ + Docker に乗せて動かす作業を行います。

ここまでのまとめ

  • twitter Gem を使うと簡単に RubyTwitter API 使えそう
  • Scaleway サーバで Docker を動かすなら C1 サーバは使えない
  • 大事な日まで心折れずにがんばろう

後半につづく

2017/4/30 - 後半の記事を書きましたのでお知らせいたします。

ngzm.hateblo.jp

macOS Sierra にしたら photoanalysisd というプロセスでホットになった

Sierra にしてしばらく遊んでたら、どうも常時CPU使用率が7割以上回っている様子で、MAC BOOK がホットになったので、どのプロセスが原因なのか見てみました。

どうも photoanalysisd というプロセスですね。

検索してみると、なるほど 「photoanalysisd 暴走」 というタイトルの記事がたくさんありました。どうやら、一時中断もできるが、しばらく放っておくとそのうちおさまるとの噂です。ならばと、そのまま数日間放置していたら、なんとなく静かになりました。

と安心したのも束の間、続いて mediaanalysisd という第二の刺客が現れ、こいつは4割くらい CPU を占有するようになりました。私はあまり動画とか保存していなかったのもあってか、mediaanalysisd の暴走は数時間で解消しました。

ということで今ここ。次は 何analysisd が暴走するのだろうか?

macOS Sierra に Karabiner-Elements 入れて「英数」キーを「Command」にして、単独で押した時は「英数」として認識させた話

2017/07/08 追記

本エントリは 2016年12月現在の情報です。それから半年+α 経って、ついに本家の Karabiner-Elements で同機能がサポートされました。その詳細を、次のエントリに記載しましたので、今後はこちらを参考にしてください。

ngzm.hateblo.jp

以下、当時の情報です。

はじめに

タイトル長い。。

MAC OS Sierra がリリースされてはや3ヶ月。"そろそろアップデートしないとなー"と思いつつも、これまで Karabiner にお世話になっていたこともありなかなかバージョンアップに踏み切れずいたのですが、、先日、改めて調べてみると Karabiner-Elements でいけそうな気配を感じたので、思い切って Sierra にアップデートしました!!という、その時の話です。

すなわち、MAC OS Sierra にアップデートした後、Karabiner-Elements でタイトルのようなことをゴニョゴニョしたときのメモです。

これまで Karabiner にお世話になっていたこと

自宅のMACでは日本語キーボードを使っています。なので、別に Karabiner 使わなくても何とかなるのですが、日本語キーボードは英語キーボードに比べ、左「Command」キーの位置が左に寄り過ぎていて気に食わない、そこで、できれば「英数」キーを左「Command」キー的に利用したい。さらに単独で「英数」キーや左「Command」キーを押した時は「英数」として認識してほしいというのが私の願望でして、その願望をうまいこと叶えてくれていたのが Karabiner でございました。

また、会社では英語キーボードを使用しており、単独で左「Command」キーを押した時は「英数」として、右「Command」の時は「かな」として認識してもらわないと、なおのこと日本語切り替えに困惑する訳でありまして、やっぱし karabiner の導入は必須となってしまうのです。

今回導入した Karabiner-Elements について

「英数」キーを左「Command」キー的に利用したいというのは、本家の Karabiner-elements でも問題なく実現できるのですが、単独で「英数」キーや左「Command」キーを押した時は「英数」として認識させるということはできませんでした。

ここで諦める訳にはいかないので、さらに調査を進めてみると、wwwjfy さんが単独で何かのキーを押した場合だけ、特別な認識をさせるという、いわゆる「StandAlone Key」という機能を開発されていて、これをマージすればよさげなことが分かりました。さっそくマージだ!とビルド環境を整備していた矢先、なんと wwwjfy さんご自身でマージ版のインストールパッケージご提供されておりましたよ。至れり尽くせり、ありがたや。

github.com

ということで、「StandAlone Key」がマージされた Karabiner-Elements を Sierra にインストールしました。

Karabiner-Elements の設定内容

無事にインストールが完了したら、まず最初にキーボードの種類をきちんと設定します。現在 Karabiner-Elements の “Keybord Type” のデフォルトが、英語配列になってますので、日本語配列のキーボードを利用する場合は、"Keybord Type" を “JIS” に変更する必要があります。以下のように Karabiner-Elements の設定画面上で設定します。

f:id:ngzm:20161222003352p:plain

続いてキースワップを設定します。今回は「英数」キーを左「Command」として認識するように設定します。こちらも Karabiner-Elements の設定画面上から行います。

f:id:ngzm:20161222003357p:plain

そしていよいよ StandAlone Key の設定です。すなわち、単独で「英数」キーや左「Command」キーを押した時は「英数」として認識させます。これについては、Karabiner−Elements の設定画面で設定できないので、定義ファイルを直接編集して行います。定義ファイルは ~/.karabiner.d/configuration/karabiner.json *1です。これに以下の定義を追加します。

   "standalone_keys" : {
       "left_command" : "japanese_eisuu"
    }

一部だけだとよく分からないので、ここまでの設定が反映された karabiner.json 全体を以下に示します。

{
    "profiles": [
        {
            "devices": [
                {
                    "disable_built_in_keyboard_if_exists": false,
                    "identifiers": {
                        "is_keyboard": true,
                        "is_pointing_device": false,
                        "product_id": 599,
                        "vendor_id": 1452
                    },
                    "ignore": false,
                    "keyboard_type": 42
                },
                {
                    "disable_built_in_keyboard_if_exists": false,
                    "identifiers": {
                        "is_keyboard": true,
                        "is_pointing_device": false,
                        "product_id": 627,
                        "vendor_id": 1452
                    },
                    "ignore": false,
                    "keyboard_type": 42
                }
            ],
            "fn_function_keys": {
                "f1": "vk_consumer_brightness_down",
                "f10": "mute",
                "f11": "volume_down",
                "f12": "volume_up",
                "f2": "vk_consumer_brightness_up",
                "f3": "vk_mission_control",
                "f4": "vk_launchpad",
                "f5": "vk_consumer_illumination_down",
                "f6": "vk_consumer_illumination_up",
                "f7": "vk_consumer_previous",
                "f8": "vk_consumer_play",
                "f9": "vk_consumer_next"
            },
            "name": "Default profile",
            "selected": true,
            "simple_modifications": {
                "japanese_eisuu": "left_command"
            },
            "standalone_keys" : {
              "left_command" : "japanese_eisuu"
            }
        }
    ]
}

一番下にある定義が、StandAlone Key の設定ですね。。

さて、これでOKかテストして、うまくいきましたよ。今回は、「英数」と左「command」キーだけでやって見ましたが、「かな」と右「command」でも同様に設定可能です。

また、英語キーボードの場合でも、この StandAlone Key を使えば「Commnd」キー単独の時は「かな」や「英数」となるように設定できるので便利かと思います。

ご注意

このエントリは2016年12月20日時点のことなので、将来は状況が変わっているためご注意ください。

なお、wwwjfy さんの StandAlone Key 機能は、現在のところ、本家の Karabiner−Elements に Pull Request が上がっております。こちらがマージされれば、本家の正式版でも同様の設定が可能になるはずです。楽しみですね。

*1: 2017年4月現在では、~/.config/karabiner/karabiner.json に変更されています

Homebrew で nodebrew をインストールしたら少々はまった

MAC OS 10.11 の MAC BOOK PRO にて。

タイトルどおりはまったのでメモ。はまった内容ですが、普通に Homebrew から nodebrew をインストールして、

$ brew install nodebrew

PATH を追加しろとか言ってくるので、その通りに PATH を設定して、さぁ stable版 node を入れようとしたら、次のエラーに遭遇したのでした。

$ nodebrew install stable

Fetching: https://nodejs.org/dist/v6.6.0/node-v6.6.0.tar.gz
Warning: Failed to create the file
Warning: /Users/naoki/.nodebrew/src/v6.6.0/node-v6.6.0.tar.gz: No such file or
Warning: directory

curl: (23) Failed writing body (0 != 940)
download failed: https://nodejs.org/dist/v6.6.0/node-v6.6.0.tar.gz

ちょっと調べてみると、どうやら同じエラーに遭遇した先輩がたくさんいる模様。原因は、 $HOME/.nodebrew が作成されていなかったから? ということで、それ作成してリトライ。

$ mkdir ~/.nodebrew
$ nodebrew install stable

Fetching: https://nodejs.org/dist/v6.6.0/node-v6.6.0.tar.gz
Warning: Failed to create the file
Warning: /Users/naoki/.nodebrew/src/v6.6.0/node-v6.6.0.tar.gz: No such file or
Warning: directory

curl: (23) Failed writing body (0 != 940)
download failed: https://nodejs.org/dist/v6.6.0/node-v6.6.0.tar.gz

あらっ?またか。
どうやら $HOME/.nodebrew/src も必要なようです。でも、なんだかこのまま行くと、「あれも」「これも」という感じで、次々に同様の作業を繰り返す気がします。なのでさらに調査してみます。。

すると、今回の正解らしき情報が得られました。それは、わざわざ手でこれらのディレクトリを作るのではなく、コマンド nodebrew setup をたたくということです。

$ nodebrew setup

Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================

これで nodebrew の実行に必要な環境がセットアップできたはずです。

$ ls ~/.nodebrew

completions/ current@     default/     iojs/        node/        nodebrew*    src/

よさそうですね。再度、node 安定版をインストールしてみます。

$ nodebrew install stable

Fetching: https://nodejs.org/dist/v6.6.0/node-v6.6.0.tar.gz
######################################################################## 100.0%
creating  ./icu_config.gypi

... (省略)
...

$ nodebrew use v6.6.0
use v6.6.0

$ nodebrew ls
v6.6.0

current: v6.6.0

うまくいきました。done。

【補足】nodebrew は、環境変数 NODEBREW_ROOT で node のインストール先を変更できますが、このときも nodebrew setup を実行すると一発で必要な環境が作成されるので非常に便利。ぜひ覚えておきましょう。