少し前に書店で気になっていた本を買い、読み終わったところでいてもたってもいられず、とりあえずここに。
- 作者: 堀本裕樹
- 出版社/メーカー: KADOKAWA / 角川書店
- 発売日: 2017/04/25
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
少し前に書店で気になっていた本を買い、読み終わったところでいてもたってもいられず、とりあえずここに。
nginxのリバースプロキシ設定で、転送先をIPではなくホスト名で指定していると、ある日突然エラーが発生する現象に遭遇しました。
ググると複数の記事が見つかったので、よくある現象?のようです。
元々は↓のように設定していました。
proxy_pass
のところで、リクエストの転送先をホスト名で指定しています。
location ~ ^/hoge { proxy_pass https://example.com; }
この状態だとnginx起動時に転送先のIPがキャッシュされ、以降はキャッシュしたIPへ転送するようになるようです。
IPが変わってもキャッシュしているIPへ転送し続けるので、AWSのELBを使っていたりすると、IPが変わった際にエラーが起きてしまうことも。
それを回避するため、proxy_pass
と併せてresolverを設定しておきます。
resolverを設定すると↓のような感じに。
location ~ ^/hoge { resolver 10.0.0.2; set $host_name example.com; proxy_pass https://$host_name; }
resolverで指定するDNSのIPは、AWSの場合はVPCのネットワーク範囲+2とのこと。
AmazonProvidedDNS は Amazon DNS サーバーです。このオプションは、VPC のインターネットゲートウェイを介して通信する必要があるインスタンスに対して DNS を有効にします。文字列 AmazonProvidedDNS は、リザーブド IP アドレスで実行中の DNS サーバーにマップされ、VPC IPv4 ネットワークの範囲に 2 をプラスした値です。例えば、10.0.0.0/16 ネットワークの DNS サーバーの位置は 10.0.0.2 となります。
DHCP オプションセット - Amazon Virtual Private Cloud
デフォルトではTTLが切れたタイミングでDNSに問い合わせ。
resolverにvalid
を設定すると、任意のタイミングで問い合わせることができるようです。
ex. valid指定
location ~ ^/hoge { resolver 10.0.0.2 valid=5s; # 5秒ごとに問い合わせ set $host_name example.com; proxy_pass https://$host_name; }
nginxのバージョンによる?のか、nginx 1.6系、1.8系ではsetでホスト名を変数に入れないとエラーになるようです。
nginx 1.10.1では、setを使わずに指定しても設定できました。
nginx proxy の名前解決問題、ファイナルアンサー? – 1Q77
Nginx のDNS 名前解決とS3 やELB へのリバースプロキシ :: by and for engineers
Nginxでproxy_passにホスト名を書いた時の名前解決のタイミング - (ひ)メモ
開発に入ると、あとから「あれ、時間どれくらいかかったっけ」みたいになることありませんか。
自分はしょっちゅうありまして、Togglを使うようにしたものの、開始/終了忘れが続出。
作業を変更するタイミングで勝手にどこかに記録されていった方が良いなーと思って、開発中は何度も行うブランチの切り替えをタイミングにしてみることにしました。
「はて、checkoutのタイミングってどうやって知るんだろう」
ちょろっと調べるとすぐに出てきました。
git checkout
が正常に終了すると、post-checkout
フックが実行されます。
8.3 Git のカスタマイズ - Git フック#その他のクライアントフック
.git/hooks
の中にはいくつかサンプルがあります。
その中にpost-checkout
ファイルを作って処理を書いておくとcheckout後に実行されるらしいので、これを利用することにします。
「はて、ブランチ名ってどうやって取得するんだろう」
これもちょっと調べると出てきました。
git rev-parse --abbrev-ref HEAD
でできるらしい。rev-parse
初めて知りました。
gitで現在のブランチ名を取得する - 技術は熱いうちに打て!
後から調べたのですが、Git1.8以降であればgit symbolic-ref --short HEAD
でも取得できるようです。
Gitリポジトリのカレントブランチ名を取得する | blog: takahiro okumura
現在のブランチ名がgit rev-parse --abbrev-ref HEAD
なら、一つ前はgit rev-parse --abbrev-ref @{-1}
かな?と思って試したら、ちゃんと前のブランチ名を取得できました。
ということで、
# 現在のブランチ名を取得 $ git rev-parse --abbrev-ref HEAD master # 前のブランチ名を取得 $ git rev-parse --abbrev-ref @{-1} develop
git rev-parse
なかなか面白い。
git rev-parseを使いこなす - Qiita
↓のように着地。
Slackへの通知は[10分で出来る]シェルスクリプトの結果をslackに投稿 - Qiitaを参考にしています。
実際にmaster -> developへブランチを変更すると…
キタ(゚∀゚)!
名前(gittn)やメッセージは整えるとして、とりあえず通知するところまでは完了。
これでSlack上で切り替えた時間がわかるので、大まかにでも作業時間がわかる(はず)。
curl叩いている分、checkoutが若干遅くなってしまったのは…どうしよう…。
何かと日付計算は使うと思いますが、「一週間前」をどう出すかについて。
「一週間前」の計算をprev_month
と同じノリでprev_week
を利用して計算した際、意図したものにならず「あれっ?」となることがありました。ので、メモ。
例えば↓のような計算。
> Date.yesterday #=> Sun, 30 Apr 2017 > Date.yesterday.prev_month #=> Thu, 30 Mar 2017 > Date.yesterday.prev_week #=> Mon, 17 Apr 2017
何も気にせずにやっていると、あれ?Date.yesterday.prev_week
は2017年4月23日じゃないの?となります。
が、よくよくソースを見ると処理が全く違うオチ。
prev_week
def prev_week(start_day = Date.beginning_of_week, same_time: false) result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day))) same_time ? copy_time_to(result) : result end
rails/calculations.rb at f2c6db41ba56afb1529e4732c59622fc0cf9f3ba · rails/rails · GitHub
prev_month
def prev_month months_ago(1) end
rails/calculations.rb at f2c6db41ba56afb1529e4732c59622fc0cf9f3ba · rails/rails · GitHub
prev_week
の中身はまだ詳しく知りたいのでおいおい。
おとなしく「一週間前」を出そうとすると、こういうことになるのでしょうか。
> Date.yesterday.weeks_ago(1) #=> Sun, 23 Apr 2017 > Date.yesterday - 1.week #=> Sun, 23 Apr 2017
何気に「このPDF、数ページだけ画像にして保存したい…」という時があると思うのです。 …なかったとしても、万が一発生した時にGrimというgemが使えそうだったのでメモ。
想定している処理は、
1. PDFを保存&S3にアップロード
2. 保存したPDFから3枚画像に切り出す
3. 切り出した画像を保存&S3にアップロード
です。では早速。
↑のイメージでモデルを作ります。
DocumentはPDFを持つように、PdfImageはcapture(=PDFから切り出した画像)を持つようにします。
Documentは複数枚の画像を持つので、has_manyで。
PDFと切り出した画像のアップロードはpaperclipを使います。
今回はpaperclipは入っている前提で進めます。
必要なファイルを生成
$ rails g model PdfImage $ rails g scaffold documents
migrationファイルに必要なフィールドを追加。
# db/migrate/201702140001_create_documents.rb class CreateDocument < ActiveRecord::Migration[5.0] def change create_table :douments do |t| t.string :name t.attachment :pdf t.timestamps end end end # db/migrate/201702140000_create_pdf_images.rb class CreatePdfImage < ActiveRecord::Migration[5.0] def change create_table :pdf_images do |t| t.attachment :capture t.references :document, foreign_key: true t.timestamps end end end
migrationファイルができたら、コマンド実行。
$ RAILS_ENV=development rails db:migrate
モデルにattachmentのvalidationなど、必要項目を足していきます。
# app/models/pdf_image.rb class PdfImage < ApplicationRecord has_attached_file :capture belongs_to :document validates_attachment_content_type :capture, :content_type => 'image/png' end # app/models/document.rb class Document < ApplicationRecord has_attached_file :pdf has_many :pdf_images, dependent: :destroy validates_attachment_content_type :pdf, :content_type => 'application/pdf' end
PDFの場合、白背景が透過されてしまい、白い背景が真っ黒になる場合もあるので、その時はhas_attached_file
のオプションにImageMagickでの変換指定することで回避できます。
has_attached_file :pdf, convert_options: {all: '-flatten'}
PDFから画像を切り出して保存&S3でアップロードまでやろうとすると時間がかかるので、Workerで処理するようにします。
class CreatePdfImagesWorker include Sidekiq::Worker sidekiq_options queue: :create_pdf_images, retry: false def perform(document_id, target_file) document = Document.find(document_id) old_pdf_images = document.pdf_images.presence pdf = Grim.reap(target_file) # 切り出した画像の一時的な保存先を生成 path = FileUtils.mkdir_p(Rails.root.join "tmp", "pdfs", document_id.to_s).first # 切り出す枚数(デフォルト3ページ分) page_num = 3 page_num = pdf.count if pdf.count < 3 page_num.times do |page| pdf_file_name = document.pdf_file_name.split(".")[0] pdf_image_path = "#{path}/#{pdf_file_name}_#{page}.png" # 指定ページを画像として切り出し if pdf[page].save(pdf_image_path) file = File.open(pdf_image_path) PdfImage.create(image: file, document_id: document_id) file.close end end # 古い画像の削除 old_pdf_images.each{|pdf_image| pdf_image.delete} if old_pdf_images # アップが完了した作業ファイル、ディレクトリを削除 FileUtils.remove_entry_secure path FileUtils.remove_entry_secure target_file end end
controller内でやることは2つです。
1. PDFのtmpファイルを自分の扱いやすいところにコピーする
2. workerを呼び出す
1. PDFのtmpファイルを自分の扱いやすいところにコピーする
privateメソッドとして/tmp以下にあるPDFをコピーするメソッドを作ります。
# 画像切り出し用に、/tmp以下に配置されるPDFのtmpファイルをコピーしておく # 戻り値はコピーしたtmp fileのpath def copy_temp_pdf tempfile = document_params[:pdf]&.tempfile return unless tempfile pdf_dir = FileUtils.mkdir_p(Rails.root.join "tmp", "pdfs").first # PDFのtmpファイルをプロジェクトルートのtmp以下にコピー FileUtils.cp tempfile, pdf_dir "#{pdf_dir}/#{File.basename(tempfile)}" # File.join(pdf_dir, File.basename(tempfile)) でも同じ end
document = Document.new
等で生成したインスタンスをsaveすると、PDFをアップロードする際にtmpファイルも消えてしまうので、saveする前にtmpファイルをコピーする copy_temp_pdf
メソッドを呼び出します。
コピーしたファイルのパスが返ってくるので、変数に入れておきます。
2. workerを呼び出す
documentのidが必要になるので、workerの呼び出しはインスタンスsave後に行います。
class DocumentsController < ApplicationController # --- 諸々省略 --- def create @document = Document.new(document_params) target_file = copy_temp_pdf if @document.save CreatePdfImagesWorker.perform_async(@document.id, target_file) if target_file format.html end end # --- 諸々省略 --- end
ざっとこんな感じでした。 ゴリゴリ気合いでやるか、複数枚アップロードするしかないのかなと思っていた矢先にこのGem。 やることが少ない分、使いやすい&わかりやすかったです。 tempファイルごにょごにょしてるあたり、もっと上手くやれたらなー。
以前のエントリでも出てきたのですが、ディスクフルな状態に遭遇することがままあります。 今となっては慣れつつありますが、たまに「ええっと?」となるので、備忘録的に。
主に使うのはdf
コマンドとdu
コマンド。
参考:
Linuxコマンド集 - 【 df 】 ディスク・ドライブの使用量を表示する:ITpro
Linuxコマンド集 - 【 du 】 ディレクトリ内のファイル容量を表示する:ITpro
df
コマンドで本当にディスクフルなのか、どこが容量を食っているのかを確認します。
h
オプションを付けると、人間が見やすいように1Mや1Gなどの単位付きで容量が表示されます。
$ df -h ファイルシステム サイズ 使用 残り 使用% マウント位置 /dev/disk1 465Gi 29Gi 436Gi 7% / devfs 178Ki 178Ki 0Bi 100% /dev
コマンドを実行すると↑のように表示されます。
見たまま、使用%が100%であればディスクフルです。アウト。
上の例はめちゃくちゃ余裕ありますが、「やれ、/が残り少ないぞ」とわかったら、次にdu
コマンドを使って、容量を使いまくっている場所を調べていきます。
s
オプションで指定したファイル/ディレクトリの総計、h
オプションで、単位付きで容量が表示されます。
/*
のようにディレクトリを指定すると、/
以下のディレクトリも確認することができます。
$ du -sh /* 2.6G /private 1.0M /sbin 4.0K /tmp 509M /usr
※ ルート権限でないと見れず「Permission denied」のエラーが出ることもあるので、その場合はsudo
をつけて実行
パイプ(|
)でsort
コマンドを繋げることで、容量の大きい順/小さい順にソートすることもできます。
参考:
Linuxコマンド集 - 【 sort 】 行を並び替える:ITpro
sort
も使う際は、du
コマンドにh
オプションをつけているとうまくソートされないことがあるので、その場合はh
オプション抜きで実行。
n
オプションで先頭の数値や記号を数値と見なし、r
オプションで逆順にソートします。
# hオプションあり $ du -sh /* | sort -nr 509M /usr 4.0K /tmp 2.6G /private 1.0M /sbin # hオプション無し $ du -s /* | sort -nr 5433696 /private 1042688 /usr 2072 /sbin 8 /tmp
何が容量を使っているのかが判明したら、後は不要ファイルを削除していくだけです。
「ディスク使用率はまだ余裕あるんだけどなぁ…」という時は、iノード数が原因だったということもあるようです。
過去一度だけ遭遇したことがあります。
qiita.com
ノード数の確認はdf
コマンドのi
オプションで調べられます。
iノードについてはこちらがわかりやすかったです。
参考:iノード(inode)とは
本当はディスクフルになる前に対応できれば良いんだけどなーと思いつつ、ちゃんと監視しているものはアラートが来ても監視されていないものは気づけない現実。 ちゃんと対応できれば良いんですけどね…。
結果から書くとディスクフルだっただけでしたが、MySQL経験が浅いということもあり、初めて見るエラーに心臓止まるかと思った出来事でした。
エラー文の「Incorrect key file for table」でググるとこちらのページがひっかかります。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 15.2.4.1 MyISAM テーブルの破損
「え?破損?」と、早速ビビリポイントが出てくるのですが、落ち着いて中を見ます。
次の状況が発生した場合、テーブルが破損するおそれがあります。
・ mysqld プロセスは、書き込みの最中に強制終了されます。
・コンピュータが予期せずシャットダウンされます (たとえば、コンピューターの電源が切られた場合など)。
・ハードウェア障害。
・サーバーが修正中のテーブルを、外部プログラム (myisamchkなど) を使用して同時に修正しています。
・MySQL または MyISAM コードのソフトウェアバグです。
MyISAM テーブルのヘルスを CHECK TABLE ステートメントを利用して確認でき、破損した MyISAM テーブルを REPAIR TABLE を利用して修復できます。
プロセスやサーバ類になにも問題ないことを確認し、もしかしたら本当にテーブルぶっ壊れたかもしれない…と思いながらmysqlcheckコマンドでテーブルのヘルスチェックを試します。
mysqlcheckは下記が参考になりました。
d.hatena.ne.jp
念のため全テーブルをチェックしましたが、全てOKで返ってきました。
頭を抱えながら再度「Incorrect key file for table」でググると、こちらの記事にたどり着きます。
d.hatena.ne.jp
容量の問題かも、という文に「そんなまさか」と思いながらdfコマンドを叩くと、案の定ディスク使用量が100%でした…。
よく考えると、確かにディスクフルだと実行できないよなぁ。
年も明けたので去年を軽く振り返りつつ、今年を考えつつ。
2016年の初めにマンダラチャートを書いていたので、それを見ながら振り返っていると、半分も出来ていない現実に出くわしてただのガチクズじゃないかと絶望しています。
中でも一番出来ていないところが、知識の「インプット&アウトプット」のアウトプット部分。
このブログを眺めるだけでも全然書けてない感がにじみ出ている(かといって他になにかしらを投稿し続けたわけでもない)ので、言い訳しようもないですね。
インプットに関しても、Web上の記事ならすぐ読んでいたものの、積ん読状態の本が発生していたので、2017年は年末に積ん読本がない状態にしたいです。
この部分は2017年も引き続き手を入れたいところ。
それと、いろいろなことに繋がるのが時間管理が下手すぎたというのも。
ワークライフバランスで言うとワークに寄ったところもあったので、うまくバランスをとって行きたかったものの、半年ぐらいしか保たず、結局ワーク寄りに…。
そもそもバランスをとりたいと思ったのも、趣味の時間を確保したい&先を考えてのことなので、ここも2017年引き続きになりそうです。
反面、エンジニアとしての技術的なところは割とできたかな…という印象です。
機械学習とかの話題の分野もちょこっとつま先突っ込んでみたり、競技プログラミング再入門してみたり。
誰でもそうだと思いますが、興味持って進められるところはやっぱり出来るんだろうなー。
今まで付け焼刃なところもあるので、2017年はもうちょっと突っ込んだところの理解もできるようにしていきたいです。サーバ周りやフロント周りを特に。
ハード関わるところが「興味ある〜」止まりだったので、何かしらのアクションを起こせるようにしたいです。
相変わらず反省するところしかないなーということに気づいたので、年末振り返る時には「ただのガチクズ」卒業できていることを祈ります。
2日目は14時頃のフェリーで稚内へ渡る予定だったので、それまでの間に姫沼まで足を伸ばしました。
バス本数が少なく、時間が合わなかったので徒歩でフラフラと。
後から思えばフェリーのレンタルサイクル利用すれば良かったなと思いました…早く気付けよと…。
鴛泊のフェリーターミナルから姫沼までは4キロと少し。徒歩1時間ほどでした。
ところどころ、先の台風による土砂崩れの痕跡が残っていたりして、本当に台風来ていたんだなあと。
恐らく上の方にあった木が歩道の横まで滑り落ちていたり、土嚢が積まれていたり、歩道が泥だらけだったり。
台風の影響を気にしながら進むと姫沼への案内板があったので、案内の通りに右折。
山の方へ向かうのでずっと上り坂になります。上り坂の途中に展望台がありました。
▼姫沼展望台
展望台を出てもう少し坂を登ると、やっと姫沼につきます。
▼姫沼
姫沼は周回の道があったのですが、うっかり反時計回りに歩いてしまい途中団体客とすれ違う時に数分足止めになったりしました。
周回は時計回りのようです。気をつけます。
ここでも土砂崩れの影響か、大きめの木が根から横倒しになっていたりしました。
帰りは下り坂なので、行きと違って楽ですね…。
途中、海に寄って遊んだりしながらフェリーターミナルに戻り、しばし休憩タイム。
鴛泊-稚内間はフェリーで約1時間40分ほど。
ハートランドフェリーでは、2等室を指定する場合は予約不要のため、当日自動券売機にて購入するようです。
他のフェリーでは連絡先等を申込書に記入し、窓口で受付してから乗船券を発行していたので、「この簡単手続きで良いのだろか…」と不安になったものの、特に問題なく乗船できました。
時期によっては、フェリーからイルカを見ることもできるようですが…乗船後数分で寝てしまったため、今回はイルカの確認どころか景色もほとんど見ずにフェリータイム終了。
稚内に着いた時間がもう夕方だったので、この日は宿へ直行しました。
帰省に合わせて、ふと行ってみたくなった利尻と稚内へ2泊3日で行ってきました。
その記録を兼ねた旅メモ。
13時10分新千歳空港発の飛行機で利尻空港へ。
移動時間はおよそ50分。14時に利尻空港へ到着しました。
空港出口のところに、ホテル名が書かれた旗を持った方がたくさんいらっしゃいました。
空港から宿泊地まで距離があるからか、送迎の車を出してくれるところが多いようです。
慣れない土地に来た身としては嬉しいですよね。
宿泊先で自転車が借りられたので、自転車を借りて早速観光へ。
利尻はサイクリングロードなる自転車専用道路(途中途中で、歩道との併用区間あり)があり、それも綺麗に舗装されていたりと結構良い道だったので景色を眺めながらサイクリングが楽しめました。
滅多に人も通らないので、マイペースで進んでも問題なし。
途中、野生のイタチに遭遇するというラッキーハプニングもありました。むっちゃ可愛い…。
鴛泊から出発して、沓形方面へ反時計回りにまっしぐら。
道中にちょっとした休憩スポット数箇所もありました。
▼名称忘れてしまいましたが、設置されていた日時計と利尻富士
出発が14時30分頃というのもあって、沓形の手前辺りで折り返して鴛泊方面へ。
夕日ヶ丘展望台へ行こうとしたところ、手前に「富士野園地」と書かれた看板が目に入ったので、フラフラと寄り道。
知らずに行っていたのですが、『北のカナリアたち』のロケ地にもなったところでした。
簡易トイレもあり、休憩スポットでもあるようです。
ポンモシリ島という小島がよく見えます。
▼ポンモシリ島
そして夕陽ヶ丘展望台。
自転車で爆走した後に展望台頂上までの階段を登り、すっかり息が上がっていましたが、そんな疲労感も吹っ飛ぶような景色でした。…体力つけねば。
▼夕陽ヶ丘展望台頂上からの眺めその1
▼夕陽ヶ丘展望台頂上からの眺めその2
▼夕陽ヶ丘展望台頂上からの眺めその3
夕焼けには少し早めでしたが、十分綺麗でした。
1日目は早々に宿泊先へ戻ったので、続きはまた後日。