Githubで認証が失敗するときの対応方法

背景

会社PCで会社のGithubのアカウントを利用していました。会社PCに個人開発用のアカウントのリポジトリを作成しようとしたところ、認証が失敗してしまいました。

1
fatal: Authentication failed for リモートURL

検索結果に表示された解決策

ググってみると、だいたい同じような回答が書いてありました。

remote.origin.urlが間違っているから、git config remote.origin.urlで正しい値に設定してね

というものばかりでした。実際にリポジトリのURLを正しく設定しても認証エラーは改善されません。

解決方法

自分の場合はremote.origin.urlにユーザー名を指定することで解決しました。

1
git config remote.origin.url https://username@github.com/username/repository.git

このusername@をつけることで認証成功しました。

まとめ

2021年8月13日でパスワード認証が無効になってしまいます。

https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/

今回はパスワード認証の問題でしたが、次回はパスワード認証をやめる方法を記載したいと思います。

参考図書

Gitで困った時に読む本

SQLで文字列を分割する方法(MySQL)

背景

あるサービスでユーザーはメールアドレスを登録するのですが、そのメールアドレスはどういったドメインが多いのか調べようと思いました。

最初に思いついた方法は、メールアドレスを@で分割して、分割した2つの文字列の2番目がドメインになっていると思うので、それでgroup byするというものでした。

ですが、SQLで文字列を分割する方法を知らなかったので、メモしておきます。

文字列の分割

MySQLにはsubstring_indexという関数があります。
https://dev.mysql.com/doc/refman/5.6/ja/string-functions.html#function_substring-index

substring_indexの引数

substring_index

1
substring_index(分割対象の文字列, 区切り文字, 返す文字列の数)

となっています。

分割対象の文字列と区切り文字については問題ないと思いますが、返す文字列の数はわかりづらいですね。

公式に書いてある例を手元で実行してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2);
+------------------------------------------+
| SUBSTRING_INDEX('www.mysql.com', '.', 2) |
+------------------------------------------+
| www.mysql |
+------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2);
+-------------------------------------------+
| SUBSTRING_INDEX('www.mysql.com', '.', -2) |
+-------------------------------------------+
| mysql.com |
+-------------------------------------------+
1 row in set (0.00 sec)

最初の例はドットで分割して、前から2つなので、www.mysqlが、後ろの例はドットで分割して、-2なので後ろから2つなので、mysql.comが返ってきます。

メールアドレスのドメイン取得

ではメールアドレスのドメインを取得してみようと思います。

1
2
3
4
5
6
7
mysql> SELECT SUBSTRING_INDEX('user@mysql.com', '@', -1);
+--------------------------------------------+
| SUBSTRING_INDEX('user@mysql.com', '@', -1) |
+--------------------------------------------+
| mysql.com |
+--------------------------------------------+
1 row in set (0.00 sec)

できました!

分割文字列の真ん中を取得したい場合

ふと思ったんですが、www.mysql.commysqlのみを取得したい場合はどうするのでしょうか。

1
2
3
4
5
6
7
mysql> SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('www.mysql.com', '.', 2), '.', -1);
+--------------------------------------------------------------------+
| SUBSTRING_INDEX(SUBSTRING_INDEX('www.mysql.com', '.', 2), '.', -1) |
+--------------------------------------------------------------------+
| mysql |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)

こうかな?と思ったんですが、ちょっと煩雑ですね…

メールアドレスのドメインで集計

以上のことがわかったので、集計するクエリを書いてみます

1
2
3
4
5
6
7
8
SELECT
SUBSTRING_INDEX(email, '@', -1) as domain,
COUNT(*)
FROM
users
GROUP BY
domain
;

無事できました。

まとめ

MySQLで文字列を分割する方法を紹介しました。今回の関数はMySQL独自のものですが、その他のプラットフォームでも同じような関数はあると思います。

文字列を操作して、クエリを実行できると集計できる幅も広がりそうですね。久々SQLを書いたのですが、分析は楽しいですね!

yumで [Errno 14] PYCURL ERROR 22 - 'The requested URL returned error 404 Not Found' が表示された時の対応

背景

昔から動いているScientific Linuxにパッケージをインストールしたいと相談を受けたので、とりあえずyumコマンドでインストールしようとしたところ、以下のようなメッセージが表示されました

1
http://ftp.scientificlinux.org/linux/scientific/6.5/x86_64/os/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"

404となっていたので、リポジトリのURLが変わってしまったのかなと思って調査した内容を記載します。

情報の収集

まず、サーバーの情報を集めます。

ディストリビューションについて

/etc/redhat-releaseを確認します。

1
2
# more /etc/redhat-release
Scientific Linux release 6.5 (Carbon)

リポジトリについて

/etc/yum.repos.d/の下のファイルを確認します。

1
2
3
4
# ls /etc/yum.repos.d/
epel-testing.repo mirrors-rpmforge-extras sl-other.repo
epel.repo mirrors-rpmforge-testing sl.repo
mirrors-rpmforge rpmforge.repo sl6x.repo

試しにyum updateを実行してみます。

1
2
3
4
5
6
7
# yum update
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
* sl: ftp.scientificlinux.org
* sl-security: ftp.scientificlinux.org
* sl6x: ftp.scientificlinux.org
* sl6x-security: ftp.scientificlinux.org

以上より、sl, sl-security, sl6x, sl6x-securityのみ有効になっていることがわかります。
それぞれsl.repo, sl6x.repoを修正すればよいことがわかります。

リポジトリのURLの修正

まず、既存のURLを確認します。

エラーになったURLは

1
http://ftp.scientificlinux.org/linux/scientific/6.5/x86_64/os/repodata/repomd.xml

でした。sl.repoでbaseurlが記載されていて

1
baseurl=http://ftp.scientificlinux.org/linux/scientific/$releasever/$basearch/os/

となっていました。なので、一旦http://ftp.scientificlinux.org/linux/scientific/にアクセスしてみます。

すると、バージョンのディレクトリ一覧が表示されました。ここの階層まではディレクトリは存在するようです。しかしバージョンの中には今回の対象である6.5はありませんでした(一番古くても7.0)

バージョン名以外のディレクトリもあったので漁ってみると、obsoleteというディレクトリの下に6.5というディレクトリが存在しました!6.5のディレクトリ内はアーキテクチャの一覧があるので、これでアクセスできそうです!

repoファイルの修正

URLのscientific6.5の間にobsoleteを追加すれば良いので、viで開いて一括置換します。

viで開いた後

1
:%s/\/scientific\//\/scientific\/obsolete\//g

というコマンドで一括置換し、保存します。sl.repo、sl6x.repoに対して行いました。

動作確認

正しくリポジトリにアクセスできるようになったか確認します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# yum update
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
* sl: ftp.scientificlinux.org
* sl-security: ftp.scientificlinux.org
* sl6x: ftp.scientificlinux.org
* sl6x-security: ftp.scientificlinux.org
sl | 3.6 kB 00:00
sl-security | 2.9 kB 00:00
sl6x | 3.7 kB 00:00
sl6x-security | 2.9 kB 00:00
Setting up Update Process
Resolving Dependencies
...
(省略)
...
Is this ok [y/N]: N
Exiting on user Command

無事リポジトリへアクセスできるようになりました。

まとめ

今回は放置されていたサーバーで急な要件が発生して慌てて対応しました。しかし、本来は常日頃からメンテナンスを行い、日々のアップデートに追従していくのが重要だと思います。オンプレならではの問題と思われるかもしれませんが、クラウド移行してもコンテナやマシーンイメージのメンテナンスは重要だと思います。

React Developer Toolsが動作しないときの対処法

背景

React.jsの概要を知りたいと思いReact.js & Next.js超入門 第2版を読みながらたまにサンプルコードを動作させたりしていました。

その本の中で、React Developer Toolsが紹介されていて、Chromeの拡張機能をインストールしました。しかし、利用環境によってはそのままで動作しなかったので、その点について記載します。

React.jsの動作環境

React.jsの動作環境はnpmを用いず、CDN上のreact.development.jsreact-dom.development.jsを読み込むことで実現しました。

VSCodeでhtmlファイルを作成し、React.jsが動作するサンプルコードを記載して、そのファイルをブラウザに読み込ませて動作させていました。なのでURLはfile://で始まっています。

React Developer Toolsが動作しない

サンプルコードをブラウザで表示後、拡張機能のアイコンをクリックしようとしたら、以下のメッセージが表示されました。

1
2
This is a restricted browser page.
React devtools cannot access this page.

ん〜なんでしょうか、ちょっとわからないのでググってみると、stackoverflowに答えが見つかりました。

https://stackoverflow.com/questions/26347489/react-dev-tools-not-loading-in-chrome-browser

1
If you opened a local html file in your browser (file://...) then you must first open chrome extensions and check off "Allow access to file URLs".

設定が必要のようです。

拡張機能の設定

stackoverflowに書いてある通り設定してみます。

  1. Chromeの拡張機能の一覧からReact Developer Toolsをクリック
  2. 設定画面の下の方、ファイルの URL へのアクセスを許可するがオフになっているのでONにする
  3. Chrome Developer Toolsを再起動する

以上になります。

Chrome Developer ToolsのElementsConsoleなどが表示されているところに、ComponentsProfilerが表示されていることが確認できました。Reactのロゴが表示されているのでわかりやすいですね。

まとめ

ファイルのURLはあまり使わないと思いますが、最初に試すときはファイルを読み込ませるのが楽でいいですね。

React Developer Toolsは正しく使うことでReact.jsの理解が捗ると思うので、使い方をちゃんと身に付けたいと思います。

目次マトリクス JavaScript編

背景

今までJavaScriptをしっかり学びたいとずっと思い続けてなかなか行動することができませんでした。

独学大全の目次マトリクスという手法を見て、JavaScriptで試してみようと思いました。

目次マトリクスとは

目次マトリクスとは多くの文献(書籍)を一望することで、共通点などを洗い出す方法です。

わたしが思うに、複数の書籍でしっかりと説明されていることは重要なのかなと思います。そういった点を洗い出すことで、JavaScriptを学ぶ上で重要な箇所を抑えていきたいと思います。

手順

目次マトリクスの作成手順は以下のようになります。

  1. 独学のテーマごとに、マトリクスを作る
  2. 文献のタイトル、著者などを表の左端のマスへ入力する
  3. 目次から「見出し」を拾い出し、マスへ入力する
  4. 必要なら各章の概略を追記する
  5. 同じ/似た内容をマーキングしたり囲んでつないだりする
  6. 文献を横断読みしながら気づいたことを抽出し、整理する

今回は1〜3まで行ってみます。

対象の文献(書籍)

JavaScriptの書籍はたくさんありますが、手元にある中で古すぎず基礎からしっかりと書いてある本と思われるものを3つ挙げてみました。その他の書籍は今後追加するとして、一旦この3冊で始めてみたいと思います。

JavaScript Primer

2020年に読んだ本のなかで、とても良い部類にはいる本でした。Webで参照できるのも魅力的です。

ハンズオンJavaScript

日本人の著者でオライリー本、しかも2020年11月出版ということで、情報も新しいと思い選びました。ボリュームが多く、どこまで情報を拾っていいかわかりませんが、可能であればまるっと理解しておくのが良さそうです。

JavaScriptモダンプログラミング完全ガイド

モダンプログラミングという言葉に惹かれて選びました。2020年12月出版ということで情報も新しいのかなと思います。

JavaScriptの目次マトリクス

では目次マトリクスを作っていきます。横1列に並べると長くなるので5章ごとに区切っています。

第1章 第2章 第3章 第4章 第5章
JavaScript Primer JavaScriptとは コメント 変数と宣言 値の評価と表示 データ型とリテラル
ハンズオンJavaScript 学び始める前に データを学ぶ 処理を学ぶ オブジェクトを学ぶ モジュールを学ぶ
JavaScriptモダンプログラミング完全ガイド 値と変数 制御構造 関数と関数型プログラミング オブジェクト指向プログラミング 数と日付/時刻
第6章 第7章 第8章 第9章 第10章
JavaScript Primer 演算子 暗黙的な型変換 関数と宣言 文と式 条件分岐
ハンズオンJavaScript 基本的な標準オブジェクトを学ぶ コレクションを学ぶ 複雑なデータの扱いを学ぶ 国際化を学ぶ 非同期処理を学ぶ
JavaScriptモダンプログラミング完全ガイド 文字列と正規表現 配列とコレクション 国際化 非同期プログラミング モジュール
第11章 第12章 第13章 第14章 第15章
JavaScript Primer ループと反復処理 オブジェクト プロトタイプオブジェクト 配列 文字列
ハンズオンJavaScript メタプログラミングを学ぶ Webを学ぶ ネットワークを学ぶ ストレージを学ぶ マルチメディアを学ぶ
JavaScriptモダンプログラミング完全ガイド メタプログラミング イテレータとジェネレータ TypeScript入門
第16章 第17章 第18章 第19章 第20章
JavaScript Primer 文字列とUnicode ラッパーオブジェクト 関数とスコープ 関数とthis クラス
ハンズオンJavaScript センサーとデバイスを学ぶ PWAを学ぶ セキュリティを学ぶ パフォーマンスを学ぶ 学び続けるために
JavaScriptモダンプログラミング完全ガイド
第21章 第22章 第23章 第24章 第25章
JavaScript Primer 例外処理 非同期処理: コールバック /Promise/Async Function Map/Set JSON Date
ハンズオンJavaScript
JavaScriptモダンプログラミング完全ガイド
第26章 第27章 第28章 第29章 第30章
JavaScript Primer Math ECMAScriptモジュール ECMAScript アプリケーション開発の準備 ユースケース: Ajax通信
ハンズオンJavaScript
JavaScriptモダンプログラミング完全ガイド
第31章 第32章
JavaScript Primer ユースケース: Node.jsでCLIアプリケーション ユースケース: Todoアプリケーション
ハンズオンJavaScript
JavaScriptモダンプログラミング完全ガイド

まとめ

今回、目次マトリクスを使って、JavaScriptを学ぶ上で重要な箇所を絞り込もうと思いました。

現状ですと、以下のキーワードが共通して出てきていると思います。

  • オブジェクト
  • モジュール
  • 非同期プログラミング
  • メタプログラミング
  • 国際化
  • 配列(コレクション)

今後も書籍を追加していく予定です。

Docker環境でCould not find 'bundler'のエラーが出た時の対処法

背景

久しぶりにあるRailsアプリケーションのローカル開発環境を動作させようとしました。git pullした後にdocker-compose upとすると、

1
2
3
4
5
6
7
Traceback (most recent call last):
2: from /usr/local/bin/bundle:23:in `<main>'
1: from /usr/local/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
/usr/local/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.2.15) required by your /app/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.2.15`
!! exit status 1

というエラーが発生してしまいました。その時の対応を記載します。

bundlerのバージョンアップを試す

gem install bundler

バージョン変更があったのか、とりあえず言われた通りgem install bundlerを実行してみます

1
2
3
4
5
6
7
8
9
10
11
12
$ docker-compose run --rm web gem install bundler
Creating app_web_run ... done
Traceback (most recent call last):
2: from /usr/local/bin/bundle:23:in `<main>'
1: from /usr/local/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
/usr/local/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.2.15) required by your /app/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
...
(中略)
...
To install the missing version, run `gem install bundler:2.2.15`
!! exit status 1

メッセージは変わりません…

bundle update –bundler

もう1つの方法、bundle update —bundlerを行なってみましょう

1
2
3
4
5
6
7
8
9
$ docker-compose run --rm web bundle update --bundler
Creating app_web_run ... done
Traceback (most recent call last):
2: from /usr/local/bin/bundle:23:in `<main>'
1: from /usr/local/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
/usr/local/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.2.15) required by your /app/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.2.15`
!! exit status 1

変わりません…

ローカルのRubyの確認

DockerではなくローカルのRubyのバージョンを確認してみます

1
2
3
4
$ ruby -v
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin17]
$ cat .ruby-version
2.7.2

コンテナ内のバージョンとはあってないみたいですね…
Bundlerのバージョンも確認します

1
2
$ bundler --version
Bundler version 2.1.4

Gemfileに記載のバージョンとは異なるようです。updateできるか試してみます。

1
2
$ bundle update --bundler
Warning: the running version of Bundler (2.1.4) is older than the version that created the lockfile (2.2.15). We suggest you to upgrade to the version that created the lockfile by running `gem install bundler:2.2.15`.

ローカルもできないようです

コンテナのbundlerのバージョンを更新する
一旦すべてのイメージとボリュームを削除することにしました

1
2
3
4
5
6
7
8
$ docker-compose down --rmi all --volumes
Removing app_web_1 ... done
Removing app_db_1 ... done
Removing network app_default
Removing volume app_mysql-data
Removing volume app_bundle
Removing image mysql:5.7
Removing image app_web

削除ができたのでdocker-compose upします

1
2
3
4
5
6
7
8
9
10
11
12
13
docker-compose up
Creating network "app_default" with the default driver
Creating volume "app_mysql-data" with default driver
Creating volume "app_bundle" with default driver
Pulling db (mysql:5.7)...
5.7: Pulling from library/mysql
...
(中略)
...
Step 1/14 : FROM ruby:2.7.2
2.7.2: Pulling from library/ruby
004f1eed87df: Pull complete
5d6f1e8117db: Pull complete

ruby:2.7.2のベースイメージからコンテナをビルドしていることがわかります
この後bundle installも終わって無事起動しました

まとめ

コンテナでバージョンの不一致が発生したら、以下のコマンドで一旦コンテナを削除する

1
docker-compose down --rmi all --volumes

MySQLのデータ(データベース、テーブルなど)も全てなくなるので注意が必要です

ローカルのRubyバージョンとコンテナのバージョンの問題を混同しないように気をつけないといけません
この切り分けがはっきりしていないと間違った方向に進んでしまいます

あとから思ったのですが、コンテナを再ビルドすれば大丈夫だったかもしれません

1
docker-compose build

Dockerfileが変更されているはずなのでこちらでも必要なコンテナイメージの再ビルドが行われたと思います

assets precompileで発生するエラー

背景

久々にコーダーが実装したcssをRailsに組み込んだら、エラーが発生してあまりうまくいかなかったのでメモしておきます。

css内の画像指定にimage-urlを指定した場合

エラー内容

cssのbackgroundで指定されている画像はimage-urlで指定します。

1
background: image-url("test/test.png") no-repeat;

引数のパスはapp/assets/images/の下から指定します。

このcssをapplication.scssで読み込むようにします。

1
@import 'test';

すると、以下のようなエラーが発生しました。

1
NoMethodError: undefined method `[]' for nil:NilClass

原因

ファイルの拡張子をcssのままで読み込んでいたため。scssとして読み込む必要があります。

参考 : https://stackoverflow.com/questions/12313612/rake-assetsprecompile-undefined-method-for-nilnilclass

cssをimportする場合

エラー内容

上記エラーが解消されたので、これでOKと思っていたら以下のエラーが発生しました。

1
stack level too deep

原因

importするファイル名はアンダースコアから開始する必要があります。

1
$ mv test.scss _test.scss

これでエラーは解消します。

まとめ

久々にcssの組み込みを行うと忘れていることが多くて戸惑ってしまいます。知識を蓄積するために書き残していくことを続けていきたいと思います。

BigQueryで時間単位の列でのパーティション分割テーブルを作成する

背景

BigQueryでパーティショニングテーブルを作成することになりました。以前も作ったのですが、2度目作るにあたって情報がまとまってないことに気づいたのでこちらに記載していきます。

BigQueryのパーティショニング

BigQueryのパーティショニングの種類は以下のページにまとまっています。

https://cloud.google.com/bigquery/docs/partitioned-tables

今回のデータは日単位で集計したいと思っています。かつ、BigQueryでは標準時間になってしまうので、こちらで指定したカラムにdate型のデータを保持して、そのカラムの値を基準にパーティショニングしたいと思います。

分割テーブルの作成

では実際に作成していきます

スキーマの作成

スキーマはjsonファイルにまとめます

1
2
3
4
5
6
7
8
9
10
11
12
[
{
"name": "id",
"type": "STRING",
"mode": "REQUIRED"
},
{
"name": "created_date",
"type": "DATE",
"mode": "REQUIRED"
}
]

created_dateというカラムを基準にパーティショニングします。test.jsonというファイルで保存します。

テーブルの作成

bqコマンドでテーブルを作成します。コマンドのオプションについてはこちらに記載があります。

オプションにはいろんな種類がありますが、利用するのは--schema--time_partitioning_fieldだけです。(--time_partitioning_typeはデフォルトがDAYのため)

1
2
$ bq mk --table --schema test.json --time_partitioning_field created_date project:dataset.test
Table 'project:dataset.test' successfully created.

テーブルの作成が終わったので内容を確認します。GCPのコンソールから作成したテーブルを選択、右の詳細タブを選択します。

表の情報の中にパーティションに関する情報が表示されます

1
2
3
4
テーブルタイプ         分割
パーティショニングの基準 Day
フィールドで分割     created_date
パーティション フィルタ 不要

正しく作成できました。

まとめ

BigQueryでパーティションテーブルを作成する方法をまとめました。次回からはスムーズに行えると思います。

ChromeDriverのバージョン自動判別

Dockerコンテナビルド後にRSpecでエラー

Dockerコンテナを久々にビルドしてRSpecを実行したらエラーが発生しました。

1
2
Selenium::WebDriver::Error::SessionNotCreatedError:
session not created: This version of ChromeDriver only supports Chrome version 84

原因はChromeとChromeDriverのバージョンがあっていないからです。

以前は動作していましたが、今回コンテナを再ビルドしたことによって問題が発生したような形になっています。原因は何でしょうか。

DockerでChromeDriverのバージョンを固定で指定することの問題点

Dockerfileを見てみましょう。

1
2
3
4
5
6
7
8
RUN yum install -y unzip \
https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm && \
sed -i 's|HERE/chrome"|HERE/chrome" --disable-dev-shm-usage --disable-setuid-sandbox --no-sandbox|g' \
"/opt/google/chrome/google-chrome" && \
cd /usr/local/src/ && \
wget https://chromedriver.storage.googleapis.com/84.0.4147.30/chromedriver_linux64.zip && \
unzip chromedriver_linux64.zip && \
install chromedriver /usr/local/bin/

この記述を見ると、google-chrome-stable_current_x86_64.rpmがバージョン84であるときは正常に動作するのですが、バージョンが更新されてしまうと、バージョン固定でインストールしているChromeDriverとバージョンが合わなくなってしまいます。

ということはDockerコンテナをビルドするタイミングでうまくいったり行かなかったりしてしまうということです。これはよくないですね。

ChromeDriverのバージョン自動判別

そこでインストールされるGoogle ChromeのバージョンにあったChromeDriverをインストールするように変更してみます。

先ほどのDockerfileのRUNコマンドを以下のように変更してみます。

1
2
3
4
5
6
7
8
9
10
RUN yum install -y unzip \
https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm && \
sed -i 's|HERE/chrome"|HERE/chrome" --disable-dev-shm-usage --disable-setuid-sandbox --no-sandbox|g' \
"/opt/google/chrome/google-chrome" && \
cd /usr/local/src/ && \
wget -O LATEST_RELEASE_ "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$(rpm -q --queryformat='%{Version}' google-chrome-stable | sed -e 's/[.][0-9]*$//')" && \
wget "https://chromedriver.storage.googleapis.com/$(cat LATEST_RELEASE_)/chromedriver_linux64.zip" && \
rm LATEST_RELEASE_ && \
unzip chromedriver_linux64.zip && \
install chromedriver /usr/local/bin/

変更があったのは以下の3行です

1
2
3
wget -O LATEST_RELEASE_ "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$(rpm -q --queryformat='%{Version}' google-chrome-stable | sed -e 's/[.][0-9]*$//')" && \
wget "https://chromedriver.storage.googleapis.com/$(cat LATEST_RELEASE_)/chromedriver_linux64.zip" && \
rm LATEST_RELEASE_ && \

なにを行なっているのか一つずつみていきましょう。

インストールされているChromeのバージョン取得

まずは1行目のコマンド置換している箇所を見ていきます。

1
rpm -q --queryformat='%{Version}' google-chrome-stable

インストールされているgoogle-chromeをrpmコマンドを使って問い合わせています。--queryformatというオプションはあまり見かけませんが、manページによると以下のようなオプションです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
検索オプション
rpm 検索の一般的な形式は:

rpm {-q|--query} [select-options] [query-options]

表示されるパッケージ情報の書式を指定することができる。 そうするために
は、

--qf|--queryformat QUERYFMT

オプションを使う。 QUERYFMT が書式文字列である。検索の書式は、標準的な
printf(3) の書式の修正である。書式は静的な文字列 (改行・タブ・その他の
特殊文字の、C 言語の標準的な文字エスケープを含む)と、 printf(3) 型の書
式文字列からなる。 しかし rpm は表示する型を既に知っているので、型指定
は省略されるべきである。 代わりに {} で囲まれた、表示されるへッダーのタ
グ名が使用される。 タグ名に大文字/小文字の区別はなく、タグ名の先頭の
RPMTAG_ 部分も省略することができる。

利用できるタグはrpm --querytagsを実行することで全て表示することができます。AmazonLinux2で実行したら、205ありました。

それではrpmコマンドを実際に実行してみます。

1
2
# rpm -q --queryformat='%{Version}' google-chrome-stable
86.0.4240.75

バージョン番号のみ表示されました。

ちなみに通常の出力は以下です。

1
2
# rpm -q google-chrome-stable
google-chrome-stable-86.0.4240.75-1.x86_64

Chromeのバージョン番号の整形

バージョン番号をパイプで渡してsedに入力として渡しています。sedの正規表現を見てみましょう。

1
's/[.][0-9]*$//'

[.]\.でも良さそうですね。数字が0個以上繰り返して、$は行末にマッチします。バージョン番号の最後のオクテットを''に置換しているということになります。

実際に試してみましょう。

1
2
$ echo '86.0.4240.75' | sed -e 's/[.][0-9]*$//'
86.0.4240

これでコマンド置換している箇所の文字列が取得できました。次は実際にwgetをしてみます。

ChromeDriverのバージョンを保存

先ほどあったコマンド

1
wget -O LATEST_RELEASE_ "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$(rpm -q --queryformat='%{Version}' google-chrome-stable | sed -e 's/[.][0-9]*$//')"

1
wget -O LATEST_RELEASE_ "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_86.0.4240"

であることがわかりました。wgetの-Oオプションは、取得したコンテンツを出力するファイル名を指定します。

実際に実行してしましょう。

1
2
3
4
5
6
7
8
9
$ wget -O LATEST_RELEASE_ "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_86.0.4240"
--2021-02-22 21:01:33-- https://chromedriver.storage.googleapis.com/LATEST_RELEASE_86.0.4240
接続要求を送信しました、応答を待っています... 200 OK
長さ: 12 [text/plain]
`LATEST_RELEASE_' に保存中

LATEST_RELEASE_ 100%[=========================================================================================================================>] 12 --.-KB/s 時間 0s

2021-02-22 21:01:33 (186 KB/s) - `LATEST_RELEASE_' へ保存完了 [12/12]

LATEST_RELEASE_に保存できました。中身を確認します。

1
2
$ cat LATEST_RELEASE_ 
86.0.4240.22

これでインストールすべきChromeDriverのバージョンが

ChromeDriverのダウンロード

実際にChromeDriverがダウンロードできるかどうか確認します。

1
2
3
4
5
6
7
8
9
$ wget "https://chromedriver.storage.googleapis.com/$(cat LATEST_RELEASE_)/chromedriver_linux64.zip"
--2021-02-22 21:14:24-- https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip
接続要求を送信しました、応答を待っています... 200 OK
長さ: 5456932 (5.2M) [application/zip]
`chromedriver_linux64.zip' に保存中

chromedriver_linux64.zip 100%[=========================================================================================================================>] 5.20M 3.17MB/s 時間 1.6s

2021-02-22 21:14:26 (3.17 MB/s) - `chromedriver_linux64.zip' へ保存完了 [5456932/5456932]

無事ダウロードできました。これで、いつでもChromeのバージョンにあったChromeDriverがダウンロード、インストールできるようになりました。

まとめ

  • ChromeDriverの最新バージョンはChromeのバージョンの一部で構成されるURLで取得できる
  • そのURLはhttps://chromedriver.storage.googleapis.com/LATEST_RELEASE_バージョン番号である
  • Chromeのバージョンは少し加工する必要がある

nginxのログを解析する

nginxのログ分析

業務でnginxのログを分析する必要が出てきたので、調査してみました。同じようなことは誰かやっていそうなのですが、あまり情報が出てきません…なのでRubyで簡単に扱えるようなものを作ろうと思いました。

nginxのログフォーマットの確認

まずはnginxのログフォーマットを確認します。nginx.confを確認します。

1
2
3
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$upstream_response_time" "$request_time"';

この形式はデフォルトなんですかね…

CSVクラスの利用

どのようにして処理をしようかと考えて、CSVクラスを利用することにしました。CSVクラスはセパレータをcol_sepで指定できますし、ダブルクォートで囲まれたセパレータは無視してくれます。

時刻のセパレータを変換

1点困った点がありました。時刻のカラムの処理です。時刻のカラムは

1
[12/Feb/2021:03:20:08 +0900]

と言ったように、[]で囲まれ、+0900の前には空白があります。これをうまく一つのカラムとして処理しなければなりません。log_formatを適切に設定しておけばよかったかもしれませんが、現状こうなってしまっているのでなんとかしなければいけません。

そこで[]""に変換すれば時刻もダブルクォートで囲うことができます。そのように変換するようにします。

クラスの作成

今回のメインの目的はremote_addrやx_fowarded_forの取得だったので、以下のようなクラスを作成しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
require 'csv'

class NginxLog
def initialize(raw)
tmp = raw.gsub(' [', ' "').gsub('] ', '" ')
@log = CSV.parse(tmp, col_sep: ' ', liberal_parsing: true).flatten
end

def remote_addr
@log[0]
end

def x_forwarded_for_ips
ips = @log[9].split(',').map &:strip
ips.reject { |ip| ['-', 'unknown'].include? ip }
end

def remote_ips
x_forwarded_for_ips << remote_addr
end
end

コンストラクタの引数に生のログを1行いれてもらえれば、必要な情報を取得できるようにします。

1
2
3
4
log = '1.1.1.1 - - [12/Feb/2021:23:48:48 +0900] "GET / HTTP/1.1" 200 38485 "https://book-reviews.blog/" "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Mobile/15E148 Safari/604.1" "2.2.2.2" "0.403" "0.404"'
nginx_log = NginxLog(log)
puts nginx_log.remote_ips
=> ["1.1.1.1", "2.2.2.2"]

まとめ

今回はCSVクラスを使ってnginxのログを解析してみました。CSVクラスはCSVだけでなく、いろんな形式のファイルに利用できそうです。

本来であればログ解析基盤にログを集約し、いつでも分析できるような状況を整えておくのがよいかと思います。