技術雑誌読んでみたら結構良かったよというお話
なんで技術雑誌を読み始めたの?
1on1で「技術雑誌を読んでみたらどうだろう」とアドバイスをもらったことがきっかけです。
知識の引き出しを増やしたいという気持ちはあったのですが、具体的にどう手を付けようか迷っていたので「何かのとっかかりになれば」という思いから読んでみました。幸い会社で「WEB+DB PRESS」と「Software Design」を定期購読していたので、気軽に手に取ることができました。
雑誌読んでみてどうだったの?
KPTで振り返ってみます。
K
- 技術書と違って、色々な知識を雑多に集めることができる
- 興味の湧く内容だけを集めることができる
- 技術書よりは薄くて読みやすい
- 本には載らないニッチな分野がある
P
- 一つの分野について体系化されていない、または体系化されるまで時間がかかる
- シリーズものはバックナンバーを読み漁る必要がある
- 興味がない分野は読む気力がでない
T
- 興味がない内容でも、触りは読んでみる
感想
良かった点
やはり雑誌というだけあって、色々な分野について書かれていました。
技術の深堀りもあれば基本的なものもあり、流行りものをキャッチアップすることもあれば、めっちゃニッチな内容(Ruby2.7のメモリ扱いやGoのビルドキャッシュなど)も書かれていました。 またCTOへのインタビュー記事などもあり、読み物としても面白かったです。
ここで感じたことは、「技術雑誌はエンジニア万人が読める内容である」ということです。今まで「技術雑誌というものは、私のような未熟者には荷が重いのではないだろうか?」と思っていました。しかし、いざ読んでみると、むしろ未熟者だからこそ読んでほしい内容が結構抑えられているなと感じました。もし前の私のような考えを持っている人がいれば、ぜひ一度読んでほしいと思います。
気になった点
雑誌であるがゆえに、興味のない分野も掲載されています。これを読む読まないは人それぞれだと思いますが、とりあえず触りだけ読むようにするくらいで良いと思います。おそらく無意識的に避けている内容に出会えるので、「いつか使うかもしれない知識のフックになれば」という気持ちでサラッと読めば良さそうです。
またシリーズものを全て抑えようとすると、バックナンバーが読める環境が必要です。必ずしも過去に遡る必要はありませんが、シリーズ全部ちゃんと読みたいときは古本を漁る必要があるかもしれません。
まとめ
当初の目的通り、知識の引き出しを増やせている気がします。雑誌で浅くても触れることができれば、あとは専門の技術本を買うなりググるなりすれば良さそうだと感じました。引き続き雑誌は読み進めていこうと思います。
Composeの起動順番をアプリケーション含め制御する
はじめに
Selenium環境をDocker内で完結させるの続きっぽい感じになっています。ですが、本記事のテーマである「起動順番の制御」は、上の記事を読まなくても理解できるよう心掛けて書きました。
例えば「続きっぽい」と感じられる要素は、実装時に書いたアプリケーション独自のものに留めています(rakeタスクやコンテナのサービスといったもの)。また、不要と思ったコード・コマンドは省略しました。
忙しい人向け
- 起動順番を制御するには、ラッパースクリプトを用意すると良い
- 方法は3種類ある
- dockerize
- wait-for-it
- ラッパーを自作する
- 特に大きな差はないのでお好みで
どんな問題が起こったのか
docker-compose up
で立ち上げると、Selenium::WebDriverクライアントでエラーが発生しました。原因はSelenium::WebDriverクライアントを作るときに、Seleniumコンテナ内のChromeが立ち上がっていないことだった。
対策として、Chromeが立ち上がったあとにクライアントが作られるよう、依存関係を制御する必要があると分かった。
depends_on では駄目なのか
こういった依存関係の制御は、 depends_on
や links
などのコマンドで制御できると思っていました。しかし実際は、サービスの起動順番しか面倒をみないようです。
つまり、コンテナ内のアプリケーションが実行可能かどうかまでは面倒を見てくれないということになります。
この仕様理由は、Compose の起動順番を制御で説明されています。以下は引用。
たとえば、データベースの準備が整うまで待つのであれば、そのことが分散システム全体に対する大きな問題になり得ます。プロダクションでは、データベースは利用不可能になったり、あるいは別のホストに移動したりする場合があるでしょう。 アプリケーションは、障害発生に対して復旧する必要があるためです。 データベースに対する接続が失敗したら、アプリケーションは再接続を試みるように扱わなくてはいけません。アプリケーションは再接続を試みるため、データベースへの接続を定期的に行う必要があるでしょう。
解決策
公式では
アプリケーションのコード上で解決する
ことが望ましいと説明しています。ただ、この処理を用意するのが面倒な場合は、「実行前にアプリケーションが立ち上がるのを待ってくれるラッパー」を用いることで対処できます。
ラッパーの準備は大きく二つに分かれます。
- ツールを使う方法
- 独自のラッパーを使う方法
ツールを使う場合
ラッパー用のツールは主に二つあります。
dockerize
dockerizeは、コンテナ内のアプリケーションの動作に関わる便利機能を提供してくれるツールです。その中でも今回は、アプリケーションがスタートするまで待機する機能を利用します。この機能では、ホスト・ポートだけでなく、HTTP(S)といった上位プロトコルも使ってヘルスチェックすることが可能です。
実装例
実装例は以下の流れになっています。
dockerize
をインストールするdockerize
を-wait
オプションで実行する<protocol>://<host>:<port>
の順番で指定する
- ヘルスチェックが通ったあと、引数のコマンドを指定する
Dockerfile
FROM ruby:2.7.0-alpine # Alpine用のdockerizeをインストール RUN apk add --no-cache openssl ARG DOCKERIZE_VERSION="v0.6.1" RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz
docker-compose.yml
services: ruby: build: . command: ['dockerize', '-wait', 'tcp://chrome:4444', 'bundle', 'exec', 'rake', 'reserve'] volumes: - .:/var/app depends_on: - chrome chrome: image: "selenium/standalone-chrome-debug:3.141.59" ports: - "4444:4444" - "5900:5900" volumes: - /dev/shm:/dev/shm
wait-for-it
wait-for-itはピュアなbashスクリプトで書かれています。そのため、依存関係を極力廃した作りになっています。また機能もシンプルになっており、扱いやすいものになっているのが特徴です。こちらのプロトコルはtcpで固定です。
実装例
Dokerizeと違い、wait-for-itのREADMEには、イメージへの含め方が書いてありませんでした。そのため以下にあげる実装方法は、私が考え調べたものになります。
- Debianのパッケージからインストールする
- willwill/wait-for-it のDockerfileを利用する
今回は後者の方法を使います。
- https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh を直接イメージにADDする
- wait-for-it.sh を実行する
- ヘルスチェックが通ったあと、引数のコマンドを指定する
Dockerfile
FROM ruby:2.7.0-alpine ARG WORK_DIR="/var/app/" WORKDIR ${WORK_DIR} # alpineはbashのインストールが必要 RUN apk add --update --no-cache bash # ルートディレクトリにスクリプトを置く # ${WORK_DIR} 以下に置くと、ローカルがマウントするときファイルが消えてしまうため ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /wait-for-it.sh RUN chmod +x /wait-for-it.sh
docker-compose.yml
services: ruby: build: . command: ['/wait-for-it.sh', 'chrome:4444', '--', 'bundle', 'exec', 'rake', 'reserve'] volumes: - .:/var/app depends_on: - chrome chrome: image: "selenium/standalone-chrome-debug:3.141.59" ports: - "4444:4444" - "5900:5900" volumes: - /dev/shm:/dev/shm
独自のラッパーを使う場合
ラッパースクリプトは自作することもできます。シェルスクリプトで、アプリケーションのヘルスチェックをするのが主です。
ヘルスチェックは、CLIでクライアントが呼び出せる場合
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "postgres" -c '\q'; do
のように、シェルスクリプト内で直接コマンドを実行しても良いです(実装は公式ドキュメント参照)。
今回はSelenium::WebDriverクライアントを作ることができるか調べたいので、ヘルスチェック用のrakeタスクを用意し実行しています。
実装例
実装例は以下の流れになっています。
- ラッパースクリプトである
wait-for-chrome.sh
を実行する - ヘルスチェック用のrakeタスクを実行し、エラーが発生したら数秒間スリープする
- ヘルスチェックが通ったら、引数で受けたコマンドを
exec
する
docker-compose.yml
services: ruby: build: . command: ['./wait-for-chrome.sh', 'bundle', 'exec', 'rake', 'reserve'] volumes: - .:/var/app depends_on: - chrome chrome: image: "selenium/standalone-chrome-debug:3.141.59" ports: - "4444:4444" - "5900:5900" volumes: - /dev/shm:/dev/shm
wait-for-chrome.sh
# シェバンはコンテナのベースイメージに合わせる # ex. alpineなら #!/bin/ash or #!/bin/sh #!/bin/bash set -e cmd="$@" until bundle exec rake health_check; do >&2 echo "chrome is unavailable - sleeping" sleep 3 done >&2 echo "chrome is up - executing command" exec $cmd
おわりに
他にも色々ラッパーはありそうですが、とりあえずここのやり方さえ抑えておけば、起動順番の制御は問題なさそうです。
参考
Selenium環境をDocker内で完結させる
はじめに
とある予約の申し込み作業が面倒になったので、ブラウザ自動操作のためSeleniumを採用しました。色々ローカルに入れるのもアレなので、Docker環境で完結させています。
構成
イメージ図は以下。
docker-compose.yml
以下のようになります。
version: "3" services: ruby: build: . volumes: - .:/var/app depends_on: - chrome chrome: image: "selenium/standalone-chrome-debug:latest ports: - "4444:4444" - "5900:5900" volumes: - /dev/shm:/dev/shm
Seleniumコンテナ
Standalone か Grid か
SeleniumHQ/docker-selenium
の公式イメージを使います。
この公式イメージは大きく、 Standalone
タイプと Grid
タイプに分かれます。テストのときに、複数のOS・ブラウザ環境が欲しいかどうかで決まります。
今回は、ただのバッチ処理(予約のための操作自動化)の用途でSeleniumを使うため、複数ブラウザ環境は必要ありません。 Standalone
タイプのイメージを使います。
debug か否か
Standalone
タイプでも更に、 debug
か否か、タイプ分けすることができます。
debug
とついているイメージは、VNCサーバーを起動することができます。VNCを使うことで、実際にブラウザが立ち上がって動作が完結するまで、処理の動きを目で追うことができます。
今回はselenium/standalone-chrome-debugを使いましたが、ここはお好みで良いと思います。
ちなみに、VNCサーバへのアクセスは
- vnc://localhost:5900 にアクセスする
- パスワード
secret
を打つ
で大丈夫です。
公式イメージの使い方メモ
docker run
や docker-compose up
といった起動コマンド時に、/dev/shm:/dev/shm
とマウントする必要があります。
docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome:3.141.59-yttrium
もしくは --shm-size=2g
と、ホストのメモリ領域を使うために、フラグ立てをする必要があります。
docker run -d -p 4444:4444 --shm-size=2g selenium/standalone-chrome:3.141.59-yttrium
Rubyコンテナ
クライアントを実行できる環境があれば、問題ないです。
# Gemfile gem 'selenium-webdriver'
selenium-webdriver gem を使う
参考リンクは以下になります。
またSeleniumコンテナにアクセスできるよう、localhost
ではなく、 docker-compose.yml
で指定したホスト名を使います。
url = 'http://chrome:4444/wd/hub' @driver = Selenium::WebDriver.for(:remote, url: url, desired_capabilities: :chrome)
wd/hub
は、Selenium Hubコンソールへのリンクです。
疎通確認する
docker-compose run --rm ruby irb
として確認(不必要な返り値は省略しています)。
irb(main):001:0> require 'selenium-webdriver' irb(main):002:0> driver = Selenium::WebDriver.for(:remote, url: 'http://chrome:4444/wd/hub', desired_capabilities: :chrome) irb(main):003:0> driver.get('https://www.google.com/?hl=ja') irb(main):004:0> driver.title => "Google"
おわりに
Seleniumはテストで使うことが主ですが、ブラウザ操作の自動化は、結構捗る範囲が広いと思います。これで予約が楽になれば良いなぁ。