Google App EngineからさくらのVPSにお引越しするまで

Google App EngineRailsのサービスを乗っけていました。
Google Cloud Platformの無料トライアル期間があったし、gcloud app deployでデプロイできてめっちゃ簡単だな〜って思っていました。
しかし、1ヶ月でAppEngineのFlex環境で27000円とGoogleCloudSQL5000円で合計32000円もしたので引越しすることにしました。
流石に高すぎ。

ここからはさくらのVPSに引越しするまでの手順をかいつまんで備忘録として残します。

mysqldumpしておいてからさくらのvpsを借りる

mysqldump -h $host -u $username -p > output.sql
さくらのvpsを借りる
LetsEncryptのスタートアップスクリプトを走らせる
(ただ、nginxを入れてletsencryptの途中まで成功しているが証明書はこの手順だと取得できていないので注意。正しくやるならドメインの設定を終わらせておいてからだと思う)

rbenv, ruby-build, rubyを入れてソースをgit clone, bundle installまで

yum update
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
cd ~/.rbenv/plugins/ruby-build
./install.sh
yum install -y readline-devel
rbenv install 2.4.2
cd /var/www/app
bitbucketからgit clone
gem install bundler
bundle install --path vendor/bundle

mysql

rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
(参考:https://dev.mysql.com/downloads/repo/yum/
yum -y install mysql-devel
bundle exec rake db:create
mysql -u[ユーザー名] -p [インポートするデータベース名] < [インポートするファイル名]
bundle exec rake db:migrate

puma.rbに下記を追加

_proj_path = "#{File.expand_path("../..", __FILE__)}"
_proj_name = File.basename(_proj_path)
_home = ENV.fetch("HOME") { "your home directory" }

pidfile "#{_home}/tmp/#{_proj_name}.pid"
bind "unix://#{_home}/tmp/#{_proj_name}.sock"
directory _proj_path

nginx.confを設定

http内に下記を追加
upstream puma {
server unix:///your app/tmp/{your app name}.sock;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name {your domain or ip address};
root /var/www/app/{your app name}/public;

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

location / {
try_files $uri $uri/index.html $uri.html @webapp;
}

location @webapp {
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://puma;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

systemctl start nginx
bundle exec pumactl -F config/puma.rb startで動くことを確認

nginx, pumaを常時起動設定

systemctl enable nginx
/etc/systemd/system/puma.serviceを作ってsystemctl enable puma.service

[Unit]
Description=Puma Application Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=var/www/app/{your app name}
Environment=HOME=var/www/app/{your app name}
Environment=RAILS_ENV=production
Environment=SECRET_KEY_BASE={bundle exec rake secretの値をコピペ}
ExecStart=/root/.rbenv/shims/bundle exec puma -C /var/www/app/{your app name}/config/puma.rb
TimeoutSec=300
Restart=always

[Install]
WantedBy=multi-user.target

お名前.comで取得したドメインをさくらのvpsと紐付ける

お名前.comで取得したドメインをさくらのvpsと紐付ける
参考:https://qiita.com/megane42/items/df84f87c0bdcdd015eb6

ttlは60など小さくしとくと反映されるのが早くて良い

https

nginx.confのlistenを修正, ssl_certificate, ssl_certificate_keyを追加

listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/{your domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{your domain}/privkey.pem;

systemctl stop nginx
certbotはstandaloneで入れる
/usr/local/certbot/certbot-auto -n certonly -a standalone -d {your domain}

nginx.confのlistenを修正, ssl_certificate, ssl_certificate_keyを追加

listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/{your domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{your domain}/privkey.pem;

systemctl start nginx
certbot-auto -n certonly --webroot -w {your document directory} -d {your domain} -m {your mail adress} --agree-tos --force-renewal

standaloneで入れてからwebrootに変更すると楽そう
webrootはサーバを止めなくても証明書が入れられるオプション

config/environments/production.rbでconfig.force_ssl = true

初めて独学するプログラミング言語のおすすめはGoogle Apps Scriptである

プログラミングの独学は難しい。
自分もプログラミングの独学について記事を書いたりしたものの、本業のiOS以外の分野での独学は難しく感じているし、下記の記事で出てくる知人はおそらくなんの連絡もないので挫折したんだろうなと思う。
qiita.com

そこで、初めて独学するプログラミング言語Google Apps Scriptをおすすめしたい。
なぜなら自分が作ったサービスが

  • 無料で作ったものを公開できる
  • Google系のサービスとの連携が簡単にできる

からである。

無料というのはちょっと始めてみようかという人には素晴らしい。
誰だって、いきなりお金をかけるのは心理的ハードルが生まれる。

そして、Google系のサービスとの連携が容易なので、普段使っているGmail, スプレッドシート, カレンダーなどを何か便利にしたいなーという欲求が生まれやすいので「何を作ったら良いかわからない」というプログラミング学習におけるよくある問題を回避しやすい。

普段、自分はiOSエンジニアなのでSwiftをおすすめしたいところではあるが、Xcodeのダウンロードは重たいし、アプリを公開するにあたって審査だったり年会費が必要となり煩わしい。
また、他にWebサービスを作る言語はあるが、サーバをどうするかだったりを考えると色々と煩わしい。

もちろんGoogle Apps Scriptは複雑なことをするにはエディタや文法がいけてないという面はあるけれど、ちょっとしたツールを作るにはとても便利である。

自分が今まで作ってきたサービス

qiita.com
qiita.com

最後に

ということで、「プログラミングを始めてみたい!」という方はいますぐGoogle Driveを開いて
新規 -> その他 -> Google Apps Script
をクリックしよう。
困ったら「Google Apps Script 入門」とかでググったり、ドットインストールで勉強すると良いと思う。

蛇足

ちなみにこの記事は音声入力で何か書いてみたくて書いた記事である。
けんすうさんや勝間和代さんが音声入力でブログを書いていると耳にしたためである。
要旨をあらかじめ決めて思うままに話してみたところ、圧倒的に書くのは早い。修正もそんなに大変ではないし。
ただ、修正を行っているうちに付け加えた部分も多く(音声入力500字、後から800文字くらい書いた)、音声入力一発でブログを書くのはそれなりに慣れが必要な気はする。
あくまで、音声入力はブログなどの文章を書く心理的ハードルを下げ、大体の部分は書けるというくらいに思っておいた方が良い。

Amazonの注文確認のgmailから、googleカレンダーにその受け取り時刻の予定を自動で入れる

Google Apps Scriptデビューしました。
未読のAmazonからの注文確認メールを2時間おきに確認して、Googleカレンダーにその受け取り時刻の予定を自動で入れています。
2時間おきというのは任意で変更できて、Google Apps Scriptエディタの時計のアイコンからトリガーを設定させればできます。
便利。

コメントは意図や出力例、つまずいたところを書きました。
よろしくお願いいたします。

function createAmazonEvent() {
  var criteria = "is:unread from:(Amazon.co.jp) ご注文の確認";
  var dateExp = /\d{2}\/\d{2}\s\d{2}:\d{2}/g; // ex. 06/15 16:00
  var dateExpWithSubString = /(\d{2})\/(\d{2})\s(\d{2}):(\d{2})/; // ()をつけることで部分文字列として返却される
  
  GmailApp.search(criteria).forEach(function(thread) {
    var messages = thread.getMessages();
    messages.forEach(function(message) {
      var body = message.getPlainBody(); //getBodyだとHTMLメールのため
      
      if (!(body.match(/お届け予定/))) {
        // Kindleの注文を除くため
        return;
      }
      
      var year = message.getDate().getFullYear();
      var myArray = body.match(dateExp); // ex. [06/15 16:00, 06/15 18:00]
      var [sMatched, sMonth, sDay, sHour, sMin] = myArray[0].match(dateExpWithSubString);
      var [eMatched, eMonth, eDay, eHour, eMin] = myArray[1].match(dateExpWithSubString);
      var startDate = new Date(year, sMonth - 1, sDay, sHour, sMin);
      var endDate = new Date(year, eMonth - 1, eDay, eHour, eMin);
      CalendarApp.getDefaultCalendar().createEvent("Amazon荷物", startDate, endDate);
      message.markRead();
    });
  });
}

参考記事:
GASでDMM英会話の予約メールから自動的にカレンダーに登録 | shotarok's Tech Blog
正規表現 - JavaScript | MDN
Calendar Service  |  Apps Script  |  Google Developers

英語が9割わからないで海外のハッカソンに出るとどうなるか(後編)

1日目午後

アイディアを決める会議をしていたのですが、何を作るかということを僕が理解したのは夕方でした。スプレッドシートに書かれている仕様を見てわかりました。
本当に聞き取れないな、と一人で苦笑していました。
ちなみに最初に一言だけ話してから何も話していないので話すこともダメダメでしたね。

そんな中、おそらく小学生か中学生くらいの子に話しかけられました。
余談ですが、結構小さい子もいっぱい参加していて、幼い頃からこういう場に来ている姿勢というのは本場すごい、って素直に感じました。
で、話しかけられた内容についてですが、午前中の僕のプレゼンでiOSエンジニアだと覚えてくれていたのでしょう。クラッシュしたけど理由を教えて欲しいというものでした。
幸い自分も経験したことのある、設定項目の書き忘れという簡単なものですぐに直してあげました。
"Thank you."と言われた時に「このハッカソンでやっと役に立てた」と涙が出そうなくらい嬉しかったのを覚えています。

***

AWS Rekognitionという画像認識のサービスを使って、眠っているのか起きているのかを判別して、眠そうならホテルをサジェストするというアプリになりました。
このAWSのサービスはiOSでも提供されていたので、サーバサイドを実装しなくて良い……
あれ? 画像認識の部分も画面も作るし実際コード書くの僕だけじゃない?
って心の中で笑ってました。
Mくん(機械学習エンジニア)には"You are MVP."って言われましたけど
ほんとそうだなって思ってました。
まあ、Mくんはどれくらい眠たいかを判別する計算式を顔の画像から学習させようとしていたし、Dさん(デザイナー)はちょっとiOSかけるので画面を良くしてくれてました。

で、Lくんは?
仕様の説明をしてくれた後に「帰る」って言い出したんですよね。PM9時です。
え? ハッカソンって24時間耐久でサービス作るんじゃないの?
って驚きました。でもみんな帰り出したし、他のチームでも帰ってる人がいたのでこういうハッカソンだったのかもしれません。
あ、でもAM1時にピザが出てたらしいので残っているチームは残っていたと思います。
僕は自分だけ頑張るのもアホらしくて、僕も帰りました。

2日目

LくんはAM11時(締め切り2時間前)というふざけた時刻に来ると約束していて、あーもうそうですかって思いながらも、僕はAM6時くらいから4時間で残りの作業を頑張って終わらせました。
MくんやDさんは来て作業してくれてたのでありがたいって思ってました。

で、Lくんですか? まず、11時に来てません。
もう知らんって思いながら、僕の方は新しいAPIの存在を2チームに分かれた方のもう1つのチームから説明されました(どういうことだ? って思うでしょう? 僕もです)
時間もないし、実装した人こっちのチームだったの?? って疑問が浮かびつつも実装したり、Dさんのgitのトラブルを解消したりしてたらタイムアップ。

で、Lくんですか? 後で発覚したのですがもう1つのチームの方でプレゼンに出ることになっていました。
お昼くらいにいつのまにか来てて、おそらく2チームで作ったサービスの出来栄えの良い方をお前選んだろ?って推測してます。
確かにそっちは、卓球とかをしている時にAlexaに話しかけると得点をカウントしてくれるサービスでキャッチーでした。Alexaの部門賞取ってましたし。
でもなめてますね。一周回ってあっぱれです。

***

結局Mくんがプレゼンすることになって、でもとても滑らかにプレゼンしたり質問に答えていたので彼はすごかったです。
おかげでトップ10に入れましたし(30チームくらい参加してました)
まあ、滑らかに話す彼と棒立ちで眠そうな顔と起きてる顔を無言で撮る僕のギャップが面白かったのかなって思ってます。

プレゼンが終わってMくんとDさんと3人でハイタッチして「僕のチームは3人チームだったんだな」って思いつつ僕の初めてのハッカソンは終了しました。

終わりに

じゃあどうするべきだったのか? と考えてみると

  1. 自分の直感で怪しそうだと思う人についていかない
  2. チームメンバーが各自ある程度役割がはっきりしていること
  3. 英語を頑張る
  4. 自分の作りたいものと作る予定のものが近いか

あたりが大事なのかと思いました。

つらかったんですけど、こんなに過酷なハッカソンでなんとかできたので、少し自信がつきました。
それは良い経験だったと思います。

英語が9割わからないで海外のハッカソンに出るとどうなるか(前編)

結論:つらい

注意:読み返してみると思ったより愚痴っぽくなってるので、苦手な方は読まれない方が良いかと思います

経緯

サンフランシスコに来たんだ、せっかくなら本場のハッカソンに出てみようか!
と思ってAngelHackというハッカソンに申し込みました。

angelhack.com

世界各地、日本でも開催されている大規模なハッカソンのようです。不安に思いつつも参加前は初めてのハッカソンを楽しみにしていました。
ハッカソンの2日間で思ったこと、起きたことを全て書いていくと長くなるので、ハイライトを書いていきたいと思います。

0日目

プリハッカソンという名目の集まりが開催されていて、チームメンバーを事前に見つけようという目的で参加しました。
日本だったとしても初対面の人に話しかけるのは勇気がいるのに、英語で話すということでとても緊張していました。
その中で話した1人目はデザイナーの方でした。
自己紹介の後に「何か作りたいものはあるの?」と聞いて話してもらったのですが、
「ふんふん、クラウドソーシング? 引っ越し? SMS?」
みたいな断片的な単語しかわからず、会話も全然弾まずやばいなって思いました。
そのあとも何人かと話したものの何を言っているかほぼわからず、後半は話しかけるなオーラを出しつつ早めに帰りました。
何しに来たんだろ、と悲しい気持ちで帰ったのを覚えています。

1日目午前中

チームメンバーが見つかっていない、頑張って探そう!
と朝食の時間に営業活動をしていました。

その中でお話しした50~60歳くらいの優しそうな老夫婦の方達が印象的でした。
「40年コードを書いてて、ハッカソン・ものを作ることが好きなんだ」とおっしゃってて素敵だなと感じました。
チームに入れてくれる? と頼むも「今回作る予定のものはiOSが関わらない、でも君のことは覚えておく、他の人にも紹介してあげるよ」と残念でしたが優しい対応をしてもらいました。
ちなみにこの方はSparkpostという今回のハッカソンで提供されているAPIを使って、ちゃんとSparkpostの部門賞を取ってて、ハッカソンの勝ち方を知っているのだなというのも印象的でした。

***

いよいよ開催の時間になって運営の方が話し始めます。
"Hey, San Franciscoooo!!!!"
みたいに参加者を煽るところがライブみたいでした。なんでしょう、ハッカソンに限らずこういうノリをよく感じたのでアメリカの文化なのかもしれません。

で、賞金や賞品、ルール、スケジュールなどを話し終わった後にチームメンバーを見つけるためのプレゼンをしたい人は前に来てくれという流れになります。
僕は先ほどの老夫婦の奥様に紹介してもらった、ハッカソン常連の方が最前列に座った影響で隣に座ってたんですね。
彼に肩を叩かれて最初に話すことになってしまい、「嫌だ、やめろ」って思いつつも「ここで話さないとチームメンバーは得られない」とも感じていました。
なので、自己紹介をした後に
"I have no idea, but i wanna make a team. So please call me!"
って言いました。我ながらひどいプレゼンですがなんかウケてました。

***

その後早速誘ってもらってアイディアを決めるために、地下の説明会場から上がりました。メンバーの数を確認すると
"one, two, …… ei, って8人いるけど!?"
チームメンバーは5人までです。なんか怪しいなって思いましたが、その時はチームに入れた喜びと英語ができないプレッシャーでその場に居続けました。
最終的に2チームに分かれて、僕のチームは

  • リーダー。ここではLくんとします。議論を進め、タスク管理、仕様決め、プレゼンを担当
  • 機械学習エンジニア。ここではMくんとします。
  • デザイナー。ここではDさんとします。

になったんですけどね。なったんですけどね……(チームの愚痴は後述します、後編に続く)

飲食店で最低限聞き取れれば良い英語

話すことに関してはメニューを指差していたり、片言で"One, here."とか言ってても通じます。
しかし、聞き取れないと話が進みません。ご飯が食べられないのは死活問題です。

そこで、最低限聞き取れれば良い英語を書いていきます。

bag, box(持ち帰るための袋、箱)

サンフランシスコ・シリコンバレーではとても良い慣習だと思うのが、「飲食店で食べきれない場合持ち帰りを勧められる」ことです。
色々な国籍の方がいて、アメリカンサイズが食べきれない人も多いのでしょう、僕もそうでした。
なので、しっかり聞き取れてないですが"……bag?"とか聞かれてたらおそらく持ち帰りのことについて聞いてます。
これはスーパーでも聞かれるので覚えておくと良いと思います。
ちなみにスーパーだと10セント(10円くらい)です。

receipt(レシート、領収書)

必ず聞かれます。"yes, please."って返してました。

check(お会計)

自分から言いたいときは"Can I have a check, please?"って言ってました。

For here or to go?(店内ですか、お持ち帰りですか?)

ちなみに本当にこんな風に言っているのか、恥ずかしながら僕は聞き取れていません。
"……トゥゴッ?"と言う部分で「あー持ち帰りか聞いてるな?」と判断していました。

Hi, how are you?(いらっしゃいませ)

学校で習ったように"I'm fine thank you, and you?"とか言うのではなく、

  • "Good, can I order?"
  • "Yeah, I'm thinking."
  • "Just looking."

などが良いと思って使っていました。

Which size?(どのサイズ?)

スタバとかですね。tallっていつも言ってました。ここはあんまり迷わない気がします。
他には"Which bread?(どのパン?)"とか"What kind?(どの種類?)"とか聞かれていたと思います。

Anything else?(他には?)

That's all.(これで全部です)が良いかと。

サンフランシスコ・シリコンバレーで初めてUberを使った感想

Airbnbについて書いたので、シェアリングエコノミー第2弾としてUberについても書くことにします。

経緯

サンフランシスコではバスや地下鉄で大体行きたいところに行けていたので、そんなにUberを使わなくても良いと思っていたのですが、シリコンバレーの方にくると公共交通機関はそこまでしっかりしていませんでした。
目的地まで徒歩とバスで1時間強、でもUberなら20分とかだとUber利用しちゃおうと考えて1週間くらいはガンガンUber乗ってました。

使い方の流れ

まず、会員登録。Facebookログインなどをしてsmsに送られてくる番号を入力してクレジットカードを登録して終わり。って感じだった気がします。スクショを取ってないのであやふやですみません。
自分の場合Paypal(MasterCard)がなぜか登録できず、Visaなら登録できたことは覚えています。ちょっと注意するところかもしれません。

次に、自分はUberアプリではなく、GoogleMapからUberを呼んでいます。これは、理由が2つあって

  1. アプリで表示される価格よりもGoogleMapの方が安いから
  2. 行き先を調べていて、バスや電車では不便だと感じたらUberを使うから

f:id:gawawa124:20170523140611p:plain

1つ目の理由についてですが、理由はちょっとわかりません。Googleがスポンサーとしてお金を出しているのか、Uberアプリから実際呼んでも実は料金が変わらないとか?

正確には上記の画像の通り、GoogleMapからだと料金には幅があって実際に支払うお金は大体下限値ちょっと上くらいで、事前に見たUberアプリの価格よりも安いなと感覚的なことを言っています。

さて、どのタクシーを呼ぶかを決めて席数を選んだらあとは待つだけ。

f:id:gawawa124:20170523140604p:plain

こんな感じで向かっている様子がmap上でわかります。
特にコミュニケーションをとることなく目的地まで連れてってくれて、お金をその場で払うこともなく降りられます。便利。
"How are you?" とか"Where are you from?" くらいの会話くらいしかしてない気がします。

1週間くらい乗って特にトラブルはなかったです。
ただ、1度だけイベント会場に呼んでしまった時に、おそらく交通規制に引っかかったのか全然来なくて、電話をかけてみたけれど自分の英語力が低く、意思疎通ができなくてキャンセルをしたことはあります。キャンセル料はその時は2ドルでした。
ただ、運転手が見つけやすい場所で待っていればほぼ大丈夫なのでガンガン使うべきだと個人的には思います。

UberPool(相乗り)について

Uberの中でもUberPoolを使っていました。安いからです。
日本だと「え、相乗りとか大丈夫なの?」みたいな声が聞こえてきそう(なんかそんな記事を見た気がします)です。
主観ですけど、意外と気にならないです。お互いにお互いのことが興味がないので会話も発生せず。
自分が一番遠くて他の乗客から降ろして行く場合、「あーちょっと遠回りしてるかも?」くらいには感じますが、著しく遅れるってことはなかったように思います。
ただ、1度だけ時間に焦っていた時にUberPoolを呼んで、他に2人いた時は焦りました。時間に余裕を持ってUberPoolを呼ぶか、相乗りにしないことをお勧めします。

余談

ある日、スーツケース載せれる? って聞いてみると、"Yes. But It depends on driver."(できるよ。でも運転手次第だね。)って返ってきました。
1度もスーツケースは載せたことないのですが、小さいスーツケースを載せていた人と同乗したこともあるし、彼もああ言っていたことなので挑戦してみるべきかもしれません。