
今回はとある開発をする際にデータベースへのアクセス問題が発生し、それを解決した経緯をブログに備忘録しておく自分用の記事です。
仕事でLAMP環境を構築する時に、ローカルのパソコン環境で、データベースアクセスができない場合の人には、ちょっとだけ参考になるかもしれません。
今回発生した問題
とある
新規プロジェクトで、公開サーバーはすでに準備されていて、データベースも用意されていたので、
ローカル開発では、Dockerでnginx+phpの環境を作り、mysqlのPDOを使ってアクセスして、クエリを実行すればいい・・・
という安易な気持ちで、以前作ったsshトンネルのシステムを実行してみたところ、トンネル接続できない?!
この段階でいくつかのトラップがあった。
> Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax;
これは、Dockerのphpコンテナに、PDO()を実行するけど、mysqlのpdoが存在しないよ!というエラーでした。
pdo-mysqlが入っていないので、コンテナにpdo-mysqlを追加して、buildし直せば、解消されました。
でも、続けて次のエラー。
> SQLSTATE[HY000] [2002] Connection refused
これは、データベースにアクセスできないエラー。
ここからほぼ1日を費やすエラー
解消の冒険と謎解きゲームが始まる。
コマンドではアクセスできるのに、PHPでアクセスできない
以前調べたコマンドは、PHPで実行するものではなく、むしろターミナルコマンドで実行するのが一般的で、
phpで実行する場合、execコマンドを使って、単純にコマンドを実行しているだけに過ぎない。
ということで、ターミナルコマンドを開いて、実際にコードを実行してみる。
実行したコード(ドメインと鍵ファイルのパスは、伏せてます)
$ ssh -f -g -N -C -L 23306:SQLサーバーへのアクセス(同一サーバーの場合は、127.0.0.1):3306 -i 鍵ファイルのパス sshアカウント@sshホスト -p 10022
上記は、23306という仮想ポートを、3306に繋ぎ合わせるトンネルコマンドで、
この後、次のコマンドを実行すると、トンネル先のmysqlにアクセスできるようになる。
$ mysql -u mysqlユーザー名 -P <ポート番号> -p
そして、23306というポートが開かれているので、次のコマンドで、ポートの確認ができる。
> lsofコマンドの場合
$ lsof -i:<ポート番号>
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh *** root ** IPv4 ****** 0t0 TCP *:<ポート番号> (LISTEN)
ssh *** root ** IPv6 ****** 0t0 TCP *:<ポート番号> (LISTEN)
> netstatコマンドの場合
$ netstat -anp | grep <ポート番号>
tcp 0 0 0.0.0.0:<ポート番号> 0.0.0.0:* LISTEN ***/ssh
tcp 0 0 :::<ポート番号> :::* LISTEN ***/ssh
上記のコマンド直打ちでは、正常に、23306ポートが立ち上がっているのが確認できます。
これを、phpコードで書くと次の様になる。
<?php
$cmd = "ssh -f -g -N -C -L 23306:SQLサーバーへのアクセス(同一サーバーの場合は、127.0.0.1):3306 -i 鍵ファイルのパス sshアカウント@sshホスト -p 10022";
exec($cmd);
$dns = "mysql:host=127.0.0.1;port=23306;charset=utf8";
$user = "mysqlのユーザー";
$pass = "mysqlのパスワード";
$sql = new \PDO($dns, $user, $pass);
$res = $sql->query("SHOW DATABASES");
$data = [];
foreach ($res as $val){
$datas[] = $val;
}
print_r($datas);
通常であれば、これを実行すると、データベースリストが結果として返ってくるんですが、PHPで実行すると、上記のエラーが表示されるだけでした。
DockerのAlpine(linux)が原因なのか?
dockerのコンテナイメージは、linuxのデータを入れると膨大に膨れ上がってストレージを圧迫してしまうので、alpine(軽量linux)を使う様にしています。
これが原因なのか?と考えて、ubuntu-linuxに切り替えようか悩んだんですが、この段階での切り替えはまだ時期早々と思い、思いとどまる。
PHPの設定に問題があるのか?
php.iniなどで、セキュリティ対策で、disable_functionsという設定内に、使用できない関数を登録する場合がある。
レンタルサーバーなどでよくセットされている内容だが、今回はdocker内の設定なので確認したところ、セットはされていませんでした。
SSHのモジュールに問題があるのか?
実行側のsshのバージョンか、特定のオプションがローカルで実行されていないのか確認したけど、どれも正常に動いている。
そもそも、ターミナルコマンドでは実行できているので、sshコマンドは疑うべきではないのかも。
サーバーによってトンネルの方式を変えないといけないのか?
そもそも、アクセスするサーバーのディストリビューションや、バージョンに応じてアクセスできない場合もあるのか?
これも、ターミナルコマンドで成功しているので、原因とは考えにくい。
詳細調査
ここまで時間がかかっていたので、AIにも聞いてみた。
・・・が、概ね上記の懸念点をリストアップされるだけで、dockerのコンテナを作り直せだとか、ubuntuで使えとか、無茶振りばかりしてくるので、ほぼ却下!
PHPでトンネル単独のコードを書いて検証
こうした問題が発生した時に、エンジニアの対応として、原因の究明のために、実行コードを分解して探索する事が重要。
という事で、以下の様なコードを書いて、PHPで実行してみた。
$cmd = "ssh -i 鍵ファイルのパス sshアカウント@sshホスト -p 10022 ls";
exec($cmd, $res);
print_r($res);
※-pのポート番号は、必要があるときだけ書くので大丈夫です。
この$cmdのコマンドだけを、ターミナルで実行すると、ログインした階層で、lsコマンドを実行した結果が返ってくる。
そして、PHPで実行してみたところ・・・何も返答が返ってきませんでした。
どうやらトンネルを掘る前に、sshアクセス自体がうまくいっていないという事が判明。
でも、なんで?となるよね。
sshアクセスをコマンドで実行した時に、下記の様な確認メッセージが表示されることがある。
> Are you sure you want to continue connecting (yes/no)?
これを解消するために、~/.ssh/config に、 以下の記述をした。
host 対象ドメイン
StrictHostKeyChecking no
この設定を、Docker内のconfigに反映するために、Dockerfileの記述内に、以下を記述。
COPY config /ユーザーフォルダ/.ssh/config
この時点で、コマンドでは、yes/no確認表示は出なくなったものの、PHPでは、相変わらずアクセスできない状態が続いている。
原因発覚!
どうやら、phpのexecでコマンド実行をする際に、config設定は適用されずに、sshでアクセスできていないという事がわかった。
というのも、上記のStrictHostKeyCheckingという設定は、sshコマンドに直接書く方法もあり、以下のコマンドをphpで実行させたら、無事にアクセスできた。
対策(問題解決する方法)
先ほどのコードのsshの-iオプションの手前に以下を加えます。
-o StrictHostKeyChecking=no
これで正常にアクセスする事がわかったので、phpでは、configが正常に動作せずに、execで使う場合は、コマンドオプションにつける必要がある事がわかった。
追記
しかし、これでもエラーが出る場合があった!
そんな時は、sshコマンドに、先ほどのコードに次のオプションを追加するとうまく接続できる様になった。
-oHostKeyAlgorithms=+ssh-rsa -oPubkeyAcceptedAlgorithms=+ssh-rsa
クッソ長くなるけど、安定したアクセスにしたい場合は、この辺のオプションを使って試してみるといいでしょう。
あとがき
「お仕事の疑問は、休日に解消する」をモットーのエンジニアなので、日曜日の貴重な1日を今回の検証に費やしてしまいました。
でも、おかげでブログを一本書けたし、今後色々な場面で使えるコードを手に入れる事ができたので、収穫ボリュームは無限大です。(個人的感想)
何より、これで仕事が進めると思うと、非常になんだかワクワクするぐらい仕事人間な自分にも気がついて、自己分析もできてしまうというオマケつき。
これでも、今付けている腕時計で計測するストレス値は、「ほぼなし」状態なんですよね。
ホントにプログラミング開発が自分にとって転職だと改めて思い知らされた休日でした。
0 件のコメント:
コメントを投稿