タグ: Ruby

プライベートGyazoサービスの構築

スクリーンショットをサーバにアップロードしてURLで共有できるGyazoというサービスがあります。
このOpen Source ClientのコードがGitHubにあるので、これでプライベートGyazoサービスを構築します。

今回の構成は

  • クライアント
    • Windows 10
    • gyazo/Gyazowinをもとにアップロード先URLを変更したものを作成
  • サーバー
    • Ubuntu 14.04
    • gyazo/GyazoのServerにあるRubyスクリプトを使う
    • Gyazoサーバプログラムはインストールから動作までDocker内で行う
    • アップロードされた画像、プログラム内部で利用するデータベースのような動的生成されるデータはdockerのマウント機能を使ってサーバ上に保存する

という形にしました。

サーバー側

必要なもの

  • Docker (記事執筆時に利用したのは1.7.1)
  • サービス用のドメイン

ドメインは http://~ でアクセスできるようにDNSで関連付けておいてください。

今回はgyazo.example.jpとしました。

  1. Gyazo用のディレクトリを作成
    mkdir gyazo
    cd gyazo
    
  2. Dockerfileを作成

    11行目の[SERVERDOMAIN]はサービス用のドメインに置き換えてください。

    例)

    RUN sed -i -e "s/gyazo.com/gyazo.example.jp\/data/" /usr/local/apache2/htdocs/upload.cgi
    
  3. イメージを作成

    docker buildでイメージを作成します。イメージにgyazoという名前を付けます。

    docker build --rm -t gyazo .
    
  4. gyazoイメージからgyazoという名前でコンテナを起動する
    docker run -d -p 80:80 --name gyazo -v `pwd`/data:/usr/local/apache2/htdocs/data -v `pwd`/db:/usr/local/apache2/htdocs/db gyazo
    
  5. http://gyazo.example.jp にアクセスして動作を確認

    「It’s works」がでればOK。

    Itsworks

クライアント側

必要なもの

  • Visual Studio 2015 Community (本記事ではインストール時に既定でインストールしています)
  1. ソースコードの取得

    gyazo/Gyazowinからソースコードをクローン、またはダウンロードします。今回はZIPでダウンロードしました。

  2. ZIPを展開して中にある、「gyazowin.sln」をダブルクリックで開く

    gyazowin.slnはVisual Studio 2015よりも古いフォーマットのファイルのため、アップグレード警告が表示されます。そのままアップグレードします。
    またVC++の開発環境がない場合、ここで追加インストールのウィザードが始まるのでインストールしたのち、gyazowin.slnを開きなおします。

    Upgrade_1Upgrade_2Upgrade_3

    InstallVC_2
    InstallVC_3

  3. コードの編集

    gyazowinはスクリーンショット画像をgyazoに送信する機能を持っていますが、送信先はソースコードにハードコーティングされているため、そこを今回用意したサーバへ変更します。

    gyazowin.cppの794行目のupload.gyazo.comgyazo.example.jpに変更します。

    Diff_gyazowin

  4. ビルドの実行

    1. メニュー「ビルド」の下にあるソリューション構成を「Debug」から「Release」に変更する
      Build Settings
    2. メニュー「ビルド」→「ソリューションのビルド」でビルドを実行

    正常に終了すれば、Releaseディレクトリが作成され、中にgyazowin.exeが生成されます。

  5. 実行確認

    1. gyazowin.exeを実行する
    2. カーソルが十字になる
    3. スクリーンショットを撮りたい範囲の左上をクリックしたまま、右下までドラッグ
    4. 指を話すと、スクリーンショットがgyazo.example.jpにアップロードされたのち、アップロード先URLで既定のブラウザが起動される。
      Gyazowinフォルダのエクスプローラをスクリーンショットしたもの
    5. サーバのdockerを起動したディレクトリの直下にdataディレクトリが作成され、そこにアップロードされた画像が配置する
      server_upload_images

備考

参考

Windowsでberks(Berkshelf)コマンドを実行した際、「”\xA0″ on Windows-31J (Encoding::InvalidByteSequenceError)」エラーが出るとき

現象

Windows上で、ChefDKをインストールし、Berksfileを作成し、

berks vendor cookbooks

のようにしたところ

> berks vendor cookbooks
Resolving cookbook dependencies...
Fetching cookbook index from https://supermarket.chef.io...
C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/json-1.8.3/lib/json/common.rb:155:in `encode': "\xA0" on Windows-31J (Encoding::InvalidByteSequenceError)
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/json-1.8.3/lib/json/common.rb:155:in `initialize'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/json-1.8.3/lib/json/common.rb:155:in `new'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/json-1.8.3/lib/json/common.rb:155:in `parse'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/ridley-4.2.0/lib/ridley/chef/cookbook/metadata.rb:473:in `from_json'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/ridley-4.2.0/lib/ridley/chef/cookbook/metadata.rb:29:in `from_json'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/ridley-4.2.0/lib/ridley/chef/cookbook.rb:36:in `from_path'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cached_cookbook.rb:15:in `from_store_path'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cookbook_store.rb:108:in `block in cookbooks'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cookbook_store.rb:98:in `collect'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cookbook_store.rb:98:in `cookbooks'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/resolver/graph.rb:8:in `populate_store'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/resolver.rb:73:in `resolve'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/installer.rb:175:in `install_from_universe'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/installer.rb:39:in `run'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/berksfile.rb:374:in `install'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/berksfile.rb:583:in `block in vendor'
        from C:/opscode/chefdk/embedded/lib/ruby/2.1.0/tmpdir.rb:88:in `mktmpdir'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/berksfile.rb:581:in `vendor'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cli.rb:387:in `vendor'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cli.rb:52:in `dispatch'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
        from C:/opscode/chefdk/embedded/apps/berkshelf/lib/berkshelf/cli.rb:27:in `execute!'
        from C:/opscode/chefdk/embedded/apps/berkshelf/bin/berks:5:in `<top (required)>'
        from C:/opscode/chefdk/bin/berks:43:in `load'
        from C:/opscode/chefdk/bin/berks:43:in `<main>'

というエラーが発生

対策

「C:\opscode\chefdk\bin\berks」 を開き、3行目に

Encoding.default_external = 'utf-8'

を追加する。

参考

DBが存在しないときにDBを作成するChefレシピを作ったときの試行錯誤メモ

開発サーバ用のcookbookを作る際に、MySQLのDBが存在しないときにDBを作成したかったときの試行錯誤メモ

opscode-cookbooks/wordpressのDB作成部分を参考にというかほぼそのまま流用

使用したboxは

http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-i386-v20130731.box

やりたいこと

  1. MySQLのインストールと起動
  2. DBユーザの作成
  3. DBの作成
  4. テーブルの作成

1.MySQLのインストールと起動

package "mysql-server" do
    action :install
end

service "mysqld" do
    action [ :enable, :start ]
    supports :status => true, :restart => true, :reload => true
end

2.DBユーザの作成

GRANT文をまとめたgrants.sql.erbをtemplatesに格納しておき、/tmpにUPの後、mysqlコマンドで流し込む。

grants.sql.erbの中身

GRANT ALL ON <%= @database %>.* TO '<%= @user %>'@'localhost' IDENTIFIED BY '<%= @password %>';
GRANT ALL ON <%= @database %>.* TO '<%= @user %>'@'%' IDENTIFIED BY '<%= @password %>';
FLUSH PRIVILEGES;

レシピに追加

execute "mysql-create-user" do
    command "/usr/bin/mysql -u root --password=\"#{node['example']['db']['rootpass']}\"  < /tmp/grants.sql"
    action :nothing
end

template "/tmp/grants.sql" do
    owner "root"
    group "root"
    mode "0600"
    variables(
        :user     => node['example']['db']['user'],
        :password => node['example']['db']['pass'],
        :database => node['example']['db']['database']
    )
    notifies :run, "execute[mysql-create-user]", :immediately
end

executeリソースではaction :nothingで実行せず、templateリソースでnotifiesを使ってexecuteリソースを実行させる

3.DBの作成

Mysql::list_dbsを利用して、DBの存在チェックを行い、なければmysqladminコマンドでDBを作成する。

package "mysql-devel" do
    action :install
end.

chef_gem "mysql" do
    action :nothing
    subscribes :install, "package[mysql-devel]", :immediately
end

execute "mysql-create-database" do
    command "/usr/bin/mysqladmin -u root create #{node['example']['db']['database']}"
    not_if do
        require 'rubygems'
        Gem.clear_paths
        require 'mysql'
        m = Mysql.new(node['example']['db']['host'], "root", node['example']['db']['rootpass'])
        m.list_dbs.include?(node['example']['db']['database'])
    end
end

executeリソースのnot_ifにあるrubyプログラム部で、Mysqlライブラリを使うためには、chefを動かしているrubyに対して、インストールする必要がある。
そこで、chef_gemリソースを使う。

またgem install msyqlの際にmysqlのヘッダファイルが必要になるので、mysql-develもインストールする。

しかしそれだけだと、最初のpackage “mysql-devel”よりも先に、chef_gemリソースが実行されてしまうので、

================================================================================
Error executing action `install` on resource 'chef_gem[mysql]'
================================================================================

Gem::Installer::ExtensionBuildError
-----------------------------------
ERROR: Failed to build gem native extension.

というエラーになってしまう。
そこで、subscribesを使って、package mysql-develを実行した後に、即時実行するように指定する。

4.テーブルの作成

2.と同様にCREATE TABLES文をまとめたtables.sqlをfilesに格納しておき、/tmpにUPの後、mysqlコマンドで流し込む。
3.と同様にテーブルがなければ実行するようにしたが、tables.sqlがアップ済みなら実行されないので冗長かもしれない。

execute "mysql-create-tables" do
    command "/usr/bin/mysql -u root #{node['example']['db']['database']} < /tmp/tables.sql"
    action :nothing
    only_if do
        require 'rubygems'
        Gem.clear_paths
        require 'mysql'
        m = Mysql.new(node['example']['db']['host'], "root", node['example']['db']['rootpass'])
        begin
            m.select_db(node['example']['db']['database'])
            m.list_tables.empty?
        rescue Mysql::Error
            return false
        end
    end
end

cookbook_file "/tmp/tables.sql" do
    owner "root"
    group "root"
    mode "0600"
    notifies :run, "execute[mysql-create-tables]", :immediately
end

注釈

  • node[‘example’]~はattributesのdefault.rbで定義した値
    default['example']['db']['user'] = 'username'
    default['example']['db']['pass'] = 'password'
    default['example']['db']['database'] = 'database-name'
    default['example']['db']['host'] = 'localhost'
    default['example']['db']['rootpass'] = ''
    

    exampleのところはクックブック名に置き換える。
    rootpassが何もないのは、インストール後のデフォルトではrootのパスワードはないため

  • grants.sqlやtables.sqlは/tmpではなくてきちんとしたところにUPしたほうがいい
  • 同様のことは、opscode-cookbooks/databaseを使えばできる。

参考になったページ