Rubyと筋肉とギターとわたし

筋トレが仕事です

【Heroku】akhileshns/heroku-deploy@v3.12.12でRailsをデプロイするとerror:0308010C:digital envelope routines::unsupportedが発生する件について

どうもてぃ。

暖冬と言われていますが、一気にさむくなりましたね。体調には充分気をつけて残り少ない今年を乗り切りたいです。

今回は結構苦戦しました。

当方、Railsアプリケーション(not api mode)をGitHub Actionを用いてHerokuへ自動デプロイをしてます。が、先日突然nodejsのバージョン違いで発生するopensslのエラーがデプロイ時に出たので解決するに至りました。

現状出ている記事のどれを参考にしても解決できず、力技でなんとかしましたのでご参考に。

環境

  • Ruby 2.7.8 (alpine image)
  • Rails 6.0.6
  • nodejs v16.20.1

エラーログ

remote: -----> Installing node-v20.9.0-linux-x64        
remote: -----> Installing yarn-v1.22.19        
remote: -----> Detecting rake tasks        
remote: -----> Preparing app for Rails asset pipeline        
remote:        Running: rake assets:precompile        
remote:        /tmp/build_0eb98de1/vendor/ruby-2.7.8/lib/ruby/2.7.0/net/protocol.rb:66: warning: already initialized constant Net::ProtocRetryError        
remote:        /tmp/build_0eb98de1/vendor/bundle/ruby/2.7.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:68: warning: previous definition of ProtocRetryError was here        
remote:        /tmp/build_0eb98de1/vendor/ruby-2.7.8/lib/ruby/2.7.0/net/protocol.rb:206: warning: already initialized constant Net::BufferedIO::BUFSIZE        
remote:        /tmp/build_0eb98de1/vendor/bundle/ruby/2.7.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:214: warning: previous definition of BUFSIZE was here        
remote:        /tmp/build_0eb98de1/vendor/ruby-2.7.8/lib/ruby/2.7.0/net/protocol.rb:503: warning: already initialized constant Net::NetPrivate::Socket        
remote:        /tmp/build_0eb98de1/vendor/bundle/ruby/2.7.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:541: warning: previous definition of Socket was here        
remote:        ** [NewRelic] FATAL : Config file at /tmp/build_0eb98de1/config/newrelic.yml doesn't include a 'staging' section!        
remote:        D, [2023-11-05T22:31:32.095473 #308] DEBUG -- sentry: Initializing the background worker with 2 threads        
remote:        D, [2023-11-05T22:31:32.095593 #308] DEBUG -- sentry: [Sessions] Sessions won't be captured without a valid release        
remote:        RAILS_ENV=staging environment is not defined in config/webpacker.yml, falling back to production environment        
remote:        yarn install v1.22.19        
remote:        [1/4] Resolving packages...        
remote:        [2/4] Fetching packages...        
remote:        [3/4] Linking dependencies...        
remote:        warning " > @babel/plugin-proposal-private-methods@7.18.6" has unmet peer dependency "@babel/core@^7.0.0-0".        
remote:        warning "@babel/plugin-proposal-private-methods > @babel/helper-create-class-features-plugin@7.22.15" has unmet peer dependency "@babel/core@^7.0.0".        
remote:        warning "@babel/plugin-proposal-private-methods > @babel/helper-create-class-features-plugin > @babel/helper-replace-supers@7.22.20" has unmet peer dependency "@babel/core@^7.0.0".        
remote:        warning " > @babel/plugin-proposal-private-property-in-object@7.21.11" has unmet peer dependency "@babel/core@^7.0.0-0".        
remote:        warning "@babel/plugin-proposal-private-property-in-object > @babel/plugin-syntax-private-property-in-object@7.14.5" has unmet peer dependency "@babel/core@^7.0.0-0".        
remote:        [4/4] Building fresh packages...        
remote:        Done in 14.64s.        
remote:        Compiling...        
remote:        Compilation failed:        
remote:        Error: error:0308010C:digital envelope routines::unsupported        
remote:            at new Hash (node:internal/crypto/hash:68:19)        
remote:            at Object.createHash (node:crypto:138:10)        
remote:            at CompressionPlugin.taskGenerator (/tmp/build_0eb98de1/node_modules/compression-webpack-plugin/dist/index.js:163:38)        
remote:            at taskGenerator.next (<anonymous>)        
remote:            at /tmp/build_0eb98de1/node_modules/compression-webpack-plugin/dist/index.js:216:49        
remote:            at CompressionPlugin.runTasks (/tmp/build_0eb98de1/node_modules/compression-webpack-plugin/dist/index.js:236:9)        
remote:            at /tmp/build_0eb98de1/node_modules/compression-webpack-plugin/dist/index.js:270:18        
remote:            at _next0 (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:37:17)        
remote:            at eval (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:53:1)        
remote:            at WebpackAssetsManifest.handleEmit (/tmp/build_0eb98de1/node_modules/webpack-assets-manifest/src/WebpackAssetsManifest.js:486:5)        
remote:            at AsyncSeriesHook.eval [as callAsync] (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:49:1)        
remote:            at AsyncSeriesHook.lazyCompileHook (/tmp/build_0eb98de1/node_modules/tapable/lib/Hook.js:154:20)        
remote:            at Compiler.emitAssets (/tmp/build_0eb98de1/node_modules/webpack/lib/Compiler.js:491:19)        
remote:            at onCompiled (/tmp/build_0eb98de1/node_modules/webpack/lib/Compiler.js:278:9)        
remote:            at /tmp/build_0eb98de1/node_modules/webpack/lib/Compiler.js:681:15        
remote:            at AsyncSeriesHook.eval [as callAsync] (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)        
remote:            at AsyncSeriesHook.lazyCompileHook (/tmp/build_0eb98de1/node_modules/tapable/lib/Hook.js:154:20)        
remote:            at /tmp/build_0eb98de1/node_modules/webpack/lib/Compiler.js:678:31        
remote:            at AsyncSeriesHook.eval [as callAsync] (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)        
remote:            at AsyncSeriesHook.lazyCompileHook (/tmp/build_0eb98de1/node_modules/tapable/lib/Hook.js:154:20)        
remote:            at /tmp/build_0eb98de1/node_modules/webpack/lib/Compilation.js:1423:35        
remote:            at AsyncSeriesHook.eval [as callAsync] (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:6:1)        
remote:            at AsyncSeriesHook.lazyCompileHook (/tmp/build_0eb98de1/node_modules/tapable/lib/Hook.js:154:20)        
remote:            at /tmp/build_0eb98de1/node_modules/webpack/lib/Compilation.js:1414:32        
remote:            at eval (eval at create (/tmp/build_0eb98de1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:14:1)        
remote:            at process.processTicksAndRejections (node:internal/process/task_queues:95:5)        
remote:                
remote:         
remote:  !        
remote:  !     Precompiling assets failed.        
remote:  !        
remote:  !     Push rejected, failed to compile Ruby app.        
remote: 
remote:  !     Push failed        
remote: Verifying deploy...        

解決方法

  • GitHub Actionに NODE_OPTIONS=--openssl-legacy-provider を追加する
  • package.jsonでnode versionを固定する(engine指定)

↑2つの方法では解決できませんでした。今出てる解決方法はだいたいそれな気がする。

実際自分がやったのはHeroku側で NODE_OPTIONS=--openssl-legacy-provider を設定することでした。

  1. 該当アプリケーション選択
  2. Settingsへ
  3. Config VarsでReveal Config Varsを押す
  4. NOTE_OPTIONS, --openssl-legacy-provider を追加

これがアプリケーションコードを修正せずHeroku側の設定のみで解決できる方法です。

デプロイ検証するために毎度コードの設定をいじってGitHub ActionsをReRunする必要はありませんでした。

おわりに

おわり。

【wordpress】contact form 7でinternal server errorが発生するときの対処法

お久しぶりです、もてぃです。

前回記事を書いてから半年以上が経過し、もうあと2ヶ月で今年も終わってしまいますね。時間の流れは残酷です。

今回はグループ会社のwordpressを新サーバーに移行した際に起きたエラーの解決方法を書いていきます。

新・旧サーバーについて

旧サーバー

諸事上によりphp, kusanagiを吹き飛ばしてしまったのでうる覚えです。

新サーバー

いずれもwebサーバーはapacheを使用しています(本当はnginxを使いたいが普段管理している人が使えないため)

contact form 7で起きたエラー

送信時に何も起きなかったので開発者コンソールを確認しました。それが以下。

error

さらに中を見てみる

error dev tool

全く意味不明。

なのでサーバーにsshしてログを確認します。

ログを確認する

centos stream 9のkusanagiで構築したサーバーの設定ファイルの場所は少し特殊です。

例えばapachephp-fpmの場所は /etc/opt/kusanagi配下に作成されます(/etc配下ではないです)。

ログも同じく変わった場所にあり、 /var/opt/kusanagi/log 配下にあります。今回対象のログは /var/opt/kusanagi/log/php-fpm/error.log を見ればOK。

ログの中身が以下。

NOTICE: PHP message: PHP Warning:  Attempt to read property "post_parent" on null in /home/kusanagi/hogehoge/DocumentRoot/wp/wp-content/plugins/code-snippets/php/snippet-ops.php(581) : eval()'d code on line 3
NOTICE: PHP message: PHP Fatal error:  Uncaught TypeError: implode(): Argument #2 ($array) must be of type ?array, string given in /home/kusanagi/hogehoge/DocumentRoot/wp/wp-content/plugins/contact-form-7-to-database-extension/CF7DBPlugin.php:849
Stack trace:
#0 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-content/plugins/contact-form-7-to-database-extension/CF7DBPlugin.php(849): implode()
#1 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-content/plugins/contact-form-7-to-database-extension/CFDBIntegrationContactForm7.php(55): CF7DBPlugin->saveFormData()
#2 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-includes/class-wp-hook.php(309): CFDBIntegrationContactForm7->saveFormData()
#3 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-includes/class-wp-hook.php(331): WP_Hook->apply_filters()
#4 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-includes/plugin.php(522): WP_Hook->do_action()
#5 /home/kusanagi/hogehoge/DocumentRoot/wp/wp-content/plugins/contact-form-7/includes/submission.php(616): do_...

CF7DBPlugin.phpの849行目を修正すればよさそう。でも可能ならプラグインの中身はさわりたくないなぁ…ということで検索。

同じ人いたけど、ちがうっぽい。

wordpress.org

どうしたものか。。

php implode()の確認

そもそものエラー内容の implode()関数がどういうふうに使われるか確認してみる。

リファレンス

www.php.net

implode(string $separator, array $array): string

Rubyでいう Array#join みたいな挙動。

今の環境がphp8なので

implode(array $array, string $separator): string

の挙動が削除されているとのこと。

contact form 7 DBの該当箇所を修正する必要があるなこれ。

該当ファイル

 843             foreach ($cf7->posted_data as $name => $value) {
 844                 $nameClean = stripslashes($name);
 845                 if ($this->fieldMatches($nameClean, $noSaveFields)) {
 846                     continue; // Don't save in DB
 847                 }
 848 
 849                 $value = is_array($value) ? implode($value, ', ') : $value; // ここ!!!!
 850                 $valueClean = stripslashes($value);
 851 
 852                 // Check if this is a file upload field
 853                 $didSaveFile = false;
 854                 if ($cf7->uploaded_files && isset($cf7->uploaded_files[$nameClean])) {
 855                     $foundUploadFiles[] = $nameClean;
 856                     $filePath = $cf7->uploaded_files[$nameClean];
 857                     if ($filePath) {
 858                         $content = file_get_contents($filePath);
 859                         $didSaveFile = $wpdb->query($wpdb->prepare($parametrizedFileQuery,
 860                             $time,
 861                             $title,
 862                             $nameClean,
 863                             $valueClean,
 864                             $order++,
 865                             $content));
 866                         if (!$didSaveFile) {
 867                             $this->getErrorLog()->log("CFDB Error: could not save uploaded file, field=$nameClean, file=$filePath");
 868                         }
 869                     }
 870                 }
 871                 if (!$didSaveFile) {
 872                     $wpdb->query($wpdb->prepare($parametrizedQuery,
 873                         $time,
 874                         $title,
 875                         $nameClean,
 876                         $valueClean,
 877                         $order++));
 878                 }
 879             }

修正したのがこれ

$value = is_array($value) ? implode(', ', $value) : $value;

これでおk。

phpの再起動 & 送信確認

$ sudo systemctl restart php-fpm.service

うまくいきましたー。

結論

wordpressプラグインはクソ。

メンテできないなら使うな。

あと、アプデするならちゃんとプラグインも整理しましょうね!反省!

【Docker】alpine containerでless: unrecognized option: Xが発生するとき

どうもてぃ。

railsを僕の大好きなdockerで動かしていて、今回も素敵なエラーが発生したので対処法をここに記す。

発生したエラー内容

% docker compose run --rm web rails c
[+] Running 2/0
 ⠿ Container postgres  Running                                                                                                                                                                                                                           0.0s
 ⠿ Container redis     Running                                                                                                                                                                                                                           0.0s
ruby/3.0.0 isn't supported by this pry-doc version
Running via Spring preloader in process 29
Loading development environment (Rails 6.1.1)
[1] pry(main)> Company.find(62).products
  Company Load (0.7ms)  SELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2  [["id", 62], ["LIMIT", 1]]
  Product Load (0.8ms)  SELECT "products".* FROM "products" WHERE "products"."company_id" = $1  [["company_id", 62]]
less: unrecognized option: X
BusyBox v1.32.1 () multi-call binary.

Usage: less [-EFIMmNSRh~] [FILE]...

View FILE (or stdin) one screenful at a time

        -E      Quit once the end of a file is reached
        -F      Quit if entire file fits on first screen
        -I      Ignore case in all searches
        -M,-m   Display status line with line numbers
                and percentage through the file
        -N      Prefix line number to each line
        -S      Truncate long lines
        -R      Remove color escape codes in input
        -~      Suppress ~s displayed past EOF

デフォルトのalpine linuxにはlessコマンドは入ってないってことらしい。alpine imageが最小構成だから当たり前か。

解決方法

二点あります。

  • Dockerfileのランタイムパッケージにlessを追加する
  • apk add lessで追加する

alpine imageを利用しているのに、サーバー起動に不要なパッケージを入れるのは好ましくないので、ランタイムパッケージにlessを追加するのは却下。

apk add lessを立ち上げてるコンテナに直接叩きこむ方法でいきましょう。

# コンテナでコマンドを叩かずシェルを起動する(bashでもおk)
$ docker compose run --rm web sh

/myapp # apk add less 
.
.
.

/myapp # rails c
[1] pry(main)> Company.first.products
# => 無事表示された

終わり

この記事を見たものに幸あれ。

今年の抱負、やりたいこと・目標

どうもてぃ。書くのが遅れてもう2月になっちゃいました。

去年はたしかtwitterで抱負をつぶやいて、結局何をやりたかったのか、やりたかったことの何を成し遂げられたのか、が全然わからなくなっていたので今回は記事にして、自分を逃げられないようにしたいと思います。

やりたいことがたくさんなので多方面で書いていきます。

仕事・エンジニアとして

今年の抱負

心身ともに自己管理に気を付けて、これまでよりも圧倒的に成長する

子供がうまれ今までよりも成長速度は遅くなってしまいましたが、ある程度諦めながら(どうしようもないので)できるときに全力を尽くして成長していこうと思います。

子供が生まれて4年目でやっと葛藤から抜け出せそうです。

やりたいこと・目標

  • Elixirを業務レベル、サービス開発レベルまで極める
    • PhonenixとAshを使っていろいろ作ってみる
  • 応用情報の資格取る(4月)
  • ブログを作って、はてなブログを卒業する
    • Next.jsで作る予定
  • 会社で定期的な勉強会を実施する
  • 今年もちゃんと稼ぐ
  • 会社でも個人でも必ず月締めをする(経費精算も)

家庭

今年の抱負

子供2人の思い出をいっぱい作る

去年末にたっかいカメラ買いました、娘(3歳)の幼稚園参観のために。

それだけじゃなく息子(1歳)も歩けるようになったし、食べるものの幅も広がってきているので、いろんなところに遊びに行ったり旅行に行ったりして、たっかいカメラで思い出を撮りまくりたいと思います。

やりたいこと・目標

ギター

今年の抱負

少なくとも週1は絶対弾く、月1で何か曲を弾けるくらいに戻す

マジでギター弾いてない。大学時代あほみたいに没頭して楽しんでたのに…。

まずは弾く習慣をつけようかなと思います。ほんと曲が弾けなくなってるし、もしかしたら楽譜も読めなくなってるかもしれない。

最近はあまりゲームやってないので、その代わりにこっちに注力してみようかなと思います。

やりたいこと・目標

  • 3か月に1回くらいのペースで曲をアップする
  • 月1で曲を弾けるようにする
  • ギターを持ったらまず基礎練やる

筋トレ

今年の抱負

BP100kg、SQ140kg、DL140kgに頑張って戻す

筋トレもほとんど宅トレしかしてないです。去年数えるくらいしかジムに行っていない…。

ずっと家にいるので脚筋がめっちゃすくなくなってるのにも関わらず、過去最高の体重なため最高にデブってます。

これは今年全部筋肉に変換するしかない、とりあえず前の状態にもどす!とギターを頑張るのと同じ感じで頑張りたいと思います。。

ちゃんと痩せるだけじゃなく筋肉ガッツリつけます。

あと今年に入って頸椎ヘルニアになってしまったので、そこもリハビリで治しながら頑張っていきたいと思います。

やりたいこと・目標

ゲーム

今年の抱負

ほどほどにして優先順位を下げる

以前スマブラ中毒者だった自分は、休憩時間にスマブラ、仕事終わったらスマブラ、docker build時間にスマブラスマブラ、すまぶら、すまbあsbt24てゃjんd;lfv「会え4bが「’いb;あstば」gじゃ

マジでスマブラジャンキーで負けたらイライラするし、やっても面白くないのにやっちゃうし、なんなんでしょうねあのゲーム。

ただ、とある大会で辞めようと決心をし無事1回戦敗退で気持ちよくやめることができたので、あまりゲームをやらなくなりました。

しかーし!今年の4月にはロックマンエグゼアドバンスドコレクションが発売され、過去のロックマンエグゼ全シリーズが遊べるようになるのです。激熱。

ダークソウルも定期的にやりたくなるし、中途半端にやってるドラクエ7、クロノトリガーゼルダの伝説ゆめを見る島、メダロット2、モンスターハンター2ndGなどなど…やりたいゲームがたくさんで人生が終わりそう。

でも自分には上記に書いたように他にもやりたいことがたくさんあるので、なるべくゲームにはまらないよう優先度をさげて楽しもうと思います。

やりたいこと・目標

  • スマブラを無駄にやらない
    • 友人とやるのはおk
  • ソウルシリーズやる
    • Bloodborneはクリアしたい
  • ロックマンエグゼ買う
  • モンハン2ndG配信する(約束)

以上!

今年の抱負絶対こなしてみせます。

毎月末に振り返りをやり、毎回この記事を読み返してちゃんとやりたいことができてるか確認していこうと思います。

皆さんもぜひ書いてみてね。

おつぽよ。

【雑記】Nestjs/Prisma/GraphQLをPlanetScaleとFly.ioへデプロイできるリポジトリを作った

どうもてぃ。

作りました。興味がある人は気軽に触ってみてください。

当該リポジトリ

github.com

デプロイ手順

前提として pscaleコマンド、flyctlコマンドが実行できるようにしておいてください。npm(yarn)が入ってないのは論外で。

planetscale.com

fly.io

PlanetScaleの作成

$ pscale auth login

$ pscale database create nestjs-sample --region ap-northeast

$ pscale branch create nestjs-sample dev
$ pscale branch promote nestjs-sample main

$ pscale password create nestjs-sample dev <name> # nameは識別子なのでなんでもおk

pscale password listでも取得できますが、DATABASE_URLの形式に書き換えるのだるいのでPlanetScaleのダッシュボードで確認してください。

データベース → connectで表示されます。

passwordがまだ作成できてない場合は New passwordを押しましょう。DATABASE_URLが出来上がったらコピーしておいてください。flyctlで使用します。

Fly.ioの作成

$ flyctl auth login

# databaseはPlanetScaleを使用するのでNo
# Dockerfileを上書きしないように注意
# Dockerfileがあるため最後にデプロイするかどうか聞かれますがNoで(DATABASE_URLを設定してないため)
# fly.tomlは上書きしてください
$ flyctl launch

# DATABASE_URLは上でコピーしたやつ
# 後ろのパラメータ大事、絶対忘れるな
$ flyctl secrets set DATABASE_URL="mysql://~~~~~~~~~~~~~~~~~~~~~~~~~/nestjs-sample?sslaccept=strict?sslcert=/etc/ssl/certs/ca-certificates.crt"

$ flyctl deploy

特に問題なければデプロイできているでしょう、きっとね。

苦労したこと

WLS2環境で認証画面が立ち上がらない

最初 pscaleコマンドでPlanetScaleへschema.prismaを反映させようとしていました。

が、まず pscale auth loginではまった。

chromeが立ち上がらない。ctrl + cでキャンセルすると認証画面のURLが表示されるが、アクセスしても無効化されている(そりゃそうだ)。

根本的にはまだ解決できていませんが、いったんsudoで実行し生成したアクセストークンを実行ユーザーのディレクトリに移動させることでうまくいきました。

解決したのでこのissueにコメントしときました。OSS活動じゃん。

根本解決は↓を今度ためしてみる予定。

github.com

ちなみに、pscaleコマンドだけでなくflyctlコマンドでもCLI認証する必要があり、同じようにスーパーユーザーで実行し、アクセストークンを移動させるというムーブをしました。

くっそだる。

MacLinux使ってる人は関係ないのでスルーでおkです。

Fly.ioでPrismaInitializationErrorが発生

Fly.ioにデプロイするタイミングでPlanetScaleにスキーマ構造を反映させているのですが、その際 PrismaInitializationErrorが発生しデプロイに失敗していました。

長いですが以下

~/github.com/planetscale.nestjs-graphql-prisma main*
% sudo flyctl deploy
[sudo] password for motty:
Update available 0.0.446 -> v0.0.451.
Run "flyctl version update" to upgrade.
==> Verifying app config
--> Verified app config
==> Building image
Remote builder fly-builder-bitter-wave-2664 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon  191.9kB
[+] Building 57.6s (18/18) FINISHED
 => [internal] load remote build context                                                                                                                                                                                                                                 0.0s
 => copy /context /                                                                                                                                                                                                                                                      0.1s
 => [internal] load metadata for docker.io/library/debian:bullseye                                                                                                                                                                                                       2.0s
 => [stage-1 1/8] FROM docker.io/library/debian:bullseye@sha256:534da5794e770279c889daa891f46f5a530b0c5de8bfbc5e40394a0164d9fa87                                                                                                                                         0.0s
 => CACHED [builder 2/7] RUN apt-get update; apt install -y curl python-is-python3 pkg-config build-essential                                                                                                                                                            0.0s
 => CACHED [builder 3/7] RUN curl https://get.volta.sh | bash                                                                                                                                                                                                            0.0s
 => CACHED [builder 4/7] RUN volta install node@18.8.0 yarn@1.22.19                                                                                                                                                                                                      0.0s
 => CACHED [builder 5/7] WORKDIR /app                                                                                                                                                                                                                                    0.0s
 => [builder 6/7] COPY . .                                                                                                                                                                                                                                               0.0s
 => [builder 7/7] RUN yarn install &&     yarn prisma generate &&     yarn build                                                                                                                                                                                        49.7s
 => CACHED [stage-1 2/8] WORKDIR /app                                                                                                                                                                                                                                    0.0s
 => CACHED [stage-1 3/8] COPY --from=builder /root/.volta /root/.volta                                                                                                                                                                                                   0.0s
 => CACHED [stage-1 4/8] COPY --from=builder /app/node_modules ./node_modules/                                                                                                                                                                                           0.0s
 => [stage-1 5/8] COPY --from=builder /app/dist ./dist                                                                                                                                                                                                                   0.1s
 => [stage-1 6/8] COPY --from=builder /app/.fly ./.fly                                                                                                                                                                                                                   0.2s
 => [stage-1 7/8] COPY --from=builder /app/package.json ./                                                                                                                                                                                                               0.1s
 => [stage-1 8/8] COPY --from=builder /app/yarn.lock ./                                                                                                                                                                                                                  0.0s
 => exporting to image                                                                                                                                                                                                                                                   0.1s
 => => exporting layers                                                                                                                                                                                                                                                  0.1s
 => => writing image sha256:393032d20e1b39441cedd0abc72eff5352edb0a377daedd8e0dabf6c293d7aa3                                                                                                                                                                             0.0s
 => => naming to registry.fly.io/nestjs-app:deployment-01GR1MYTD32RT3HDPMPYEGNK86                                                                                                                                                                                        0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/nestjs-app]
1e854d6563d3: Pushed
1d6767cc453b: Pushed
f7e90164f428: Pushed
fbf3a65b1a93: Pushed
5d3c3bdc31ec: Layer already exists
5b1800ba7f88: Layer already exists
1b526d844305: Layer already exists
a9099c3159f5: Layer already exists
deployment-01GR1MYTD32RT3HDPMPYEGNK86: digest: sha256:0e210bfe9ec945b3e5c9cd95571ca559e3db8eaa140cba21b0fdec2cba066698 size: 1996
--> Pushing image done
image: registry.fly.io/nestjs-app:deployment-01GR1MYTD32RT3HDPMPYEGNK86
image size: 847 MB
==> Creating release
--> release v3 created

--> You can detach the terminal anytime without stopping the deployment
==> Monitoring deployment
Logs: https://fly.io/apps/nestjs-app/monitoring

 1 desired, 1 placed, 0 healthy, 1 unhealthy [restarts: 2] [health checks: 1 total, 1 critical]
Failed Instances

Failure #1

Instance
ID              PROCESS VERSION REGION  DESIRED STATUS  HEALTH CHECKS           RESTARTS        CREATED
cca60ea1        app     3       nrt     run     failed  1 total, 1 critical     2               46s ago

Recent Events
TIMESTAMP               TYPE            MESSAGE
2023-01-30T15:34:25Z    Received        Task received by client
2023-01-30T15:34:25Z    Task Setup      Building Task Directory
2023-01-30T15:34:51Z    Started         Task started by client
2023-01-30T15:34:57Z    Terminated      Exit Code: 1
2023-01-30T15:34:57Z    Restarting      Task restarting in 1.14956903s
2023-01-30T15:35:04Z    Started         Task started by client
2023-01-30T15:35:10Z    Terminated      Exit Code: 1
2023-01-30T15:35:10Z    Restarting      Task restarting in 1.058345s
2023-01-30T15:35:17Z    Started         Task started by client
2023-01-30T15:35:23Z    Terminated      Exit Code: 1
2023-01-30T15:35:23Z    Not Restarting  Exceeded allowed attempts 2 in interval 5m0s and mode is "fail"
2023-01-30T15:35:23Z    Alloc Unhealthy Unhealthy because of failed task

2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] AppModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] PostsModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] AuthorsModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +0ms
2023-01-30T15:35:20Z   [info][Nest] 659  - 01/30/2023, 3:35:20 PM     LOG [InstanceLoader] GraphQLModule dependencies initialized +1ms
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [RoutesResolver] AppController {/}: +199ms
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [RouterExplorer] Mapped {/, GET} route +4ms
2023-01-30T15:35:21Z   [info]prisma:info Starting a mysql pool with 3 connections.
2023-01-30T15:35:21Z   [info][Nest] 659  - 01/30/2023, 3:35:21 PM     LOG [PrismaService] info: Starting a mysql pool with 3 connections.
2023-01-30T15:35:21Z   [info]/app/node_modules/@prisma/client/runtime/index.js:24780
2023-01-30T15:35:21Z   [info]          throw new PrismaClientInitializationError(error2.message, this.config.clientVersion, error2.error_code);
2023-01-30T15:35:21Z   [info]                ^
2023-01-30T15:35:21Z   [info]PrismaClientInitializationError: Error opening a TLS connection: error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:16000069:STORE routines:func(0):unregistered scheme:../deps/openssl/openssl/crypto/store/store_register.c:237:scheme=file, error:80000002:system library:func(0):reason(2):../deps/openssl/openssl/providers/implementations/storemgmt/file_store.c:267:calling stat(/etc/ssl/certs), error:0A000086:SSL routines:func(0):certificate verify failed:../deps/openssl/openssl/ssl/statem/statem_clnt.c:1896: (unable to get local issuer certificate)
2023-01-30T15:35:21Z   [info]    at startFn (/app/node_modules/@prisma/client/runtime/index.js:24780:17)
2023-01-30T15:35:21Z   [info]    at async Proxy.onModuleInit (/app/dist/src/prisma/prisma.service.js:40:9)
2023-01-30T15:35:21Z   [info]    at async Promise.all (index 0)
2023-01-30T15:35:21Z   [info]    at async callModuleInitHook (/app/node_modules/@nestjs/core/hooks/on-module-init.hook.js:43:5)
2023-01-30T15:35:21Z   [info]    at async NestApplication.callInitHook (/app/node_modules/@nestjs/core/nest-application-context.js:178:13)
2023-01-30T15:35:21Z   [info]    at async NestApplication.init (/app/node_modules/@nestjs/core/nest-application.js:96:9)
2023-01-30T15:35:21Z   [info]    at async NestApplication.listen (/app/node_modules/@nestjs/core/nest-application.js:158:33)
2023-01-30T15:35:21Z   [info]    at async bootstrap (/app/dist/src/main.js:11:5) {
2023-01-30T15:35:21Z   [info]  clientVersion: '4.9.0',
2023-01-30T15:35:21Z   [info]  errorCode: 'P1011'
2023-01-30T15:35:21Z   [info]}
2023-01-30T15:35:21Z   [info]Node.js v18.8.0
2023-01-30T15:35:21Z   [info]error Command failed with exit code 1.
2023-01-30T15:35:21Z   [info]info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
2023-01-30T15:35:21Z   [info]Starting clean up.
--> v3 failed - Failed due to unhealthy allocations - no stable job version to auto revert to and deploying as v4

--> Troubleshooting guide at https://fly.io/docs/getting-started/troubleshooting/
Error abort

Dockerfileに関しては flyctl launchした際に生成されるDockerfileで少しばかりカスタマイズしています。

似た問題のIssueを見つけました。

github.com

sslcert pathを設定しないといけない…と。しかもそれがPrisma公式に書いてあった。

www.prisma.io

PlanetScaleにもdebianだったらこれ設定しろよって書いてました。

planetscale.com

そしてflyctl launchして生成されるDockerfileのdebian:bullseye imageには、なんと/etc/sslディレクトリがなかった。証明書が存在しなかったのです…。

これはコンテナを立てて確認済みです。

正しくはbuilder imageではcurlをいれてるので証明書ca-certificates.crtはあるのですが、実行image には存在してませんでした。

そのため、実行imageの方にCOPYしてあげる必要があった…と(apt installする手もありますが、実行imageが肥大化するので無し)。

FROM debian:bullseye as builder

ARG NODE_VERSION=18.8.0
ARG YARN_VERSION=1.22.19

ENV NODE_ENV development
ENV VOLTA_HOME /root/.volta
ENV PATH /root/.volta/bin:$PATH

RUN apt update && \
    apt install -y curl python-is-python3 pkg-config build-essential apt-transport-https
RUN curl https://get.volta.sh | bash
RUN volta install node@${NODE_VERSION} yarn@${YARN_VERSION}

WORKDIR /app

COPY . .

RUN yarn install && \
    yarn prisma generate && \
    yarn build



FROM debian:bullseye

ENV NODE_ENV production
ENV PATH /root/.volta/bin:$PATH
LABEL fly_launch_runtime="nodejs"

WORKDIR /app

COPY --from=builder /root/.volta /root/.volta
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/.fly ./.fly
COPY --from=builder /app/package.json ./
COPY --from=builder /app/yarn.lock ./
COPY --from=builder /etc/ssl /etc # これが必要!!!!!!

CMD ["sh", ".fly/start.sh"]

あとDATABASE_URLにパラメータを設定したものをsecretsに反映させます。

$ flyctl secrets set DATABASE_URL='mysql://~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/nestjs-sample?sslaccept=strict?sslcert=/etc/ssl/certs/ca-certificates.crt'

これで無事認証も通り flyctl deployPrisma schemaをPlanetScaleへ反映させることができました。

おめでとう。

おわり

私はいつも会社で新規プロジェクトが始まると、新しい技術を取り入れようとし一瞬で作れるリポジトリ作成マンになります。

自分にとっても会社にとっても他のエンジニア様たちにとってもメリットしかないので今後も続けていきてぇっすね。

ぜひぜひ使ってみてね。

これであなたもモダンなインフラを扱えるようになりますよぉ。

次はRenderかRailwayでデプロイする記事書こうかな。

【Selenium】location_once_scrolled_into_viewでelement not interactableが発生したときの対処方法

どうもてぃ。

自社開発でseleniumをよく使うのですが、とあるサイトをクローリングしているときに出くわしたエラーを力技で解決したので備忘録として残します。

環境

コード

require 'selenium-webdriver'

options = Selenium::WebDriver::Chrome::Options.new
client = Selenium::WebDriver::Remote::Http::Default.new
wait = Selenium::WebDriver::Wait.new(timeout: 20)
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--window-size=1920,1080')
options.add_argument('--lang=ja-JP')
driver = Selenium::WebDriver.for(:chrome, capabilities: [options], http_client: client)

driver.get('https://example.com')
footer = driver.find_element(:tag_name, 'footer')
footer.location_once_scrolled_into_view

エラー内容

element not interactable
  (Session info: chrome=109.0.5414.74)
#0 0x555fe9d48303 <unknown>: element not interactable (Selenium::WebDriver::Error::ElementNotInteractableError)
  (Session info: chrome=109.0.5414.74)
        from #1 0x555fe9b1cbbd <unknown>
        from #2 0x555fe9b530bc <unknown>
        from #3 0x555fe9b52669 <unknown>
        from #4 0x555fe9b79982 <unknown>
        from #5 0x555fe9b4e22f <unknown>
        from #6 0x555fe9b79b4e <unknown>
        from #7 0x555fe9b9188c <unknown>
        from #8 0x555fe9b79753 <unknown>
        from #9 0x555fe9b4ca14 <unknown>
        from #10 0x555fe9b4db7e <unknown>
        from #11 0x555fe9d9732e <unknown>
        from #12 0x555fe9d9ac0e <unknown>
        from #13 0x555fe9d7d610 <unknown>
        from #14 0x555fe9d9bc23 <unknown>
        from #15 0x555fe9d6f545 <unknown>
        from #16 0x555fe9dbc6a8 <unknown>
        from #17 0x555fe9dbc836 <unknown>
        from #18 0x555fe9dd7d13 <unknown>
        from #19 0x7f3bf207e609 start_thread
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/response.rb:56:in `assert_ok'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/response.rb:35:in `initialize'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/http/common.rb:83:in `new'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/http/common.rb:83:in `create_response'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/http/default.rb:103:in `request'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/http/common.rb:59:in `call'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/bridge.rb:625:in `execute'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/bridge.rb:411:in `send_keys_to_element'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/remote/bridge.rb:493:in `element_location_once_scrolled_into_view'
        from /home/motty/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/selenium-webdriver-4.7.1/lib/selenium/webdriver/common/element.rb:307:in `location_once_scrolled_into_view'
        from scraping.rb:49:in `block (2 levels) in <main>'
        from scraping.rb:36:in `each'
        from scraping.rb:36:in `block in <main>'
        from scraping.rb:24:in `each'
        from scraping.rb:24:in `<main>'

よくわからん…

動的な要素だからとか??

力技を実行

jsの力を発動します。

seleniumでjsを実行する場合はexecute_scriptを使用します。

require 'selenium-webdriver'

options = Selenium::WebDriver::Chrome::Options.new
client = Selenium::WebDriver::Remote::Http::Default.new
wait = Selenium::WebDriver::Wait.new(timeout: 20)
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--window-size=1920,1080')
options.add_argument('--lang=ja-JP')
driver = Selenium::WebDriver.for(:chrome, capabilities: [options], http_client: client)

driver.get('https://example.com')
footer = driver.find_element(:tag_name, 'footer')
driver.execute_script("window.scrollTo(#{footer.location.x}, #{footer.location.y});")

要素の座標はdriver.find_elementして取得した要素に対して、locationにアクセスしてあげればx座標とy座標が入ってるので、この子たちを使ってあげましょう。

終わり

実際のコードは画像をfind_elementしていて、headless modeで起動せずに目視でちゃんと要素が表示されているにも関わらず、タイトルのようなエラーが出ていました。

解決策を探しているうちにissueも上がっていた気がしますが、なんかcloseされてたので今回は力技でひとまず解決。

同じエラーが発生したときの正攻法(?)として誰かの力になればばばば。

おつでした。

【Elixir】docker compose構成のelixirとpostgresqlが接続できなかった話

あけましておめでとうございますもてぃ。

今年一発目の記事はElixirです。

僕はdockerが大好きで愛してやまないので、Elixir * PostgreSQL構成のdockerリポジトリを作りました。

github.com

その際はまった内容を備忘録として残しておきたいと思います。

環境

docker-compose.ymlの構成

こんな感じ。

version: '3'

services:
  postgres:
    build: ./postgres
    image: ecto-database
    container_name: ecto-database
    environment:
      POSTGRES_HOST_AUTH_METHOD: 'trust'
      TZ: 'Asia/Tokyo'
    volumes:
      - ./postgres/init:/docker-entrypoint-initdb.d
      - ecto-data:/var/lib/postgresql/data
    ports:
      - '5555:5432'
    restart: always
  app:
    build: .
    image: ecto-app
    container_name: ecto-app
    volumes:
      - .:/app
    tty: true
    stdin_open: true
    restart: always
    depends_on:
      - postgres

volumes:
  ecto-data:
    driver: local

ライブラリにはectopostgrexを使用してます。

ecto.create

app containerに対してmix ecto.createを実行しています。

# docker exec -it ecto-app mix ecto.createでもおk
% docker compose exec app mix ecto.create
Compiling 3 files (.ex)
Generated ecto_app app

05:34:55.399 [error] GenServer #PID<0.284.0> terminating
** (DBConnection.ConnectionError) tcp connect (postgres:5555): connection refused - :econnrefused
    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol

05:34:55.407 [error] GenServer #PID<0.290.0> terminating
** (DBConnection.ConnectionError) tcp connect (postgres:5555): connection refused - :econnrefused
    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
** (Mix) The database for EctoApp.Repo couldn't be created: killed

いろいろ調べてみたら「postgresqlサーバーが立ち上がってない」だのいろいろ書かれてましたが、docker compose upで立ち上げたpostgresql コンテナは問題なく立ち上がってました。

docker-compose.ymlでentripointも設定して権限もしっかり付与しているので、ecto_appユーザーが接続できてないってこともないです。

念のため確認

% docker compose exec postgres psql -U ecto_app
psql (14.1)
Type "help" for help.

ecto_app=# \du
                                   List of roles
 Role name |                         Attributes                         | Member of
-----------+------------------------------------------------------------+-----------
 ecto_app  | Superuser, Create role, Create DB                          | {}
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}

ecto_app=#

ちゃんと権限もありましたね。

configの見直し

接続設定はconfig/config.exsに書かれてます。

import Config

config :ecto_app, EctoApp.Repo,
  database: "ecto_app",
  username: "ecto_app",
  password: "ecto_app_psql",
  hostname: "localhost",
  port: "5555"

config :ecto_app, ecto_repos: [EctoApp.Repo]

if Mix.env() == :dev do
  config :mix_test_watch,
    tasks: [
      "credo"
    ]
end

もともとpostgresqlをローカルにいれていない自分は、DBが必要になったときに毎回postgresql用のdocker-compose.ymlを作っていて、そのtemplateを毎度使っていました。

その設定をそのまま上のconfig.exsに反映していたのがまずかったのです…。

解決策

docker-composeで立ち上げる際のポート、ホストはdocker network上で生成されるものにおき替える必要があります。

docker-composeをみるとdatabaseのホストはpostgres、ポートはdocker network上では5432を開放しています。なのでその通りにconfig.exsを変更してあげる。

import Config

config :ecto_app, EctoApp.Repo,
  database: "ecto_app",
  username: "ecto_app",
  password: "ecto_app_psql",
  hostname: "postgres",
  port: "5432"

config :ecto_app, ecto_repos: [EctoApp.Repo]

if Mix.env() == :dev do
  config :mix_test_watch,
    tasks: [
      "credo"
    ]
end

再度ecto.createしてみると…

% docker compose exec app mix ecto.create
Compiling 3 files (.ex)
Generated ecto_app app
The database for EctoApp.Repo has already been created

なんかすでに作られてましたが、うまくコマンド実行できました。

終わり

postgresqlとelixirの簡単なdocker compose構成が出来上がりました。

これでphoenixにも応用できるはずなので、phoenix tutorialをやる際はちょっとこのリポジトリをいじって圧倒的環境構築したいとおもいます。

Elixir/Phoenix極めるぞー!

(今年の抱負はまた別の記事で)