ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

Active StorageでGoogle Cloud Storageに画像を保存する

はじめに

ホビープロジェクトでモバイル向けのシンプルなTODOアプリを作ることになりました。TODOといえばWebアプリサンプルのド定番だけど、本当に自分にあった使えるものを作ってきっちり公開まではやろうと思ってます。それは置いておいて、サーバサイドをRailsで書きました。

TODOには写真が添付できると良い、というのが要件にあったので、Active Storageを使うことにします。せっかくRails 5.2を使っているので!

Active Storageとは

最高のガイドがあるので基本的にこれを読めばいいです。

railsguides.jp

ただ、調べてもS3にやってみた、という例が多く、今回は気が向いてGoogle Cloud Storageに保存することにしたのでそちらを記事に残しておこうかな、というのが本記事の主旨です。

ちなみに気が向いた理由ですが、アプリの認証をGoogleでやっていて、すでにプロジェクトがあったからです。

Active Storageを使う準備

参考になりました。ありがとうございます。【Rails 5.2】 Active Storageの使い方 - Qiita

Gemfileに必要なドライバをインストールしておきましょう。

gem "google-cloud-storage", "~> 1.3", require: false

bundle

そしてActive Storage用のテーブルを生成するためのmigrationを行う必要があります。これは内部的に使われているようで、実際は触らずに使えます。

$ rails active_storage:install
$ rails db:migrate

画像を持たせるmodelにhas_one_attached :imageを追加。今回はTask modelに持たせました。key名はimageでもphotoでもお好きなものを。

app/models/task.rb

class Task < ApplicationRecord
  belongs_to :user
  belongs_to :tag
  has_one_attached :image

# (skip)
end

デフォルトでconfig/storage.ymlはdiskになっているので、development環境でこの時点でファイルのアップロードができます。templateはerbでやりました。form_forで受けて画像を添付したいリソースのcreate actionを行うときに、file fieldでファイルをアップロードして、先ほどmodelで入力したkey名をparamにのせてあげます。これだけでできます。すごい。

<%= form_for @task,:url => {:action => :create} do |f| %>
  <p>
  <%= f.label :name %>
  <%= f.text_field :name %>
  </p>

  <%= f.file_field :image %>

  <%= f.submit %>

(root)/storage以下にファイルがアップロードされるでしょう。config/storage.ymlにすでにこのように書いてあるからですね。

  5 local:
  6   service: Disk
  7   root: <%= Rails.root.join("storage") %>

Google Cloud Storageを使う準備

さて、Rails guideを見るにconfig/storage.ymlに以下を埋める必要がありそうです。

google:
  service: GCS
  keyfile: {
    type: "service_account",
    project_id: "",
    private_key_id: "",
    private_key: "",
    client_email: "",
    client_id: "",
    auth_uri: "https://accounts.google.com/o/oauth2/auth",
    token_uri: "https://accounts.google.com/o/oauth2/token",
    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
    client_x509_cert_url: ""
  }
  project: ""
  bucket: ""

service_accountを作ってproject作ってバケット作ればいけそうな雰囲気がしますね。

Service Account作る

プロジェクトは適当に作ってある前提で、サービスアカウントはIAMと管理->サービスアカウント

ユニークな名前をいれましょう。

f:id:take_she12:20181111174935p:plain

作成されたら権限を付与します。閲覧だけでは足りないので編集権限をつけます。

f:id:take_she12:20181111174944p:plain

Active Storageがアクセスするためのkeyを作成します。

f:id:take_she12:20181111174954p:plain

作成するとダウンロードされます。

APIを有効にする

Google Cloud Storage JSON APIを有効にしてください。しないとアクセス時に弾かれます。あと、有効になるまで数分かかります。

https://console.cloud.google.com/apis/api/storage-api.googleapis.com/overview?project=simple-todo-kamoshare

ちなみに認証情報を作成する、とありますが多分不要です。この目的に適したサービスアカウントがすでに存在します、と言われるので。

f:id:take_she12:20181111175449p:plain

バケットに権限を与える

バケットは普通にぼちぽち作っていただいて、バケットに権限を与えないと弾かれます。サービスアカウントが常に権限もってるじゃん、って思うんですけどね。

ストレージ->ブラウザでバケットの右側の3点ボックスから権限を編集

f:id:take_she12:20181111175819p:plain

で、ここがびっくりなんですが、ストレージ管理者の権限が必要です。強すぎでは、、、と思うんですが、サービスアカウント実態にも確かにこれ相当のものを持たせてるから仕方ないかなと無理やり納得しています。

f:id:take_she12:20181111175939p:plain

Storage.ymlにダウンロードしたkey情報を反映

なんと、ダウンロードしたkeyを見ると中身はそのままstorage.ymlに載せるべき内容がのっています。このままいれればいいですね。

しかし、中身にはprivate_keyが入っており、localで動かすならまだしもコミットはできないので適切に秘匿しなければなりません。

Encrypted Credential

参考になりました。ありがとうございます。secrets.ymlや環境変数をRails 5.2のEncrypted Credentialsに移行する - Qiita

editorで開きます。

$ EDITOR=vim bin/rails credentials:edit
  1 # aws:
  2 #   access_key_id: 123
  3 #   secret_access_key: 345
  4
  5 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting co    okies.
  6 secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  7
  8 private_key_id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  9 private_key: xxxxxxxxxxxxxxxxxxxxxxxxx

こんな感じで追加してあげます。config/master.keyは秘密鍵になるので絶対に漏らさない/なくさないようにしましょう。(.gitignoreで無視されるようになっています)

最終的なconfig/storage.ymlはこんな感じになります。

google:
  service: GCS
  keyfile: {
    type: "service_account",
    project_id: "simple-todo-project",
    private_key_id: <%= Rails.application.credentials.private_key_id %>,
    private_key: <%= Rails.application.credentials.private_key %>,
    client_email: "active-storage-sample@simple-todo-project.iam.gserviceaccount.com",
    client_id: "1111111111111111111111",
    auth_uri: "https://accounts.google.com/o/oauth2/auth",
    token_uri: "https://oauth2.googleapis.com/token",
    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
    client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/active-storage-sample%40simple-todo-project.iam.gserviceaccount.com"
  }
  project: "simple-todo-project"
  bucket: "simple-todo-project"

生成されたconfig/credentials.yml.encはコミットしても大丈夫です。

最後にdevelopmentでもgoogle cloud storageを使うように変更しましょう。

config/environments/development.rb

-  config.active_storage.service = :local
+  config.active_storage.service = :google

おわりに

Active Storage、model側にカラムを追加することなく画像を持たせることができてとても便利だと思いました。ちゃんとリソースを削除すれば画像も削除されます。

GCPのプロジェクト、サービスアカウント、APIに使い慣れてる人ならわかるとは思いますが、はじめて使うひとの参考になれば幸いです。

Twitterのお気に入りをインクリメンタルサーチするサービスfavsearchを作った

はじめに

作った!バーン!

http://favsearch.chaspy.me

リポジトリ

github.com

今回もqsと同じく仲良し@kamontiaと作りました。

なぜ作ったのか

favってひとによって使い方違うと思う、ラフにハート送るために使ってるひともいれば、ブックマークがわりに(まぁ今ブックマークって機能あるっぽいけど)つかってる人もいると思う。で、あー過去のfavでなんかあったなーって思い出せないとか、検索したいなーっていうシーンが何度かあって、作った。

ただの検索じゃ面白くない、みんな大好きpecoよろしくインクリメンタルサーチしたいよね。

技術スタック

ってほど重厚なものはなくて、Twitter API使ってfav取得して絞るだけの薄いアプリです。

sinatraのappをherokuに載せています。webアプリ作るのにsinatraは最高便利。frontendはVue.jsを使ってみました。herokuのreview app機能でPRごとに環境できるの最高便利ですね。Quipperでもやっていることですね。

productionへのdeployだけCircleCIでやってるけど、これもherokuの方だけにしちゃっていいかもなーって思ってる。

herokuは最高便利。hobby web appでEC2なんていらんのだ。

運用まわり

これまたherokuのadd-onで無料枠で使えるものを使っています。

あとadd-onじゃなくて別でpingdomでSynthetic Monitoringをしています。1ドメインまでならfree planが実はある。

Free Website Monitoring | Pingdom

Metrics MonitoringがDatadog。 Heroku Buildpack

全部Quipperで使ってるものですね。

インクリメンタルサーチの実装

Vue.jsのデータバインディングを使った。フロントエンド(というかjavascript)さっぱりわからないマンなのにさらにフレームワークまで使って「Rails勉強してます」状態になってて非常にダメだなぁと思いつつ、まぁ一箇所だけだしシュっと使ってみるかぁってかんじでやった。非常に楽でしたね。

この本をめちゃくちゃサラっと読んだ。何事も語彙がわからんと理解がどうにもならんのでそういう意味で役に立った。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

具体的にはTwitter認証後、favoriteを取得してarrayに格納したあと、arrayに対して、入力formにいれたwordでfileterかけることで実現してます。

favsearch/favorite.erb at 4e70bdb999cd15139f402a28e39ff7ce20ff0eb7 · chaspy/favsearch · GitHub

制限事項

重大な制限がありまして!最新200件のfavしか取得できません!今後改善予定です!

今後

とりあえずMVP実装した!という段階で、まだまだ改良点はあると思います。(英語で大文字小文字区別しちゃってるところとか。)

使ってみて気軽に「こんな検索したいなー」「こんな表示できたらいいなー」っていうフィードバックもらえると嬉しいです。よろしくお願いします。

2018年10月目標

なんか9月張り切ったので少し休むというかパワーバランスを変える。

特に10月はイベントの予定もないので、登壇とかイベントはおやすみで、自主主催のクローズド勉強会でも発表はなし。

で、OSS活動の割合も減らす。ちょっとずつはやるつもりだけど、先月よりは稼働を落とす。

4ヶ月目に入る。振り返りもした。ちょっと切り替えて仕事のパワーを増そうと思うのと、健康の方にコストをかけたい。どうしても体型管理には1日1,2hのコストを割かないといけないので後回しにしていた。しかしそろそろやばいのでちょいと痩せます。筋トレとジョギング再開して、体重マイナス3kg落とすぐらいで。数値目標はあとで。

んーそんなもんかなー。音楽活動も増やしたいので、こんな感じで。

あと月末は母親が全国大会でくるので応援に行くのも楽しみだー。

2018年9月振り返り

なんかいろいろあったな、

blog.chaspy.me

概ねここに書いたことはできたと思う。書籍で進化的アーキテクチャとクリーンアーキテクチャは読めていない。その代わりに以下が読みかけって感じ。

Kubernetes完全ガイド (impress top gear)

Kubernetes完全ガイド (impress top gear)

技術書翻訳レビュー

前半はこれにかなりのコストをかけた。だいたい20hぐらい稼働かかってるんじゃないかな。早めにシメたかったので一番に終わらせた。

レビューでも結構大変なのに、翻訳するのはもっと大変ですね。来月は何か1つ自分でも翻訳に挑戦したい。

思想として、広く初学者を助ける、ということをやりたいと思っているので、翻訳もその一部だなと思う。英語の学習がてら、何かした貢献できたらな、と思っていたので、いいタイミングだった。ありがとうございます。

まだスケジュール的なものは未定なので、待ちましょう。

ISUCON

ぶっちゃけ特段事前準備はしていないのでアレですが、いい経験になりました。

blog.chaspy.me

OSS活動

@kamontiaとの趣味開発でツール作れたのはよかった。結構ここにコストを割いた気がする。

blog.chaspy.me

登壇

2本した。qsについてと、Merugo.rbでもう1つ。

blog.chaspy.me

おわりに

とりあえず、なんかわりと常に何かしていた感じで、あっという間の9月だった。よく頑張りました。読み残しの本があるぐらいで、やっぱりイベント駆動は強いな、いやでも達成しちゃうので。

仕事も3ヶ月経った。来月からはパワーバランスをまた考え直したい。

Pull RequestがMergeされたらBranchを削除するGitHub AppをAWS API GagewayとLambdaで作った

はじめに

作った。

github.com

appのinstallはこっち

github.com

めっちゃ単純な図。最近噂のdraw.ioで書きました。良い。

f:id:take_she12:20181001231501p:plain

あと一緒に作った@kamontiaがQiitaに記事書いてくれました。

qiita.com

めんどくさいの認証まわりぐらいだと思うので、その辺はこの記事読むと良いと思います。

きっかけ

なかなかあのmerge後のbranchを消すボタンを@kamontiaが押してくれないのでつくった。

やりかた

GitHub app自体作ったことがなかったので、最初は公式のサンプルをやった。

developer.github.com

これでsinatraのアプリを使ってlocal環境を使って試せる。GitHubからどういうリクエストが飛んでいるかがわかる。

AWS

休日に日曜大工でオラっとやったらできてしまった感じ。何気にAPI GatewayとLambdaを自分で触るのははじめてだったので良い機会になりました。

GitHub Appの方でどのEvent契機に動くかと、CallBack URLを設定できるので、PullRequest Eventに対して、LambdaのAPIを入れると、PRに何か起きるたびにPOST / のリクエストが飛ぶ。

Lambdaのlogicも至極シンプルで、PullRequestEventがclosed or mergedの時に、対象リポジトリのbranchを削除する。それだけ。

嬉しいの?

個人的に別に押せばいいじゃんって思うのでこれにありがたみを感じるのは@kamontiaぐらいだと思うんだけど、どうしても自分の運用するリポジトリでなかなかbranchが削除されずリモートにbranchが溜まってしまう人は導入してみてもいいかもしれません。

今後

大した設定内容じゃないけど、一応Terraform管理はしておきたい。

おわりに

おわり。

ISUCON8反省会

いろいろと考えないといけないこと、アウトプットしないといけないことが溜まってるわりに、やっていきがあふれていたので月末の日曜にがっつり消化している感じ。

ISUCON8に出た。

見ての通り、まぁ準備不足(過去問を解いていない)なのに何を言うかという感じではあるが、せっかくなので書いておく。

@sat0yuがQuiper Product Blogにいい感じの記事を書いてくれている。

quipper.hatenablog.com

反省と学び

今回、CentOS7とMariaDBということで、個人的にかなり馴染みのあるOSとDBであった。にもかかわらず。

ちなみにH2Oははじめましてでした。

大きな学びはこんなところ。

  • その場で学んで行く時間はない。手慣れたもので勝負すべき。そのためにはインフラ構成変更も辞さない。
    • ただし"手慣れた"ものを増やすために、普段からのキャッチアップがここで生きる。
  • 変更を容易にRevertできるようにすべき。
    • 今回はソースコードとconfigに関しては前回参加した@ujihisaがgit管理下においてdeployを容易にしてくれた
    • MariaDBのvup、使ってないサーバであげたんだけど、このDBに対してアプリの書き込みを向けることができなかった
  • 敵を知れ。ベンチマークの理解。
    • 優先度をあやまった。DBやWebのloadbalanceより、ベンチマークで何が走るのか。すなわちinitでDBの初期化が何が走るのかまず見るべきだった
    • このDBやwebの構成変更ができなかった=ベンチマークの初期化処理とアプリの挙動を理解していなかったことが主な敗因

前職でちょうどMariaDBのvupをして、性能があがった経験からやろうと思ったんですけど、ダメですね、それはそうかもしれないけど、優先順位が違う。

来年に向けて

じゃあどんなアクションをとるかということ。

たぶん、過去問を数年分といて、ベンチマークツールの使い方、一般的な対策、そういう準備をしても、予選通過はできないと思う。このぐらいはどのチームもやっているからだ。

個人の取り組みとしては、大きく以下を1年間で身に付けたい

  • 計測 - 観察 - 改善のサイクルをいろんなミドルウェアで行う
  • SQL / メジャーなDatabaseの性能/基本操作の学習
    • MySQL系はまぁまぁ触れたがPostgresやMongoはあんまり。(Qで使ってるのに!)

多分来年参加するとしてもインフラ担当で出たいので、シェアの高いWeb/DBに関しては余暇を作って抑えておきたい。

その走りとしてWeb serverのベンチ環境を作ったので月イチぐらいで遊んでいきたいと思う。

それと同時に実運用するアプリが作れたらいいんだと思うけど、まぁそれは@kamontiaとなにかやれればいいね。

おわりに

しかし参加してよかったしかない、無力さを感じることは、成長につながる。機会をくれたお2人に感謝。

入社して3ヶ月

前回

blog.chaspy.me

3ヶ月経って書くと過去の僕が言っているので書くが、正直この頃期待していた課題は解消できていない。また、周囲の期待を明確に話した訳でもないが、期待以下となっている。

問題は新しいことの学び方より、既存システムの理解だ。

これが満足にできていないので、雑用も、メインのタスクも、チームメンバーのPRレビューも時間がかかる。そして失敗もする。(した)

これをじっくりやるべきなのが最初の3ヶ月だったはずだが、情けない。

とある個別の(なぜか他とは違う)サービスに関しては、検証環境構築タスクを通じて、1日じっくり時間をかけたおかげで理解できた。これが早い遅いかはともかく、既存のドキュメントやコードをとにかく時間をかけさえすれば理解はできるので、他でもそうすべきである。

1ヶ月時点で振り返って、2Q(6-9月)のgoal設定を個人的にしたんだが、課題が明確に見えていなかったなぁと思う、あってはいるんだけど、ふわっとしている。

さっき一人反省会をしたが、Perfect Understanding Fridayを作って、何か1つ「完全に理解した」を作って、アウトプットする取り組みをしたい。今の課題の末端としてはタイムマネジメントで、集中タイムを意図的に作り出せていないというのもあるので、その解消も兼ねる。

そのアウトプットは個人的に作ったQuipper SRE Handbookかもしれないし、単に雑多なメモを書き下しissueかもしれない。が、アウトプットをして自分の理解内容をメンバーに見てもらうことと、他チームや今後入ってくるひとに有益なものになるようにしたい。

スペシャリティを発揮し、「お前にしかできないことは何よ?」に答えられるようになるまではもう少し時間がかかりそうで、そのためにはまず既存の理解をしっかりしたい。

次は「入社して半年」。既存のことなら任せろ、さらにこのことは俺に任せろ、となっていることを願う。