TECH PLAY

WESEEK, Inc.

WESEEK, Inc. の技術ブログ

å…š75ä»¶

こんにちは、GROWI.cloud の開発・運甚を担圓しおいる WESEEK の゚ンゞニアの䌊勢です。 今回は、 GROWI.cloud の開発で困った問題が起きお、その問題を解消した時の話をご玹介したす。 背景 GROWI.cloud は node.js を䞻な蚀語ずしお開発しおおり、そのプロゞェクトを圹割に応じお「プロゞェクトA」「プロゞェクトB」... ず分割しおいたす この「プロゞェクトA」「プロゞェクトB」... が互いに通信するこずで、 GROWI.cloud のサヌビスは成り立っおいたす たた最近になっお、開発環境は VSCode の devcontainer(, docker-compose) を利甚するこずになりたした 各プロゞェクト単䜓での開発に限っおは、他のプロゞェクトずの連係に瀟内テスト環境を利甚しおいたため、devcontainer 化の圱響はありたせんでした 開発環境内で「プロゞェクトA」ず「プロゞェクトB」を連係しお開発する堎合においお、䞡プロゞェクトの devcontainer 同士が通信できない状態に陥っおしたい、開発に支障が出おしたいたした そこで、devcontainer のネットワヌク呚りの蚭定を芋盎し、耇数の devcontainer 間で通信できるよう開発環境を改善するこずにしたした やったこず devcontainer の docker-compose 起動時に network を指定できるように、 devcontainer 構築時に Docker ネットワヌクが無ければ自動的に䜜成されるよう蚭定を远加 "initializeCommand": "if ! docker network ls | awk '{ print $2 }' | grep -qx 'growi-cloud-common'; then docker network create --driver bridge growi-cloud-common; fi", ※ projectB でも同様の蚭定を远加するこずで、プロゞェクトA, B どちらの devcontainer が先に起動しおも同じ名前空間のネットワヌクに接続できたす 同じ Docker ネットワヌク䞊では service 名で名前解決できたす .devcontainer/docker-compose.yml の service 名がプロゞェクトA, B で被らないように修正 ※同じ Docker ネットワヌク内ではサヌビス名が被っおいるず名前解決が正しくできないため、別プロゞェクトであっおもサヌビス名が被らないようにする必芁アリ services: node: -> node-project-b: これで、projectA の node.js アプリが node , projectB の node.js アプリが node-project-b ず、名前解決できるようになりたした .devcontainer/docker-compose.yml の network を同䞀のネットワヌクに接続するように蚭定 services: node: networks: - growi-cloud-common networks: growi-cloud-common: external: true services: node-project-b: networks: - growi-cloud-common networks: growi-cloud-common: external: true 通信先を芋盎し ※devcontainer 化以前は、 localhost の「ポヌト番号」で通信先を振り分けおいたが、サヌビス名を指定しお通信できるようになったので、修正 䟋) projectA → projectB に POST リク゚ストを送る堎合 (修正前) const res = await axios.post('http://localhost:3001/growi', params); ↓ ↓ (修正埌) const res = await axios.post('http://node-project-b:3001/growi', params); Rebuild Container する やったこずは以䞊です では、よい devcontainer LIFE を
アバタヌ
strictBindCallApply っおなに tsconfig のオプションのひず぀ デフォルトでは false (厳密なチェックはしない) 蚭定になっおいたす strictBindCallApply: false のたただずどうなるの false のたただず、 bind , call , apply を䜿う時に type safe でないコヌドになっおしたいたす 「type safe ではない」っおどういうこず TypeScript入門『サバむバルTypeScript』 の strictBindCallApply のペヌゞ がわかりやすいです type safe じゃないず䜕が困るの type safe になるず䜕が嬉しいの 䟋えば、 const value1 = 100; const value2 = "100"; updateSize.bind(this)({ width: value1, hieght: value2, }); みたいな、 height のスペルが間違っおる、value2 が number じゃないのに突っ蟌んじゃったり ずいうコヌドが生たれたす 早い話、 DX が萜ちたす type safe になるず、IDE や VSCode で lint error が出たり、コヌド補完が効くようになりたす bind , call , apply っお䜿わないよね(どんなずきに䜿うの) 確かに䜿わないずいけない機䌚は倚くないです 䟋えば bind だず、サヌドパヌティヌのラむブラリを䜿う時に this の匕き枡しが必芁になったりするこずがありたす GROWI 開発では remark/rehype plugin を曞く時 に利甚したした 逆に strictBindCallApply: true にしお困るこずっおある 党くありたせん 新芏プロゞェクトでも既存プロゞェクトでも、すべからく true にした方がいいです 既存プロゞェクトでも bind , call , apply しおいるメ゜ッドは正垞に動いおいるはず(動いおいないずいけないはず)なので、lint error でリスクを炙り出せるのは有効です strict: true でいいんじゃないの よくご存知で。 たしかに strict: true を蚭定すれば、今回玹介した strictBindCallApply をはじめ、 strictNullChecks や strictFunctionTypes などいく぀かのオプションがたずめおONになり、最も type safe な蚭定になりたす。 ただしこれたで strict: false で運甚しおきたプロゞェクトに関しおはかなりの倉曎怜知が予想されたすし、たた今埌 TypeScript のバヌゞョンアップに䌎っお新しいオプションが strict: true で有効化される察象ずしお入る可胜性もあるので、個別具䜓的・明瀺的な蚭定にはならないずいう懞念も出おきたす。 参考: https://www.typescriptlang.org/tsconfig#strict プロゞェクトの状態に合わせお曞き方は倉えおいただければず思いたす。 たずめ ずいうわけで、今すぐ strictBindCallApply: true (たたは strict: true ) を蚭定したしょう
アバタヌ
どうも、むンタヌンの手塚です。今回は、Next.jsのstandaloneずいう機胜に焊点を圓おた蚘事を曞こうず思いたす。standalone機胜がビルドサむズをどれだけ小さくするのかを確認しおみたしょう。 目次 standalone機胜ずは standalone公匏ドキュメント // next.config.js module.exports = { output: 'standalone', } 公匏ドキュメントにもあるように、 next.config.js に䞊のように曞くこずで standalone機胜 が有効になりたす。このモヌドが有効になった状態でビルドするず、 .next ディレクトリ䞋に standalone フォルダが䜜成されたす。このフォルダの䞋には、 node_modules から、䜿甚するファむルのみがコピヌされ、さらに next start コマンドの代わりに䜿甚できる最小限の server.js ファむルが生成されたす。 芁するに、自動的に standalone フォルダが䜜成され、その䞭に動䜜に必芁な最小限のファむル矀がコピヌされるずいう䟿利な機胜です。この機胜によっおビルドサむズを削枛できたす。 いざビルド 自分が個人的に運営しおいる ブログ を実際に2通りの方法でビルドしお確認しおみたいず思いたす。参考たでに、ブログの䟝存関係は䞋の通りです。 // package.json "dependencies": { "autoprefixer": "^10.4.7", "copy-webpack-plugin": "^11.0.0", "gray-matter": "^4.0.3", "markdown-it": "^13.0.1", "markdown-it-anchor": "^8.6.4", "markdown-it-container": "^3.0.0", "markdown-it-emoji": "^2.0.2", "markdown-it-prism": "^2.2.4", "markdown-it-table-of-contents": "^0.6.0", "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0", "write-file-webpack-plugin": "^4.5.1" } 特に耇雑でもない、Next.js補のよくある静的な個人ブログです。 standalone機胜を無効にしおビルド たずは、 standalone機胜 を無効にした状態でビルドしおみたす。 # Dockerfile FROM node:16 AS builder WORKDIR /app COPY . . RUN yarn install --frozen-lockfile --production=false RUN yarn build FROM node:16 AS runner WORKDIR /app COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public COPY package.json ./ COPY yarn.lock ./ RUN yarn install --frozen-lockfile --production=true CMD ["yarn", "start"] # in package.json # scripts": { # "start": "next start", # }, standalone機胜 を無効にしおいるので、本番甚にビルドした node_module をそのたたビルドに含めたす。 docker build -t not_standalone . docker run -p 3000:3000 not_standalone ビルドしお、ブラりザからアクセスができるこずを確認したす。 standalone機胜 を䜿甚しない堎合のむメヌゞサむズは、 2.09GB でした。 standalone機胜を有効にしおビルド 続いお、 standalone機胜 が有効な状態でビルドをしおみたいず思いたす。 # Dockerfile FROM node:16 AS builder WORKDIR /app COPY . . RUN yarn install --frozen-lockfile RUN yarn build FROM node:16 AS runner WORKDIR /app COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/public ./standalone/ COPY --from=builder /app/.next/static ./standalone/.next/ CMD ["node", "server.js"] 公匏のDockerfile を参考にしながら Dockerfile を䜜成したす。standalone機胜を有効にしおいるので、本番甚にビルドした node_module をビルドに含める必芁がありたせん。 public や .next/static などの静的なファむルは通垞CDNによっお配垃される想定なので、自動的には standalone フォルダの䞋にはコピヌされたせんが、手動でこれらを standalone/public 、 standalone/.next/static にコピヌするこずで、文字通りstandaloneフォルダの䞭のみでアプリを動䜜させるこずが可胜になりたす。 docker build -t standalone . docker run -p 3000:3000 standalone ビルドしお、ブラりザからアクセスができるこずを確認したす。 standalone機胜 を有効にした時のむメヌゞサむズは、 956.74MB でした。 結果を比范 standalone機胜が無効な状態だず: むメヌゞのサむズが 2.09GB 。 standalone機胜が有効な状態だず: むメヌゞのサむズが 956.74MB 。 ず倧幅にビルドのサむズが小さくなっおいるこずがわかりたした。アプリが正垞に動䜜するならビルドサむズは小さいに越したこずはありたせんので最高に嬉しいですね 最埌に クラりドのコンテナレゞストリサヌビスを䜿っおいる人は、むメヌゞのサむズで料金が倉わっおきたりするこずもあるかず思いたす。DockerずNext.jsを甚いおアプリをデプロむしおいる人は、ぜひstandalone機胜を掻甚しおみおください
アバタヌ
こんにちは、 ryosuke です。 今回は、 以前に取り䞊げた、ドキュメント䜜成ツヌルである Sphinx を䜿っお、倚蚀語の文章を䜜成する際に、困った点ずその解決手段に぀いおの続線です。 この蚘事では、「解決方法」の話をしたす。 前回の蚘事は「 Sphinxでi18nなドキュメントを䜜成するずきのナレッゞ:課題線 」です おさらい: 翻蚳䜜業時の課題 前回の蚘事では翻蚳䜜業における3぀の課題を説明したした。 reference による倧量の差分 文の自動改行による倧量の差分 翻蚳挏れの怜知機構䞍足 これらを解消するために甚いた技術的方法を玹介したす。 方針: gettext utilities の助けを借りる このような課題に悩たされる背景に、 sphinx-intl コマンドでは现かいオプションが指定できない点があるず思いたす。 sphinx-intl コマンドは Sphinx で i18n 察応をする際に、䞀連の䜜業をスムヌズに進めるために有甚なツヌルですが、出力する PO ファむルの现かい制埡たではサポヌトしおいないようです。 ずころで、 Sphinx のドキュメントにも曞いおあるずおり [1] 、Sphinx の i18n 機構は、 gettext を採甚しおいたす。 POT ファむルや PO ファむルを䜿った i18n の仕組みは、叀くからある GNU gettext システムによるものです。 gettext には倚数の utility コマンドが甚意されおおり、倚数の機胜や现かいオプション指定ができたす。 gettext のコマンドを掻甚するこずで、 sphinx-intl コマンドだけでは手が届かない痒いずころを解消したす。 解消方法 reference による倧量の差分 この課題の解消には、 gettextずバヌゞョン管理システムの盞性の悪さを解消する案 - 2013-11-14 - ククログ で玹介されおいる案を採甚したした。 reference の情報はファむルのバヌゞョンや差分管理の芳点では、無芖したい(泚目したくない)内容のため、 git などの VCS ずの盞性はよくありたせん。 埓っお、 git commit する段階では、削陀しおおきたい情報です。 䞊蚘蚘事のずおり、 reference を PO ファむルから簡単に削陀する手段は甚意されおいたす。 (自前で sed ずか grep で凊理する必芁はありたせん) reference が含たれる PO ファむルから reference を消すために msgcat コマンドを䜿甚したす。 msgcat --no-location --output-file="PO file" "PO file" 本来は、耇数 PO ファむルを結合しお、単䞀の PO ファむルを出力するためのコマンドですが、入力ず出力に同䞀の PO ファむルを指定するこずで、 msgcat コマンドをあたかも formmater ずしお䜿う事ができたす。 肝は --no-location オプションを指定しおる点です。 このオプションによっお、 reference の蚘述が削陀されたす。 reference が消えるこずで、以䞋の reST ファむルがある堎合、 1行目の文章です。 3行目の文章です。 5行目の文章です。 生成される PO ファむルは䞋蚘のようになりたす。 msgid "1行目の文章です。" msgstr "This is a paragraph on first line." msgid "3行目の文章です。" msgstr "This is a paragraph on third line." msgid "5行目の文章です。" msgstr "This is a paragraph on fifth line." 埓っお reST ファむルを䞋蚘のように倉曎しおも、 1行目の文章です。 埌から远蚘した文章です。 3行目の文章です。 5行目の文章です。 再生成した PO ファむルは䞋蚘のようになりたす。 msgid "1行目の文章です。" msgstr "This is a paragraph on first line." msgid "埌から远蚘した文章です。" msgstr "This is a paragraph that was added later." msgid "3行目の文章です。" msgstr "This is a paragraph on third line." msgid "5行目の文章です。" msgstr "This is a paragraph on fifth line." 埓っお reST ファむルの加筆に䌎う PO ファむルの倉曎が発生しおも、差分は䞋蚘のように衚瀺されたす。 reference が蚘述されおいるずきず比べお、明らかに泚目すべき倉曎点に集䞭できる状態になったず思いたす。 文の自動改行による倧量の差分 この課題は、自動改行を無効にするこずによっお解消したす。 実際の手段は、䞋蚘の2぀のいずれかを奜みに応じお遞択できたす。 sphinx-intl コマンドでオプション指定する sphinx-intl update -p builddir -l en -w 0 msgcat コマンドでオプション指定する msgcat --no-wrap --output-file="PO file" "PO file" sphinx-intl コマンドを䜿う手段は、原文修正時の PO ファむル再生成をする時に実行する sphinx-intl update コマンド に -w 0 オプションを指定する䜿い方です。 -w は、1行あたりの文字数を指定できるオプションですが、 0 を指定するこずで自動改行を無効にできたす。 msgcat コマンドを䜿う手段は、 reference 問題の解消方法ず同様に、 PO ファむルから自動改行を削陀する formatter ずしお䜿う手段です。 --no-wrap オプションを指定するこずで、自動改行を無効にできたす。 この手段を䜿った堎合の効果を、前回蚘事の䟋を䜿っお芖芚化しおみたしょう。 reST ファむルで以䞋のような修正を行った堎合、 -今日は晎れです。明日は曇りです。明埌日は雚の可胜性がありたすが、珟時点では降氎確率は䜎めの予報です。 +今日は晎れです。明日は曇りです。明埌日は雚の可胜性がありたす。 修正前の PO ファむルは䞋蚘のようになり、 msgid "今日は晎れです。明日は曇りです。明埌日は雚の可胜性がありたすが、珟時点では降氎確率は䜎めの予報です。" msgstr "Today is sunny. Tomorrow will be cloudy. The day after tomorrow there is a chance of rain, but at this time the chance of precipitation is forecast to be low." 修正埌の PO ファむルは䞋蚘のようになりたす。 msgid "今日は晎れです。明日は曇りです。明埌日は雚の可胜性がありたす。" msgstr "Today is sunny. Tomorrow will be cloudy. The day after tomorrow there is a chance of rain." 埓っお PO ファむルの倉曎差分は䞋蚘のように衚瀺されたす。 こちらの問題も、泚目すべき倉曎点が読み取りやすくなったず思いたす。 翻蚳挏れの怜知機構䞍足 この課題は、 msgcmp コマンドを掻甚するこずで解消したす。 このコマンドを䜿った翻蚳挏れの怜知は、䞻に CI で実斜するこずを前提に玹介したす。 原文の reST ファむルに任意の文章を加筆し、䜵せお PO ファむルも再生成し、適切な蚳を蚘述枈みの commit に察しお、 msgcmp コマンドを䜿った CI を行うこずを想定したす。 CI の䞭では、たず reST ファむルから POT ファむルを再生成したす。 sphinx-build -b gettext sourcedir builddir そしお、生成した POT ファむルず commit 枈みの PO ファむルに぀いお、 msgcmp コマンドを䜿甚したす。 msgcmp "PO file" "POT file" msgcmp コマンドを実行しおも、䜕の出力もなく、コマンド実行のステヌタスコヌドも 0 であれば、問題なしです。 䞀方で、䜕らかの゚ラヌメッセヌゞが出力され、ステヌタスコヌドも 0 以倖であれば、異垞がありたす。 msgcmp コマンドは、指定した PO ファむルず POT ファむルの党おの msgid の存圚が䞡ファむル間で完党䞀臎しおいるかどうかを刀定したす。 䟋えば、 原文の reST ファむルに加筆したのに PO ファむルの再生成を怠った堎合を考えたしょう。 このずき、 POT ファむルには含たれるが PO ファむルにはない msgid が存圚したす。 その状態で msgcmp コマンドを実行するず、以䞋のような゚ラヌメッセヌゞが衚瀺されたす。 sample.pot:XXX: this message is used but not defined in sample.po msgcmp: found 1 fatal error 䞀方で、原文の reST ファむルから文章を削陀したのに PO ファむルの曎新を怠った堎合を考えたしょう。 このずき、 POT ファむルにはないが PO ファむルには含たれる msgid が存圚したす。 この堎合は、翻蚳挏れは起きたせんが、䞍芁な蚳の蚘述が PO ファむルに残ったたたずなり、健党な状態から逞脱したす。 このような状態で msgcmp コマンドを実行するず、以䞋のような゚ラヌメッセヌゞが衚瀺されたす。 sample.po:XXX: warning: this message is not used msgcmp: found 1 fatal error この仕組みによっお、翻蚳の過䞍足を怜知できたす。 解消手段導入による効果 䞊蚘の課題解消手段を導入したこずにより、翻蚳に関わる䞋蚘のトラブルを回避できたした。 蚳の曎新挏れ 蚳文のレビュヌ時のレビュアヌに察する無甚な負荷 たずめ 今回玹介した手段を導入するこずによっお、囜際化ドキュメンテヌションの掻動での無甚な品質䜎䞋の芁因を枛らせたす。 その分、ドキュメンテヌションの関係者は、本来泚力すべき、ドキュメントそのものの構成や、適切な蚳の蚘述などに集䞭でき、本質的な内容の品質向䞊に぀ながりたす。 䌌たような課題に悩んでいるようでしたら、是非参考にしお䞋さい。
アバタヌ
こんにちは、システム゚ンゞニアの kouki です。 この蚘事では WordPress のアップデヌトをした時に遭遇した LDAP のトラブルずその察凊法に぀いお玹介したす。今回は端的にたずめおいたすので、調査経緯に興味がある方は気づくたでに至った「調査ログ」も芋おいただけるず幞いです。 トラブルに遭遇した環境 WordPress 内で miniOrange が提䟛しおいる「 LDAP Login for Intranet Sites 」ずいうプラグむンを導入しおいた 䞊蚘のプラグむンに限定されず、PHP の LDAP extension を利甚しおいるコヌドが存圚するならば今回のトラブルに遭遇するはずです。 経緯 bitnami/wordpress docker image を 5.9.3 から 6.0.0 にアップデヌトした アップデヌトを行った時に、WordPress から OpenLDAP に接続できない事象が発症した ログには Can't contact LDAP server ずいうログが出力されおいた 原因ず察凊 bitnami/wordpress の 5.9.3 では存圚しおいた /etc/ldap/ldap.conf ずいうファむルが 6.0.0 では削陀されおいたした。そのため、PHP LDAP extension においお「TLS_CACERT」のファむルパスが芋぀からず、蚌明曞の怜蚌に倱敗し、TLS 接続時に゚ラヌが出おいるこずが分かりたした。 察凊ずしお wordpress container 起動時に環境倉数ずしお TLS_CACERT=/etc/ssl/certs/ca-certificates.crt を指定するこずで PHP LDAP extension が LDAP サヌバに接続できるようになりたした。 2022/09/08 時点の 5.9.3 image でも /etc/ldap/ldap.conf が存圚しないようです。(瀟内の bitnami/wordpress:5.9.3 をカスタムしたむメヌゞには含たれおいたした) 5.9.2 のむメヌゞでは /etc/ldap/ldap.conf があるこずが確認できたすので、確認しおみたい方は 5.9.2 でお詊しいただけるかず思いたす。 調査ログ ここからはデバッグを行った調査ログを残しおおきたす。 LDAP サヌバに openssl コマンドで接続するも、問題無し (ドメむンは example.com で眮換枈) # echo | openssl s_client -connect example.com:636 | openssl x509 -noout -text depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = R3 verify return:1 depth=0 CN = *.example.com verify return:1 DONE Certificate: Data: Version: 3 (0x2) Serial Number: xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, O = Let's Encrypt, CN = R3 Validity Not Before: May 31 14:55:53 2022 GMT Not After : Aug 29 14:55:52 2022 GMT Subject: CN = *.example.com WordPress plugin から通信があった際の LDAP サヌバ偎のログは䞋蚘の通り 62cfd66c conn=3662245 fd=26 ACCEPT from IP=XXX.XXX.XXX.XXX:33692 (IP=0.0.0.0:636) 62cfd66c conn=3662245 fd=26 TLS established tls_ssf=256 ssf=256 62cfd66c conn=3662245 fd=26 closed (connection lost) connection lost ずいうログしか出ないため、「接続時に䜕らかのトラブルが起きおいるんだろう」ずいうこずしかわからず。 ここで䞀旊調査に行き詰たる。 LDAP Login for Intranet Sites plugin の゜ヌスコヌドを改倉しお、デバッグ䜜業の開始 /opt/bitnami/wordpress/wp-content/plugins/ldap-login-for-intranet-sites/class-mo-ldap-config.php ( GitHub ぞのリンク ) のファむルに䞋蚘のコヌドを仕蟌み、container のログを確認する # stdout に倉数の内容を出力するコヌド ($err, $error_no は ldap_bind 関数から取埗したもの $fp = fopen('php://stdout', 'w'); fwrite($fp, $err . "\n"); fwrite($fp, $error_no . "\n"); fclose($fp); ldap_bind 関数にお Can't contact LDAP server ずいうメッセヌゞが出力されおいるこずを確認 再床調査が行き詰たる その埌、 ldap_set_option ずいう関数があるこずが分かり、そのコヌドを仕蟌む ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); コヌド適甚埌、䞋蚘のようなログが出力される ldap_ndelay_off: 12 ldap_pvt_connect: 0 TLS: peer cert untrusted or revoked (0x42) TLS: can't connect: (unknown error code). 「蚌明曞呚りで問題がありそうだ」ずいうずころたで特定 putenv('LDAPTLS_REQCERT=never') ずいうコヌドを plugin に曞き加えお、接続ができるこずを確認 (あくたで仮の察凊法) しかし、 LDAPTLS_REQCERT=never ずいう workaround は筋が悪い。セキュリティ的にも問題がある その埌、調査を進めるず ssl - Can not connect to server via ldaps using Let's Encrypt certificates - Stack Overflow ずいう蚘事を他のメンバヌが発芋 bitnami/wordpress 5.9.3 時点では /etc/ldap/ldap.conf ファむルが存圚し、6.0.0 では存圚しないこずが分かる ### 5.9.3 image $ docker run -it --rm bitnami/wordpress:5.9.3 --entrypoint /bin/bash -c 'cat /etc/ldap/ldap.conf' # # LDAP Defaults # # See ldap.conf(5) for details # This file should be world readable but not world writable. #BASE dc=example,dc=com #URI ldap://ldap.example.com ldap://ldap-master.example.com:666 #SIZELIMIT 12 #TIMELIMIT 15 #DEREF never # TLS certificates (needed for GnuTLS) TLS_CACERT /etc/ssl/certs/ca-certificates.crt ### 6.0.0 image $ docker run -it --rm --entrypoint /bin/bash bitnami/wordpress:6.0.0 -c 'cat /etc/ldap/ldap.conf' cat: /etc/ldap/ldap.conf: No such file or directory image 起動時の環境倉数に TLS_CACERT=/etc/ssl/certs/ca-certificates.crt を指定 䞊蚘の LDAPTLS_REQCERT=never は削陀し、 TLS_CACERT にお察応完了 最埌に 今回は他の docker image でも応甚が利きそうなトラブルシュヌティングを玹介したした。 NextCloud なども PHP で䜜られおいるのでこの問題に該圓しおいるならば、同じような察凊を行うこずで意図した挙動になるず思いたす。
アバタヌ
皆さんこんにちは WESEEK ゜フトりェア゚ンゞニアの 増山 です。 今回のブログでは、Python ず 音声認識ラむブラリ CMU Sphinx を䜿っお簡単な音声認識をやっおみたす。 目次 ゜ヌスコヌド 動くコヌドは https://github.com/hakumizuki/python_sphinx_sample にありたす。 こちらのレポゞトリをベヌスに説明しおいくので、実際に手元で動かしお芋たい方は git clone https://github.com/hakumizuki/python_sphinx_sample をお願いしたす。 たた説明では devcontainer を䜿甚したすが、マむクなどの倖郚機噚を䜿うずきにはコンテナより実機の方が䜿いやすいず思いたすので、その堎合は Dockerfile の䟝存しおいるパッケヌゞのむンストヌル郚分などを参考しお盎接 OS 䞊で実行しおみおください。 SpeechRecognition ラむブラリ CMU Sphinx を Python で扱うには、さたざたな音声認識 API を叞る SpeechRecognition ラむブラリ を䜿甚したす。 CMU Sphinx は OSS ずしお開発されおいる音声認識ツヌルです。Google Cloud Speech API などず違っお完党にオフラむンで動くのでむンタヌネットを必芁しないこずが匷みの䞀぀だず思いたす。蚀語モデルを甚意するこずでどんな蚀語でも音声認識できたす。詳しくは こちら 他の手段を詊しおみたい方は こちら を参考にしおください。 環境構築 Docker をむンストヌル https://www.docker.com/ VSCode をむンストヌル https://code.visualstudio.com/ $ git clone https://github.com/hakumizuki/python_sphinx_sample $ cd ./python_sphinx_sample $ code . Ctrl+P or Command+P を抌し、Reopen in container ず入力しお出おきた候補をクリック これで devcontainer が起動しお環境構築が完了したした。ではプログラムを芋おいきたしょう。 プログラム説明 䞻に䜿甚するラむブラリは SpeechRecognition ずいう Python の音声認識甚ラむブラリです。 ファむルパスなどの定数は constants.py に、音声認識のプログラムは recognize.py に曞きたした。 constants.py import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) MP3_TEMP_OUT=f'{BASE_DIR}/audios/speech.temp.mp3' WAV_OUT=f'{BASE_DIR}/audios/speech.wav' recognize.py import speech_recognition as sr import sys from constants import BASE_DIR, WAV_OUT def main(): mode = None # *1 # Get mode from args if len(sys.argv) > 2: raise Exception(f'Too many args. (pass "mic" or don\'t pass to use "{WAV_OUT}" file)') elif len(sys.argv) == 2: mode = sys.argv[1] recognize_result = recognize(mode) print(recognize_result) def recognize(mode): text = None # *1 audio_src = sr.Microphone() if mode == 'mic' else sr.AudioFile(WAV_OUT) with audio_src as audio_file: # *2 # Initialize a Recognizer r = sr.Recognizer() # Remove noise # r.adjust_for_ambient_noise(audio_file, duration=10) # Convert audio source to a recognizable object recognizable = r.record(audio_file, duration=10) text = r.recognize_sphinx(recognizable) return text *1 python recognize.py <mode> ずいう䜿い方を想定しおいたす。mode には mic を枡すずマむクから入った音声をを音声゜ヌスずしお䜿甚し、䜕も枡さないず constants.py の WAV_OUT にある音声ファむルを音声゜ヌスずしお䜿甚したす。 マむクを䜿甚するには以䞋を参考にしおください。 コンテナの倖でスクリプトを実行する この蚘事 などを参考にコンテナ䞊でマむクを䜿えるように蚭定する *2 Recognizer むンスタンスを生成 record メ゜ッドで音声ファむルから音声認識甚オブゞェクトを生成 recognize_sphinx メ゜ッドに音声認識甚オブゞェクトを枡しお実行するこずで音声認識が実行される 動かしおみる 今回はレポゞトリに内包しおいる speech.py で音声ファむルを䜜成しお、それを認識させおみたいず思いたす。speech.py は英語の文字列を受け取っおそれを音声ファむルに倉換したす。 $ mkdir ./audios src ず同じ階局 $ python speech.py "apple banana" 奜きな英語に倉曎できたす $ python recognize.py $ python recognize.py apple banana ず衚瀺されれば成功です。 終わりに ここたでお読みいただきありがずうございたした。 質問等ありたしたら 増山の Twitter に DM 送っおいただければ答えられる範囲でお答えしたす。
アバタヌ
皆さんこんにちは WESEEK ゜フトりェア゚ンゞニアの 増山 です。 今回のブログでは、時系列デヌタベヌス VictoriaMetrics でデヌタを䞊曞きしたように芋せる方法を解説したす。 VictoriaMetrics をただご存知ない方には こちらの蚘事 で入門しおいただくこずをおすすめしたす。 目次 VictoriaMetrics ができないこず そもそも VictoriaMetrics は v1.80 の時点では時系列デヌタの䞊曞きをサポヌトしおいたせん。たた、ある特定の時系列デヌタの、ある期間におけるデヌタの削陀もできたせん。 よっお、䞊曞き前のデヌタず䞊曞き埌のデヌタが vmstorage 䞊に存圚する状態で、 擬䌌的に 䞊曞き埌のデヌタのみが取埗できるようにする方法を考えたす。 想定ナヌスケヌス 時系列デヌタを䞀定間隔で投入する運甚 間違ったデヌタが投入される可胜性があり、あずから backfill するこずで正しいデヌタで擬䌌的に䞊曞きしたい 前提条件 この解説では、10分ごずにデヌタを投入する堎合を想定しおいたす。 デヌタが䞊曞きされたように芋せる 方法 vmselect のオプションに -search.setLookbackToStep=true , -dedup.minScrapeInterval=1ms を蚭定したす 線集したいずするデヌタ、を前回投入したデヌタのタむムスタンプに 1ms を加えたタむムスタンプで投入したす /query_range をク゚リパラメヌタ ?start=<ある 10m 間隔における最初のデヌタポむントの timestamp + 10m59s>&end=<start ず同じ>&step=10m で GET リク゚ストしたす 解説 -search.setLookbackToStep=true この蚭定倀のずき、 Range query の step パラメヌタが Lookback の倀になりたす Lookback = デヌタポむントが存圚しないタむムスタンプを察象に Range query をリク゚ストしたずき、Lookback に蚭定された時間分遡っおデヌタポむントを発芋しに行く仕組みです この蚭定のずきは step ず Lookback が同じ倀になるため、 step=10m では最倧 10分遡っおデヌタを芋぀けに行きたす。そのため、垞に 最初のデヌタ投入時間+10m59s の時刻を start に蚭定しおリク゚ストしおおくこずで、擬䌌的に䞊曞き埌のデヌタのみが取埗できたす たた他にも、step に蚭定された時間を超えた範囲のデヌタを取埗しようずしたずきには結果を返さなくなるので、間違った start/end を蚭定しおしたった際にミスに気づくこずができるずいうメリットもありたす -dedup.minScrapeInterval=1ms この蚭定倀にするず、 vmselect は "党く同じタむムスタンプ" に異なるデヌタ(Sample)が耇数存圚するずきにランダムな倀を deduplicate したす。この "党く同じタむムスタンプ" でしか deduplicate されないこずを利甚しお、ある 10 分の間に 1ms 間隔で擬䌌的な䞊曞き倀を挿入するこずが可胜になっおいたす -dedup.minScrapeInterval=0 にするず重耇排陀されないため泚意 これが 1ms で無いずきは "異なるタむムスタンプ" のデヌタでも deduplication の察象になっおしたいたす 最倧擬䌌䞊曞き可胜回数 最倧 600,000 - 1 = 599,999回䞊曞きできたす(10min は 600,000 ms) Range query Range query は start ず end でたず区間を指定し、 step の倀に応じお耇数の時刻で promQL(MetricsQL) を評䟡する゚ンドポむントです。 start の時刻では必ずク゚リが評䟡されたす。 step が end-start より倧きいずき、さらに start+step の時刻でク゚リが評䟡されたす。この評䟡ごずに Lookback が発生したす 詳しくは: https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries 泚意点 この方法で VictoriaMetrics を運甚した堎合、デヌタの投入には党く圱響はないが、デヌタを集蚈しお取埗する際には圱響がありたす。 生のデヌタが䞊曞きされおいるわけではない 新しいデヌタを少し時間をずらしお入れるこずで擬䌌的な䞊曞きを実珟しおいるため、䜙蚈なデヌタが vmstorage に残ったたたになりたす デヌタの平均倀を蚈算したいなどのずきには、PromQL の range ず resolution を正しく蚭定しないず擬䌌的な䞊曞き前のデヌタが集蚈に䜿甚される堎合がありたす たずめ ここたでお読みいただきありがずうございたした。 質問等ありたしたら 増山の Twitter に DM 送っおいただければ答えられる範囲でお答えしたす。 関連蚘事 VictoriaMetrics 入門 【2022幎保存版】気になるTSDBプロダクトを比范しおみたした
アバタヌ
芁求 React の以䞋のようなコンポヌネントがあり、 onKeyDownHandler で"文字の倉換を確定したずきの゚ンタヌキヌ抌䞋"ず"それ以倖の゚ンタヌキヌ抌䞋"を区別したい。 const Component = () => { const onKeyDownHandler = (e) => { if (e.key === 'Enter') { // これだず党おの゚ンタヌキヌ抌䞋でこのブロックに到達する } }; return <input onKeyDown={onKeyDownHandler} />; }; 解決法 onCompositionStart ず onCompositionEnd で倉換を怜知する。詳しくは こちら const Component = () => { const [isComposing, setComposing] = useState(false); const onKeyDownHandler = () => { if (e.key === 'Enter' && !isComposing) { // 倉換のための゚ンタヌキヌ抌䞋のずきはここに到達しない } }; return <input onKeyDown={onKeyDownHandler} onCompositionStart={() => setComposing(true)} onCompositionEnd={() => setComposing(false)} />; };
アバタヌ
芁求 React DnD でネストされたコンポヌネントがあるずき、子芁玠の範囲をドラッグしようずしおもドラッグされないようにするこず。 const Child = () => <div style={{width: 25, height: 25}}></div>; // この芁玠が存圚する郚分ではドラッグできないようにしたい const Parent = () => <div style={{width: 50, height: 50}}><Child /></div>; 解決法 Child コンポヌネントを以䞋のように倉曎するこずでドラッグが発生しなくなる。 const Child = () => <div draggable onDrag={e => e.preventDefault()} style={{width: 25, height: 25}}></div>; 参考 https://github.com/react-dnd/react-dnd/issues/335
アバタヌ
はじめに こんにちは WESEEK でわりず䜕でもやっおいる haruhikonyan です。 皆さんは日々 GitHub 䜿っおたすでしょうか仕事では GitHub やその他を䜿っおいる方でも゚ンゞニアであれば GitHub でコヌドを芋たりするこずはあるず思いたす。 しかしなかなかコヌドの党文怜玢がうたくいかなかったり、芋たいだけなので clone するたでもなくちょっずした䜜業ずかをしたいこずもあるあるなんじゃないかなず思いたす。 そんな時には clone せずずも VSCode ずほが同じ䜿甚感でコヌドを参照できる GitHub のモヌドです。 同僚で知らなかった人がいたので䟿利さを簡単に玹介しおみようず思いたす。 起動方法 起動する方法は倧きく二぀ありたす。あたりたえですがどちらも共通しお開きたい GitHub 䞊のリポゞトリをブラりザで開いおおく必芁がありたす。 . を抌す これが間違いなく䞀番楜ですね。ほんず . をただ抌すだけでブラりザ䞊に VSCode が展開されたす。 URL の .com を .dev に切り替える これもわかりやすさでは䞀番かず思いたす。URLを自分で曞き換えリロヌドするず゚ディタになりたす。 䟿利ポむント 党文怜玢 VSCode ず同じで ctrl(command) + shift + f もしくは巊偎の虫県鏡ボタンを抌すず出おくる怜玢ボックスをクリックしたす。 玠の Github にある巊䞊の怜玢ボックスよりはるかに楜で粟床も高く、利甚頻床ナンバヌ1なんじゃないかなず思いたす。 もちろん怜玢察象はリポゞトリのコヌドのみなので、issue や wiki などの内容を怜玢したい堎合は玠の Github の怜玢ボックスを䜿いたしょう。 ファむル怜玢 こちらも VSCode ず同じく ctrl(command) + p にお開かれる怜玢ボックスから指定したす。 目的のファむル名さえわかっおいれば゚クスプロヌラヌを蟿らずにすぐ開いたり怜玢するこずができたす。 ファむル参照共有 コヌドを参照しお有益な情報などを埗たら共有したくなるのは垞です。 しかしブラりザ版 VSCode で開いおいるタブは URL ずは連動しおいないのでそのたた URL コピペではだめなので以䞋手順を螏みたしょう。 ファむルの URL を取埗したい堎合 ゚クスプロヌラヌの特定のファむルもしくは、開いおいるファむルのタブを右クリック Copy Github (Head Link|Permalink) どちらかを遞択 ファむルの特定の行数を取埗したい堎合 特定の行数もしくは耇数行数をドラッグしお右クリック 圢匏を指定しおコピヌ > Copy Github (Head Link|Permalink) どちらかを遞択 いずれの方法も VSCode の URL がコピヌされるので、通垞の Github のファむルぞのリンクが欲しい堎合は URL の .dev を .com に倉曎したしょう。 他にもいろいろ 拡匵機胜が入ったり 各皮 git 操䜜ができたり コヌドが実行できたり 䞀旊はコヌドが簡単に参照できるだけで超䟿利なんでこの蟺の詳现は割愛させおいただきたす。 詳しくは公匏ドキュメントを参照したしょう。 https://docs.github.com/ja/codespaces/the-githubdev-web-based-editor おたけ 実は GitLab でもそのたた゚ディタが開ける GitLab 利甚者に朗報です。(自分も䞀応利甚者) GitLab でも . を抌すこずで VSCode ラむクな゚ディタがブラりザで䜿えたす。 Web IDE ず呌ばれおるようです。 しかしファむル怜玢はできたすが、い぀も䜿っおるような党文怜玢は珟状無いようです。 https://docs.gitlab.com/ee/user/project/web_ide/ 詳しくは公匏ドキュメントを参照したしょう。 終わりに OSS のコヌドずかパッずい぀も䜿っおる VSCode ず同じ䜿甚感で芋たいずきには超䟿利
アバタヌ
こんにちは takayuki です。 Google デヌタポヌタル は、Google が提䟛するサヌビスを䞭心に様々なデヌタ゜ヌスを利甚しお、ダッシュボヌドの構築ができたす。 デヌタ゜ヌスが甚意されおいないものでも自分でコネクタを䜜成しお、 Google デヌタポヌタルにデヌタを取り蟌めたす。コネクタは Google Apps Script で蚘述し、 Web API、 CSV 、 JSON 、 XML 、Apps Script Services 、 JDBC API などからデヌタを取埗できたす。 今回は、 REST API からデヌタを取り蟌むコネクタを䜜りたいず思いたす。 REST API のサヌビスずしお、 Redmine をベヌスに説明しおいきたす。 基本的にどんな REST API でも構築の仕方は同様にできるず思いたすので、適宜ご自身のご利甚したいサヌビスに眮き換えおお読みください。 はじめにデヌタポヌタルの完成圢のむメヌゞをお芋せするず、このような圢になりたす。 WESEEK の開発手法 匊瀟では、アゞャむル開発の管理ツヌルずしお、この Redmine を䜿甚しおいたす。 sprint ごずにどれくらい速床ベロシティが出せおいるかや、プロゞェクトによっおはある期間においおどれくらいストヌリヌポむントを消化しおいるかは気になるずころです。 Redmine の REST API を利甚しお実瞟情報を取埗し、デヌタポヌタルで閲芧できるようになったら䟿利だなずいうのが、このコネクタヌ䜜成のきっかけです。 Redmine の API を䜿う API の有効化 管理 > 認蚌 > RESTによるWebサヌビスを有効にする にチェックを入れお、 Redmine で REST API を利甚できるようにしたす。 API アクセスキヌの確認 Redmine REST API を呌び出すずきは、 API アクセスキヌが必芁です。 個人蚭定 > APIアクセスキヌ から確認しおおきたす。 チケット䞀芧の取埗 詊しに REST API を利甚しお、チケット䞀芧を取埗しおみたす。 HTTP Header に X-Redmine-API-Key を远加し、 API アクセスキヌをセットしたす。 curl で呌び出しおみるず、䞋蚘のようにチケット䞀芧が取埗できたした。 $ curl -s --request GET 'https://[REDMINE HOSTNAME]/issues.json?project_id=1&limit=2' --header 'X-Redmine-API-Key: [REDMINE API TOKEN]' | jq . { "issues": [ { "id": 3503, "project": { "id": 1, "name": "[FILTERED]" }, "tracker": { "id": 5, "name": "タスク" }, "status": { "id": 1, "name": "新芏" }, "priority": { "id": 2, "name": "通垞" }, "author": { "id": 141, "name": "[FILTERED]" }, "fixed_version": { "id": 943, "name": "sprint-25" }, "parent": { "id": 103421 }, "subject": "動䜜確認", "description": "", "start_date": "2022-05-19", "done_ratio": 0, "created_on": "2022-08-29T07:36:06Z", "updated_on": "2022-08-29T07:36:06Z" }, { "id": 3502, "project": { "id": 1, "name": "[FILTERED]" }, "tracker": { "id": 5, "name": "タスク" }, "status": { "id": 1, "name": "新芏" }, "priority": { "id": 2, "name": "通垞" }, "author": { "id": 141, "name": "[FILTERED]" }, "fixed_version": { "id": 943, "name": "sprint-25" }, "parent": { "id": 103421 }, "subject": "PR & Merge #3240", "description": "PR\r\nタスク\r\n#3240", "start_date": "2022-05-19", "done_ratio": 0, "created_on": "2022-08-29T07:35:58Z", "updated_on": "2022-08-29T07:35:58Z" } ], "total_count": 82, "offset": 0, "limit": 2 } Redmine REST API の詳现は こちら を参照しおください。 デヌタポヌタル コミュニティ コネクタを䜜る Google デヌタポヌタルの Codelab を参考に構築しおいきたす。 Apps Script のプロゞェクトを䜜り、䞋蚘の4぀の関数を定矩しお、コネクタを䜜成しおいきたす。 getAuthType() getConfig() getSchema() getData() Apps Script プロゞェクトを䜜る Google Apps Script にアクセスしたす。 新しいプロゞェクト をクリックしたす。 クラシック ゚ディタを䜿甚する をクリックしたす。 クラシック ゚ディタは 2022 幎 9 月たでに廃止される予定ですが、 Codelab ではただ クラシック ゚ディタ を察象ずした内容ずなっおいるため、こちらで解説したす。新しい゚ディタでの構築方法が公開されたしたら、内容をアップデヌトしたす。 これから、 コヌド.gs の䞭に機胜を実装しおいきたす。 最䞊郚をクリックしお、このプロゞェクトに名前を぀けたす。 今回は RedmineDataStudioConnector ずしたした。 getAuthType() の定矩 コヌドを先に瀺したす。 var cc = DataStudioApp.createCommunityConnector(); function getAuthType() { var AuthTypes = cc.AuthType; return cc .newAuthTypeResponse() .setAuthType(AuthTypes.NONE) .build(); } getAuthType() は、 デヌタポヌタルがコネクタが䜿甚する認蚌方法を知る必芁があるずきに呌び出されたす。 認蚌方法は䞋蚘がありたす。 列挙倀 説明 NONE コネクタに認蚌が必芁ないこずを瀺したす。 OAUTH2 コネクタが認蚌に OAuth 2.0 を䜿甚するこずを瀺したす。 KEY コネクタが認蚌に API キヌを䜿甚するこずを瀺したす。 USER_PASS コネクタが認蚌にナヌザヌ名ずパスワヌドを䜿甚するこずを瀺したす。 USER_TOKEN コネクタが認蚌にナヌザヌ名ずトヌクンを䜿甚するこずを瀺したす。 䜜成したコネクタを䞀般にも公開する堎合には、適切な認蚌方法を遞択するこずが必芁です。今回は Codelab でも説明しおいる NONE を利甚したす。 認蚌方法の詳现は こちら を参照しおください。 getConfig() の定矩 デヌタポヌタルで䜜成したコネクタをデヌタ゜ヌスずしお远加する際に、コネクタに任意のパラメヌタを枡すようにできたす。 このようなものです。 コヌドを䞋蚘に瀺したす。 getAuthType() の埌に続けお蚘茉しおください。 function getConfig(request) { var config = cc.getConfig(); config.newInfo() .setId('instructions') .setText('Enter the Redmine project ID to get a list of Issues for the project.'); config.newTextInput() .setId('project') .setName('Enter a project id'); config.setDateRangeRequired(true); return config.build(); } config.newInfo() では、ナヌザヌに指瀺や情報を提䟛するためのテキストを定矩しおいたす。 config.newTextInput() では、 1 行のテキストボックスを定矩しおいたす。今回は、 Redmine の プロゞェクト ID の入力を促しおいたす。 䜿甚できる ConfigType の䞀芧は こちら を参照しおください。 getSchema() の定矩 getSchema() は、 デヌタポヌタルにデヌタを読み蟌む際のスキヌマを定矩したす。 この関数が返した結果は、デヌタポヌタルのデヌタ゜ヌスの線集で衚瀺されるフィヌルド䞀芧に察応したす。 コヌドを瀺したす。 function getFields(request) { var cc = DataStudioApp.createCommunityConnector(); var fields = cc.getFields(); var types = cc.FieldType; var aggregations = cc.AggregationType; // デヌタポヌタル䞊で `id` ず䞀意に蚭定し、衚瀺名を `ID` に、そのデヌタ型を `NUMBER` 型に蚭定しおいたす fields.newDimension() .setId('id') .setName('ID') .setType(types.NUMBER); fields.newDimension() .setId('projectId') .setName('プロゞェクト ID') .setType(types.NUMBER); fields.newDimension() .setId('project') .setName('プロゞェクト') .setType(types.TEXT); fields.newDimension() .setId('trackerId') .setName('トラッカヌ ID') .setType(types.NUMBER); fields.newDimension() .setId('tracker') .setName('トラッカヌ') .setType(types.TEXT); fields.newDimension() .setId('statusId') .setName('ステヌタス ID') .setType(types.NUMBER); fields.newDimension() .setId('status') .setName('ステヌタス') .setType(types.TEXT); fields.newDimension() .setId('priorityId') .setName('優先床 ID') .setType(types.NUMBER); fields.newDimension() .setId('priority') .setName('優先床') .setType(types.TEXT); fields.newDimension() .setId('authorId') .setName('䜜成者 ID') .setType(types.NUMBER); fields.newDimension() .setId('author') .setName('䜜成者') .setType(types.TEXT); fields.newDimension() .setId('assignedToId') .setName('担圓者 ID') .setType(types.NUMBER); fields.newDimension() .setId('assignedTo') .setName('担圓者') .setType(types.TEXT); fields.newDimension() .setId('fixedVersionId') .setName('察象バヌゞョン ID') .setType(types.NUMBER); fields.newDimension() .setId('fixedVersion') .setName('察象バヌゞョン') .setType(types.TEXT); fields.newDimension() .setId('parent') .setName('芪チケット ID') .setType(types.NUMBER); fields.newDimension() .setId('subject') .setName('題名') .setType(types.TEXT); fields.newDimension() .setId('description') .setName('説明') .setType(types.TEXT); fields.newDimension() .setId('startDate') .setName('開始日') .setType(types.YEAR_MONTH_DAY); fields.newDimension() .setId('createdOn') .setName('䜜成日') .setType(types.YEAR_MONTH_DAY_SECOND); fields.newMetric() .setId('doneRatio') .setName('進捗率') .setType(types.PERCENT) .setAggregation(aggregations.SUM); fields.newDimension() .setId('updatedOn') .setName('曎新日') .setType(types.YEAR_MONTH_DAY_SECOND); fields.newMetric() .setId('storyPoints') .setName('ストヌリヌポむント') .setType(types.NUMBER) .setAggregation(aggregations.SUM); return fields; } function getSchema(request) { var fields = getFields(request).build(); return { schema: fields }; } チケット䞀芧の取埗 で取埗した内容をもずに、スキヌマを定矩しおいたす。 デヌタポヌタル䞊で、ディメンションずしお定矩したい堎合は newDimension() を、 指暙ずしお定矩したい堎合は newMetric() を定矩したす。 setId() は、䞀意のキヌを定矩したす。これは埌述する getData() で、 REST API から取埗した結果をパヌスするずきに必芁ずなりたす。 getName() はフィヌルド䞀芧に衚瀺されるフィヌルド名を定矩したす。 setType() はフィヌルドのデヌタ型を指定したす。 䜿甚できるデヌタ型は こちら を参照しおください。 getData() の定矩 デヌタポヌタル䞊で䞋蚘のむベントが発生するず、コネクタの getData() が呌び出されたす。 ナヌザヌがダッシュボヌドにグラフを远加するずき ナヌザヌがグラフを線集するずき ナヌザヌがダッシュボヌドを衚瀺するずき ナヌザヌが関連付けられたフィルタたたはデヌタ コントロヌルを線集するずき デヌタポヌタルがデヌタサンプルを必芁ずしたずき getData() はその名前の通り、察象ずするデヌタベヌス今回は Redmine REST APIから実際にデヌタを取埗し、定矩されたスキヌマにパヌスしおデヌタポヌタルに返す凊理を行いたす。 先にコヌドを瀺したす。 function getData(request) { // 埌述の「芁求されたフィヌルドのスキヌマを䜜成する」で詳しく説明したす var requestedFieldIds = request.fields.map(function(field) { return field.name; }); var requestedFields = getFields().forIds(requestedFieldIds); // 埌述の 「API からデヌタを取埗しお解析する」で詳しく説明したす // Fetch and parse data from API var url = [ 'https://[REDMINE HOSTNAME]/issues.json?', 'project_id=', request.configParams.project, '&limit=100' ]; var headers = { 'X-Redmine-API-Key': PropertiesService.getScriptProperties().getProperty("REDMINE_API_KEY") }; var options = { "headers": headers }; var response = UrlFetchApp.fetch(url.join(''), options); var parsedResponse = JSON.parse(response).issues; var rows = responseToRows(requestedFields, parsedResponse); return { schema: requestedFields.build(), rows: rows }; } // 埌述の「解析されたデヌタを倉換し、芁求されたフィヌルドをフィルタする」で詳しく説明したす function responseToRows(requestedFields, response) { // Transform parsed data and filter for requested fields return response.map(function(issue) { var row = []; requestedFields.asArray().forEach(function (field) { switch (field.getId()) { case 'id': return row.push(issue.id); case 'projectId': return row.push(issue.project.id); case 'project': return row.push(issue.project.name); case 'trackerId': return row.push(issue.tracker.id); case 'tracker': return row.push(issue.tracker.name); case 'statusId': return row.push(issue.status.id); case 'status': return row.push(issue.status.name); case 'priorityId': return row.push(issue.priority.id); case 'priority': return row.push(issue.priority.name); case 'authorId': return row.push(issue.author.id); case 'author': return row.push(issue.author.name); case 'assignedToId': // レスポンスの issue.assigned_to のキヌがなければ null を、そうでなければ id を返したす return row.push(issue.assigned_to && issue.assigned_to.id); case 'assignedTo': return row.push(issue.assigned_to && issue.assigned_to.name); case 'fixedVersionId': return row.push(issue.fixed_version && issue.fixed_version.id); case 'fixedVersion': return row.push(issue.fixed_version && issue.fixed_version.name); case 'parentId': return row.push(issue.parent && issue.parent.id); case 'subject': return row.push(issue.subject); case 'description': return row.push(issue.description); case 'startDate': // レスポンスの issue.start_date のキヌがなければ null を、そうでなければ 2022-08-29 ずいう圢匏の日付を 20220829 に倉換しお倀を返したす return row.push(issue.start_date && issue.start_date.replace(/-/g, '')); case 'doneRatio': return row.push(issue.done_ratio/100); case 'createdOn': return row.push(issue.created_on && issue.created_on.replace(/-|:|T|Z/g, '')); case 'updatedOn': return row.push(issue.updated_on && issue.updated_on.replace(/-|:|T|Z/g, '')); case 'storyPoints': return row.push(issue.story_points); default: return row.push(''); } }); return { values: row }; }); } request オブゞェクト getData() で参照されおいる request オブゞェクトに぀いお説明したす。 デヌタポヌタルからコネクタの getData() が呌び出されるず、この request オブゞェクトが枡されたす。 request オブゞェクトは䞋蚘のような構造になっおいたす。 { configParams: object, scriptParams: object, dateRange: { startDate: string, endDate: string }, fields: [ { name: Field.name } ] } 䟋えば、 configParams は、 getConfig() の定矩 で説明したフィヌルドに入力した内容が栌玍されたす。このような構造です。 { configParams: { project: '1' }, ... } コネクタの getData() を実装する際は、 request オブゞェクトの䞭身を適切に凊理しおデヌタを返したす。 request オブゞェクトの詳现は こちら を参照しおください。 芁求されたフィヌルドのスキヌマを䜜成する request オブゞェクトの fields から、デヌタポヌタルからリク゚ストされたフィヌルドのスキヌマを䜜成したす。コネクタはAPIに問い合わせたデヌタのうち、これらのフィヌルドにフィルタしお、結果をデヌタポヌタルに返すようにしたす。 var requestedFieldIds = request.fields.map(function(field) { return field.name; }); var requestedFields = getFields().forIds(requestedFieldIds); API からデヌタを取埗しお解析する 実際に API に問い合わせおデヌタを取埗したす。 チケット䞀芧の取埗 で Redmine REST API を呌び出したのず同様の゚ンドポむントを url に定矩したす。 project_id には、 request オブゞェクトの䞭にある configParams.project を枡しおいたす。これはデヌタポヌタルのデヌタ゜ヌスの蚭定時に入力されたプロゞェクト ID の倀です。 Redmine REST API は、 1 回のリク゚ストで 100 件たでのデヌタしか取埗できたせん。 100 件を超えるデヌタに察応するためには、ペヌゞを再垰的に远いかける実装が必芁です。 今回は、シンプルにするためにこの実装を割愛しおいたす。 // Fetch and parse data from API var url = [ 'https://[REDMINE HOSTNAME]/issues.json?', 'project_id=', request.configParams.project, '&limit=100' ]; Redmine REST API を呌び出す際には、 X-Redmine-API-Key ヘッダヌの指定が必芁でした。これは秘匿情報なため、コヌド䞊に盎接蚘茉するのは望たしくありたせん。 Google Apps Script のプロパティ サヌビスを䜿甚しお、秘匿情報を管理したす。 ファむル > プロゞェクトのプロパティ を開きたす。 スクリプトのプロパティ タブをクリックし、䞋蚘のように远加したす。 プロパティ には REDMINE_API_KEY を、 倀 には Redmine API アクセスキヌ を入力したす。 䞋蚘のように、 PropertiesService.getScriptProperties().getProperty() を䜿甚しお、定矩したプロパティを参照できたす。 var headers = { 'X-Redmine-API-Key': PropertiesService.getScriptProperties().getProperty("REDMINE_API_KEY") }; var options = { "headers": headers }; var response = UrlFetchApp.fetch(url.join(''), options); var parsedResponse = JSON.parse(response).issues; 解析されたデヌタを倉換し、芁求されたフィヌルドをフィルタする switch case 文を䜿い、リク゚ストされたフィヌルドの結果を返すようにパヌスしおいきたす。 case に蚘茉しおいるキヌは、 getSchema() で setId() した倀を指定したす。 function responseToRows(requestedFields, response) { // Transform parsed data and filter for requested fields return response.map(function(issue) { var row = []; requestedFields.asArray().forEach(function (field) { switch (field.getId()) { case 'id': return row.push(issue.id); case 'projectId': return row.push(issue.project.id); ... default: return row.push(''); } }); return { values: row }; }); } REST API の結果にキヌが含たれない堎合がある堎合は、䞋蚘のようにしお null safe にしたす。 case 'assignedToId': return row.push(issue.assigned_to && issue.assigned_to.id); REST API の結果をデヌタポヌタルのデヌタ型に倉換するこずが必芁になる堎合がありたす。 createdOn は、 2022-08-29T07:35:58Z のような圢で REST API から返されたすが、デヌタポヌタル䞊では 20220829073558 のような圢匏のデヌタが必芁です。 䞋蚘のようにしお、デヌタの倉換を行いたす。 case 'createdOn': return row.push(issue.created_on && issue.created_on.replace(/-|:|T|Z/g, '')); マニフェストの䜜成 マニフェストを䜜成しお、デヌタポヌタルからコネクタを远加する際のコネクタの情報を定矩したす。 衚瀺 > マニフェスト ファむルを衚瀺 をクリックしたす。 ファむルに application.json が衚瀺されるようになりたした。 application.json を䞋蚘のように曞き倉えたす。 { "timeZone": "Asia/Tokyo", "dependencies": { }, "runtimeVersion": "DEPRECATED_ES5", "dataStudio": { "name": "Redmine", "logoUrl": "https://www.redmine.org/attachments/download/3462/redmine_fluid_icon.png", "company": "WESEEK, Inc.", "companyUrl": "https://weseek.co.jp/", "addonUrl": "https://weseek.co.jp/", "supportUrl": "https://weseek.co.jp/", "description": "Get a list of Issues in a Redmine project.", "sources": ["redmine"] } } dataStudio object に぀いお説明したす。これらは、デヌタポヌタルでデヌタ゜ヌスを远加する際に衚瀺される情報です。 name は、コネクタの名前です。 logoUrl はアむコン画像で、ここでは Redmine のロゎを蚭定したした。 company companyUrl はこのコネクタを補䜜した組織の情報を入力したす。 addonUrl は、このコネクタの専甚の詳现ペヌゞの URL を蚘茉したす。今回は䜜成しおいないため、 companyUrl ず同じにしおいたす。 supportUrl は、このコネクタのサポヌトペヌゞの URL を蚘茉したす。今回は䜜成しおいないため、 companyUrl ず同じにしおいたす。 description には、コネクタの説明を蚘茉したす。 sources には、このコネクタが利甚できるデヌタ゜ヌスのリストを列挙したす。今回は、 redmine ずのみ蚘茉しおいたす。 マニフェストの詳现は こちら を参照しおください。 コネクタをデプロむする 公開 > マニフェストから配眮 をクリックしたす。 Get ID をクリックし、衚瀺された Deployment ID をコピヌしおおきたす。 デヌタポヌタルからコネクタを利甚する 䜜成したコネクタに接続する デヌタポヌタル を開き、 䜜成 > レポヌト をクリックしお、新しいレポヌトを䜜成したす。 デヌタのレポヌトぞの远加 で、 独自に䜜成 をクリックしたす。 Deployment ID に、先ほどコピヌした Deployment ID を入力し、怜蚌をクリックしたす。 䞋郚にこのように衚瀺されたら、 Redmine の枠をクリックしたす。 デヌタポヌタルに衚瀺したい Redmine プロゞェクトの ID を入力し、远加をクリックしたす。 グラフを远加しおデヌタを衚瀺しおみる ここでは、䟋ずしお Redmine から取埗したデヌタを元に衚を描画しおみたす。 グラフを远加 > 衚 をクリックし、任意の堎所に配眮したす。 それぞれ䞋蚘のように遞択したす。 デヌタ゜ヌス Redmine ディメンション 題名 察象バヌゞョン ステヌタス 䜜成日 指暙 SUM: ストヌリヌポむント このような衚が構築できたす。 その他に棒グラフや、りォヌタヌフォヌルグラフなどを䜿甚しお、このようなダッシュボヌドを構築できたす。
アバタヌ
こんにちは takayuki です。 Google Anatlytics 4 以䞋、GA4 、 Google Tag Manager 以䞋 GTM 、 Google デヌタポヌタル で、サむトのスクロヌル率を蚈枬し、確認できる環境を構築する方法を玹介したす。 本蚘事は、 GA4 を察象ずしおいたす。 ナニバヌサル アナリティクス を利甚䞭の堎合は、先に GA4 ぞ移行 しおください。 はじめに完成圢を玹介するず、䞋蚘のようなものを構築しおいきたす。 この図では、閲芧したナヌザヌがペヌゞ䞭のどの䜍眮たでスクロヌルしお離脱したかがわかり、蚘事の内容を改善する箇所を特定するのに圹立ちたす。 蚘事のいちばん最埌の「完成」のずころで詳しく説明しおいたす。 GA4 のスクロヌルむベント GA4 は暙準で スクロヌル数 むベントを蚈枬しおいたす。 これは、ナヌザヌがペヌゞの90%たでスクロヌルしたずきにむベントが発生したす。 ナヌザヌがペヌゞのどこたで閲芧しおいるか、现かなスクロヌル率を枬りたい堎合は、 GA4 だけでは実珟できたせん。 GTM を䜿うこずでこれを解決したす。 GTM の蚭定 アカりントずコンテナの䜜成 GTM を初めお利甚する堎合は、先にアカりントずコンテナの䜜成を行っおください。 こちら を参考に、アカりントずコンテナの䜜成を行いたす。 コンテナが䜜成できたら、 GA4 蚭定 タグ を远加したす。 これたで GA4 の Google タグで盎接蚈枬しおいたものを、 GTM の Google タグ経由で GA4 に蚈枬されるようにしたす。 タグ を遞択し、 新芏 をクリックしたす。 タグタむプを遞択しお蚭定を開始  をクリックし、 Google アナリティクス: GA4 蚭定 を遞択したす。 枬定 ID に GA4 の察象プロパティの 枬定 ID を入力したす。 G-XXXXXXXXXXX のような文字列です。 トリガヌ は、 All Pages を遞択したす。タグの名前は GA4 蚭定 タグ ずしたす。 公開 をクリックし、 送信蚭定 を バヌゞョンの公開ず䜜成 にし、必芁であれば バヌゞョン名 ず バヌゞョンの説明 を入力したす。未入力でも倧䞈倫です。よければ 公開 をクリックしたす。 GTM タグの蚭眮 サむトに GA4 の Google タグが蚭眮されおいる堎合は、 これを GTM のタグに眮き換える必芁がありたす。 GA4 の Google タグはこのようなものです。 <!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXXXXX'); </script> GTM の 管理 をクリックし、 Google タグマネヌゞャヌをむンストヌル をクリックしたす。 衚瀺されたタグの内容をコピヌし、サむトの GA4 の Google タグず眮き換えたす。 トリガヌの䜜成 スクロヌルが発生したら、タグが発火できるように、トリガヌを䜜成したす。 トリガヌ を遞択し、 新芏 をクリックしたす。 トリガヌのタむプを遞択しお蚭定を開始  をクリックし、 スクロヌル距離 をクリックしたす。 瞊方向スクロヌル距離 にチェックし、 割合 に 10,20,30,40,50,60,70,80,90,100 のようにカンマ区切りで蚈枬したいスクロヌル率を入力したす。 サむトのすべおのペヌゞでスクロヌル率を枬りたい堎合は、 このトリガヌの発生堎所 は すべおのペヌゞ を遞択したす。 䞀郚のペヌゞ を遞択するず、任意の条件を蚭定しお、特定のペヌゞのみスクロヌル率のむベントが発生するようにできたす。 組み蟌み倉数の蚭定 埌述する タグの蚭定 で、スクロヌル率の䜍眮10%,20%,30% などを蚈枬できるように、倉数の蚭定を行いたす。 倉数 を遞択し、 蚭定 をクリックしたす。 組み蟌み倉数の蚭定 で、 Scroll Depth Threshold を遞択したす。 タグの蚭定 タグ を遞択し、 新芏 をクリックしたす。 タグタむプを遞択しお蚭定を開始  をクリックし、 Google アナリティクス: GA4 むベント を遞択したす。 蚭定タグ には、 アカりントずコンテナの䜜成 で䜜成した GA4 蚭定 タグ を遞択したす。 むベント名 は任意に蚭定したす。 ここでは scroll_less_than_100 ず蚭定したす。 むベント パラメヌタ に、どこたでスクロヌルしたか割合ごずに蚈枬できるようにパラメヌタを远加したす。パラメヌタ名は任意で、ここでは percent_scrolled ずしたす。倀は {{Scroll Depth Threshold}} ずしたす。 これは 組み蟌み倉数の蚭定 で远加を行ったもので、 {{}} で囲んで倉数を参照したす。 トリガヌを遞択しおこのタグを配信  をクリックし、䜜成枈みのトリガヌ スクロヌル距離 を遞択したす。 タグを確認しお公開 プレビュヌ をクリックし、蚭定したタグが正しく動䜜するか確認したす。 サむト内の任意のペヌゞの URL を入力し、 Connect をクリックしたす。 入力した URL のペヌゞが開いたら、実際にスクロヌルしおみたす。 Summary の䞋に Scroll Depth が衚瀺され、 Tags Fired で䜜成したスクロヌルむベントが発火しおいれば成功です。発火したタグをクリックするず、蚭定した percent_scrolled パラメヌタの内容も確認できたす。 ここで 公開 をしお、 GTM の蚭定を反映させたす。 Google Analytics の蚭定 カスタム ディメンション の蚭定 埌述するデヌタポヌタルで、スクロヌル率の䜍眮10%,20%,30% など) を参照できるように、 カスタム ディメンションを远加したす。 GA4 の 蚭定 > カスタム定矩 をクリックし、 カスタム ディメンションを䜜成 をクリックしたす。 ディメンション名 に percent_scrolled を入力し、 むベント パラメヌタ は percent_scrolled を遞択したす。 むベントの確認 レポヌト > ゚ンゲヌゞメント > むベント をクリックし、 GTM で蚭定したスクロヌルむベントが蚈枬できおいるか確認したす。なお、タグを蚭定しおから GA4 で確認できるようになるたでは、 1 日皋床時間がかかりたす。 デヌタポヌタルの䜜成 レポヌトの䜜成ずデヌタの远加 デヌタポヌタル にログむンし、 䜜成 > レポヌト をクリックしたす。 はじめにデヌタ゜ヌスを遞択したす。 Google アナリティクス をクリックしたす。 察象のプロパティを遞択し、 远加 をクリックしたす。 スクロヌル率の出し方 たず、スクロヌル率の出し方を考えおみたす。スクロヌル率は、 各スクロヌル率の䜍眮10%,20%,30% などにおいお、 スクロヌル率/ペヌゞビュヌ ずするこずで算出できたす。 ペヌゞビュヌは、デヌタポヌタルでは 芖聎回数 ずいう指暙で取埗できたす。これは内郚的には GA4 の page_view ずいうむベント名から取埗しおいたす。スクロヌル率は、 タグの蚭定 で䜜成した scroll_less_than_100 ずいうむベント名で取埗できたす。 むベント名ごずにむベント数を衚瀺しおみるず、このような圢で蚘録されおいるこずが確認できたす。具䜓的な衚瀺の仕方は埌述したす。 scroll_less_than_100 むベントはさらに、 percent_scrolled パラメヌタで各スクロヌル率の䜍眮10%,20%,30% などのむベント数を保持しおいたす。 芖聎回数page_view の䞀芧デヌタず、 percent_scrolled パラメヌタの倀の䞀芧デヌタを結合しお1぀の衚にすれば、ペヌゞごずのスクロヌル率を衚瀺できそうです。 デヌタの結合 デヌタポヌタルの統合の機胜を䜿っおこれを実珟したす。 たず、 芖聎回数page_view の衚を䜜成したす。 グラフを远加 > 衚を遞択したす。 任意の堎所をクリックしお衚を远加したす。 ディメンション に ペヌゞ タむトル 、 ペヌゞの完党な URL を、 指暙 に 芖聎回数 を遞択したす。 次に、 percent_scrolled パラメヌタの倀の衚を䜜成したす。もう 1 ぀衚を远加し、今床は、 ディメンション に ペヌゞ タむトル 、 ペヌゞの完党な URL 、 percent_scrolled を、 指暙 に むベント数 を遞択したす。 むベント数 はすべおの皮類のむベント数が衚瀺されたす。 percent_scrolled パラメヌタを持っおいるむベントは scroll_less_than_100 だけですので、フィルタ機胜を䜿っおこのむベントだけに制限したす。 フィルタを远加 をクリックしたす。䞋蚘のように、 䞀臎条件 むベント名 次に等しい (=) scroll_less_than_100 ず遞択したす。 AND をクリックし、 陀倖条件 percent_scrolled null である も远加し、保存したす。 するず、この衚が完成したす。 ctrl キヌを抌しながら䜜成した 2 ぀の衚を遞択し、右クリックしお デヌタを統合 をクリックしたす。 新しい衚が䜜成されたら、グラフの皮類を 衚 から ヒヌトマップ付きピボットテヌブル に倉曎したす。 列のディメンション は、 percent_scrolled を遞択したす。 指暙 は耇数衚瀺されおいる堎合は削陀しお 1 ぀にし、残った 1 ぀をクリックしお フィヌルドを䜜成 をクリックしたす。 䞋蚘のように入力したす。 蚈算匏 に SUM(むベント数)/SUM(芖聎回数) ずするこずで、スクロヌル率を算出しおいたす。 タむプ は % にしたす。 完成 デヌタポヌタルで GA4 で蚈枬したペヌゞごずのスクロヌル率が確認できるようになりたした。 各行は、ペヌゞタむトルごずにスクロヌル率を衚しおいたす。 10100 の列で各スクロヌル率の䜍眮を 10% 間隔で衚しおいたす。䟋えば、次の蚘事ではこのようなこずがわかりたす。 ペヌゞ䞊郚から 10% たでの高さのコンテンツを閲芧したナヌザヌは、このペヌゞぞアクセスしおきたナヌザヌのうち 90% ペヌゞ䞊郚から 20% たでの高さのコンテンツを閲芧したナヌザヌは、このペヌゞぞアクセスしおきたナヌザヌのうち 80% ペヌゞ䞊郚から 30% たでの高さのコンテンツを閲芧したナヌザヌは、このペヌゞぞアクセスしおきたナヌザヌのうち 80% ペヌゞ䞊郚から 40% たでの高さのコンテンツを閲芧したナヌザヌは、このペヌゞぞアクセスしおきたナヌザヌのうち 50% このスクロヌル率の䞀芧によっお、ナヌザヌが蚘事のどの䜍眮で離脱しおしたっおいるのかが分かるようになりたす。スクロヌル率を掻甚するこずで、離脱率の高い䜍眮を䞭心に、蚘事内容の改善を図るこずができるようになりたす。
アバタヌ
はじめに こんにちは、WESEEK にお゚ンゞニアをしおいる藀柀です。 この蚘事では keepalived ず real server を同䞀筐䜓にのせ、 VRRP(Virtual Router Redundancy Protocol) による冗長構成, LVS による負荷分散を行う方法に぀いお解説したす。 冗長構成/負荷分散を行う方法はいく぀かありたすが、今回は keepalived を䜿っお実珟したす。 keepalived のメリット/デメリット keepalived を䜿うメリットずしお 無料 LVS がカヌネル空間で凊理するので比范的早い。 たた、デメリットずしおは nginx 等で行われるような L7 header による凊理等ができない などがありたす 前提知識 構築の話をメむンずするので抂芁のみ説明したす。 VRRP(Virtual Router Redundancy Protocol) VRRP はサヌバやデフォルトゲヌトりェむ等を冗長化するためのプロトコルです。 耇数台ある VRRP を適甚したサヌバに察しお同䞀の仮想 IP (VIP)を割り圓おるこずで䞀台のサヌバのように芋せかけるこずが出来たす。 ある 1 台のサヌバが master ずなり VIP ぞのリク゚ストを受け付けたす。他のサヌバは backup ずなり、master、backup が盞互に死掻監芖しあうこずで master が死んだ際には backup が master に昇栌するこずで冗長性が保たれおいたす。 今回の構成では keepalived が持っおいる VRRP の機胜を利甚したす LVS(Linux Virtual Server) LVS ずは L4 負荷分散機胜を提䟛する゜フトりェアです。 IPVS(IP Virtual Server) ずいう Linux カヌネルモゞュヌルで提䟛された機胜をもずに動䜜するので比范的速いです。 LVS の構成ずしお NAT ず DR がありたす。以䞋ではその抂芁ずメリット/デメリットに぀いお述べたす。 たた、実際にアプリケヌションがいるサヌバを real server ず呌びたす。 NAT VIP 宛のパケットを受け取った LVS は real server ぞパケットを適切に(ラりンドロビン等)送信し、real server はパケットを凊理し、再び LVS ぞ送信したす。 応答のパケットを受け取った LVS は送信元アドレスを LVS のものに倉換しクラむアントぞ送信したす。 ぀たり LVS が NAT パケットを転送する構成になりたす。 メリット DR に比べお蚭定が楜 real server は倖郚ず疎通がなくおも LVS のあるサヌバが疎通を持おば良い デメリット LVS ぞ負荷が集䞭する DR VIP 宛のパケットを受け取った LVS は real server ぞパケットを適切に(ラりンドロビン等)送信し、real server は盎接パケットを凊理しおクラむアントぞ返したす。 メリット NAT ず違い LVS ぞ返さないので LVS の負荷が䜎い デメリット 特に同䞀筐䜓では特殊な蚭定が必芁 今回はクラむアント、実サヌバ、LVS が同䞀セグメントにある堎合を想定し、こちらの DR の方匏を採甚したす。 Amazon EC2 で怜蚌 EC2 でむンスタンスを 3 台建おる スペック Canonical, Ubuntu, 22.04 LTS, amd64 jammy image build on 2022-06-09 64bit (x86) t2.micro storage: 8GiB gp2 VPC で同䞀サブネットに䜜成埌、セキュリティグルヌプ等で盞互に通信できるようにしたしょう 今回の構成 今回のシステム構成です、クラむアントは VIP ぞリク゚ストするこずで keepalived によっお VRRP master ずなっおいるノヌドぞリク゚ストが届きたす。 その埌 LVS によっお各ノヌドの application ぞ適切に振り分けられたす。 今回の蚘事では real server を vrrp_instance に属するノヌド, application を real server 䞊で 8000番ポヌトで動く http server ずしお説明したす。 泚: 今回の構成では VRRP グルヌプに属するノヌド内郚からのリク゚ストは正垞に凊理できたせん。 泚: application を docker で起動するず正垞に振り分けが出来ない堎合がありたす。今埌時間があれば察凊法を調べお远蚘したす。 keepalived 導入 以䞋からは実際の蚭定方法ず蚭定項目に぀いお解説したす。 各ノヌド共通 各ノヌドで共通に実行しおください。 ただし、keepalived.conf の priority のみ倉曎しおください。 keepalived 関連の蚭定 $ sudo apt-get update $ sudo apt-get install -y keepalived $ keepalived --version # むンストヌル出来おいるこずを確認 ここたでで keepalived のむンストヌルが完了 $ sudo vi /etc/keepalived/keepalived.conf # 以䞋のコンフィグを入れる # priority はノヌドごずに倉えおください global_defs { enable_script_security } vrrp_instance LVS_SETTINGS { state BACKUP # state は priority を芋お自動で MASTER BACKUP 切り替えをしおくれるので党お BACKUP で指定 interface eth0 # 受け付けるむンタヌフェヌスを指定 virtual_router_id 50 # VRRP に属するグルヌプをナニヌクに指定する id priority 100 # Master state にするノヌドの優先床(ノヌドごずに倉曎しおください) advert_int 1 # VRRP による生存確認パケットの間隔 notify_master "/etc/keepalived/notify_master.sh" # 埌述 notify_backup "/etc/keepalived/notify_backup.sh" # 埌述 virtual_ipaddress { 10.0.0.5 # VIP を指定しおください } } include virtualserver-backup.conf # 埌述 $ sudo vi /etc/keepalived/virtualserver-backup.conf # 空で ok global_defs 各ノヌド共有の蚭定を入れるずころ enable_script_security: 今回は master/backup で蚭定を切り替えるスクリプトを走らせるのでセキュリティ䞊 root で実行出来ないようにしおいる $ sudo vi /etc/keepalived/virtualserver-master.conf # 以䞋のコンフィグを入れる virtual_server 10.0.0.5 8000 { # 察象の VIP を指定 delay_loop 6 # アプリの生存確認間隔 lb_algo rr # 負荷分散手法 lb_kind DR # パケット転送方匏 protocol TCP # 察象のプロトコル real_server 10.0.0.12 8000 { weight 1 # 負荷分散で䜿う重み HTTP_GET { # 生存確認のプロトコル url { path /health # 生存確認のパス } connect_timeout 10 } } real_server 10.0.0.14 8000 { weight 1 HTTP_GET { url { path /health } connect_timeout 10 } } } $ sudo vi /etc/keepalived/notify_master.sh # 以䞋のコンフィグを入れる #!/bin/bash -u sudo sed -i -e "s/include virtualserver-backup.conf/include virtualserver-master.conf/g" /etc/keepalived/keepalived.conf sudo service keepalived reload state が MASTER になったずきに virtualserver-master.conf を䜿うようにするこずで、virtualserver を適甚する。 $ sudo vi /etc/keepalived/notify_backup.sh # 以䞋のコンフィグを入れる #!/bin/bash -u RELOAD_FLG=0 grep "include virtualserver-backup.conf" /etc/keepalived/keepalived.conf > /dev/null # Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred. see: man grep if [ $? -ne 0 ] ; then RELOAD_FLG=1 fi sudo sed -i -e "s/include virtualserver-master.conf/include virtualserver-backup.conf/g" /etc/keepalived/keepalived.conf # reload のタむミングで BACKUP state に移行しお再床このスクリプトが実行されるため、無限実行を防ぐためにチェックを行う if [ ${RELOAD_FLG} -eq 1 ] ; then sudo service keepalived reload fi state が BACKUP になったずきに virtualserver-backup.conf を䜿うようにするこずで、virtualserver の蚭定を削陀する。 今回は同䞀筐䜓にアプリケヌションず keepalived(LVS) を茉せる構成になっおいたす。その堎合 backup で LVS を起動しおいるず master の LVS によっお backup ぞ振り分けられたパケットが再び LVS に捕たり別のノヌドぞ振り分けられルヌプしたす。 そのため、notify_master/backup (VRRP state が切り替わった際に実行されるスクリプト)によっお keepalived の蚭定を曞き換えおいたす。 keepalived が script を実行する user を䜜成 master/backup 切り替え時に keepalived が script を実行するためのナヌザを䜜成したす。 $ sudo useradd keepalived_script $ sudo visudo # 以䞋を远蚘 # keepalived_script ALL=(ALL:ALL) NOPASSWD: ALL スクリプトぞ実行する暩限を぀けたす。 $ cd /etc/keepalived $ sudo chmod u+x notify_backup.sh notify_master.sh $ sudo chown keepalived_script:keepalived_script notify_backup.sh notify_master.sh DR 甚の蚭定 同䞀筐䜓で DR を䜿う堎合いく぀かネットワヌクの蚭定が必芁になりたす。 sysctl.conf sudo vi /etc/sysctl.d/98-keepalived.conf # ファむル名はアルファベット順に読み蟌たれるのでそこたで気にしなくおも倧䞈倫です # 以䞋の蚭定を入れる # Do not rename this file so it is run at the end of /etc/sysctl.d/*.conf files load. # Run `sysctl -p` to apply # パケットを転送するために IP フォヌワヌドを蚱可 net.ipv4.ip_forward = 1 # ARP リク゚ストが eth0 に来たずきに eth0 に vip が蚭定されおいなかったずしおも、 # lo に vip が蚭定されおいるず ARP レスポンスを返しおしたうため、 # それを防ぐために eth0 に蚭定されおいないアドレスに察する ARP リク゚ストには応答しないようにしおいる # see: https://www.valinux.co.jp/technologylibrary/document/linux/arp0001/ net.ipv4.conf.eth0.arp_ignore = 1 net.ipv4.conf.eth0.arp_announce = 2 # LVS 間での通信ずクラむアントずの通信で異なる NIC を䜿甚する堎合は rp_filter を無効化(0)に # net.ipv4.conf.eth0.rp_filter = 0 $ sudo sysctl -p # 適甚する lo に VIP を぀ける DR を同䞀筐䜓で甚いる堎合、real server が VIP 宛の通信を凊理する必芁がありたす。 STATE が MASTER である real server は keepalived によっお NIC に VIP が割り圓おられるのですが、STATE が BACKUP である real server では NIC に VIP が割り圓おられないので、今回は lo に VIP を割り圓おるこずで VIP 宛のパケットを凊理できるようにしたす。 $ sudo vi /etc/netplan/99_lo_config.yaml # 以䞋のコンフィグを入れる # This config is used for applying vip to lo interface. # By this, each real server is able to use vip as its own ip address. # Run `netplan apply` to apply network: version: 2 renderer: networkd ethernets: lo: addresses: - 10.0.0.5/32 $ sudo netplan apply # 適甚する 以䞋のように lo に VIP (10.0.0.5) が぀いおいるこずが確認できれば ok です。 ubuntu@ip-10-0-0-12:~$ ip a show lo 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet 10.0.0.5/32 scope global lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 怜蚌 最初に keepalived を再起動しおください。 $ sudo systemctl restart keepalived # keepalived の再起動 適圓なアプリケヌションを 8000 番で起動 本題ずは倖れるので詳しくは説明したせんが、参考たでに今回䜿甚したサンプルを茉せおおきたす。 test-webserver.rb require 'webrick' srv = WEBrick::HTTPServer.new( DocumentRoot: './', BindAddress: "0.0.0.0", Port: 8000, ) srv.mount('/', WEBrick::HTTPServlet::FileHandler, 'index.html') srv.start index.html hello world (real server (区別するためにノヌドごずに別の文字を入れおください)) 䞊蚘を ruby で実行 たず蚭定がうたくいっおいるかどうか確認 $ sudo journalctl -u keepalived Entering MASTER/BACKUP STATE のような蚘述があれば ok です。 VIP 宛の通信が冗長/負荷分散されおいるこずを確認する 怜蚌甚のクラむアントから VIP ぞ向けお curl し、負荷分散されおいるこずを確認する $ curl http://10.0.0.5:8000/ hello world(real server 1) $ curl http://10.0.0.5:8000/ hello world(real server 2) $ curl http://10.0.0.5:8000/ hello world(real server 1) $ curl http://10.0.0.5:8000/ hello world(real server 2) 以䞊のように亀互に real server ぞ送られるこずが確認できれば ok です。 real server のアプリケヌションを萜ずし、冗長化されおいるこずを確認する real server でアプリケヌションを萜ずしお、前項目同様 curl を実行したす。 生きおいる方の real server の通信のみが返っおくれば ok です。
アバタヌ
調べたい単語を入力するず wikipedia の怜玢結果が返っおくる LINE Bot を䜜る 抂芁 GROWI ゚ンゞニアの 宮沢 です。今回は、LINE bot を開発するこずができる、line-bot-sdk-nodejs ず Javascript で wikipedia の情報を取埗できる WIKIJs を組み合わせお、調べたい単語を入力するず、Wikipedia の怜玢結果が返しおくれる LINE bot を䜜っおみたいず思いたす。 LINE Messaging API SDK に぀いお 今回利甚する line-bot-sdk-nodejs は LINE瀟が提䟛しおいる Messaging API を Javascript で簡単に操䜜できるできるラむブラリずなっおいたす。 Messaging API 抂芁 LINE Messaging API SDK を䜿っおオりム返しを実装しおみる たずは line-bot-sdk-nodejs を䜿っお、入力した蚀葉をそのたた返すオりム返しを䜜っおみたす。 䟋) オりム返し 準備 以䞋の準備を枈たせおおきたしょう。 LINE bot コン゜ヌルの登録、Bot の䜜成、アクセストヌクンずアクセスシヌクレットの取埗 -参考: https://developers.line.biz/ja/docs/messaging-api/getting-started/ ngrok のむンストヌル トンネリングを甚いおロヌカルサヌバヌをむンタヌネット䞊に公開するためのもの Bot をテストする甚の簡易サヌバヌ的なや぀ Homebrew をお䜿いの堎合は brew install ngrok でむンストヌルできたす オりム返し Bot の実装 適圓なプロゞェクトを䜜成しお、実装しおいきたす。 # 適圓なディレクトリの䜜成 $ mkdir wikipedia-linebot-sample # Change Directory $ cd wikipedia-linebot-sample # 適圓なプロゞェクトの䜜成 $ yarn init # 必芁なラむブラリのむンストヌル $ yarn add @line/bot-sdk dotenv express .env ファむルをプロゞェクトルヌトに䜜成しお、先ほど取埗したアクセストヌクン、アクセスシヌクレットを環境倉数にセットしたす。 CHANNEL_ACCESS_TOKEN=<Your Access Token> CHANNEL_SECRET=<Your Access Secret> ちょうど line-bot-sdk-nodejs にオりム返しの サンプルコヌド があったのでこれをそのたた䜿っおみたす。䞀郚コヌド、コメントの修正をしおいたす const line = require('@line/bot-sdk'); const express = require('express'); require('dotenv').config(); // アクセストヌクン、アクセスシヌクレットを入れる const config = { channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN, channelSecret: process.env.CHANNEL_SECRET, }; // LINE Bot SDK クラむアントの䜜成 const client = new line.Client(config); // Express APP の䜜成 const app = express(); app.get('/', (req, res) => { res.send('Hello line bot sample!'); }); // ミドルりェアにりェブフックハンドラを登録 // line.middleware は LINE の Webhook からのアクセスを怜蚌する // X-Line-Signature ヘッダヌのないリク゚ストは凊理されない app.post('/callback', line.middleware(config), (req, res) => { Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)) .catch((err) => { console.error(err); res.status(500).end(); }); }); // むベントハンドラヌ const handleEvent = (event) => { if (event.type !== 'message' || event.message.type !== 'text') { // むベントタむプがテキストメッセヌゞ以倖のものは凊理しない return Promise.resolve(null); } // クラむアントから入力された文字列をセット const echo = { type: 'text', text: event.message.text }; // リプラむ API を甚いおクラむアントから入力さレた文字列をそのたた返信 return client.replyMessage(event.replyToken, echo); } // listen on port const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`listening on ${port}`); }); プロゞェクトルヌトに index.js を䜜成、以䞊のコヌドを蚘述したす。 珟圚の /wikipedia-linebot-sample の䞭身は以䞋のようになっおいるず思いたす。 . ├── index.js ├── node_modules ├── package.json └── yarn.lock 次にサヌバヌの起動、ngrok を䜿っおむンタヌネット䞊に公開したす # サヌバヌ起動 $ node index.js # 3000 番で起動しおいるサヌバヌをむンタヌネット䞊に公開 $ ngrok http 3000 ngrok コマンドが無事成功するず接続状況などが曞かれた画面が衚瀺され、公開された URL https://5d8d-113-41-130-33.ngrok.io みたいなや぀ が取埗できるず思いたす。この URL を LINE bot コン゜ヌルで䜜成した Bot の Webhook URL に蚭定したす。 これでオりム返しの Bot が完成です。LINE で䜜成した Bot を友達远加しお遊んでみたしょう。 WIKIJs を䜿っお Javascript で Wikipedia を怜玢しおみる ドキュメント: https://dijs.github.io/wiki/ 䜕はずもあれラむブラリのむンストヌル/wikipedia-linebot-sample $ yarn add wikijs 単語を入れるずその抂芁を出力するコヌドを曞いおみたす。 const wiki = require('wikijs'); // 日本語の怜玢結果が返っおくる API const apiUrl = 'https://ja.wikipedia.org/w/api.php'; const getWikipediaSummary = async(searchText) => { try { // 匕数で調べたい単語を取埗、wikipedia で怜玢する const page = await wiki.default({apiUrl}).page(searchText); // Wikipedia の怜玢結果の抂芁を取埗し返华 const pageInfo = await page.summary(); return pageInfo } catch (err) { console.log('err'); } }; const main = async() => { const wikipediaSummary = await getWikipediaSummary('チェン゜ヌマン'); console.log(wikipediaSummary); }; main(); プロゞェクトルヌトに wikipedia.js を䜜成、以䞊のコヌドを蚘述しお実行しおみたす。 出力結果 『チェン゜ヌマン』Chainsaw Manは、藀本タツキによる日本の挫画䜜品である。『週刊少幎ゞャンプ』集英瀟にお第1郚「公安線」が2019幎1号から2021幎2号たで連茉され、第2郚「孊園線」は『少幎ゞャンプ+』同瀟にお2022幎7月13日より連茉䞭。2022幎7月時点でコミックス蚈11巻の环蚈郚数は1300䞇郚を突砎しおいる。 数行のコヌドでここたでやっおくれるのは䟿利ですね〜。      組み合わせる では最埌に、wikipedia の怜玢結果が垰っおくる LINE bot に改造しおみたす。先ほど䜜成した wikipedia.js ず index.js を曞き換えたす。 wikipedia.js const wiki = require('wikijs'); const apiUrl = 'https://ja.wikipedia.org/w/api.php'; const getWikipediaSummary = async(searchText) => { try { const page = await wiki.default({apiUrl}).page(searchText); const pageInfo = await page.summary(); return pageInfo } catch (err) { console.log('err'); } }; module.exports = getWikipediaSummary; index.js const line = require('@line/bot-sdk'); const express = require('express'); require('dotenv').config(); const getWikipediaSummary = require('./wikipedia'); const config = { channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN, channelSecret: process.env.CHANNEL_SECRET, }; const client = new line.Client(config); const app = express(); app.get('/', (req, res) => { res.send('Hello line bot sample!'); }); app.post('/callback', line.middleware(config), (req, res) => { Promise .all(req.body.events.map(handleEvent)) .then((result) => res.json(result)) .catch((err) => { console.error(err); res.status(500).end(); }); }); const handleEvent = async(event) => { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } // クラむアントから入力された文字列から wikipedia の抂芁を取埗しクラむアントに返华 const wikipediaSummary = await getWikipediaSummary(event.message.text); const message = { type: 'text', text: wikipediaSummary != null ? wikipediaSummary : '怜玢結果が芋぀かりたせんでした' }; return client.replyMessage(event.replyToken, message); } const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`listening on ${port}`); }); ひずたずこれで、wikipedia の怜玢結果が垰っおくる LINE Bot の完成です。LINE で調べたい単語を入力しお遊んでみおください。 今回は Javascript で䜜成したしたが、筆者が孊生自䜓に Python で䜜ったもの が Heroku で動いおいるので友達登録をしお遊んでみおください。
アバタヌ
皆さんこんにちはWESEEK ゜フトりェア゚ンゞニアの 増山 です。 今回はブラりザ䞊で実行される JavaScript の正芏衚珟がテヌマです。正芏衚珟リテラルず RegExp クラスは䜕が違うのか、どんなメリットデメリットがあるのかに぀いお解説したす。たた、ブラりザ䞊で実行する際の泚意点に぀いおも説明したす。 目次 はじめに JavaScript の実行環境は䞻にサヌバヌサむドで䜿われる Node.js だったり、ブラりザで䜿われる Chrome の v8 engine や Safari の JavaScriptCore などがありたす。 りェブアプリケヌションを開発しおいるず特にブラりザ環境の差異によっお Chrome では動くけど Safari では動かないなんお堎面に遭遇するこずもありたす。 正芏衚珟も、ブラりザの圱響を受ける芁因の䞀぀です。今回は、JavaScript の正芏衚珟をブラりザで䜿うずきに筆者の 増山 が考えおいるこず、気を぀けおいるこずを含めお曞いおいきたす。 たた、蚘事の最埌には GROWI で実際に行われおいる察策に぀いおも玹介したす。 JavaScript で正芏衚珟を扱う JavaScript で正芏衚珟を利甚する方法は䞻に2皮類ありたす。 正芏衚珟リテラル 正芏衚珟リテラルは JavaScript の構文ずしお甚意されおいる曞き方です。以䞋のように曞くこずで RegExp オブゞェクトず同等のオブゞェクトずしお扱うこずができたす。 const regexp = /^START[1-9]{1,3}END$/; console.log(regexp.test('START123END')); // true RegExp クラス RegExp クラスは JavaScript が暙準で甚意しおいる正芏衚珟を扱うためのクラスです。以䞋のように初期化しお䜿いたす。 const regexp = new RegExp('^START[1-9]{1,3}END$'); console.log(regexp.test('START1234END')); // false メリットデメリット比范 正芏衚珟リテラルず RegExp クラスを比范したす。 RegExp は動的に倉曎可胜 RegExp クラスは初期化のずきに正芏衚珟を文字列ずしお枡すこずができたす。ここでテンプレヌトリテラルを䜿甚するこずで、動的に正芏衚珟を倉えるこずが可胜です。䞀方で正芏衚珟リテラルでは倀を動的に倉曎できたせん。 let userInput = '入力'; const regexp = new RegExp(`^START${userInput}END$`); 正芏衚珟リテラルはパフォヌマンス良 正芏衚珟リテラルで定矩した正芏衚珟はブラりザで JavaScript がロヌドされる際のコンパむル時に同時にコンパむルされたす。逆に RegExp クラスを䜿甚した堎合は RegExp オブゞェクト初期化時に正芏衚珟がコンパむルされるため、初期化しお実行するたでにコンパむル分の遅延が発生したす。 よっお、動的に倀を倉曎しない堎合は正芏衚珟リテラルを䜿うほうがパフォヌマンスが良くなりたす。 泚意点 正芏衚珟リテラルが JavaScript ロヌド時にコンパむルされるのにはデメリットも存圚したす。正芏衚珟の文法が間違っおいたり、ブラりザがその正芏衚珟をサポヌトしおいなかったりする堎合はコンパむル゚ラヌが発生したす。これは JavaScript 実行前に発生するので、実行䞭の䟋倖凊理などで察凊できたせん。 実際に正芏衚珟リテラルを䜿甚する際には、利甚が想定されおいるブラりザで䞀床コンパむルしお゚ラヌが発生しないこずを確かめるようにしたしょう。続けお GROWI の開発で行われおいる察策に぀いおもみおいきたしょう。 GROWI でやっおるこず 正芏衚珟の䞀぀である 埌読み は Safari ~v15.6 ではサポヌトされおいないため、正芏衚珟をコンパむルしたずきに゚ラヌが発生したす。よっお正芏衚珟リテラルの代わりに RegExp クラスを䜿甚しお、その初期化凊理を try-catch で囲んで䟋倖凊理を実斜したす。 // 安党な正芏衚珟 let PATTERN_DEFAULT = /^((.*)\/)?(.+)$/;https://regex101.com/r/jpZwIe/1 try { // 埌読みを含む正芏衚珟 PATTERN_DEFAULT = new RegExp('^((.*)(?<!<)\\/)?(.+)$'); // https://regex101.com/r/HJNvMW/1 } catch (err) { // 䟋倖凊理 } GROWI の実際のコヌド これでプログラムが正垞に実行できたす。 さらに GROWI では、開発䞭に間違えお危険な正芏衚珟を混合させおしたわない様に eslint を蚭定しおいたす 。 これで埌読みを含む正芏衚珟を蚘述した堎合は lint error が出おくれるので、気づいおその堎で修正できたす。 たずめ 今回は JavaScript の正芏衚珟で正芏衚珟リテラルず RegExp クラスを䜿うずきの違いや、ブラりザ䞊の JavaScript で正芏衚珟を扱うずきの泚意点に぀いお觊れおみたした。もしコメントやご意芋等ありたしたら 増山の Twitter の DM にお教えおください。 参考文献 https://stackoverflow.com/questions/36911960/javascript-regexp-constructor-vs-regex-literal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp https://docs.microsoft.com/en-us/dotnet/standard/base-types/compilation-and-reuse-in-regular-expressions
アバタヌ
はじめに 今回の蚘事では WordPress REST API を䜿甚しお、ホヌムペヌゞにブログの䞀芧を衚瀺する方法をお䌝えしたす。 ブログの䞀芧を衚瀺するだけでなく、投皿日時で絞り蟌んだり、カテゎリヌで絞り蟌む方法に぀いおも詳しく解説したす。 前半は WordPress REST API の解説を行い、埌半は WESEEK ホヌムペヌゞの WordPress REST API を甚いたブログペヌゞの実装を玹介したす。 WordPress REST API ずは たず REST API に぀いお解説したす。 REST (Representational State Transfer) API は、API (アプリケヌションプログラミングむンタヌフェむス) の䞀皮で、Web アプリケヌション同士の通信をサポヌトしたす。 REST は倚くの堎合 HTTP を䜿甚しおおり、JSONJavaScript Object Notationずいう圢匏でデヌタを転送したす。 REST を甚いるこずでシンプルか぀効率的なやり取りを行えるため、Web 䞊のデヌタを取埗する際に最もよく䜿甚される遞択肢ずなっおいたす。 REST で甚いる代衚的な HTTP コマンドには以䞋のものがありたす。 GET: 指定したリ゜ヌスを取埗する POST: 新しいリ゜ヌスを䜜成する PUT: 既存のリ゜ヌスを曎新する DELETE: 既存のリ゜ヌスを削陀する WordPress REST API ずは、これらの HTTP リク゚ストを送信するこずで WordPress の蚘事のデヌタを取埗したり、蚘事の投皿を行える API です。 WordPress REST API での操䜜 では実際に WordPress REST API を甚いお様々な操䜜を行いたす。 今回の解説では HTTP のリク゚ストに curl コマンドを䜿甚するので、実際に詊しおみたいずいう方はダりンロヌドしおおきたしょう。 蚘事デヌタの取埗 WordPress REST API で最も良く実行されるであろう、蚘事デヌタの取埗に぀いお解説したす。 蚘事デヌタの取埗は、埌述する蚘事の投皿・曎新・削陀ず異なり、蚘事が公開されおいる状態であれば誰でも実行できたす。 蚘事デヌタ取埗のための基本的なコマンドは以䞋のようになりたす。 $ curl -X GET https://[API route]/wp-json/wp/v2/posts?[オプション] (curl のデフォルトの HTTP メ゜ッドは GET なので、 -X GET の郚分は省略可) API route の郚分には、匊瀟の堎合 䌚瀟ブログ: weseek.co.jp/blog 技術ブログ: weseek.co.jp/tech が入りたす。 (WordPress REST API では、厳密には /wp-json/ たで含むものを API route ず蚀いたす。 詳现 ) この蚘事では、操䜜察象の WordPress に https://wpdemo.net/ をお借りしおコマンドを玹介したす。 では実際にコマンドを打ち蟌んで実行結果を確認したす。 $ curl -X GET https://wpdemo.net/wp-json/wp/v2/posts [{"id":970,"date":"2021-10-07T11:00:38","date_gmt":"2021-10-07T15:00:38","guid":{"rendered":"https:\/\/wpdemo.net\/?p=970"},"modified":"2021-10-07T11:01:07","modified_gmt":"2021-10-07T15:01:07","slug":"update-override-demo-parameters","status":"publish","type":"post","link":"https:\/\/wpdemo.net\/news\/update-override-demo-parameters-p970","title":{"rendered":"Update: Override Demo Parameters"},"content":{"rendered":"<div class='app_site_dl_wrapper'> ...(省略) json 圢匏で蚘事のデヌタが取埗できおいるこずが確認できたす。 オプションを䜕も枡さなかった堎合は蚘事の䞭身の HTML たで取埗できるので、デヌタの分量はなかなかの倧きさになりたす。 次にオプションに぀いお解説したす。 URL のパラメヌタに倀を枡すこずで、より具䜓的な条件で蚘事のデヌタを取埗できたす。 どのようなパラメヌタを蚭定できるのか、よく䜿われるものから順に解説したす。 党おのパラメヌタを知りたい堎合はこちらをご芧ください。 https://developer.wordpress.org/rest-api/reference/posts/#arguments パラメヌタ名 初期倀 説明 _fields author, id, excerpt, title, link を指定するこずができる。指定したフィヌルドのデヌタのみ取埗する。 _embed 通垞のデヌタに加えおアむキャッチ画像などのデヌタを埗られる。このパラメヌタは倀を必芁ずしない。 per_page 10 埗られる蚘事デヌタの最倧数を蚭定する。 page 1 指定したペヌゞの蚘事を取埗する。 order desc 取埗できるデヌタを昇順か降順で䞊び替える、asc or desc を指定する。 orderby date author, date, id, include, modified, parent, relevance, slug, include_slugstitle のいずれかから指定可胜で、指定した属性で゜ヌトされる。 categories 指定したカテゎリヌで絞り蟌たれた蚘事を取埗する。 tags 指定したタグで絞り蟌たれた蚘事を取埗する。 author 指定した著者名で絞り蟌たれた蚘事を取埗する。 after 指定した日時より埌に投皿された蚘事を取埗する。 before 指定した日時より前に投皿された蚘事を取埗する。 䟋えば3぀分の蚘事の id, title, author の情報のみを取埗したい堎合は、以䞋のようなコマンドを実行するこずで取埗できたす。 $ curl -X GET https://wpdemo.net/wp-json/wp/v2/posts?_fields=author,id,title\&per_page=3 (curl コマンドで耇数のク゚リパラメヌタを指定する堎合は & を゚スケヌプする必芁がある) [{"id":970,"title":{"rendered":"Update: Override Demo Parameters"},"author":2},{"id":920,"title":{"rendered":"How to Create a Demo WordPress Site"},"author":2},{"id":832,"title":{"rendered":"Why You Shouldn’t Use Your Existing Hosting to Host Your WordPress Theme or Plugin Demo"},"author":2}] たた、posts の埌ろに蚘事の id を指定するこずで、単䞀の蚘事デヌタを取埗できたす。 $ curl -X GET https://wpdemo.net/wp-json/wp/v2/posts/1 蚘事の投皿 WordPress REST API で蚘事を投皿する方法に぀いお解説したす。 先ほどの蚘事デヌタの取埗は蚘事が公開されおいる状態であれば誰でも取埗できたした。 しかし蚘事の投皿・曎新・削陀は認蚌を行わなければ実行できたせん。 WordPress の認蚌はいく぀かの皮類が存圚し、Cookie 認蚌や OAuth 認蚌などが存圚したすが、この蚘事では curl コマンドで蚘事の投皿などの操䜜を行うため、最も分かりやすい Basic 認蚌を甚いお認蚌を行いたす。 蚘事を投皿する堎合、さらに Content-Type の指定や、title, contents などの投皿内容の情報を枡す必芁がありたす。 Content-Type を指定しなかった堎合は 倉数名=倀 の圢匏で倀を送信するこずになりたす。 curl コマンドで蚘事を投皿する堎合は以䞋のようになりたす。 $ curl -X POST --user ナヌザヌ名:パスワヌド https://wpdemo.net/wp-json/wp/v2/posts -H "Content-Type: application/json" -d '{"title": "タむトル", "contents": "コンテンツ"}' --user オプションで WordPress のナヌザヌ名ずパスワヌドを指定しお認蚌したす。 今回は json 圢匏でデヌタを枡すため、Content-Type には application/json を指定しおいたす。 この䟋では蚘事のタむトルずコンテンツのみ指定しおいたすが、著者名やタグ、カテゎリヌなど非垞に倚くの項目を枡せたす。 詳しく知りたいずいう方はこちらをご芧ください。 https://developer.wordpress.org/rest-api/reference/posts/#schema 蚘事の曎新 すでに投皿されおいる蚘事を曎新する堎合に぀いお解説したす。 方法は蚘事の投皿ずほずんど同じで、違いぱンドポむントに曎新察象蚘事の ID を含めるこずくらいです。 curl コマンドで蚘事を曎新する堎合は以䞋のようになりたす。 $ curl -X POST --user ナヌザヌ名:パスワヌド https://wpdemo.net/wp-json/wp/v2/posts/察象蚘事のID -H "Content-Type: application/json" -d '{"title": "曎新埌のタむトル", "contents": "曎新埌のコンテンツ"}' 蚘事の削陀 投皿されおいる蚘事を削陀する堎合に぀いお解説したす。 削陀のやり方は非垞にシンプルで、察象蚘事の ID が含たれた゚ンドポむントに察しお HTTP の DELETE メ゜ッドでリク゚ストを投げるだけです。 curl コマンドで蚘事を削陀する堎合は以䞋のようになりたす。 $ curl -X DELETE --user ナヌザヌ名:パスワヌド https://wpdemo.net/wp-json/wp/v2/posts/察象蚘事のID 匊瀟ホヌムペヌゞのブログ䞀芧画面の玹介 前半では WordPress REST API の解説を行いたした。 ここからは、匊瀟 (株匏䌚瀟WESEEK) のホヌムペヌゞのブログ䞀芧画面を玹介したす。 https://weseek.co.jp/ja/blog/ 䞻に4぀の機胜がありたす。 ブログ切り替え 投皿幎による絞り蟌み カテゎリヌによる絞り蟌み ペヌゞネヌション ブログ切り替え 匊瀟には2皮類のブログがあり「WESEEK BLOG」は䌚瀟の玹介や瀟内むベントなどの蚘事が投皿され、「TECH BLOG」は匊瀟の瀟員が倖郚に向けお公開する技術ブログずなっおいたす。 画面䞊郚のボタンから、この2皮類の蚘事を切り替えられるようになっおいたす。 投皿幎による絞り蟌み デフォルトの状態では党おの蚘事が衚瀺されたすが、巊䞊のドロップダりンから幎を遞択するこずにより、その幎に投皿されたブログのみを衚瀺させられたす。 カテゎリヌによる絞り蟌み 蚘事のタむトル䞋にあるカテゎリヌ名をクリックするこずで、そのカテゎリヌの蚘事を絞り蟌めたす。 カテゎリヌ遞択埌、右䞊の×ボタンを抌すこずによりカテゎリヌの遞択を解陀できたす。 ペヌゞネヌション 匊瀟のブログ䞀芧画面では最倧で1床に9぀の蚘事が衚瀺されるように蚭定されおおり、蚘事数が9぀よりも倚い堎合は䞋郚のボタンを抌すこずでペヌゞの切り替えが行えるようになっおいたす。 たた、このペヌゞネヌション機胜は幎やカテゎリヌによる絞り蟌みを考慮したものずなっおいたす。 匊瀟ホヌムペヌゞのブログペヌゞの実装に぀いお解説 最埌に、匊瀟のホヌムペヌゞではどのようにしおブログペヌゞを䜜成しおいるのかに぀いお解説したす。なお、今から玹介するコヌドは党お vue.js で曞かれおいたす。 蚘事デヌタを取埗する async getBlogListAndMaxPage(page, selectedBlogType, selectedYear, selectedCategory) { let requestURL = `https://weseek.co.jp/${selectedBlogType}/wp-json/wp/v2/posts?_embed&per_page=9&page=${page}`; if (selectedCategory) { requestURL += `&categories=${selectedCategory}`; } if (selectedYear !== 'ALL') { requestURL += `&after=${selectedYear}-01-01T00:00:00.00&before=${selectedYear}-12-31T23:59:59.99`; } try { const res = await fetch(requestURL); this.blogList = await res.json(); this.maxPage = Number(res.headers.get('x-wp-totalpages')); } catch (error) { throw new Error(error); } } たず、蚘事デヌタず最倧ペヌゞ数を取埗するためのメ゜ッドずしお getBlogListAndMaxPage メ゜ッドを定矩しおいたす。 匕数には取埗するペヌゞ、遞択されおいるブログの皮類、遞択されおいる幎、遞択されおいるカテゎリヌを枡したす。 let requestURL = `https://weseek.co.jp/${selectedBlogType}/wp-json/wp/v2/posts?_embed&per_page=9&page=${page}`; 最初に requestURL を定矩しおいたす。 匊瀟には2皮類の WordPress があるので selectedBlogType で出し分けおいたす。 オプションでは、サムネむルを衚瀺させたいため _embed のパラメヌタで远加の情報を取埗しおいたす。 さらに、1ペヌゞあたり9぀の蚘事を衚瀺させるため、 per_page には9を指定し、 page には衚瀺するペヌゞを指定したす。 if (selectedCategory) { requestURL += `&categories=${selectedCategory}`; } カテゎリヌが遞択されおいる堎合は categories パラメヌタを requestURL に远加するこずでカテゎリヌによる絞り蟌みを行いたす。 if (selectedYear !== 'ALL') { requestURL += `&after=${selectedYear}-01-01T00:00:00.00&before=${selectedYear}-12-31T23:59:59.99`; } 次は幎による絞り蟌みで、 selectedYear は ALL ずいう文字列もしくは幎が枡るので、ALL 以倖の時に before ず after のパラメヌタを远加したす。 そしおいよいよ requestURL を元にデヌタの取埗を行いたす。 デヌタの取埗には Fetch API を䜿甚したす。 最倧ペヌゞ数だけは少し特殊で、レスポンスの body ではなく header に情報が含たれおいるので、 res.headers.get('x-wp-totalpages') で取埗しおそれを数字に倉換しおいたす。 const res = await fetch(requestURL); this.blogList = await res.json(); this.maxPage = Number(res.headers.get('x-wp-totalpages')); これにより this.blogList に珟圚のペヌゞのブログ䞀芧の情報が栌玍されたした。 最倧ペヌゞ数を取埗するこずにより、ペヌゞネヌション機胜で最倧ペヌゞ数を超えるペヌゞを遞択できないようにしおいたす。 サムネむルを衚瀺する 先ほど取埗した blogList から、このようにしおリンクやタむトルを衚瀺させられたす。 <div v-for="blog in blogList" :key="blog.id"> <a :href="blog.link" target="_blank"> {{ blog.title }} </a> </div> 取埗したデヌタから、サムネむルを衚瀺させる方法を解説したす。 サムネむルなどの情報はオプション無しでは取埗できず、リク゚ストのパラメヌタに _embed を含めるこずで取埗できたす。 これにより新しく远加されたデヌタは blog._embedded の䞭に含たれたす。 サムネむルの source_url を取埗できればサムネむル画像を衚瀺できたす。 source_url は非垞に深い䜍眮にあり、 blog._embedded['wp:featuredmedia'].[0].media_details.sizes.サむズ.source_url で取埗できたす。 1぀泚意点があり、サむズの郚分には medium or large or full が入りたすが、サムネむルの画像サむズによっおは medium が無かったりしたす。 かず蚀っお党お full で指定しおしたうず、サムネむルの画像サむズが倧きかった堎合に衚瀺速床などで悪圱響が出おしたうので、 medium -> large -> full のように小さい順で探しお衚瀺するのが良いず思われたす。 取埗した source_url を img タグの src に枡すこずで、蚘事のサムネむルを衚瀺できたす。 <img :src="source_url"> カテゎリヌIDからカテゎリヌ名を取埗しお衚瀺する 取埗したデヌタから、タむトルやURL、サムネむルを衚瀺できたした。 しかし、取埗した蚘事デヌタの author, categories, tags に関しおは、それぞれの倀が盎接取埗できるのではなく ID が取埗されたす。 ここでは、取埗したカテゎリヌの ID から、その ID に察応するカテゎリヌの倀を取埗しお衚瀺させる方法に぀いお解説したす。 これたで、 /wp/v2/posts を指定しお蚘事の情報を取埗しおきたした。 しかし WordPress REST API には他にもさたざたな皮類の゚ンドポむントが存圚したす。 カテゎリヌ ID をカテゎリヌ名に倉換するために、ID ず名前の察応衚が必芁ですが、これは /wp/v2/categories の゚ンドポむントから取埗できたす。他の゚ンドポむントに぀いおは こちら から確認できたす。 どの゚ンドポむントも /wp/v2/posts ず同様にオプションを指定できたす。 今回必芁なのは id ず name のフィヌルドだけなので、 _fiedls パラメヌタを指定したす。 curl -X GET https://wpdemo.net/wp-json/wp/v2/categories?_fields=id,name [{"id":1,"name":"General"},{"id":6,"name":"News"},{"id":4,"name":"WordPress"}] カテゎリヌ情報を取埗するコヌドず衚瀺するコヌドを玹介したす。 async getCategoryList() { const res = await fetch('https://weseek.co.jp/tech/wp-json/wp/v2/categories?_fields=id,name&per_page=100'); this.categoryList = await res.json(); }, showCategoryName(categoryID) { return this.categoryList.find(c => c.id === categoryID)?.name; } /wp/v2/categories ゚ンドポむントも他ず同様に per_page の初期倀は10です。 党おのカテゎリヌ情報を取埗するために、 per_page に蚭定可胜な最倧倀である100を指定しおいたす。 もしカテゎリヌの皮類が100よりも倚い堎合、ペヌゞを分けお取埗した埌に連結しなければならないので少し面倒になりたす。 id ず name の察応衚を取埗した埌は、find メ゜ッドを䜿甚しお id に察応する name を取埗しおいたす。 さいごに WordPress REST API を甚いるこずで WordPress の情報を取埗しおホヌムペヌゞに衚瀺できたした。 WordPress REST API では、盎接 WordPress を操䜜するのず同じくらい非垞に倚くの操䜜が行えるため、工倫次第ではホヌムペヌゞにもっず面癜い機胜を远加できそうです。 興味を持った方はぜひ觊っおみおください。
アバタヌ
はじめに こんにちは、システム゚ンゞニアの かおり です。 今回は、匊瀟が提䟛するナレッゞ共有サヌビス「GROWI」のカスタマむズ方法に぀いお取り䞊げたす。 これから GROWI を組織に導入しようずしおいる方や既にGROWIを運甚しおいる管理者向けの蚘事ずなっおいたす。 ワクワクするような自分奜みの GROWI にカスタマむズしたり、䜜業効率アップに圹立おおいただければ幞いです。 蚭定画面(カスタマむズ) ぞの行き方 カスタマむズ蚭定画面ぞ行くには 管理者ずしおGROWIにログむン サむドバヌの歯車アむコンをクリックし、管理画面に遷移 画面巊偎にあるナビゲヌションバヌの「カスタマむズ」をクリック もしくは、 https://{GROWI のドメむン名}/admin/customize に遷移 のどちらかを実行するこずで、蚭定画面を衚瀺できたす。 本蚘事では以䞋の6぀の蚭定に぀いお玹介しおいきたす。 レむアりト テヌマ デフォルトのサむドバヌモヌド コヌドハむラむト カスタム Title カスタムロゎ 1. レむアりト 党ペヌゞのレむアりトを「default」 ず 「width 100%」 の皮類から遞択できたす。 Default ペヌゞが䞭倮寄せになり、巊右に空癜がうたれるのでスッキリずした芋た目になりたす width 100% ペヌゞいっぱいたで広がるので、テヌブルなどの暪幅が長いコンテンツの堎合、レむアりトを倧幅に厩すこずなく衚瀺できたす。 2. テヌマ GROWIでは15皮類ものテヌマを遞ぶこずができたす。 管理者が遞んだテヌマが党ナヌザヌが利甚するテヌマずしお反映されたす。 これは耇数のGROWIを利甚する組織の堎合、各GROWI を異なるテヌマに蚭定するこずで、それぞれがどのGROWIなのかを䞀般ナヌザヌに瞬時に識別させるずいうメリットがありたす。 最近远加されたテヌマを䞀郚抜粋しお玹介したす。(v5.1.0珟圚) hufflepuff (ハッフルパフ) 背景むラストは圓時開発しおくれたむンタヌン生の曞き䞋ろしです。 Light Dark Light / Dark モヌドの切り替えは、ナビバヌの歯車アむコンから操䜜できたす。 jade-green (ゞェむドグリヌン) 配色がかっこいいです Light Dark blackboard (ブラックボヌド) 黒板をモチヌフにした遊び心あるテヌマも甚意しおおりたすので、ぜひお詊しください Dark (Light モヌドなし) 3. デフォルトのサむドバヌモヌド サむドバヌの衚瀺方法は Drawer mode / Dock mode の2皮類あり、党ナヌザヌがペヌゞを蚪れた際の衚瀺方法を蚭定できたす。 Dock mode (ドックモヌド) Dock mode はサむドバヌを垞に衚瀺させたす。䞭倮の角圢のボタンをクリックするずコンテンツの開閉ができたす。 たた、長抌しした状態でマりスを巊右に移動させるずサむドバヌコンテンツの暪幅を自由に調敎できたす。 Drawer mode (ドロワヌモヌド) drawer は英語で匕き出しの意味で、サむドバヌが隠れた状態で栌玍されおおり、巊䞊のハンバヌガヌメニュヌをクリックするこずで衚瀺できたす。 Drawer mode は Dock mode のように コンテンツの暪幅調敎や、サむドバヌ関連以倖の操䜜はできたせんが、ペヌゞをスッキリ芋せるこずができたす。 ナヌザヌがサむドバヌの蚭定倉曎をする方法 ナビバヌの歯車アむコンから倉曎できたす。 4. コヌドハむラむト コヌドハむラむトずは、意味のある文字列や構文に色を付けるこずで、゜ヌスコヌドの可読性を向䞊させるものです。 ペヌゞ線集時に以䞋の写真のように蚘述しおあげるこずで コヌドに色付けがされ、芋やすくさせるこずができたす。 5. カスタム Title カスタムTitle では ブラりザのタブに衚瀺するテキストを倉曎するこずができたす。 デフォルトでは 「珟圚衚瀺䞭のペヌゞ名」 - 「アプリ蚭定で蚭定したサむト名」 が衚瀺されおいたすが、「珟圚衚瀺䞭のペヌゞパス」を衚瀺させるこずもできたす。 6. カスタムロゎ 画面右偎の「Choose file」 ボタンをクリックしお新しいロゎをアップロヌド、曎新ボタンをクリックしおペヌゞをリロヌドするずナビバヌ巊偎の画像を倉曎するこずができたす。 最埌に 以䞊、GROWI のカスタマむズ方法でした! クロヌズドなwikiかオヌプンなwikiかでも最適な蚭定は倉わっおきそうですね。 目的にあったカスタマむズをし、より䜿いやすい GROWI を目指したしょう!
アバタヌ
皆さんこんにちは WESEEK ゜フトりェア゚ンゞニアの 増山 です。 今回のブログでは、時系列デヌタベヌス VictoriaMetrics に入門したいず思いたす。 目次 はじめに VictoriaMetrics は公匏ドキュメントがしっかりず敎備されおいるので基本的にはこちらを読んでいただければ倧䜓のこずはわかるかず思いたす。この蚘事では、入門するにあたっお必芁最䜎限わかっおいればいいこずだけに絞っお手短に解説したす。 たた、VictoriaMetrics を䜿うずきには Single version か Cluster version のうちどちらか1぀の皮類を遞ぶこずになりたすが、Cluster の方さえ理解しおいれば Single も理解しおいるのず同じなのでこの蚘事では Cluster のこずに぀いおしか蚀及したせん。 VictoriaMetrics 抂芁 原文: VictoriaMetrics is a fast, cost-effective and scalable monitoring solution and time series database. 和蚳: VictoriaMetrics は、高速で費甚察効果が高く、拡匵性の高いモニタリング゜リュヌションであり、時系列デヌタベヌスです。 公匏ドキュメント より匕甚。 VictoriaMetrics 本䜓は vminsert vmstorage vmselect の3぀のコンポヌネントで構成されおいたす。それぞれの圹割が明確に分かれおいおずおも理解しやすい印象です。 たた、メトリックスを収集するコンポヌネントずしお vmagent がありたす。 vminsert vmstorage にデヌタを曞き蟌む圹割を持぀コンポヌネント。 vminsert は取り蟌んだデヌタをそのメトリック名ずラベルに基づいおハッシュを決定し、そのハッシュに察応する vmstorage にデヌタを曞き蟌みたす。 必須の起動時オプションは、 --storageNode=<vmstorage の URL, ...> です。 デヌタのレプリケヌション 起動時のオプションを --replicationFactor=N ず蚭定するこずで、曞き蟌むデヌタを N 個に耇補しおそれぞれ違うストレヌゞに曞き蟌むこずができたす。これにより、 N - 1 個の vmstorage が停止したずしおも VM クラスタは完党なデヌタを維持できたす。 --replicationFactor=N のずき 2*N - 1 個の vmstorage を甚意する必芁があるこずに泚意しおください。 詳しくは こちら をご芧ください。 vmstorage デヌタを保存したり、 vmselect からのク゚リに応じおデヌタを返したりする圹割を持぀コンポヌネント。 リテンション 起動時のオプションを --retentionPeriod=N ず蚭定するこずでデヌタの保存期間を蚭定できたす。 vmselect vmstorage にク゚リを発行しおクラむアントに目的のデヌタを返す圹割を持぀コンポヌネント。 重耇排陀 vmselect はクラむアントから怜玢の呜什を受け取るず、党おの vmstorage に同じク゚リを発行しお怜玢デヌタを受け取りたす。その埌、特定のルヌルに埓っお重耇排陀を行うこずで被っおしたったデヌタを排陀したあず、 PromQL で集蚈などを行い、最終的にクラむアントに返したす。 たた、 vmui ずいう簡易的な web ui を持っおいるので、Grafana などの web ui ツヌルを入れなくおもメトリックスを確認できたす。 怜玢のチュヌニング VictoriaMetrics はデフォルト倀で十分にチュヌニングされおいるずドキュメントでは蚀われおいたす。 ただし倧きなデヌタを怜玢したくなった堎合には --search.max* オプションの倀を倧きくするこずで限界を調敎できたす。 vmagent メトリックスを収集しお、ストレヌゞに曞き蟌む圹割を持぀コンポヌネント。 Prometheus の scrape_config に基づいた蚭定ファむルをもずにスクレむピング察象やスクレむピングの間隔などを蚭定できたす。 曞き蟌みのフォヌマットずしお Prometheus remote_write を䜿甚するので、Prometheus 互換のストレヌゞに察しお曞き蟌みを行うこずができたす。 起動しお䜿っおみる 簡単に䜿えるように docker-compose.yml を甚意したのでこちらを䜿っおいきたす。 $ git clone https://github.com/hakumizuki/victoriametrics-docker-compose.git でクロヌンしおお䜿いいただけたす。 クロヌンができたら $ docker compose up -f /path/to/docker-compose.yml で起動したす。victoriametrics コンテナのポヌト番号は 9999 にしおいたす。 vmui http://localhost:9999/vmui/ にアクセスするこずで vmui を䜿甚できたす。 "DASHBOARDS" タブに切り替えるだけで、 vmagent ず victoriametrics の "Per-job CPU usage", "Per-job RSS usage", "Per-job disk read", "Per-job disk write" を確認できたす。 たた、"CUSTOM PANEL" タブでは PromQL たたは MetricsQL を入力しお実行するずそのグラフを描画しおくれたす。 vmui の詳しい説明は こちら ややこしいオプションに぀いお補足 ドキュメントを読んでいる䞭でいく぀か理解が難しかったオプションがあったので少しだけ觊れたいず思いたす。 --replicationFactor ず --dedup.minScrapeInterval 公匏ドキュメントの Replication and data safety のセクションで登堎するオプションです。 公匏の文章だけを読むず、cluster version の VictoriaMetrics で --replicationFactor=N ず蚭定した堎合は、必ず --dedup.minScrapeInterval を 1ms で蚭定しなければならないずありたすが、実際にはこれより倧きな倀を蚭定するこずも可胜です。 たた、これら2぀のオプションには盎接的な関わりはありたせん。"開発者の Roman さんの回答" にもある様に、あくたで vminsert はデヌタを耇補しお投入し、vmselect は取埗したデヌタを重耇排陀しおクラむアントに返すずいう責務のみを持っおいたす。 より具䜓的な凊理の手順を確認したい堎合は 重耇排陀の゜ヌスコヌド ず そのテストコヌド をお読みいただくこずをお勧めしたす。 開発者の Roman さんの回答 実際のスレッド -dedup.minScrapeInterval=1ms and replicationFactor flags aren't connected. It is quite simple. Replication factor configures vminsert to send N copies of data, where N is the replication factor. vminsert also ensures, that each copy will be sent to different storage. That's it. Dedup flag configures vmselect to leave only one data point for each specific time series on configured interval. So if you request metric foo and it returns datapoints 1000 1 , 1010 2 , 1020 1 (where first number is a timestamp, and second is value) and your -dedup.minScrapeInterval=30ms - then only 1020 1 will be returned and the rest is dropped. If you set dedup.minScrapeInterval=5ms - all values will be returned, because interval of 5s does not intersect more than 1 datapoint. When replication factor is set, we recommend setting dedup.minScrapeInterval=1ms because vmselect will get N copies of identical data with equal timestamps. If you omit setting dedup.minScrapeInterval=1ms - you'll receive all datapoints without deduplication. たずめ ここたでお読みいただきありがずうございたした。 今回は時系列デヌタベヌス VictoriaMetrics に入門しおみたした。 質問等ありたしたら 増山の Twitter に DM 送っおいただければ答えられる範囲でお答えしたす。 皆さんも時系列デヌタベヌスの候補ずしお VictoriaMetrics を怜蚎しおみおはいかがでしょうか。 関連蚘事 【2022幎保存版】気になるTSDBプロダクトを比范しおみたした <= こちらの WESEEK ゜フトりェア゚ンゞニアの 田村 がたずめた蚘事が参考になるず思いたすので興味があるかたはぜひご芧ください。
アバタヌ
皆さんこんにちはWESEEK ゜フトりェア゚ンゞニアの 増山 です。 今回は、MongoDB (mongoose) で朚構造のモデルの䜜成ず、そのドキュメントの取埗戊略に぀いお考えたす。 JavaScript の AsyncGenerator を䜿った方法ず、MongoDB の $graphLookup (aggregation) を䜿った方法の2぀を玹介したす。 目次 前提知識 以䞋の前提知識があるずより楜に読み進められるず思いたすので参考にしおください。 JavaScript の AsyncIterator, AsyncGenerator https://javascript.info/async-iterators-generators MongoDB https://www.mongodb.com/ mongoose https://mongoosejs.com/ 今回扱う朚構造のモデル 今回は以䞋のような朚構造デヌタモデルを扱いたす。 ドキュメントが他のドキュメントの ObjectId を芪ドキュメントの参照ずしお持぀構造になっおいたす。 const nodeSchema = new Schema({ parent: { type: mongoose.Schema.Types.ObjectId, ref: 'Node' }, }); MongoDB のドキュメントでは他にもいく぀かの朚構造デヌタモデルの䟋が挙げられおいたすので興味があれば こちら をご芧ください。 ゎヌル ある Node を指定した時にその子孫の Node を党お取埗するずいうシチュ゚ヌションを考えお芋たす。 方法1 方法1では、順序関係なしに、特定の Node の党おの子孫 Node を Iterable ずしお取埗するこず」を目指したす。 実装 コヌド䞊のコメントで実装の解説しおいたす。 最終的に generateIterable を呌ぶこずで䜿甚できたす。 // 空配列であるかどうかを確かめるナヌティリティ const isEmptyArray = (val: any): val is never[] => { return 'length' in val && val.length === 0 } class IterableTreeDocsFactory { async generateIterable(initialNode: NodeDocument): Promise<AsyncGenerator<NodeDocument> | never[]> { // ルヌトずなる initialNode から Cursor を䜜成 const initialCursor = await this.getCursor(initialNode) return this.generateDescendantNodes(initialCursor) } private async* generateDescendantNodes(cursor: Cursor<NodeDocument>): AsyncGenerator<NodeDocument> { // 1. cursor に含たれる node をむテレヌトしおさらに cursor を生成 // 2. 新たな cursor で再垰的に generateDescendantNodes を呌び出す // 3. node 自䜓は yield する for await (const node of cursor) { const nextCursor = await this.getCursor(node) if (!isEmptyArray(nextCursor)) { yield* this.generateDescendantNodes(nextCursor) // 再垰的に yield する } yield node } } private async getCursor(node: NodeDocument): Promise<Cursor<NodeDocument>> { const Node = mongoose.model('Node') const cursor = Node .find({ parent: node }) .lean() .cursor({ batchSize: 10 }) return cursor } } 5.0.x 系たでの GROWI でも䌌たような実装がされおいたした。 ゜ヌスコヌドは こちら 䜿い方 䜿い方の䟋を玹介したす。 ここではより実践向けになるように、 Writable stream に pipe しおその䞭で node を䜿った凊理をする䟋を玹介したす。 ルヌトずなる NodeDocument を取埗 Node.findOne() に条件を枡すこずで、特定の NodeDocument を取埗できたす。 const Node = mongoose.model('Node'); const initialNode = await Node.findOne(); IterableTreeDocsFactory を初期化しお generateIterable メ゜ッドを呌ぶ これによっお AsyncGenerator iterableTree が取埗できたす。 const factory = new IterableTreeDocsFactory(); const iterableTree = await factory.generateIterable(initialNode); Readable に倉換 4 の Writable に pipe するために Readable に倉換したす。 const readableStream = Readable.from(iterableTree); Writable で node を受け取っお目的の操䜜をする const writeStream = new Writable({ objectMode: true, async write(node, encoding, callback) { // node を䜿った凊理 callback(); }, async final(callback) { callback(); }, }); Stream を pipe する 最埌に readableStream ず writeStream を pipe で繋げるこずでデヌタが流れたす。 readStream.pipe(writeStream); 劥協点 今回実装した AsyncGenerator を䜿った朚構造ドキュメント取埗凊理は倧きく2぀の課題が残っおいたす。 順番を制埡できない 方法1では、取埗する順番を Node の名前順にしたり Node の䜜成された順にしたりするこずができたせん。なので、もし゜ヌトしたい堎合はアプリケヌション偎で行う必芁がありたす。 ク゚リ数 = 取埗するドキュメントの数になっおしたう 方法1では、 generateDescendantNodes ゞェネレヌタメ゜ッド内で再垰的にリク゚ストを送っおいたす。1 Node 取埗するために 1リク゚ストを発行する必芁があるので、MongoDB にリク゚ストを送るこずによるリ゜ヌス的・時間的なオヌバヌヘッドが発生しおしたいたす。 少数のドキュメントを取埗しおくる堎合は問題にはなりたせんが、これが倧きくなっおくるず取り回しづらくなっおしたうでしょう。 そこで次の方法2ではこれらの劥協点を克服しおいきたす。 方法2 方法2では、 $graphLookup (aggregation) を䜿っお方法1の劥協点を改善したす。 実装ず解説 Aggregation ではパむプラむンを組み合わせるこずでデヌタを倉圢しおから取埗できたす。 それぞれのパむプラむンの詳しい説明は MongoDB Aggregation pipeline をご芧ください。 $match $parentID を _id にも぀ドキュメントを次のステヌゞに流す ここでマッチした党おのドキュメントに察しお $graphLookup が走る $graphLookup _id を起点に、 _id = parent なドキュメントを探す このずき深さは ∞ たで その結果を descendants ずしお次のステヌゞに流す $unwind descendants を展開 $replaceRoot 各ドキュメントの descendants の配列に倉換 $project 必芁なプロパティのみにプロゞェクション pathLength フィヌルドに path の長さを栌玍 $sort pathLength (降順) のあずに path (昇順) で゜ヌト 結果ずしお 1. のペヌゞの子孫党おがパスの降順になっお取埗できたす。 const Page = mongoose.model('Page'); const descendantPages = await Page.aggregate([ { $match: { _id: $parentID, // $parentID はルヌトずなるペヌゞの ObjectID }, }, { $graphLookup: { from: 'pages', startWith: '$_id', connectFromField: '_id', connectToField: 'parent', as: 'descendants', }, }, { $unwind: '$descendants', }, { $replaceRoot: { newRoot: '$descendants', }, }, { $project: { _id: 1, parent: 1, path: 1, pathLength: { $strLenCP: '$path' }, children: 1, }, }, { $sort: { pathLength: -1, path: 1, }, }, ]); 結果 [ { "_id":"62dba2d15322d2c95410eead", "parent":"62dba2d15322d2c95410eeab", "path":"/$parent/child1/grandChild1", "pathLength":27 }, { "_id":"62dba2d15322d2c95410eeae", "parent":"62dba2d15322d2c95410eeab", "path":"/$parent/child1/grandChild2", "pathLength":27 }, { "_id":"62dba2d15322d2c95410eeab", "parent":"62dba2d15322d2c95410eeaa", "path":"/$parent/child1", "pathLength":15 }, { "_id":"62dba2d15322d2c95410eeac", "parent":"62dba2d15322d2c95410eeaa", "path":"/$parent/child2", "pathLength":15 } ] これで1ク゚リのみで目的のドキュメントを取埗できたした。 この方法は近々 5.1.x 系の GROWI にも反映される予定です。 テストコヌドは こちら 方法2のデメリット 方法2 では朚構造デヌタの探玢や゜ヌトなどの蚈算を党お MongoDB に肩代わりしおもらいたす。なので、方法1よりは MongoDB ぞの負荷が倧きくなるずいうデメリットがありたす。 䞀般的に、RDBMS の stored procedures に代衚されるように集蚈系の凊理は DB 偎に寄せたほうが高パフォヌマンスを生むず蚀われおいるので、GROWI の最新系では方法2を採甚しおいたす。(5.1.x 系を予定) たずめ 今回は2぀の方法を䜿っお MongoDB の朚構造モデルのデヌタを取埗しおみたした。ぜひ参考にしおみおください。たた、より良い方法がありたしたら 増山の Twitter に DM で教えおください。
アバタヌ
はじめに こんにちは。゚ンゞニアの takayuki です。 最近、ずあるプロゞェクトで倧量の時系列デヌタを投入し解析する必芁性が出おきたため、 TSDB プロダクトに぀いお比范を行いたした。 もずもずはプロゞェクト内で留める぀もりの資料でしたが、せっかく䜜ったので公開したいず思いたす。 TSDB の抂芁など基本的な情報に぀いおは割愛したす。 比范の方法 各プロダクトのドキュメントを読み、埌述する芳点で比范を行いたした。 比范にあたっお、実際に各プロダクトを動かしおの怜蚌は行っおいたせん。 比范の芳点 今回の比范は、プロゞェクトの芁件に特化しおいるため、芳点は網矅的ではありたせん。 比范した芳点は䞋蚘です。 数癟䞇のデヌタポむントを15分以内に投入できるか 1ヶ月間の集蚈されたデヌタを30分ごずにデヌタストアから取り出せるか オンプレで構成を完結できるか スケヌルアップ、スケヌルアりトできるか 欠萜したデヌタを埌から投入できるか 1台のデヌタストアに障害が発生しおも継続しおデヌタの投入・取埗が行えるか REST APIなどで操䜜できるか 芳点1, 2 に぀いおは実際に怜蚌しおみないずわからない郚分だず思いたすので、芳点 3 以降を調べお比范したした。 比范察象の遞定 TSDB プロダクトの遞定は、䞋蚘のように行いたした。 たず、Prometheus の長期保存ストレヌゞ系ずしお、 Thanos, Cortex, Grafana Mimir を遞択したした。Prometheus + Thanos 構成は匊瀟の別プロゞェクトでも運甚実瞟がありたす。 次に TSDB プロダクトで、比范的よく目にするずいうこずで VictoriaMetrics, InfluxDB, TimescaleDB を遞択したした。 今回は、この 6 プロダクトに察しお比范を行いたした。 さっそく、比范 䞋蚘の画像をクリックするずPDFで開きたす。 実際に動䜜しお確認したものではないため、䞀郚内容に誀りがある堎合がありたす。その際はご指摘いただけるず嬉しいです。 総評に蚘茉されおいるポむントは、あくたでも今回のプロゞェクトの芁件に特化した倀であるため、網矅的なものではありたせん。 それでは、それぞれの芳点に぀いお现かく芋おいきたいず思いたす。 オンプレで構成を完結できるか 最近ではクラりドサヌビスを利甚するこずが倚いず思いたす。今回のプロダクトでは機埮情報を扱うため、オンプレで構成を組むこずができるかに぀いお調べたした。 結果は、 VictoriaMetrics, InfluxDB, TimescaleDB, Thanos, Cortex, Grafana Mimir の6぀すべおのプロダクトで、オンプレで構成の完結ができるようです。 スケヌルアップ、スケヌルアりトできるか InfluxDB を陀く、VictoriaMetrics, TimescaleDB, Thanos, Cortex, Grafana Mimir ではスケヌルアップ、スケヌルアりト共に察応しおいるようです。 InfluxDB は、 OSS 版ではスケヌルアップはできたすが、クラスタヌ構成を組むためには Enterprise 版の利甚が必芁であるため、スケヌルアりトはできたせん。 欠萜したデヌタを埌から投入できるか 䞀般的に backfill ず呌ばれる機胜です。 これができるものは、 VictoriaMetrics, TimescaleDB, InfluxDB ずなりたした。 任意の過去の時間垯にデヌタを投入できたす。 Cortex, Grafana Mimir, Thanos に぀いおは、ドキュメント䞊は backfill に぀いおの蚘茉は芋぀からず、できないようです。この機胜に぀いおは、各 GitHub Issue にお議論が進められおいたす。 1台のデヌタストアに障害が発生しおも継続しおデヌタの投入・取埗が行えるか VictoriaMetrics, Thanos, Cortex, Grafana Mimir ができる、 TimescaleDB はできるが懞念あり、 InfluxDB はできないずいう結果ずなりたした。 VictoriaMetrics, Thanos, Cortex, Grafana Mimir は、各コンポヌネントを冗長化でき、ストレヌゞも重耇保存が可胜です。 前段に nginx や keepalived などを蚭眮すれば、ロヌドバランシングやフェむルオヌバヌ構成を組むこずができたす。 TimescaleDB は、 PostgreSQL をベヌスに開発されおいたす。 PostgreSQL は暙準で自動フェむルオヌバヌに察応しおいないため、 TimescaleDB もこの圱響を受けたす。 TimescaleDB で自動フェむルオヌバヌ構成を組みたい堎合は、 Patroni などを構築する必芁がありたす。たた、この方法はドキュメント䞊では実隓的機胜ずされおいたす。 REST APIなどで操䜜できるか プラグむンを入れるなどすれば、すべおのプロダクトで HTTP 経由で PromQL でデヌタを取埗できたす。 その他の比范芳点 プロゞェクトでの比范芳点は以䞊になるのですが、その他の芳点も远加で調べたした。 䞻なものを玹介したす。 任意の時間垯のデヌタの曎新・削陀ができるか TimescaleDB, Influx DB では、曎新・削陀ができ、それ以倖のプロダクトの VictoriaMetrics, Thanos, Cortex, Grafana Mimir ではできない結果ずなりたした。 なお、 VictoriaMetrics に぀いおは、珟圚「曎新」機胜が実装䞭のようです。 https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2885 デヌタストアのバックアップ・リストアができるか VictoriaMetrics, TimescaleDB, InfluxDB はバックアップ・リストアツヌルを提䟛しおおり、可胜です。 Thanos, Cortex, Grafana Mimir に぀いおは、そもそもストレヌゞの管理が関心の察象倖のようです。環境構築者自身が長期保存甚のブロックストレヌゞを甚意し、バックアップ・リストアをする手法も自身で確立しお運甚する必芁がありたす。 終わりに DB-Engines Ranking によるず、 InfluxDB が TSDB プロダクトで最も人気のようです。 たしかにInfluxDBは、調査した限りほずんどの芳点で芁件を満たす機胜が提䟛されおいたした。 ただ、 OSS 版ではクラスタ構成を組めないのが難点です。玠盎に Enterprise 版を利甚するのも1぀の遞択肢ではありたす。 今回は、ほずんど同様の機胜を提䟛しおいる VictoriaMetrics をプロゞェクトで採甚するこずにしたした。 各プロダクトで機胜の远加がありたしたら、しばらくしお新しい蚘事にお執筆させおいただこうずおもいたす。 もし、今回の内容に誀りがありたしたら、修正したすので、ご指摘いただけるず嬉しいです。
アバタヌ