blog.kotamiyake.me

為せば成る、為さねば成らぬ何事も

個人のRailsプロジェクトでDockerを使って環境構築をしていた際にハマったお話。

原因

  • Rackのバージョンが上がって、ホストとポートを分割する挙動が変わりアンダースコアのホストがマッチしなくなった

Rackの挙動の変更点は以下の通り。

対策

  • docker-composeのサービス名をハイフンで繋ぐよう修正する

経緯

元々以下のような構成でdocker-composeを使って環境を構築していました。

アプリ本体とwebpack-dev-serverを動かすアプリを分離する構成です。

services:
  app:
    <<: *app
    command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpack_dev_server
    ports:
      - 3000:3000
    depends_on:
      - db
  webpack_dev_server:
    <<: *app
    command: ["bin/webpack-dev-server"]
    environment:
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    ports:
      - 3035:3035

それから開発中にRackのバージョンをアップデートしたところ、以下のエラーが吐き出されてjsが参照できなくなりました。

#<SocketError: Failed to open TCP connection to webpack_dev_server:3035:80 (getaddrinfo: Name or service not known)>

今回は開発環境での出来事だったので特に問題はなかったのですが、みなさんもgemのアップデートの際にはくれぐれもご注意を…。

mysqldumpの実行がサーバーのリソースを圧迫する問題があって、それを解消するための対応が行われた際に調べたことをまとめておく。

結論

  • --single-transaction オプションと --quick オプションを使う

--single-transaction オプションとは?

  • トランザクション分離モードをREPEATABLE READにする
  • データをダンプする前にSTART TRANSACTION SQLステートメントを発行する
  • InnoDBテーブルのみが一貫した状態でダンプされる
    • MyISAMまたはMEMORYテーブルは状態を変更する可能性がある
  • 有効なダンプファイルを取得するために以下のステートメントを発行してはいけない
    • ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE
    • 上記のステートメントを使用すると、mysqldump によって実行され、テーブルの内容を取得するSELECTが、正しくない内容を取得したり失敗したりすることがある

--quick オプションとは?

  • テーブルの全レコードをメモリにバッファするのではなく1行ずつダンプする

参考記事

いつも調べるのでメモとして残しておく。

$ ps -ef | awk 'NR == 1 || /KEYWORD/'

grep -v してコマンド実行自体のプロセスを表示しない。

# ex.
$ ps -ef | awk 'NR == 1 || /puma/' | grep -v awk
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501   420     1   0 22Feb20 ??         0:28.78 /usr/local/Cellar/puma-dev/0.12/bin/puma-dev -launchd -dir ~/.puma-dev -d test -timeout 15m0s

参考記事

毎回調べるのでメモ。

以下はRailsで開発している場合を想定しているが、ダンプするデータを適宜調整すれば特に問題ないはず。

まずはステージング環境の DATABASE_URLSTAGING_DATABASE_URL としてReview Appsの環境変数に設定する。

そしてpostdeploy scriptを使ってリストアするよう設定する。

"scripts": {
  "postdeploy": "pg_dump -a -T ar_internal_metadata -T schema_migrations $STAGING_DATABASE_URL | psql $DATABASE_URL"
}

ポイント

  • Herokuのヘルプページを参考にすると丸ごとリストアするのでマイグレーションが合った際に挙動が不安定になるため、データだけダンプしてリストする(実行順序がrelease phase → postdeployの順番のため
  • ar_internal_metadata をリストアしようとすると key の重複でエラーが発生するのでダンプしない
  • release phaseでマイグレーションが実行されるようにしていて schema_migrations は生成されるのでダンプしない

参考記事

最近、仕事でアプリのパフォーマンス改善することがあったのだけれど、ちゃんと計測することが大事だということで、benchmark-ipsを使ってみた。

今後使うこともあると思うのでメモっておく。

設定とかの絡みがあったので rails runner で実行した。

require 'yaml'
require 'benchmark/ips'

settings = YAML.load(ERB.new(File.read(File.expand_path('./config/database.yml'))).result)

ActiveRecord::Base.establish_connection(
  settings['development']
)

Benchmark.ips do |x|
  x.config(time: 5, warmup: 2)

  x.report('#method_a') do
    # do something
  end

  x.report('#method_b') do
    # do something
  end

  x.compare!
end

デフォルトの検索オプションに除外するオプションを追加する。

Exclude folders · Issue #128 · junegunn/fzf

私はブラウザの動画を撮影する際にNimbus Capture – – Take screenshots and record video from your screenというChrome Extensionを利用しています。

このExtensionはとても便利なのですが、動画のダウンロード形式がwebmのためGitHubなどに貼り付けることができません。

オンラインで変換してくれるサービスがあるのですが、わざわざブラウザを開いてアップロードして、というのが面倒だったのでコマンドラインで変換できるようにスクリプトを書いてみました。

もし不具合がありましたら、Twitterでご連絡していただけると助かります。

docker imagesdocker images -a とすると、REPOSITRYが <none> で、尚且TAGも <none> と表示されるイメージが生成されることがあります。

どうやらこれには2種類のケースがあるようです。

  1. Dockerイメージの中間層に位置する部分として使われているイメージが <none> と表示される
  2. 同一のDockerfileを使用してTAGの異なるDockerイメージをビルドしようとすると古いイメージが <none> と表示される

よくググって <none> になっているイメージを消そう系は、すべて2.のケースが当てはまるのだと思います。これらのイメージを dangling images と言うらしいです。

そもそも1.のケースで生成された <none> のイメージは、以下のようなエラーメッセージが表示されて削除できないかと思います。

$ docker image rm f3097c2cbe74
Error response from daemon: conflict: unable to delete f3097c2cbe74 (cannot be forced) - image has dependent child images

また dangling images を消すために色々な記事で何やらややこしいコマンドを紹介されていますが、最近のバージョンでは docker image prune としてあげるだけでお掃除ができるようになっています。

Dockerについてはまだまだわからないことだらけなので、引き続き勉強していって学んだことを共有していきたいと思います。

参考記事

仕事でGIL(Global Interpreter Lock)についての話がでてきたので、改めてどういうものなのか整理してみました。

GILとは何なのか?

Wikipediaによると、

グローバルインタプリタロック(英: Global Interpreter Lock, GIL)とは、プログラミング言語のインタプリタのスレッドによって保持されるスレッドセーフでないコードを、他のスレッドと共有してしまうことを防ぐための排他 ロックである。

グローバルインタプリタロック – Wikipedia

スレッドによって保持されるスレッドセーフでないコードを、他のスレッドと共有してしまうこと」というのは一体どういうことなのでしょうか。

まずはスレッドについての理解を深める必要がありそうです。

計算機上で、複数の処理を同時に実行する(並行計算、マルチタスク)場合、処理を分割し、同時に実行する部分を指定する事が出来る、処理の分割の単位として、スレッドとプロセスがある。

スレッド (コンピュータ) – Wikipedia

またまたWikipediaからの引用になるのですが、複数の処理を並列で動かす場合には、処理分割の単位としてスレッドとプロセスがあります。

それぞれの違いやメリット・デメリットについては、参考記事を読んでいただきたいのですが、ここで重要なのは処理分割の単位としてスレッドを選んだ場合には、同一メモリ上で処理が実行されるため、複数のスレッドで同じデータが操作される可能性があるということです。

それが最初にWikipediaから引用した部分の「スレッドによって保持されるスレッドセーフでないコードを、他のスレッドと共有してしまうこと」に繋がります。

そしてその問題を防いでくれる機構がGILというわけです。

RubyにおけるGIL

Rubyのマルチスレッドで動くという触れ込みだが、実際にはGILの影響で完全にスレッド数に比例してパフォーマンスがあがるというわけではようです。

ただRubyのコードで処理時間を取られることは少なく、ほとんどがGILが解除されるI/O待ちに処理時間が取られるので、かなりのケースで並列処理が可能なようです。

まとめ

簡単な説明となりましたが、さらに詳しく理解したい方は参考記事を読んだり、そこに出てくるキーワードでさらに調査していただければと思います。

実際にRubyがどのように動いているかについて読んでいただいた方の理解が深まれば幸いです。

参考記事

いつもやり方を調べるので備忘録として。細かいオプションの意味は man sed で確認する。

ag -l BEFORE | xargs sed -i '' -e 's/BEFORE/AFTER/g'

参考記事