- 投稿日:2021-12-24T22:43:50+09:00
JavaScriptでjsonの中身を確認したら[object Object]と表示された場合の対処法
はじめに 最近、API開発をしていて外部API時のエラーオブジェクトの構造を知りたい時がありました。 しかし、対象オブジェクトをログ出力しても[object Object]と表示されてしまいます。 そこで、オブジェクトの中身を表示する方法を調べたので紹介します。 方法1 : JSON.stringifyを使う 以下のようにJSON.stringifyを使う方法です。 console.log(JSON.stringify(object)) 他の記事でもよく見かけます。 確かにこれでオブジェクトの中身が表示されるのですが、正直みづらいです。 そこで、次の方法が個人的におすすめです。 方法2 : console.dirを使う 以下のようにオブジェクトをconsole.dirで出力すると、良い感じに見やすく表示されます。 console.dir(object)
- 投稿日:2021-12-24T21:23:20+09:00
Docker上のyarnの依存関係を解決。
docker-compose upで RUN yarn upgrade の部分でエラーがでた。 Step 13/19 : RUN yarn upgrade ---> Running in 3c8f3922483b yarn upgrade v1.22.17 warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. [1/4] Resolving packages... info There appears to be trouble with your network connection. Retrying... warning @rails/webpacker > webpack > watchpack > watchpack-chokidar2 > chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies. warning @rails/webpacker > webpack > watchpack > watchpack-chokidar2 > chokidar > fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. warning @rails/webpacker > postcss-preset-env > postcss-color-functional-notation > postcss-values-parser > flatten@1.0.3: flatten is deprecated in favor of utility frameworks such as lodash. warning @rails/webpacker > webpack > node-libs-browser > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. warning @rails/webpacker > optimize-css-assets-webpack-plugin > cssnano > cssnano-preset-default > postcss-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x. warning @rails/webpacker > webpack > micromatch > snapdragon > source-map-resolve > resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated warning @rails/webpacker > webpack > micromatch > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated [2/4] Fetching packages... error @npmcli/fs@1.1.0: The engine "node" is incompatible with this module. Expected version "^12.13.0 || ^14.15.0 || >=16". Got "10.24.0" error Found incompatible module. info Visit https://yarnpkg.com/en/docs/cli/upgrade for documentation about this command. The command '/bin/sh -c yarn upgrade' returned a non-zero code: 1 ERROR: Service 'web' failed to build : Build failed warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. そのとおりでしかないので、package-lock.jsonを消した。npmとyarnを混在させてた。packge.jsonをどっちも使うから互換性あると思ってたが、lockファイルはまた別の話か。 The engine “node” is incompatible with this module が核となる部分か。 error @npmcli/fs@1.1.0: The engine "node" is incompatible with this module. Expected version "^12.13.0 || ^14.15.0 || >=16". Got "10.24.0" nodeのバージョンが古いと怒られてる気がする node -v v16.13.1 ローカルではこうだったが、docker上では Got "10.24.0"となってるので 16系のnodeをインストールするDockerfileを記述したい。 Dockerfileにこのように記述してみた。 RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ #←ここ変更 apt-get install -y nodejs docker-compose upを実行。 ├─ wrap-ansi@5.1.0 ├─ ws@8.4.0 ├─ xtend@4.0.2 ├─ yaml@1.10.2 ├─ yargs-parser@13.1.2 ├─ yargs@13.3.2 ├─ yocto-queue@0.1.0 └─ youtube-player@5.5.2 Done in 116.92s. RUN yarn upgrade を突破した!!!やったぜ!! s:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 しかしwebpackerコンテナはこのようになってた。。 依存関係がやばいからだとおもうので、過去の自分の記事を参考に https://qiita.com/divclass123/items/5cee396071540256e11e Dockerfileに RUN yarn upgrade-interactive --latest これをついか。 この記事のように、このコマンドは対話型で、依存関係を直していくので、 RUN yarn upgrade-interactive --latest このコマンド書くだけでは不十分なのではないか。 実際自分がローカルで、yarn upgrade-interactive --latestを叩いたときには 対話型の画面になって、aを押した後にエンターを押した。 RUN a RUN enter とドッカーファイルに書くことができればいいのだが。。。 もしくは、COPY . /coffee_passport でキャッシュが使われてる気がするので、 ローカルで依存を直しても、依存を直す以前のディレクトリがコピーされていたら全く意味がないので docker-compose build --no-cache を実行する必要があるのではないか。 Step 15/21 : RUN yarn upgrade-interactive --latest ---> Running in 5330c7208d1b yarn upgrade-interactive v1.22.17 success All of your dependencies are up to date. Done in 6.03s. とdocker-composeのログをみたらなっていた。 つまり RUN yarn upgrade-interactive --latest 単体でも機能していた。 これで依存関係が治ればいいのだが、、。。。 Recreating coffee_passport_webpacker_1 ... error ERROR: for coffee_passport_webpacker_1 cannot stop container: d0dea180963dc92a187a59c4a4Recreating coffee_passport_web_1 ... error xit event ERROR: for coffee_passport_web_1 cannot stop container: a07b86322e13b4796550a99f01af6c0fd4a2279a3f05e5b210eb06cd0a171c57: tried to kill container, but did not receive an exit event ERROR: for webpacker cannot stop container: d0dea180963dc92a187a59c4a462dfebc52a85d5071cfec4ef746f162dd59009: tried to kill container, but did not receive an exit event ERROR: for web cannot stop container: a07b86322e13b4796550a99f01af6c0fd4a2279a3f05e5b210eb06cd0a171c57: tried to kill container, but did not receive an exit event ERROR: Encountered errors while bringing up the project. 依存関係とは別のエラーがでた。。。一旦dockerをまっさらにして、やり直そう。 コンテナが止まらないので、再起動。 すべてのネットワークと、ボリューム、コンテナを削除できた。 docker-compose build --no-cache を実行。 --no-cacheつける意味ない気がするが念には念を くそほど、長くロードされてる。 ---> 69212fdb637b Successfully built 69212fdb637b Successfully tagged coffee_passport_webpacker:latest Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them と一旦ビルドは成功。 docker-compose up を実行。 [24] ./app/javascript/packs/components/about_coffee_passport.vue + 2 modules 3.2 KiB {3} {11} {12} {14} [built] | 3 modules [25] ./app/javascript/packs/components/contact.vue + 2 modules 1.74 KiB {4} {11} {12} {14} [built] | 3 modules [49] ./app/javascript/packs/router/router.js 469 bytes {11} {12} {14} [built] [52] ./app/javascript/packs/components/static_pages/appExplain.vue + 4 modules 5.41 KiB {0} {9} [built] | 5 modules [53] ./app/javascript/packs/components/footer.vue + 2 modules 1.16 KiB {6} {11} [built] | 3 modules [60] ./app/javascript/packs/tag.js 5.77 KiB {1} {15} [built] [61] ./app/javascript/packs/preview.js 2.77 KiB {1} {13} [built] [64] ./app/javascript/packs/app_explain.js 329 bytes {0} [built] [65] ./app/javascript/packs/application.js 911 bytes {1} [built] [74] ./app/javascript/packs/footer.js 192 bytes {11} [built] [89] ./app/javascript/packs/hello_vue.js + 5 modules 9.73 KiB {12} [built] | ./app/javascript/packs/hello_vue.js 2.19 KiB [built] | ./app/javascript/app.vue 552 bytes [built] | ./app/javascript/app.vue?vue&type=template&id=286cd28a&scoped=true& 213 bytes [built] | ./app/javascript/app.vue?vue&type=script&lang=js& 364 bytes [built] | ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./app/javascript/app.vue?vue&type=template&id=286cd28a&scoped=true& 3.95 KiB [built] | ./node_modules/babel-loader/lib??ref--7-0!./node_modules/vue-loader/lib??vue-loader-options!./app/javascript/app.vue?vue&type=script&lang=js& 2.44 KiB [built] [90] ./app/javascript/packs/components/Home.vue + 2 modules 826 bytes {2} [built] | 3 modules + 76 hidden modules こんな感じでコンパイルも成功してそう。 だし、docekr-compose up成功してくれ、、、 webpacker_1 | /coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object. (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 だめだぁ。。。・・・・ reating coffee_passport_db_1 ... done Creating coffee_passport_selenium_chrome_1 ... done Creating coffee_passport_webpacker_1 ... done Creating coffee_passport_web_1 ... done Attaching to coffee_passport_db_1, coffee_passport_webpacker_1, coffee_passport_web_1 db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started. db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started. db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Initializing database files db_1 | 2021-12-23T15:14:49.935688Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.21) initializing of server in progress as process 44 db_1 | 2021-12-23T15:14:49.944995Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:14:50.495413Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:14:52.240716Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option. webpacker_1 | /coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 db_1 | 2021-12-23 15:14:55+00:00 [Note] [Entrypoint]: Database files initialized db_1 | 2021-12-23 15:14:55+00:00 [Note] [Entrypoint]: Starting temporary server db_1 | 2021-12-23T15:14:55.874976Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 91 db_1 | 2021-12-23T15:14:55.894098Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:14:56.143204Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:14:56.253183Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock db_1 | 2021-12-23T15:14:56.401692Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. db_1 | 2021-12-23T15:14:56.402164Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. db_1 | 2021-12-23T15:14:56.407071Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. db_1 | 2021-12-23T15:14:56.434607Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL. db_1 | 2021-12-23 15:14:56+00:00 [Note] [Entrypoint]: Temporary server started. db_1 | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it. db_1 | db_1 | 2021-12-23 15:14:59+00:00 [Note] [Entrypoint]: Stopping temporary server db_1 | 2021-12-23T15:14:59.658326Z 10 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.21). db_1 | 2021-12-23T15:15:01.954163Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.21) MySQL Community Server - GPL. db_1 | 2021-12-23 15:15:02+00:00 [Note] [Entrypoint]: Temporary server stopped db_1 | db_1 | 2021-12-23 15:15:02+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up. db_1 | db_1 | 2021-12-23T15:15:02.932785Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 1 db_1 | 2021-12-23T15:15:02.943118Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:15:03.122522Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:15:03.216008Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock db_1 | 2021-12-23T15:15:03.332617Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. db_1 | 2021-12-23T15:15:03.332862Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. db_1 | 2021-12-23T15:15:03.336670Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. db_1 | 2021-12-23T15:15:03.357237Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL. しかもwebコンテナがdoneになってるのにログに表示されないし localhost:3000も受け付けてくれない。 このページは動作してませんとなる・ soichirohara@SoichironoMacBook-Pro coffee_passport % docker-compose ps Name Command State Ports coffee_passport_db_1 docker-entrypoint.sh --def Up 0.0.0.0:3306->3306/tcp, ... 33060/tcp coffee_passport_selenium_ch /opt/bin/entry_point.sh Up 4444/tcp, 5900/tcp rome_1 coffee_passport_web_1 entrypoint.sh ./bin/rails Up 0.0.0.0:3000->3000/tcp ... coffee_passport_webpacker_1 entrypoint.sh ./bin/webpac Exit 1 ... coffee_passport_web_1 コンテナ立ち上がってるはずなのに。。。 coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 return func(...args); ^ TypeError: Class constructor ServeCommand cannot be invoked without 'new' at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) at Module._compile (node:internal/modules/cjs/loader:1101:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Module.require (node:internal/modules/cjs/loader:1005:19) at require (node:internal/modules/cjs/helpers:102:18) webpackerコンテナ全体のログはこんな感じ TypeError: Class constructor ServeCommand cannot be invoked without 'new' をググったら。 これは今のところうまくいきました。package.jsonファイルのwebpack-dev-serverバージョンを次のように変更します。 "webpack-dev-server": "~3" そして、yarn installまたはを実行しnpm installます。 ソース:https://stackoverflow.com/a/69050300/2774342 PSこれは一時的な解決策になる可能性がありますが、新しいバージョンがリリースされるため、将来の参照(数か月後だと思います)のために、この回答は非推奨になります。 とかいてあったので webpack-dev-serverをバージョン3のままにする これを行うには、package.jsonファイルを更新します。 package.json "webpack-dev-server": "~3" をこうする。 - RUN bundle exec rails webpacker:compile - RUN yarn upgrade-interactive --latest をけした。コンパイル専用のコンテナがあるので、わざわざDockerfileに記述しなくてもいいとおもった。 RUN yarn upgrade-interactive --latest は "webpack-dev-server": "~3"とバージョンを固定したいので、 アップデートをしてしまうコマンドを削除 RUN yarn install --check-files を追記 docker-compose up で成功!! やったぜ!! 上手く言ったdockerfileとdocker-compose.yml FROM ruby:2.6.5 ## nodejsとyarnはwebpackをインストールする際に必要 # yarnパッケージ管理ツールをインストール RUN curl http://deb.debian.org/debian/dists/buster/main/binary-amd64/by-hash/SHA256/935deda18d5bdc25fb1813d0ec99b6e0e32a084b203e518af0cf7dc79ee8ebda | head RUN apt-get update && apt-get install -y curl apt-transport-https wget && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && apt-get install -y yarn && apt-get install -y graphviz RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ apt-get install -y nodejs # chromeの追加 RUN apt-get update && apt-get install -y unzip && \ CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ unzip ~/chromedriver_linux64.zip -d ~/ && \ rm ~/chromedriver_linux64.zip && \ chown root:root ~/chromedriver && \ chmod 755 ~/chromedriver && \ mv ~/chromedriver /usr/bin/chromedriver && \ sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get update && apt-get install -y google-chrome-stable RUN /bin/sh -c /bin/sh -c bundle update --bundler RUN gem install bundler:2.1.4 RUN mkdir /coffee_passport WORKDIR /coffee_passport COPY . /coffee_passport COPY Gemfile /coffee_passport/Gemfile COPY Gemfile.lock /coffee_passport/Gemfile.lock # RUN bundle update rails # RUN bundle update # RUN bundle update mimemagic # RUN bundle update capybara selenium-webdriver #RUN bundle update nokogiri marcel mimemagic RUN bundle install RUN yarn upgrade #RUN yarn upgrade-interactive --latest #RUN bundle exec rails webpacker:comp RUN yarn install --check-files # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"] docker-compose.yml version: '3' services: db: image: mysql:8.0.21 cap_add: - SYS_NICE # コンテナにLinux機能を追加するオプションのようです。SYS_NICEは、プロセスの優先度(nice値)をあげます。 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_HOST: db ports: - '3306:3306' volumes: - mysql-data:/var/lib/mysql command: --default-authentication-plugin=mysql_native_password # 認証方式を8系以前のものにする web: &web build: . command: ./bin/rails s -b 0 stdin_open: true tty: true # この2文を追加でコンテナ内の標準入出力をローカルマシンのターミナルにアタッチする準備が整います。 volumes: - .:/coffee_passport ports: - "3000:3000" depends_on: - db environment: WEBPACKER_DEV_SERVER_HOST: webpacker MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} RAILS_MASTER_KEY: ${RAILS_MASTER_KEY} SENDGRID_API_KEY: ${SENDGRID_API_KEY} ADMIN_USER_PASSWORD: ${ADMIN_USER_PASSWORD} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} GOOGLE_USER_NAME: ${GOOGLE_USER_NAME} GOOGLE_PASSWORD: ${GOOGLE_PASSWORD} SENDGRID_USER_NAME: ${SENDGRID_USER_NAME} SENDGRID_PASSWORD: ${SENDGRID_PASSWORD} PAYJP_SECRET_KEY: ${PAYJP_SECRET_KEY} PAYJP_PUBLIC_KEY: ${PAYJP_PUBLIC_KEY} MYSQL_HOST: db # selenium_chrome を使うために以下の行を追加 SELENIUM_DRIVER_URL: http://selenium_chrome:4444/wd/hub" selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none webpacker: <<: *web command: ./bin/webpack-dev-server environment: WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 ports: - "3035:3035" volumes: mysql-data: driver: local vendor_bundle: driver: local
- 投稿日:2021-12-24T21:23:20+09:00
Dockerでyarnの依存関係を解決。
docker-compose upで RUN yarn upgrade の部分でエラーがでた。 Step 13/19 : RUN yarn upgrade ---> Running in 3c8f3922483b yarn upgrade v1.22.17 warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. [1/4] Resolving packages... info There appears to be trouble with your network connection. Retrying... warning @rails/webpacker > webpack > watchpack > watchpack-chokidar2 > chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies. warning @rails/webpacker > webpack > watchpack > watchpack-chokidar2 > chokidar > fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. warning @rails/webpacker > postcss-preset-env > postcss-color-functional-notation > postcss-values-parser > flatten@1.0.3: flatten is deprecated in favor of utility frameworks such as lodash. warning @rails/webpacker > webpack > node-libs-browser > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. warning @rails/webpacker > optimize-css-assets-webpack-plugin > cssnano > cssnano-preset-default > postcss-svgo > svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x. warning @rails/webpacker > webpack > micromatch > snapdragon > source-map-resolve > resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated warning @rails/webpacker > webpack > micromatch > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated [2/4] Fetching packages... error @npmcli/fs@1.1.0: The engine "node" is incompatible with this module. Expected version "^12.13.0 || ^14.15.0 || >=16". Got "10.24.0" error Found incompatible module. info Visit https://yarnpkg.com/en/docs/cli/upgrade for documentation about this command. The command '/bin/sh -c yarn upgrade' returned a non-zero code: 1 ERROR: Service 'web' failed to build : Build failed warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. そのとおりでしかないので、package-lock.jsonを消した。npmとyarnを混在させてた。packge.jsonをどっちも使うから互換性あると思ってたが、lockファイルはまた別の話か。 The engine “node” is incompatible with this module が核となる部分か。 error @npmcli/fs@1.1.0: The engine "node" is incompatible with this module. Expected version "^12.13.0 || ^14.15.0 || >=16". Got "10.24.0" nodeのバージョンが古いと怒られてる気がする node -v v16.13.1 ローカルではこうだったが、docker上では Got "10.24.0"となってるので 16系のnodeをインストールするDockerfileを記述したい。 Dockerfileにこのように記述してみた。 RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ #←ここ変更 apt-get install -y nodejs docker-compose upを実行。 ├─ wrap-ansi@5.1.0 ├─ ws@8.4.0 ├─ xtend@4.0.2 ├─ yaml@1.10.2 ├─ yargs-parser@13.1.2 ├─ yargs@13.3.2 ├─ yocto-queue@0.1.0 └─ youtube-player@5.5.2 Done in 116.92s. RUN yarn upgrade を突破した!!!やったぜ!! s:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 しかしwebpackerコンテナはこのようになってた。。 依存関係がやばいからだとおもうので、過去の自分の記事を参考に https://qiita.com/divclass123/items/5cee396071540256e11e Dockerfileに RUN yarn upgrade-interactive --latest これをついか。 この記事のように、このコマンドは対話型で、依存関係を直していくので、 RUN yarn upgrade-interactive --latest このコマンド書くだけでは不十分なのではないか。 実際自分がローカルで、yarn upgrade-interactive --latestを叩いたときには 対話型の画面になって、aを押した後にエンターを押した。 RUN a RUN enter とドッカーファイルに書くことができればいいのだが。。。 もしくは、COPY . /coffee_passport でキャッシュが使われてる気がするので、 ローカルで依存を直しても、依存を直す以前のディレクトリがコピーされていたら全く意味がないので docker-compose build --no-cache を実行する必要があるのではないか。 Step 15/21 : RUN yarn upgrade-interactive --latest ---> Running in 5330c7208d1b yarn upgrade-interactive v1.22.17 success All of your dependencies are up to date. Done in 6.03s. とdocker-composeのログをみたらなっていた。 つまり RUN yarn upgrade-interactive --latest 単体でも機能していた。 これで依存関係が治ればいいのだが、、。。。 Recreating coffee_passport_webpacker_1 ... error ERROR: for coffee_passport_webpacker_1 cannot stop container: d0dea180963dc92a187a59c4a4Recreating coffee_passport_web_1 ... error xit event ERROR: for coffee_passport_web_1 cannot stop container: a07b86322e13b4796550a99f01af6c0fd4a2279a3f05e5b210eb06cd0a171c57: tried to kill container, but did not receive an exit event ERROR: for webpacker cannot stop container: d0dea180963dc92a187a59c4a462dfebc52a85d5071cfec4ef746f162dd59009: tried to kill container, but did not receive an exit event ERROR: for web cannot stop container: a07b86322e13b4796550a99f01af6c0fd4a2279a3f05e5b210eb06cd0a171c57: tried to kill container, but did not receive an exit event ERROR: Encountered errors while bringing up the project. 依存関係とは別のエラーがでた。。。一旦dockerをまっさらにして、やり直そう。 コンテナが止まらないので、再起動。 すべてのネットワークと、ボリューム、コンテナを削除できた。 docker-compose build --no-cache を実行。 --no-cacheつける意味ない気がするが念には念を くそほど、長くロードされてる。 ---> 69212fdb637b Successfully built 69212fdb637b Successfully tagged coffee_passport_webpacker:latest Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them と一旦ビルドは成功。 docker-compose up を実行。 [24] ./app/javascript/packs/components/about_coffee_passport.vue + 2 modules 3.2 KiB {3} {11} {12} {14} [built] | 3 modules [25] ./app/javascript/packs/components/contact.vue + 2 modules 1.74 KiB {4} {11} {12} {14} [built] | 3 modules [49] ./app/javascript/packs/router/router.js 469 bytes {11} {12} {14} [built] [52] ./app/javascript/packs/components/static_pages/appExplain.vue + 4 modules 5.41 KiB {0} {9} [built] | 5 modules [53] ./app/javascript/packs/components/footer.vue + 2 modules 1.16 KiB {6} {11} [built] | 3 modules [60] ./app/javascript/packs/tag.js 5.77 KiB {1} {15} [built] [61] ./app/javascript/packs/preview.js 2.77 KiB {1} {13} [built] [64] ./app/javascript/packs/app_explain.js 329 bytes {0} [built] [65] ./app/javascript/packs/application.js 911 bytes {1} [built] [74] ./app/javascript/packs/footer.js 192 bytes {11} [built] [89] ./app/javascript/packs/hello_vue.js + 5 modules 9.73 KiB {12} [built] | ./app/javascript/packs/hello_vue.js 2.19 KiB [built] | ./app/javascript/app.vue 552 bytes [built] | ./app/javascript/app.vue?vue&type=template&id=286cd28a&scoped=true& 213 bytes [built] | ./app/javascript/app.vue?vue&type=script&lang=js& 364 bytes [built] | ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./app/javascript/app.vue?vue&type=template&id=286cd28a&scoped=true& 3.95 KiB [built] | ./node_modules/babel-loader/lib??ref--7-0!./node_modules/vue-loader/lib??vue-loader-options!./app/javascript/app.vue?vue&type=script&lang=js& 2.44 KiB [built] [90] ./app/javascript/packs/components/Home.vue + 2 modules 826 bytes {2} [built] | 3 modules + 76 hidden modules こんな感じでコンパイルも成功してそう。 だし、docekr-compose up成功してくれ、、、 webpacker_1 | /coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object. (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 だめだぁ。。。・・・・ reating coffee_passport_db_1 ... done Creating coffee_passport_selenium_chrome_1 ... done Creating coffee_passport_webpacker_1 ... done Creating coffee_passport_web_1 ... done Attaching to coffee_passport_db_1, coffee_passport_webpacker_1, coffee_passport_web_1 db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started. db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql' db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started. db_1 | 2021-12-23 15:14:49+00:00 [Note] [Entrypoint]: Initializing database files db_1 | 2021-12-23T15:14:49.935688Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.21) initializing of server in progress as process 44 db_1 | 2021-12-23T15:14:49.944995Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:14:50.495413Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:14:52.240716Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option. webpacker_1 | /coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 webpacker_1 | return func(...args); webpacker_1 | ^ webpacker_1 | webpacker_1 | TypeError: Class constructor ServeCommand cannot be invoked without 'new' webpacker_1 | at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) webpacker_1 | at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) webpacker_1 | at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 webpacker_1 | at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) webpacker_1 | at Module._compile (node:internal/modules/cjs/loader:1101:14) webpacker_1 | at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) webpacker_1 | at Module.load (node:internal/modules/cjs/loader:981:32) webpacker_1 | at Function.Module._load (node:internal/modules/cjs/loader:822:12) webpacker_1 | at Module.require (node:internal/modules/cjs/loader:1005:19) webpacker_1 | at require (node:internal/modules/cjs/helpers:102:18) coffee_passport_webpacker_1 exited with code 1 db_1 | 2021-12-23 15:14:55+00:00 [Note] [Entrypoint]: Database files initialized db_1 | 2021-12-23 15:14:55+00:00 [Note] [Entrypoint]: Starting temporary server db_1 | 2021-12-23T15:14:55.874976Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 91 db_1 | 2021-12-23T15:14:55.894098Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:14:56.143204Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:14:56.253183Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock db_1 | 2021-12-23T15:14:56.401692Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. db_1 | 2021-12-23T15:14:56.402164Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. db_1 | 2021-12-23T15:14:56.407071Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. db_1 | 2021-12-23T15:14:56.434607Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL. db_1 | 2021-12-23 15:14:56+00:00 [Note] [Entrypoint]: Temporary server started. db_1 | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it. db_1 | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it. db_1 | db_1 | 2021-12-23 15:14:59+00:00 [Note] [Entrypoint]: Stopping temporary server db_1 | 2021-12-23T15:14:59.658326Z 10 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.21). db_1 | 2021-12-23T15:15:01.954163Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.21) MySQL Community Server - GPL. db_1 | 2021-12-23 15:15:02+00:00 [Note] [Entrypoint]: Temporary server stopped db_1 | db_1 | 2021-12-23 15:15:02+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up. db_1 | db_1 | 2021-12-23T15:15:02.932785Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 1 db_1 | 2021-12-23T15:15:02.943118Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. db_1 | 2021-12-23T15:15:03.122522Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. db_1 | 2021-12-23T15:15:03.216008Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock db_1 | 2021-12-23T15:15:03.332617Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed. db_1 | 2021-12-23T15:15:03.332862Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel. db_1 | 2021-12-23T15:15:03.336670Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory. db_1 | 2021-12-23T15:15:03.357237Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL. しかもwebコンテナがdoneになってるのにログに表示されないし localhost:3000も受け付けてくれない。 このページは動作してませんとなる・ soichirohara@SoichironoMacBook-Pro coffee_passport % docker-compose ps Name Command State Ports coffee_passport_db_1 docker-entrypoint.sh --def Up 0.0.0.0:3306->3306/tcp, ... 33060/tcp coffee_passport_selenium_ch /opt/bin/entry_point.sh Up 4444/tcp, 5900/tcp rome_1 coffee_passport_web_1 entrypoint.sh ./bin/rails Up 0.0.0.0:3000->3000/tcp ... coffee_passport_webpacker_1 entrypoint.sh ./bin/webpac Exit 1 ... coffee_passport_web_1 コンテナ立ち上がってるはずなのに。。。 coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46 return func(...args); ^ TypeError: Class constructor ServeCommand cannot be invoked without 'new' at runWhenInstalled (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9) at promptForInstallation (/coffee_passport/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10) at /coffee_passport/node_modules/webpack-cli/bin/cli.js:32:43 at Object.<anonymous> (/coffee_passport/node_modules/webpack-cli/bin/cli.js:366:3) at Module._compile (node:internal/modules/cjs/loader:1101:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Module.require (node:internal/modules/cjs/loader:1005:19) at require (node:internal/modules/cjs/helpers:102:18) webpackerコンテナ全体のログはこんな感じ TypeError: Class constructor ServeCommand cannot be invoked without 'new' をググったら。 これは今のところうまくいきました。package.jsonファイルのwebpack-dev-serverバージョンを次のように変更します。 "webpack-dev-server": "~3" そして、yarn installまたはを実行しnpm installます。 ソース:https://stackoverflow.com/a/69050300/2774342 PSこれは一時的な解決策になる可能性がありますが、新しいバージョンがリリースされるため、将来の参照(数か月後だと思います)のために、この回答は非推奨になります。 とかいてあったので webpack-dev-serverをバージョン3のままにする これを行うには、package.jsonファイルを更新します。 package.json "webpack-dev-server": "~3" をこうする。 - RUN bundle exec rails webpacker:compile - RUN yarn upgrade-interactive --latest をけした。コンパイル専用のコンテナがあるので、わざわざDockerfileに記述しなくてもいいとおもった。 RUN yarn upgrade-interactive --latest は "webpack-dev-server": "~3"とバージョンを固定したいので、 アップデートをしてしまうコマンドを削除 RUN yarn install --check-files を追記 docker-compose up で成功!! やったぜ!! 上手く言ったdockerfileとdocker-compose.yml FROM ruby:2.6.5 ## nodejsとyarnはwebpackをインストールする際に必要 # yarnパッケージ管理ツールをインストール RUN curl http://deb.debian.org/debian/dists/buster/main/binary-amd64/by-hash/SHA256/935deda18d5bdc25fb1813d0ec99b6e0e32a084b203e518af0cf7dc79ee8ebda | head RUN apt-get update && apt-get install -y curl apt-transport-https wget && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && apt-get install -y yarn && apt-get install -y graphviz RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ apt-get install -y nodejs # chromeの追加 RUN apt-get update && apt-get install -y unzip && \ CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \ wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \ unzip ~/chromedriver_linux64.zip -d ~/ && \ rm ~/chromedriver_linux64.zip && \ chown root:root ~/chromedriver && \ chmod 755 ~/chromedriver && \ mv ~/chromedriver /usr/bin/chromedriver && \ sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get update && apt-get install -y google-chrome-stable RUN /bin/sh -c /bin/sh -c bundle update --bundler RUN gem install bundler:2.1.4 RUN mkdir /coffee_passport WORKDIR /coffee_passport COPY . /coffee_passport COPY Gemfile /coffee_passport/Gemfile COPY Gemfile.lock /coffee_passport/Gemfile.lock # RUN bundle update rails # RUN bundle update # RUN bundle update mimemagic # RUN bundle update capybara selenium-webdriver #RUN bundle update nokogiri marcel mimemagic RUN bundle install RUN yarn upgrade #RUN yarn upgrade-interactive --latest #RUN bundle exec rails webpacker:comp RUN yarn install --check-files # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"] docker-compose.yml version: '3' services: db: image: mysql:8.0.21 cap_add: - SYS_NICE # コンテナにLinux機能を追加するオプションのようです。SYS_NICEは、プロセスの優先度(nice値)をあげます。 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_HOST: db ports: - '3306:3306' volumes: - mysql-data:/var/lib/mysql command: --default-authentication-plugin=mysql_native_password # 認証方式を8系以前のものにする web: &web build: . command: ./bin/rails s -b 0 stdin_open: true tty: true # この2文を追加でコンテナ内の標準入出力をローカルマシンのターミナルにアタッチする準備が整います。 volumes: - .:/coffee_passport ports: - "3000:3000" depends_on: - db environment: WEBPACKER_DEV_SERVER_HOST: webpacker MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} RAILS_MASTER_KEY: ${RAILS_MASTER_KEY} SENDGRID_API_KEY: ${SENDGRID_API_KEY} ADMIN_USER_PASSWORD: ${ADMIN_USER_PASSWORD} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} GOOGLE_USER_NAME: ${GOOGLE_USER_NAME} GOOGLE_PASSWORD: ${GOOGLE_PASSWORD} SENDGRID_USER_NAME: ${SENDGRID_USER_NAME} SENDGRID_PASSWORD: ${SENDGRID_PASSWORD} PAYJP_SECRET_KEY: ${PAYJP_SECRET_KEY} PAYJP_PUBLIC_KEY: ${PAYJP_PUBLIC_KEY} MYSQL_HOST: db # selenium_chrome を使うために以下の行を追加 SELENIUM_DRIVER_URL: http://selenium_chrome:4444/wd/hub" selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none webpacker: <<: *web command: ./bin/webpack-dev-server environment: WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 ports: - "3035:3035" volumes: mysql-data: driver: local vendor_bundle: driver: local
- 投稿日:2021-12-24T20:14:29+09:00
Dockerfileベストプラクティス - Node.jsアプリケーション編
この記事は ZOZO Advent Calendar 2021 24日目 の記事です。 概要 最近業務でNode.jsを扱うことがありました。そこでNode.jsアプリケーションをDocker化する上でのチェックポイントとDockerfileベストプラクティスのおさらいをまとめました。 実は Next.jsのドキュメント に答えらしきものがあることに後で気づきました。 環境 $ node -v v16.13.1 $ npm -v 8.1.2 $ docker -v Docker version 20.10.8, build 3967b7d サンプルアプリケーションの作成 サンプルアプリケーションは Next.js を使用しました。 Dockerfileとはあまり関係ないですが、ついでなのでNext.jsの起動方法も確認してみます。 まずは Next.js公式チュートリアル を参考に下記のコマンドを実行します。 $ npx create-next-app nextjs-blog-on-docker --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter" 動作確認をします。 $ cd nextjs-blog-on-docker $ npm run dev > dev > next dev ready - started server on 0.0.0.0:3000, url: http://localhost:3000 event - compiled client and server successfully in 2.5s (158 modules) Attention: Next.js now collects completely anonymous telemetry regarding usage. This information is used to shape Next.js' roadmap and prioritize features. You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL: https://nextjs.org/telemetry wait - compiling / (client and server)... event - compiled client and server successfully in 895 ms (174 modules) http://localhost:3000 にアクセスしてアプリケーションが起動しているのを確認したら ctrl+cで停止しておきます。 Dockerfileの作成 アプリケーションのルートディレクトリにDockerfileを作成し、必要最低限の内容を書いて保存します。 $ touch Dockerfile FROM node:16 WORKDIR /app COPY . . RUN npm install RUN npm run build EXPOSE 8080 CMD [ "npm", "start" ] imageをbuildしサイズを確認したところ1.19GBあることがわかりました。 $ docker build . -t nextjs-web-app-on-docker ...略 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nextjs-web-app-on-docker latest 9812cd37e688 4 minutes ago 1.19GB dockerを起動し動作確認をします。 $ docker run -p 8080:3000 -d nextjs-web-app-on-docker fe330307542efd19930e26e0c43ad9121b7d99d79614849a78da3143d15ecb8b $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fe330307542e nextjs-web-app-on-docker "docker-entrypoint.s…" 21 seconds ago Up 20 seconds 8080/tcp, 0.0.0.0:8080->3000/tcp, :::8080->3000/tcp romantic_hugle $ docker logs fe330307542e > start > next start ready - started server on 0.0.0.0:3000, url: http://localhost:3000 http://localhost:8080 にアクセスし前述と同じWelcomeページが確認できたのでコンテナは停止しておきます。 $ docker stop $(docker container ls -q) Dockerfileのベストプラクティス ここから本題になります。 1. .dockerignore で不要なファイルを除外 プロダクション環境にデプロイする上で不要なファイルを.dockerignoreに記述して除外します。 加えて.env、.aws、.npmrcなど秘匿情報が書かれている可能性があるファイルもあれば除外しておきます。 **/node_modules/ **/.git **/npm-debug.log ちなみに .gitignore を自動生成するツールとして gibo というものがありますが、 .dockerignore を自動生成する dobo というツールもあるようです。 2. マルチステージ・ビルドを使用する アプリケーションのbuild環境と実行環境を分離して、実行環境には最終的な成果物だけを配置することによりimageサイズの縮小を図ります。 # 依存パッケージのインストール FROM node:16 as deps WORKDIR /app # packeg.jsonとpackage-lock.jsonのみコピーする COPY package*.json ./ RUN npm install # Build環境 FROM node:16 as builder WORKDIR /app COPY . . # depsステージでインストールしたパッケージをコピーする COPY --from=deps /app/node_modules ./node_modules RUN npm run build # 実行環境 FROM node:16 WORKDIR /app ENV NODE_ENV production # ファビコンが格納されたディレクトリをコピーする COPY --from=builder /app/public ./public # buildによって.next配下に生成されたhtml、JSON、JSファイルをコピーする COPY --from=builder /app/.next ./.next COPY --from=builder /app/package.json ./package.json EXPOSE 8080 CMD [ "npm", "start" ] 3. CMDにはnpmコマンドを使用しない npmコマンドはシグナルをNode.jsアプリケーションに転送することができないので、KubernetesからGraceful Shutdownが実行できません。よって npm start の代わりに node_modules/.bin/next start を実行します。 (実は node_modules/.bin/next start でも不十分という話し もあります。別の機会に調べようと思います。) 参考: * nodebestpractices/bootstrap-using-node.japanese.md at master · goldbergyoni/nodebestpractices * 2021年1月時点Next.jsのGraceful Shutdown実装状況調査 - tom-256.log - CMD [ "npm", "start" ] + CMD [ "node_modules/.bin/next", "start" ] 4. 実行環境では軽量なベースimageを使用する ベースimageに含まれているライブラリの数が多いほど脆弱性を生む可能性が高いため、プロダクション環境ではなるべく必要最低限で軽量なimageを使用します。 いくつか候補がありましたが、Googleによってメンテンスされている Distroless を使用することにしました。 - FROM node:16 + FROM gcr.io/distroless/nodejs:16 Distrolessはそのままだとshellにログインもできないので少し工夫が必要です。その方法は後述します。 5. 特権ユーザを使用しない セキュリティ面やDocker内で生成されたドキュメントの権限などに不都合があるので、rootユーザーを使う必要がなければ USER を用いてユーザを変更します。 また、Distrolessはadduserといったコマンドが使えないので、はじめから作成されているnonrootというユーザーを利用します。 参考: Distrolessイメージをroot以外のユーザーで実行する - COPY --from=builder /app/.next ./.next + COPY --from=builder --chown=nonroot:nonroot /app/.next ./.next + USER nonroot コンテナにログインしてユーザーを確認してみます。 シェルにログインするには gcr.io/distroless/nodejs:debug imageを使用します。 - FROM gcr.io/distroless/nodejs:16 + FROM gcr.io/distroless/nodejs:debug # image build $ docker build . -t nextjs-web-app-on-docker --no-cache # シェルにログイン $ docker run -p 8080:3000 --entrypoint=sh -ti nextjs-web-app-on-docker # ユーザーを確認 /app $ whoami nonroot nonrootになっているのが確認できました。 6. npm installの代わりにnpm ciを使用する npm ciはnpm installと同様に依存パッケージをダウンロードします。npm installとの違いはpackage-lock.jsonの更新をしないことで、これによって開発時とプロダクション時のコードの差がなくなります。また、node_modulesディレクトリを削除し常にクリーンインストールを行います。さらに --only=production オプションでプロダクション環境では不要なdevDependenciesなパッケージを削除しimageサイズを抑えることができます。 # 依存パッケージのインストール FROM node:16 AS deps WORKDIR /app COPY package*.json ./ # devDependenciesなパッケージを削除 RUN npm ci --only=production # Build環境 FROM node:16 as builder WORKDIR /app COPY . . RUN npm ci RUN npm run build # 実行環境 FROM gcr.io/distroless/nodejs:16 WORKDIR /app ENV NODE_ENV production COPY --from=builder /app/public ./public COPY --from=builder --chown=nonroot:nonroot /app/.next ./.next # 不要なものが取り除かれたnode_modulesをプロダクションにコピーする COPY --from=deps /app/node_modules ./node_modules COPY --from=builder /app/package.json ./package.json USER nonroot EXPOSE 8080 CMD [ "node_modules/.bin/next", "start" ] 7. ステップの順番を最適化する package.json、package-lock.jsonの更新頻度は少ないため、先にコピーをしておきnpm ci(パッケージのダウンロード)の処理をキャッシュさせます。その後からbuildステップを実行することで全体の実行時間を短くさせます。 # Build環境 FROM node:16 as builder WORKDIR /app - COPY . . + COPY package*.json ./ RUN npm ci + COPY . . RUN npm run build 8. Lintを使用する Lintツールは hadolint を利用しました。試しにFROMを削除して実行すると下記のようなエラーが出ました。このように事前にミスを防ぎます。 $ brew install hadolint $ hadolint Dockerfile Dockerfile:21 DL3022 warning: COPY --from should reference a previously defined FROM alias 9. 脆弱性スキャンを使用する スキャンツールは Dockle を利用しました。 $ brew install goodwithtech/r/dockle $ dockle nextjs-web-app-on-docker:0.0.1 SKIP - DKL-LI-0001: Avoid empty password * failed to detect etc/shadow,etc/master.passwd INFO - CIS-DI-0005: Enable Content trust for Docker * export DOCKER_CONTENT_TRUST=1 before docker pull/build INFO - CIS-DI-0006: Add HEALTHCHECK instruction to the container image * not found HEALTHCHECK statement Dockleは見つかった脆弱性を5段階で評価してくれます。 結果 最終的なdockerfileは下記です。 # 依存パッケージのインストール FROM node:16 AS deps WORKDIR /app COPY package*.json ./ RUN npm ci --only=production # Build環境 FROM node:16 as builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 実行環境 FROM gcr.io/distroless/nodejs:debug WORKDIR /app ENV NODE_ENV production COPY --from=builder /app/public ./public COPY --from=builder --chown=nonroot:nonroot /app/.next ./.next COPY --from=deps /app/node_modules ./node_modules COPY --from=builder /app/package.json ./package.json USER nonroot EXPOSE 8080 CMD [ "node_modules/.bin/next", "start" ] どれくらいimageが軽くなったか確認します。 $ docker build . -t nextjs-web-app-on-docker:0.0.1 --no-cache ...略 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nextjs-web-app-on-docker 0.0.1 8e81f00afeeb 3 minutes ago 321MB 1.19GB → 321MBまで軽量化することができました。 お疲れさまでした。 明日25日の大トリは @t_shimokawa さんの記事です。
- 投稿日:2021-12-24T19:49:04+09:00
SESAMEのAPIから施錠解錠する
セサミを買いました 最近、セサミというスマートロックを買いました。このスマートロックはよくできていて、私の今年買ってよかったものランキングぶっちぎり1位です。価格も安いので是非試してみてください。 セサミは Web API が無料で公開されており、この記事ではNodeJS から使うときのサンプルコードと手順をご紹介します。 今回使うもの 以下の手順はSESAME4を動かすためのものです。今回はインターネット越しにSESAMEにアクセスするためWi-Fiモジュールが必要となります。 Web API キーの取得 こちらのページからログインするとWeb APIキーを取得できます。 https://partners.candyhouse.co/login アプリから招待用QRコードを発行 セサミのスマホアプリを開き、APIから操作したいデバイスを選択して招待用のQRを発行します。 QRからAPI呼び出しに必要な値を抽出 先ほどのQRをこちらのページに読み込ませるとAPI呼び出しに必要な情報を取得できます。 下の画像のような感じで抜き出せますので、UUID(デバイスID)とSecret Keyをメモします。 APIから操作してみる 私のGitHubにNode.jsを使ったサンプルコードがありますのでそちらを使ってみてください。 プロジェクトをダウンロードしたら、まずライブラリを集めます。 npm install そして.envファイルを作成して先ほど取得した情報を埋めてください。 SESAME_UUID=<QRから抽出したUUID> SESAME_API_KEY=<APIキー> KEY_SECRET_HEX=<QRから抽出したシークレットキー> 最後にindex.jsを実行すると鍵が動くと思います。 node index.js 最後に 以上、走り書き程度ですが参考になれば嬉しいです。 スマートロックにしてから鍵を持ち歩く必要がなくなりました。荷物が減るのでとてもおすすめです。是非挑戦してみてください! 参考 公式ドキュメント
- 投稿日:2021-12-24T16:53:53+09:00
複数のTouchDesignerプロジェクトで確実にデータ共有する
この記事はTouchDesigner Advent Calendar 2021の24日目の記事です。 自分はスポーツ中継などのCG開発をTouchDesignerでやっています。 野球中継などで画面の下に選手名や得点が表示されているアレです。 グラフィックの技術は割と地味なので、それ以外の箇所を深堀りしてみます。 構成 UIはReact.js、バックエンドはNode.js、グラフィックをTouchDesignerでという構成が多いです。 ブラウザでデータを入力するとテキストファイルが更新されて、Table DAT経由でグラフィックが更新される形です。 要件 いわゆるスポーツ中継などの案件は、 機材の設営は前日か当日に行う 試合前に選手情報などを流し込み、試合中は公式ウェブサイトや現地映像を見ながらデータ入力する 映像に合わせてテンポよくCGを表示する データ入力を行うスタッフは1人ではない(得点を入力するスタッフ、選手情報を入力するスタッフ) Video Device OutでFill/Keyを出力してキーヤーで合成してもらう 役割によって複数機材から複数系統でCGを出力する(得点を表示させるスタッフと選手名を表示させるスタッフは別) 案件によってはディレクターさんが指示したタイミングでCGを出せないと死ぬ という感じです。 もちろん死ぬのは冗談ですが、CGのOn/Offをキーボードで切り替えており、TouchDesignerのウインドウが非アクティブになっていると効かないので、バックグラウンドでもキーボード入力を受け付けるSticky Keyboard In CHOPを書きました。便利です。 難しい点 複数の人間が入力したデータ(選手情報や得点など)を複数の機材で共有する必要がある 設営やオペレーションを行うスタッフと開発者は別人である 現場によるが既設のネットワーク環境(インターネットも)がない 課題 当初はブラウザから普通にHTTPでTouchDesignerのWeb Server DATを叩いていました。 たとえば選手交代のときは、Web Server DATにどのチームのどの選手が誰々と交代しましたよ、というのをHTTPで投げます。複数の機材がある時は、2台に向かってHTTPで投げてました。 しかし、年間何度もやっていると、なんだかネットワークの調子が悪いときがあります。 こっちの機材ではデータが更新されてるのに、こっちには反映されないとか、そもそも応答がなくなるというような感じです。 原因は様々ですが、本番中に原因を探るのが非常に難しいですので、データ共有にHTTPというかTCPを使うのをやめることにしました。 解決方法 データの入力を受け付ける機材をメイン、データ共有を受ける機材をワーカーと呼びます。 React.jsで入力されたデータはバックエンドのNode.jsでタブ区切りのテキストファイルに保存する fs.watchでそのディレクトリを監視し、変更があったら該当ファイルをreadして、シリアルポートから出力する(ファイル名と内容を\x1dで区切ったもの) ワーカーはシリアルポートからの入力があったらテキストファイルを更新する メインもワーカーもTouchDesignerでデータ参照するときはTable DATでSync to Fileにチェックを入れて使う この方法ですと、TCPのような軟弱な仕組みに頼らなくても物理線で2台の機材がSyncされます。 しかもTouchDesignerとは関係なくバックグラウンドで同期していますので、TouchDesignerのプロジェクトでは何も考えずにデータを参照するだけで良いです。 さらにシリアル通信のメリットとしては、自分はRS422のG/TXD+/TXD-をXLR3に変換して普通の音声ケーブルで接続しているので、パラボックスなどで、いくつでも好きなだけ分配出来ます。やったことはありませんが10台でデータ共有なども余裕(なはず)です。距離も理論上1.2kmまで伸ばすことが出来ます。 ここまで、全くTouchDesigner感がありませんが、参考までにNode.jsのコードを載せておきます。 複数の入力のタイミングがかぶるとシリアル通信はまずいので、全てasync.queue経由でキューの管理をしています。 実際のコードからの抜粋です。 メリークリスマス main.js const fs = require("fs") const { readFile } = require("fs/promises") const async = require("async") const SerialPort = require("serialport") const serialport = new SerialPort(process.env.SERIALPORT, { baudRate: 806400, }) const data_dir = "../data" const read_file = async (path) => await readFile(`${data_dir}/${path}`, "utf8") const queue = async.queue(async (task) => { const ports = await SerialPort.list() if (ports.find((p) => p.path === process.env.SERIALPORT)) { const payload = `\x02${task.path}\x1d${task.data}\x03` serialport.write(payload, (err) => { if (err) console.log(err) }) } }, 1) worker.js const fs = require("fs") const async = require("async") const SerialPort = require("serialport") const Readline = require("@serialport/parser-readline") const serialport = new SerialPort(process.env.SERIALPORT, { baudRate: 806400, }) const parser = serialport.pipe(new Readline({ delimiter: "\x03" })) const data_dir = `../data-worker` const queue = async.queue((task, executed) => { const tasksRemaining = queue.length() fs.open(`${data_dir}/${task.path}`, "w", (err, fd) => { if (err) return executed(err, { task, tasksRemaining }) fs.writeFile(fd, task.data, (err) => { if (err) return executed(err, { task, tasksRemaining }) fs.close(fd, (err) => { if (err) return executed(err, { task, tasksRemaining }) return executed(null, { task, tasksRemaining }) }) }) }) }, 1) parser.on("data", (payload) => { if (!payload.match(/^\x02/)) return payload = payload.replace(/^\x02/, "") const vals = payload.split(/\x1d/) const path = vals.shift() const data = vals.shift() const task = { path, data } queue.push(task, (err, { task, remaining }) => { if (err) { console.log(`[Error] An error occurred while processing task: ${path}`) } }) })
- 投稿日:2021-12-24T11:11:39+09:00
CSSスプライトを使って画像読込を効率化したい
この記事は ミライトデザイン Advent Calendar 2021 16日目の記事です。 やりたいこと Webページ上でアイコンのような小さな画像ファイルをたくさんロードするのは通信が非効率になりがち。 これらのファイルを1つのファイルにまとめて出力したい! ということでCSSスプライト&結合済画像ファイルを自動で生成しよう。 今回はnpmモジュールの spritesmith-cli を使って変換している。 準備 変換用JSコード spritesmith-cli を使って変換するためのコード。 ファイルは必ず .spritesmith.js という名前で保存しよう。 .spritesmith.js 'use strict'; const util = require('util'); module.exports = { cssTemplate: '.spritesmith.css.handlebars', destImage: 'images/hoge.png', destCSS: 'css/_hoge.scss', imgPath: '/images/hoge.png', padding: 2, src: 'data/hoge/*.png' }; cssTemplate: テンプレートファイル(後述)のファイルパス destImage: スプライト画像の出力先ファイルパス destCSS: スプライト画像を利用するためのCSSの出力先ファイルパス imgPath: スプライト画像をWeb上から読み出すためのパス padding: 画像ごとのパディング(少しとっておくと隣の画像が表示される事故が起きない) src: 元画像ファイルのパス 変換用CSSテンプレート 以下.scssを出力するためのサンプル。 今回Retina対策として画像は倍サイズのものを用意して1/2で表示している。 .spritesmith.css.handlebars .icon { {{#items}} &.icon-{{name}} { &:before { content: ""; background-image: url({{{escaped_image}}}); background-position: ({{px.offset_x}} / 2) ({{px.offset_y}} / 2); background-repeat: no-repeat; background-size: ({{px.total_width}} / 2) ({{px.total_height}} / 2); width: ({{px.width}} / 2); height: ({{px.height}} / 2); } } {{/items}} } 変換方法 以下コマンドを実行すると.scss/.pngファイルが自動生成される。 適当に組み込んでビルドして使おう。 npx spritesmith-cli 使い方 以下のようにHTMLを記述することで画像を表示できる。 index.html <i class="icon icon-xxx"></i>
- 投稿日:2021-12-24T10:12:24+09:00
WebpackとViteをプロジェクト導入して比較した話
みなさんこんにちは。suginokoです。 寒くなってきたので布団を着て仕事をしています。(弊社はフルリモートで仕事ができます。暖かくして仕事しましょう。) 前置き 今日は弊社Web開発における開発環境についての記事書いてみようかなと思います。 弊社の開発環境構築に普段はWebpackを使って環境構築しているのですが、 Webpackで別に開発する必要なくない?もっと簡単に導入できそうなのあるのではないかな 開発環境の立ち上がりが遅すぎてムカつく。もっとはやく動かないもんかね というのが根底にあり、調べてみることにしました。 Viteというのは、私自身が前からesbuildに注目していて、これWebpackの代わりになるのではないかなと思っていたところで、Viteが出てきた(詳しい話は後述します。中でesbuild使っている)ので比較してみようかなと思った次第です。 (ようやくテストではなくて実際のプロジェクトに導入できてうれしい。 速いってことはわかったけど、どれくらい速くなるとか、開発環境構築楽になるとかわからなかったので、比較して使いたいなと思った次第です。 普段のWebpackでの開発環境構築 プロジェクトでは普段Reactを使っているのでそれに合った環境構築をWebpackで行います。 そこにTypeScriptを入れる入れない、Redux使うなり他のモジュール入れるなりそれは開発環境作る人で様々ですが、おおよそは style系のloader、プラグイン devServer alias入れるとか入れないとか TypeScriptを入れるならts-loader入れるとか vendor.js分けるとか まあ細かいところはもう少しありますが、大体上記の導入して整えていきます。 これらをViteでも同じように行って比較していきます。 Viteでの環境構築 (TypeScript入れるほうの構築めっちゃ時間かかった・・・) Webpackと同じ構成にするので、React + TypeScript + Sassくらいか。Redux(toolkit)もいれちゃったかもしれないけど、だいたいこんな感じで構成 import { defineConfig } from 'vite' import path from 'path' import react from '@vitejs/plugin-react' const src = path.resolve(__dirname, 'src') // const dist = path.resolve(__dirname, 'dist') const ALIAS = {...} // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { console.log('** mode **', mode) return { base: './', server: { port: 11080, open: true }, plugins: [react()], publicDir: 'public', // 明示的に build: { sourcemap: mode == 'develop' ? true : false, minify: mode == 'production' ? 'terser' : false, // dev stg pro // outDir: dist, outDir: './dist', }, // root: './src', difine: { global: 'window', // global指定しないと取得不可 }, resolve: { alias: ALIAS }, } }) Loaderとか書かなくていい。これが最高すぎる。 そもそもViteの場合、取説にあるように、 # npm 6.x npm init vite@latest my-vue-app --template react # ts npm init vite@latest my-vue-app --template react-ts # npm 7+, extra double-dash is needed: npm init vite@latest my-vue-app -- --template react # ts npm init vite@latest my-vue-app -- --template react-ts # yarn yarn create vite my-vue-app --template vue するだけでReact(+TypeScript)の環境構築ができます。最高。 一応Webpack v5の構成 const webpack = require('webpack') const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') // webpack5ではデフォルトでTerserPlugin入ったらしいがまだ必要らしい const TerserPlugin = require('terser-webpack-plugin') // OptimizeCSSAssetsPluginが消えたのでcss-minimizer-webpack-pluginに切り替える const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const mode = process.env.NODE_ENV || 'production' const isDevserver = process.env.IS_DEVSERVER == 1 const port = process.env.PORT || '1111' const src = path.resolve(__dirname, 'src') const dist = path.resolve(__dirname, 'dist') console.log('** mode **', mode) module.exports = { // mode 必須 mode: mode === 'production' ? mode : 'development', cache: { type: 'filesystem', buildDependencies: { config: [__filename] } }, devtool: mode === 'production' ? false : 'eval-cheap-module-source-map' // : 'eval-cheap-module-source-map' , entry: isDevserver ? [ 'react-hot-loader/patch', `webpack-dev-server/client?http://localhost:${ port }`, 'webpack/hot/only-dev-server', `${ src }/main.dev.tsx`, ] : { 'bundle': `${ src }/main.tsx` }, output: { path: dist, filename: isDevserver ? '[name].js' : '[name].js?[hash]', // filename: isDevserver ? 'bundle.js' : '[name].js?[hash]', // bundle.jsにするとエラーになるv4ではいけたが。 publicPath: '/', clean: true }, module: { rules: [ { test: /\.ts(x?)$/, exclude: [/node_modules/], use: { loader: 'ts-loader' } }, { test: /\.scss|.css$/, use: [ isDevserver ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: false, sourceMap: true } }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] }, plugins: [ new HtmlWebpackPlugin({ template: `${ src }/index.html`, inject: 'body', filename: `index.html` }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new MiniCssExtractPlugin({ filename: '[name].css?[contenthash]' }) ], optimization: { splitChunks: { name: 'vendor', chunks: 'all', }, minimize: true, minimizer: [ new CssMinimizerPlugin(), new TerserPlugin({ terserOptions: { compress: { drop_console: true, }, output: { comments: false, } } }) ] }, devServer: { static: { directory: dist, }, historyApiFallback: true, host: 'localhost', compress: true, port: port, hot: true, open: true }, resolve: { alias: {...}, extensions: ['.ts', '.tsx', '.js'], }, performance: { hints: false }, // ES5(IE11等)向けの指定(webpack 5以上で必要) target: isDevserver ? ["web"] : ["web", "es5"], } 。。。長いな~~ この構成するだけでViteの倍くらい体感かかったかもです。 実験のディレクトリ構成(それぞれおおよそ同じ感じで) node_modules package.json postcs s.config.js tsconfig.json webpack.config.js(Viteではvite.config.js) (+viteではenvファイルとか) src ┗components(ここでは共通してApp.tsxのみ) ┗index.html ┗main.tsx 開発環境の起動について時間を比較してみる とりあえずは現時点でのプロジェクトに使っているWebpackでの開発環境ではどれくらいで起動するのか計ってみました。 まあまあ大きい案件だからなのか、Webpack v4 だからなのかわからないけど、13954msやら20871msは遅すぎやしませんかね。 既に動いているプロジェクトだとあんまり意味がなかったので、実験的にViteと同じ構成を作ってWebpack v5も試してみます。(React + TypeScript + Sass) とりあえず、テスト的にWebpack v5の開発環境(React+TypeScript+scssくらいでいいかな)作って、現状のほぼまっさらなViteと同じような構成で開発環境を起動してみましょう。 Webpack v5 6226ms Vite 1370ms Webpack v5もだいぶ速くなったけど、Vite優勢ですね!(なんならViteのほうの構成のほうが少し多いのにこの速度。すばら。 ビルド時の時間の比較 Webpack v5 12.44s Vite 8.9s 少しViteの方がはやそう React(+TypeScript)の案件ならViteでもいいかもしれないですね。 Viteは何故速いのか Webpackはbuildするときにモジュール全体をクロールしてビルドする必要があるので、全部の依存関係が解消されないとビルドされない。 アプリケーションが大きくなるほど遅くなるということでしょう。 (だから大きめの案件だと開発環境の起動が遅かったのですね) 公式にもあるのですが、Viteはこの問題を解消するために依存関係とソースコードのカテゴリで分割することで開発サーバーの起動時間を解消したようです。 モジュールとかは頻繁に変更されることはないので、事前バンドルしておいてページの読み込みを解消したらしいです。(これをキャッシュして使うっぽい。node_modules/.viteにキャッシュするらしい)その事前バンドルをGo製のesbuildを使っているとのこと。(これが爆速) ソースコードはネイティブのESMを使ってブラウザの要求に応じてバンドルせずそのまま提供しているようです。 本番buildではrollupを使っているそうです。(バージョン違うとbuild時にrollupのエラーも出たんで、ちゃんと最新バージョン使ったほうがいです) …rollupはお試しなのかしら。esbuildを本番buildで使いたいけど、特定のコード分割とCSSの取り扱いの関係でまだ使ってないそうなので、将来的には本番buildもesbuildを使うのかもしれないです。 詳しいことは日本語ドキュメントもあるのでみてみてください。 Vite TypeScript使うときだけやや面倒くさい じゃあもうWebpackではなくてViteでいいじゃん!って思うんですけど、ややTypeScriptの扱いが面倒で、 TypeScriptはサポートされているんですけど、型チェックをしてくれません。 なので自分は2窓使って監視するようにしました。 tsc --watch で一応型でエラーしたらわかるので、見つつ対応しています。 また、ブラウザで型チェックしてくれるvite-plugin-checkerも入れてみました。 一応ブラウザでもエラー出るようになってくれたのですが、tsc --watchでどうもエラーが出る箇所が違うみたいで、微妙な感じです。 無いよりはいいかな・・・という感じ。 まあでもTypeScript入れないなら圧倒的にViteのほうがよさげに見えました。 (CSSのbuild設定もこれでは足りないっぽくて実験しながらまだいじくってます。ぶつぶつ) 最後に 年末最後に投稿できてよかったです。 しばらくVite使ってみようかなと思います。
- 投稿日:2021-12-24T07:31:29+09:00
オライリーの特約書店をBlazorでマップ化したウェブサイトを作成した話
概要 みんな大好き「オライリー」の特約書店をマップとして可視化するウェブサイトをBlazor WebAssemblyで作ったので紹介します。 元は.NET 5時代に作ったものなのですが、.NET 6にマイグレーションをした上で少しだけ手を加えたりしたので改めてネタとして紹介しようかと思います。 どういったウェブサイト? オライリーの書籍を販売している書店を、マップや一覧で確認可能なものです。 都道府県別や、現在地点から近い店舗を表示する機能なども備えてます。 旅先でオライリーの書籍が欲しくなるような、オライリー中毒のあなたもこれで大丈夫! (もちろん、レスポンシブデザインなのでスマホでもOK) 全国のオライリー特約店が日本地図にマッピングされているのは壮観です。 情報ソースや構造など 情報のソースとしてはオーム社さんで公開されてる、オライリーの取扱店情報を使用しています。 http://www.ohmsha.co.jp/data/bookstore/bookstore_home.htm この情報には緯度軽度がないので別途、plus codeの情報取得し緯度軽度に変換してマップ状に可視化しています。 https://maps.google.com/pluscodes/ アプリケーションとしては2つあり、 情報をJSonファイルとして出力するNode.jsアプリケーション(ツール) JSonファイルをデータストアとして使用するBlazorアプリケーション といった構造になっています。 Blazor WebAssemblyをGitHub Page上でホストし、デプロイはGitHub Actionsを使用しています。 Node.jsとBlazor用の2つのGitHub Actionを用意し、Node.js側はスケジュールにより定期的に実行されて、情報を自動更新する仕組みになっています。 GitHub Actionsのスケジュール実行に関しては下記が参考になります。 https://qiita.com/Broccolingual/items/bf0fa0d00920fbfda371 Blazor側使用技術 MudBlazor BlazorのUIフレームワークです。 マテリアルデザインで作られていて、CSSやHTMLを直接駆使しなくても、簡単にモダンな見た目のWEBサイトが構築できるようになります。 UIのコンポーネントも豊富で、かなり使い勝手が高い便利なフレームワークです。 今回.NET5から6にアップデートするにあたっては、破壊的な変更が何点かありましたが、簡単に対応することはできました。 (アイコン周りの名称が変わった事やインジェクトするメソッドが追加された等) MudBlazorに関しては、@jsakamotoさんの記事が参考になります。 leaflet Webで地図を表示するためのJavascirptライブラリです。 地図周りの処理はこのライブラリを使用して実装しています。 BlazorはJavascirptの相互運用機能が用意されているので、Javascirptで作られた豊富な資産が利用できるのもメリットですね。 こんな感じで、地図上に表示するデータをleafletに渡してマーカーを表示し、マーカーが選択された時にBlazor側にコールバックを行う、といったような相互作用も簡単にできてしまいます。 blazor @inject IJSRuntime js; <div> <div id="mapcontainer" style="height: @MapHeight; width: 100%; margin:0;"></div> </div> @code { // コールバック [Parameter] public EventCallback<Store> OnStoreMarkerSelected { get; set; } ... public async Task InitAsync(List<Store> stores, Domains.Models.Position position) { // jsの動的ロード var mapModule = await js.InvokeAsync<IJSObjectReference>("import", "./js/leaflet-map.js"); ... // コールバック用に参照を渡す var objRef = DotNetObjectReference.Create(this); await mapModule.InvokeVoidAsync("initMap", objRef); ... // 表示用のデータを渡す await mapModule.InvokeVoidAsync("setData", stores); } // 地図上でマーカーが選択されたら呼ばれるコールバック [JSInvokable] public void OnStoreMarkerClicked(Store store) { OnStoreMarkerSelected.InvokeAsync(store); } } leaflet let map; let dotnet; export function initMap(dotNetHelper, viewPoint) { // コールバック用にblazor側のインスタンスを保持 dotnet = dotNetHelper; ... } let layer; export function setData(storeList) { ... // Blazor側から渡された店舗情報をマーカーとして設定 storeList.forEach((store) => { layer = addMakerWithClickEvent(layer, store); }); map.addLayer(layer); } // 店舗情報をマーカーとして配置 function addMakerWithClickEvent(layer, storeData) { try { const maker = L.marker([storeData.position.latitude, storeData.position.longitude], { draggable: false, }).on('click', function (e) { onMarkerClicked(storeData); }); //マーカークリック時 layer.addLayer(maker); return layer; } catch (err) { console.error("add error", err); } } // blazor側にマップ上で選択された店舗情報をコールバックする function onMarkerClicked(store) { dotnet.invokeMethodAsync('OnStoreMarkerClicked', store); } もともとleafletは使ったことがあり、js側での操作に慣れていたため、試してはいませんが、leafletをBlazor用にラップしたライブラリなどもあるようなのでこちらを使うともっと手軽に実装できるかもしれませんね。 有名なjs系のライブラリだと、Blazor用にラップしたものなどが用意されていて簡単に使えたりします。 GeoCoordinate.NetCore 2点間の緯度、経度から距離を算出することができるライブラリです。 現在地と各店舗の距離を算出して、最寄りの店舗を探すといった事を実現しています。 .NET FrameworkにはSystem.Device.Location.GeoCoordinateクラスで算出できますが、.NET(.NET Core)にはこのクラスがないため、こちらを使用しています。 Blazor向けに作られたものではありませんが、こういった.NET用のライブラリがそのまま使えてしまうのもBlazorの強みです。 GISなどを使った、本格的な地理情報を用いたシステムを作る際にはNetTopologySuiteを使うほうがよさそうですが、距離の精度や細かい設定なども必要ないので、とりあえずお手軽さを優先しました。 まとめ 簡単ですが、Blazorを使用して作成したアプリケーションを紹介しました。 C#で書いたアプリケーションがGitHub PageやFirebase等の静的ページホスティングサービスで手軽にデプロイできるのが、Blazor(WebAssembly)の良さだと思うので、思いつたネタ等はとりあえず勢いで作ってしまったりすると、面白いのではないかと思います。 豆知識 ご存知な方も多いと思いますが、オライリー本の表紙で使用されている動物は、下記のイラスト集から使用されているものが多いです。 この書籍の最初に書いてありますが、所持者は最大4つまで1つの作品でイラストを使用する事ができます。 (それ以上は連絡と許諾が必要な模様) kindleで電子書籍版も購入可能で、入手容易なのも嬉しいポイントです。 オライリー好きならば1冊は持っておきたいですね!! 実は今回紹介したページも、ランダムで4種類の動物の画像が出るようになっています。
- 投稿日:2021-12-24T02:43:17+09:00
タスクマネージャーをアップデートした!
前回の記事 作成したタスクマネージャーbot 追加した機能 ・WebViewでタスクの登録 ・通知機能 ・リッチメニュー 使用した技術 フロントエンド ・LINE バックエンド ・Node.js(Express) ・firebase(cloud functions, firestore) ・Messaging API ・dayJs WebView ・LIFF(LINE SDK) ・ReactHooks ・Material-UI ・firebase(Hosting, firestore) システム構成 Cloud Functions + LIFF 1.main関数 : チャットボットとのやりとりを行う 2.register関数 : タスクが登録できたことを知らせる 3.reminder関数 : 指定された時間に通知をする + 4.LIFF : タスクの登録を行う それぞれの関数について main関数 main関数の主な役割は登録したタスクの表示です。 下記シーケンス図はタスク一覧表示の処理の流れです。 main関数はwebhookURL1として設定しています。 つまりユーザーがメッセージを送信すると、main関数にpost(webhookイベントオブジェクト)が送られます。 webhookイベントオブジェクトの中身はこちら 1.webhookイベントオブジェクトを受け取ったmain関数は、メッセージ内容を確認します。 メッセージ内容が「@show」であれば2へ 2.ユーザIDとfirebaseに登録してあるIDと照合し一致するタスク取り出します。 3.取り出したタスクをボタンテンプレート形式でユーザに送信するよう処理を行なっています。 ざっくり説明するとmain関数は1~3の処理を行なっています。 1webhookとはユーザーがメッセージを送信したり、友達追加したりなどイベントが発生すると、指定したURLにpostを投げる仕組み・技術のことです register関数 register関数はタスクが登録された時に登録できたことを通知するプログラムです。 トリガーをonCreateにすることでfirestoreにタスクが登録された時に、処理が走るようになっています。 Node.js exports.register = functions.region("asia-northeast1") .firestore .document('todos/{todoId}') .onCreate((snap, context) => { const userId = snap.data().userId; const todoName = snap.data().name; return Handle.registerMessage(userId, todoName); }); firestoreにタスクが登録されるとそのタスクからユーザIDとタスク名を取得し、ユーザID宛にpushメッセージを送る処理をHandle.registerMessage()で書いています。 remider関数 reminder関数は通知設定されたタスクを時間通りに通知するプログラムです。 トリガーは1分毎に実行されるように書いていて、現在時刻と通知時間が一致するタスクがあるかどうかを毎分照合しています。 一致するタスクがあればそのタスクデータを取り出し、登録したユーザーにpushメッセージを送るコードをHandle.reminder()で書いています。 Node.js exports.reminder = functions.region('asia-northeast1') .pubsub.schedule('every 1 minutes') .timeZone('Asia/Tokyo') .onRun((context) => { return Handle.reminder(); }) LIFF タスクの登録はwebアプリケーションで行なっています。 LIFFとはwebアプリケーションをLINEの中で起動させる仕組みのことで、LINEから離れずに操作することができます。 こんな感じ↓↓↓↓ React.js useEffect(() => { liff.init({liffId: LIFF_ID}) .then(() => { getProfile(); }) .catch((err) => { console.log(err.code, err.message); }) }, []) const getProfile = () => { liff.ready.then( async() => { const context = await liff.getContext() setUid(context.userId); }) } webアプリケーションの中でLINESDKを用いて、liff.init()→liff.getContext()することで ユーザー情報を取得できます。公式ドキュメント 本アプリではタスクにユーザIDをタグ付けすることで、誰のタスクかを管理しています。 まとめ コードによるプログラム解説ではなく、設計段階の解説をしてみました。 こちらのLINEBotはまだまだ改良していくつもりなので、追加機能の案やバグがあればドシドシコメントお願いします!! また、今回の記事で分かりにくいことがあればコメント頂ければ幸いです! 実装予定機能 毎週リマインド機能 スヌーズ機能 参考文献 https://developers.line.biz/ja/docs/messaging-api/ https://dev.classmethod.jp/articles/try-cloud-functions-scheduler-pubsub/ https://qiita.com/hirothings/items/37430b2408a5a7a85972 https://ginpen.com/2019/06/01/firestore-indexes-json/ https://github.com/iamkun/dayjs/blob/dev/docs/en/API-reference.md#list-of-all-available-formats
- 投稿日:2021-12-24T00:18:43+09:00
【Node.js】Expressからaxiosを使ってバックエンドサーバーの画像を取得する際はresponseTypeにarrayBufferを指定する
はじめに ルー大柴ぽいのが否めませんが表題の通りです。 訳あってBFF(Backend For Frontend)を採用する必要があり、クライアントサーバーとバックエンドサーバーでのやりとりで詰まったのでメモを残します。 ※個人の見解です。明確なエビデンスを探し切ることができませんでした。根拠が乏しいので、真に受けないことをおすすめします。 背景 バックエンドサーバーから画像を取得しようとしたら次のようなレスポンスがクライアントサーバーに返ってきた。 結論 サーバー側でaxiosを使って画像をgetする時はresponseTypeをarrayBufferにする必要があるらしい。 引用①(axiosのドキュメントを引用している) how does axios handle blob vs arraybuffer as responseType , From axios docs: // responseType indicates the type of data that the server will respond with // options are: 'arraybuffer', 'document', 'json', You can configure the type of the dataproperty using Axios' responseTypeobject. responseTypeis set to 'json', which means Axios will try to parse the response as JSON. However, that isn't correct if you're looking to, say, download an image using Axios. You can set responseTypeto 'arraybuffer'to get the response as an ArrayBuffer: 引用② You can configure the type of the data property using Axios' responseType object. By default, responseType is set to 'json', which means Axios will try to parse the response as JSON. However, that isn't correct if you're looking to, say, download an image using Axios. You can set responseType to 'arraybuffer' to get the response as an ArrayBuffer: サーバー側でaxiosを使っていて画像のやりとりをするときはresponseがarrayBufferで指定して受け取らないと文字化けしてしまう。とのことで、クライアント⇄サーバーの経験はあってもサーバー⇄サーバーは未経験でしたので解決までに時間がかかりました。次の記事は今回はまったのと同じ状況だったのと図解もされているので非常に参考になりました。 axiosでarrayBufferを使うとき const axios = require('axios'); const res = await axios.get('https://images.unsplash.com/photo-1506812574058-fc75fa93fead', { responseType: 'arraybuffer' }); const fs = require('fs'); fs.writeFileSync('./south-beach.jpg', res.data); axiosでarrayBufferを使わないとき const axios = require('axios'); const res = await axios.get('https://httpbin.org/get', { params: { answer: 42 } }); res.headers['content-type']; // 'application/json' const axios = require('axios'); const res = await axios.get('https://httpbin.org/html'); res.headers['content-type']; // 'text/html; charset=utf-8' typeof res.data; // 'string' res.data; // '... <h1>Herman Melville - Moby-Dick</h1> ...' さいごに BFFで検索するとズッ友の方が出てくるあたりまだユースケースは少ないのかなと思いました。が、有名企業が採用していることもあるのでBFFを採用するところはこれから増えてくるような気がします。もしクライアントサーバーからaxiosを使って画像情報のやりとりをする際はarrayBufferを使うことを念頭に置くと良いと思います。 参考URL