WordPressという『技術的負債』から脱出できる日は来るのか

WordPressを好きで使っている技術者は誰もいない

ノーコードでブログを開設出来るためアフィリエイトブログの代名詞となったWordPress。20年前の掲示板サイトと同じ『PHP』というミドルウェアが基盤として使われており、表示がとにかく遅い化石のようなサイトになるのが特徴です。

WordPressが遅いのは、PHPが「呼ばれるたびにWebページをまるごと生成して表示する」旧世代の技術なのが理由です。Googleなど現代的なサイト、特にWebアプリと呼ばれるようなサービスではサイト表示にJavaScriptというスクリプト言語がふんだんに使われており、一度ページを読み込んだあとはユーザーの操作などに応じて動的に必要な部分だけを読み込んで更新します。

更新時に読み込むデータが少ないほど訪問者にもサーバーにも負担が少ないので、技術者なら誰でもJavaScriptベースのサイトを作りたいと思っています。しかし、実際にはそう簡単にはいきません。

『SEO』というバッドノウハウのせいでWordPressは延命した

Googleなどの検索エンジンは、世界中のWebサイトを定期的に巡回してデータベースを更新し、検索結果に表示します。この巡回ソフトを『クローラー』と呼びますが、かつてはクローラーの性能が低く、ChromeなどのWebブラウザの表示ほど正確にサイトデータを収集できませんでした。具体的には、JavaScriptを多用したサイトの巡回が苦手だったのです。

そのため、表示が高速なJavaScriptベースのサイトより、ページをHTMLとしてベタッと表示するPHPのサイトの方がGoogleに収集されやすく、検索結果の上位に表示されやすいという不条理な状況が生まれました。「WordPressはSEOに強い」と言われてきた理由です。

つまり、WordPressを用いたサイトは「人間にとっての使い勝手よりGoogleのクローラーの使い勝手を優先する」という本末転倒なことをしているということになります。

Googleもそうした状況を良いとは思っていなかったので、Chromeと同等の機能をクローラーに持たせるようになりました。このアプリはHeadless Chromeとして一般にも公開されています。

生成AIで無意味化するSEO

昨年にChatGPTを筆頭に生成AIショックが起こり、一般人がWebサイトを検索してアフィリエイトブログやまとめサイトに悩まされることなく問題を解決出来るようになりました。もちろん生成AIもGoogleと同様に世界中のWebサイトを巡回して学習しているのですが、生成AIは不快な広告を見せたり下らない宣伝文を訪問者に読ませることなく、短文でズバリと解決策を提示してくれます。

ネットユーザーは買い物をしたい時だけは検索を続けると思いますが、そうでない時は生成AIに聞いて済ませた方が「タイパが良い」のですから、この先のアフィリエイトブログの運命は推して知るべしです。

検索エンジンが無意味化したことでSEOも無意味化し、結果的にWordPressという化石アプリもWebマーケティングの観点からは無意味化しました。SEO業者は「生成AIO」といった売り文句で生き残りを図ると思いますが、生成AIはよっぽどのことが無ければ外部サイトを見ないで済むような回答をするはずですから、効果は限定的と言えます。

卒業したいのに卒業できないWordPress

もはや「WordPressを卒業しない理由」はなくなりました。そこでJavaScriptをふんだんに活用したブログエンジンを探すことになりますが、なぜかWordPressを代替する決定的なソフトは未だに存在しません。

WordPressにはサードパーティーによる豊富なプラグインがあり、ビジネスのエコシステムとして回っているので簡単には他製品がひっくり返すことが出来ない状況にあります。

私が最近注目しているのはStrapi – Open source Node.js Headless CMSで、ノーコードでブログを開設できます。日本語に未対応なのでまだ自分で導入するのは時期尚早と判断していますが、高速なサイト表示を実現するための技術的な要件は満たしているようです。

実際にStrapiでブログを運用するにはAWSやGitHub Pagesなどのクラウド基盤で簡単に動かせる必要があります。既に国内でも「社内向けAPIポータルサイトのCMSをAWS App Runnerで作った」のようにAWSでStrapiを運用するケースが出てきているようです。

今はまだフロントエンドに強い技術者でないとStrapiを使いこなすのは難しいですが、AWSなどの大手クラウドがLightSailのような一般人向けのサービスでStrapiを簡単に開設出来るようになれば、私のように「WordPressを未だに使っているのは恥ずかしい」と思っている技術屋から順番に脱WordPressが始まると予想しています。

LightsailのWordPressは3.5ドルプランだと落ちまくる件

メンテナンスのたびにサイトが落ちる

 無職の私は、生活のために100円でも出費を切り詰めなければなりません。WordPressでサイトを再構築した際も、AWSのLightSailは「月額 3.50 USD から」の安さが売りなので、迷わず月3.5ドルのコースを選びました

 しばらく様子を見ていましたが、1日のアクセス数が30人前後なのでサイトへのアクセス負荷は問題なし。その後、写真や動画を掲載するために別途CloudFrontでCDNを構築して大きなファイルを逃がしたため、ギリギリ乗り切れるだろうと高をくくっていました。

 しかし、その考えは甘かったのです……。

プラグインの処理で高負荷がかかりサイトが落ちる

 しかし、サイトを整備してプラグインが増えてきたことから、プラグインの設定変更を伴うメンテナンス作業を行うたびに管理画面がフリーズ状態になり「サイトが落ちている」という警告メールがWordPressから届くようになってしまいました。

プラグインの設定変更を伴うメンテナンス作業を行うたびに管理画面がフリーズ状態になり「サイトが落ちている」という警告メールがWordPressから届く

プラグインがブラックボックスなので、設定変更での問題解決は難しい

 一般的なWebサーバーやデータベースサーバーなどが高負荷で落ちる場合は、使用メモリなどの設定を変えたりデータを整理することで負荷軽減が可能なこともあります。しかしプラグインは色々なベンダーから提供されている上に、ブラックボックスで処理内容がユーザーにはよく分かりません。個別の設定変更では解決が難しいので、泣く泣くLightSailのプランを月3.5ドルから月5ドルにアップグレードすることにしました。

WordPressでプラグインを使うなら、メモリ1GBは必要

 LightSailの3.5ドルプランはメモリが512MBと「5年前のスマホかよ」という少なさです。LightSailのWordPressインスタンスはbitnamiスタックというシステム構成でOSはDebian Linux。GUIが無くCUI操作なので512MBでも動作はします。しかしギリギリなので、重い処理をするとメモリ不足で処理が止まってしまいます。CPUは遅くても待てば済むことが多いのですが、メモリ不足は致命的です。

 今時のVPSではGCPOCIなど大手がメモリ1GBのVMを無料で提供しています。プラグイン作者もメモリ消費を考慮してはいるはずですが、既に「WordPressを立てるならメモリ1GBがミニマム」と判断されていると思われます。ソシャゲの肥大化と同じですね😭

LightSailがメモリ512MBでWordPressインスタンスを立てられてしまう理由

 天下のAWS、WordPressでちょっと欲張ると3.5ドルプランでは管理しきれなくなることを知らないはずがありません。しかし実際には3.5ドルプランでWordPressインスタンスを立てられてしまい、私のように管理画面が落ちることになってしまいます。

 これは、LightSailが『OS/アプリケーションスタック』と『インスタンスプラン』を一切紐づけておらず、全ての組み合わせでインスタンスを立てられるのが理由です。端的に言えば、プラン選択もアプリ選択も『自助』『自己責任』という考え方でサービスが構築されている、ということです。

 WordPressはまだ512MBでも動きはしますが、ショッピングカートの『Magento』ではアップデートに2GBのメモリが必要で、512MBでは初期設定すらまともに出来ません(月10ドルのプランなら動くでしょう、たぶん)。このように、自分が使いたいアプリと用途でちゃんと動くミニマムのプランを見極めてから始めるのが基本です。

 とは言え、サービスが落ちると致命的な企業サイトでなければ「とりあえず立ててみて、ダメそうならスナップショットを取ってアップグレードする」という「痛い目に遭って覚える」やり方もアリでしょう(負け惜しみ)。ボタンひとつでサーバーを落として引っ越せるため失敗のダメージが少ないのはクラウドの長所です。

泣く泣く月5ドルのプランに移行する

 というわけで、無職で経済的に厳しい折ではありますが、月3.5ドルのプランから月5ドルのプランに移行することにしました。

手順① スナップショットの作成

 LightSailは、システムの再構築なしにプラン変更が可能です。具体的には、まず既存インスタンスのスナップショットを取ります。

手順② スナップショットから新規インスタンスを作成

 スナップショットが出来たら、クリックして『新規インスタンスの作成』を選びます。

手動スナップショットを作成し、スナップショットから新規インスタンスを作成する

 古いスナップショットを使うと先祖返りしてしまうので、アップグレードしたいインスタンスの最新のスナップショットであることを再確認します。

手順③ 月5ドルのインスタンスプランを選んで作成

 『新規インスタンスプランの選択』で、左から2番目の『$5 USD』のプランを選びます。メモリが1GBと倍増するほか、ストレージ40GB、転送量2TBと全体的にスペックが倍増します。

 一時的に同じ中身のインスタンスが2つ併存する状況となります。固定IPアドレスが古いインスタンスに紐づけられているので、新しいインスタンスはまだ公開されていない状況です。

手順④ 固定IPアドレスの付け替え

 『静的パブリックIPアドレス』の管理画面で古いインスタンスから固定IPアドレスの割り当てを『デタッチ』で解除します。この操作に伴って一時的にサイトが落ちるので、アクセスが少ない時間帯に実施するのが無難です。

 続いて、固定IPアドレスを新しいインスタンスに紐づけます。

 アタッチが終わったら、Webブラウザでサイトが無事表示されていることを確認します。キャッシュの影響でサイトが落ちていても表示されてしまうことがあるので、動作確認は他のブラウザやスマホを使うかキャッシュを削除してから行うのが安全です。

手順⑤ 古いインスタンスの削除

 インスタンスがふたつ併存した状態では二重に課金されてしまうので、古いインスタンスを削除します。インスタンスを削除すると自動スナップショットも同時に削除されるので気を付けましょう。

手順⑥ 手動スナップショット作成と自動スナップショットの設定

 インスタンスを作り直すとスナップショットの設定も消えてしまいますので、作り直します。念のため、手動スナップショットも作成しておいた方が良いでしょう。

メモリが1GBになったが、それでもギリギリ感が……

 新しいインスタンスのメモリ使用状況を確認すると、メモリ1GBでも128MBしかメモリが余っていません。

 スワップがあるとは言え、データがSSDに落ちると処理速度が大幅に下がってしまいます。スワップ処理がフリーズにつながることもあるので、やはり最低限メモリ1GBはあった方が良いでしょう。もちろん、その分お高くつきますが……😭

WordPress静的出力プラグイン『Simply Static』のリンク切れを修正するbashスクリプト

WordPressのセキュリティが不安なのは「ページを動的に生成するから」

 既に何度か指摘していますが、Wordpressはセキュリティに様々な問題を抱えています。特に根深い問題が「動的にページを生成する」ことです。具体的には

  • WordPressはPHPで動的にページを生成する(ユーザーがアクセスするたびにプログラムを動かしている)
  • 「動的にページを生成する」ということは、プログラムにセキュリティの問題があると常に攻撃されるリスクがあることを意味する
  • WordPressは本体が静的にページを出力する機能を持たず、コンテンツ管理用サーバー(CMS)とコンテンツ配信サーバーを分ける機能も無い
  • WordPressのロードマップを見る限り、動的なページ生成に伴うセキュリティ上の問題を解決する意思が開発者に無さげ

という感じで、特に改善の見込みが無いのがかなり絶望的な状況です。フロントエンジニア界隈でWordpressが『技術的負債』とまで言われる理由です。

「何でもプラグインで解決する」のがWordpress流

 Wordpressは、オプション的な機能はすべてサードパーティーのプラグインに任せる文化です。このことがWordpressの多機能化に寄与し、トップシェアのCMSに成長する原動力となりました。

 個人的には、セキュリティはシステムの根幹部分であるため、プラグインに任せるのはおかしいと思っています。しかしWordpressのプラグインが提供する多彩な機能を自力で実装するのは無理なので、Wordpressの静的サイト化も当面はプラグインで実現するのが現実的です。

 本家が自力で対応しようとしていないので「ほかに現実的な方法が無い」ということです。

WordPressのページを静的サイトとして出力するプラグイン『Simply Static』

現在、Wordpressで無償利用できる静的サイトジェネレータープラグインは『Simply Static』です。

 このプラグインにS3やGoogleドライブなどに静的サイトを出力する機能はありませんが、zipファイルでのダウンロードが可能です。

 私は当サイトのコンテンツをGitHub Pagesに試験的に静的出力しています(手動)。

https://masaru-kmt.github.io/

リンク切れの嵐……犯人は『URLエンコード』

 ……と、ここまではキレイな話ですが、現実はそう甘くありません。zipファイルをGitのワーキングディレクトリに展開して

$ git add .
$ git commit -m ‘simply-static-1-1626516830.zip’
$ git push

するとリンク切れの嵐が……。原因を調べたところ『日本語のURL』にあることが判明しました。具体的には

  • WordPressはデフォルトで記事タイトルをURLのフォルダ名として使用する
  • 日本語で記事タイトルを書くと、URLには当然日本語のフォルダ名が含まれる
  • WordPressはURLの日本語をUTF-8で出力する
  • ところが、Simply StaticプラグインはURLを『URLエンコード』で変換して出力する
  • 結果、ページ内のリンクが「URLデコード状態」でリンク先のフォルダ名が「URLエンコード」状態なのでリンクが切れる

というカラクリになります。

 以前の記事「WordPressサイトを静的に出力してGitHub Pagesを作る」ではこの問題に敢えて触れていませんでした。というのも、Wordpress側の設定を変えれば

  • 記事タイトルをURLに含めない
  • 投稿時に、都度URLを英数字で設定する(手動)

のいずれかの対応が可能だからです。

そもそも、URLに日本語を使うのは適切なのか?

 URLに日本語を平気でぶち込んで来るCMSは、私が知る限りWordpressくらいです。ウェブに詳しい方なら、URLに日本語が含まれているだけで「濃厚なWordpress臭」を感じて敬遠するかも知れません。また文字コード的にも、UTF-8(Unicode)が支配的になる以前からシフトJISやEUCなどでウェブページを作られていた方も、文字化けで苦しんだ経験から「日本語のURL?ダメゼッタイ!」と思われていても無理はありません。

 私はこれらの問題を理解した上で、敢えて日本語のURLをそのまま使うことにしました。URLがUTF-8を含むこと自体は「WHATWGでは、URLはUTF-8とされています」ので不正ではありませんし、事実Google ChromeなどのWebブラウザでは正しく表示されています。

 そして何よりも

URLはまだWebブラウザで見える状況なのだから、日本語の方が日本人にはわかりやすい

からです。スマホアプリではURLが(たとえ存在していても)既に見えなくなっていますが、ウェブサイトとして運営している限りは日本語URLの方がユーザーに親切だと判断しました。

わがままを言うなら自分で変換するしかないじゃない

 というわけで、前回は手動でURLデコードを行っていましたが、さすがに実運用としてはあり得ない手間なので、ものすごく面倒臭いのをこらえて思い切ってフォルダ名を一括変換するbashスクリプトを作成しました。

https://github.com/Masaru-KMT/WordpressURLdecoder

 ……言うてコードは数行ですがな(;´Д`)

#!/bin/bash
# WordPressURLdecoder
# WordPressプラグイン『Simply Static』が出力する
# URLエンコードされたフォルダ名を一括デコードするbashスクリプト
# Version: 2021-07-17

# WordPressのデータはプラグイン『Simply Static』でダウンロードします
# https://ja.wordpress.org/plugins/simply-static/

# 【注意】別途nkfのインストールが必要です
# sudo apt install nkf
# (Ubuntuの場合)


#  [変数設定]スクリプトを格納・実行するディレクトリ
scrdir="/home/masaru/"

# [変数設定]『Simply Static』が出力するzipファイルの解凍先ディレクトリ
workdir="/home/masaru/temp/"


# ディレクトリ一覧の取得(dirlist.txtに格納)
dirlist="${scrdir}dirlist.txt"
find $workdir -type d > $dirlist

# ディレクトリ一覧を一行ずつ読み込みnkfでデコードしたファイル名に変更
cat ${dirlist}  | while read line
do
 newname=$(echo $line  |  nkf -w --url-input)
 echo "${line} -> ${newname}"
 mv $line $newname
done

 ふだんPythonばっか書いたりJavaScriptに泣かされたりしていてシェルコマンドは1行しか書かないので長めのシェルスクリプトは書きたくないのですが……。機械学習でもないのにPythonをわざわざ書くのもおっくうだったので思い切って書いてみたら意外と簡単でした。

 ふだんはこの手のやっつけスクリプトは恥ずかしいので表に出さないのですが、Wordpress界隈でニーズが多そうなのと手頃なフリーソフトが見当たらなかったので無保証で公開することにしました。何よりパソコンが壊れたときにスクリプトをサルベージ出来ないと困るのは自分なので……💦

Oracle Cloudの無料枠が太っ腹(ただし初心者向きではない)

AWS以外のクラウドには仮想マシンの無料枠がある

 AWSで構築したcan.ne.jpですが、維持費に毎月数ドルかかります。「AWSのメールマガジンで毎月バウチャーをもらえばホニャララ」という話もあるのですが、AWSでWebサイトを作ると初年無料を除けば基本的に有料です(2021年4月現在)。

 一方で、AWSに対して出遅れ感があるGCP(Google Compute Engine)やOCI(Oracle Cloud Infrastructure)は永年無料でWebサイトを構築出来る仮想マシン(Virtural Machine, VM)を立てられます。

 クラウドに慣れる目的もあり、GCPとOCIの双方で仮想マシンを立ててみましたが、特にオラクルのOCIが太っ腹だったのでご紹介します。

無料枠なのにAWSのLightSailよりハイスペック

 OCIの無料枠『Always Freeリソース』にはデータベースやストレージもありますが、好きなデータベースをインストールして使えるのはAlways Freeコンピュート仮想マシン(VM)インスタンスです。スペックは

  • シェイプ: VM.Standard.E2.1.Micro
  • プロセッサ: 追加のCPUリソースを使用する機能を持つ1/8 OCPU
  • メモリー: 1 GB
  • ネットワーキング: 1つのパブリックIPアドレスと最大480 Mbpsネットワーク帯域幅を持つ1つのVNICが含まれます
  • オペレーティング・システム: 次のいずれかのAlways Free対応オペレーティング・システムを選択できます:
    • Oracle Linux (Oracle Autonomous Linuxを含む)
    • Canonical Ubuntu Linux
    • CentOS Linux

1/8 OCPUってなんぞや?

 「1/8 OCPUってなんぞや?」と調べてみたところ、AMD EPYC 7551 32-Core ProcessorというCPUの1コアが割り当てられていました。最近のCPUは6コア12スレッドくらいあるので、その1コアぶんくらいでしょう。

[opc@mysql ~]$ sudo cat /proc/cpuinfo
processor	: 0
vendor_id	: AuthenticAMD
cpu family	: 23
model		: 1
model name	: AMD EPYC 7551 32-Core Processor
stepping	: 2
microcode	: 0x1000065
cpu MHz		: 1996.249
cache size	: 512 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 nt_good virt_ssbd arat npt nrip_save
bugs		: fxsave_leak sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass
bogomips	: 3992.49
TLB size	: 1024 4K pages
clflush size	: 64
cache_alignment	: 64
address sizes	: 40 bits physical, 48 bits virtual
power management:

processor	: 1
vendor_id	: AuthenticAMD
cpu family	: 23
model		: 1
model name	: AMD EPYC 7551 32-Core Processor
stepping	: 2
microcode	: 0x1000065
cpu MHz		: 1996.249
cache size	: 512 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 1
initial apicid	: 1
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 nt_good virt_ssbd arat npt nrip_save
bugs		: fxsave_leak sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass
bogomips	: 3992.49
TLB size	: 1024 4K pages
clflush size	: 64
cache_alignment	: 64
address sizes	: 40 bits physical, 48 bits virtual

メモリー1GBは余裕がある

 can.ne.jpが動いているAWS LightSailは最安プランではメモリが512MBしかありません。

LightSailの最安プランはメモリが512MBしかない

 OCIの仮想マシンはメモリが2倍の1GBあるので、アクセス負荷への耐性がLightSailの最安プランよりずっと高いと思われます。今から自分がWordPressのブログサイトを作るなら、少し面倒でもOCIで自分で構築すると思います。

ストレージは100GBまでいけそう

 画像をたくさん置くブログでは気になるストレージ容量ですが、「コンピュート・インスタンスをプロビジョニングする場合、インスタンスはストレージ用に50 GBのブート・ボリュームを自動的に受け取ります」との記述があり基本50GBです。

 さらに「ブロック・ボリュームを作成してアタッチすると、コンピュート・インスタンスのストレージ容量を拡張できます」「すべてのテナンシは、合計100 GBのAlways Freeブロック・ボリューム・ストレージと、5つのボリューム・バックアップを受け取ります」とあるので、ブロックボリュームをアタッチすることで100GBまで拡張出来ると思われます。

 上のLightSailプランの5倍ですね……。

やっちまった…… Oracle Linuxってなに??

 深く考えずにポチポチして仮想マシンを作ったところ、Oracle Linux 7.9という独自Linuxが入ってしまいました。Red Hat Enterprise Linuxのクローンなのでコマンド周りはCentOSに近いのですが、正直

「特に理由が無いのに、よく知らないOSは使いたくない。面倒くさい」

 というのが本音です。UbuntuやCentOSも選べるらしいので、これからOCIを始める方はOS選びに気をつけて下さい。

セキュリティ周りが面倒くさい(間違ってはいないけど)

 OCIでインスタンスを作ると、他社とは異なりデフォルトでファイアウォールが有効になっています。ファイアウォールを設定してポート開放しないとWebサイトすら公開出来ないので、初心者向きではありません。

 とは言え、セキュリティの問題はサイトを公開する以上は避けて通れないので、敢えて苦しんで設定してみるのも良いかと思います。

 また、AWSやGCPには標準で備わっている『Webブラウザ版のSSHクライアント』が無い(ような)ので、RLoginなどのSSHクライアントをインストールするかシェル版のsshを長いコマンドを打って起動する必要があります。こういった「とっつきやすさ」では、OCIはまだAWSやGCPより一歩遅れているように思います。

 オラクルのビジネスモデルを考えると、そもそも初心者やライトユーザーは見込み顧客として想定していないと思いますが……💦

【追記】ufwでポートが開かない問題、私も遭遇しました😭

 OCIでのポート開放で、「ufwを用いてポートを開放できない問題に遭遇」する問題に私も遭遇しました。

 1つ目のインスタンスにLogstashを入れたところ凄いメモリ食いでメモリ1GBでもフリーズが頻発。泣く泣く他のサーバーを落としてOCIのサイトを見ていたところ「2つのAMDコンピュートVM」との記述を発見。「Oracleどんだけ太っ腹なんだ」と思いつつ2個めを立ち上げたところ、SSLが開きません。

 セキュリティーグループを作りイングレスを設定しても無反応。「これはインスタンス内部の問題か?」と思いufwを設定するも無反応。かなり絶望的な気分で1時間くらい検索しまくったところ

結論だけ先に言っておくと、Oracle Cloud上だとufwはバグっているのでiptablesをいじってポート開放すればいいです。

Oracle Cloudでポートを開放されない問題の解決策 – viasnake.com

との超ありがたい記述が。こんなんわかんねえよ先に言っといてくれよOCIさん😭😭😭

 ……というわけでサイトの記述に従ってiptablesを直接書き換えて無事解決しました。ufwは幅広く使われているのでポート開放でドハマりしたUbuntuユーザーは少なくないはず。不具合に対処するか、せめて「ufw使うな」と公式に書いておいてほしかったです。

Next.jsで苦しんでいます

Node.js以来、Web周辺の技術はすっかり変わってしまった

 WordPressによるサイト構築で苦しむことはなかった私ですが、Node.js以降時代が変わりSPA(Single Page Application)やJamstack(JavaScript/APIs/Markup)など訳の分からない技術であふれています。

 「PHPのままでいいじゃん」と正直思っているのですが、時代の変化から目を背けるとどんどん「昔の人」になり、「仕事では使えねーやつ」と若い人から後ろ指を差されてしまいます。

 基本的にはJavaScriptを用いたWebサイトの高速化とアプリ化、API連携あたりがトレンドなのだろうとは理解していますが、あまりにも複雑過ぎて手を出せないまま今日まで来てしまいました。さらにTypeScriptなる派生言語まで出てきて、おじさんはどこから始めたら良いかすら分からず頭を抱えています。

またインストールおじさんに

 取り敢えず環境を構築してみないと分かるか分からないかも分からないので、泣きながら環境構築を始めました。

 しかしフロントエンド周りの環境は複雑を極めており、エラーと試行錯誤とフォルダごと削除を何時間も繰り返すような地獄の様相です。素人なので確たることは言えませんが、フレームワークが林立して「混乱している」ように感じます。npm?npx?yarn?と首をかしげているうちに環境がぶっ壊れてしまいます。

 一番きつかったのが、またもやAnacondaとの競合です。Anacondaが起因と見られるエラーでインストールが止まってしまいました。仮想環境を切り替えても解決しなかったので、止むなくUbuntu LinuxにWeb専用のアカウントを追加し、そちらで作業することで何とかHello Worldまではこぎつけることが出来ました。

データ分析用とWeb開発用はアカウントを分けた方が良い

 完璧な設定をすればデータ分析用とWeb開発用で共通のアカウントを使うことも出来るのでしょう。しかし複数のフレームワークが混ざることに伴うトラブルシューティングの手間を考えれば、最初からWeb開発用のアカウントを別途作成した方が苦しまずに済むのではないかと思います。

 半日環境構築で格闘して皆さまのお役に立てそうなTipsはこれだけでした……。

今のフロントエンドは『プログラマー』になる覚悟が必要

 HTML/CSS、PHPあたりを軽くかじってWordPressなどのCMSをポチポチしてきただけの人には、相当厳しいと思います。今のフロントエンドは、Pythonなどと同様『プログラマー』になる覚悟が必要です。

 WordPressですべての用が足りている人は、無理に次のトレンドに乗る必要はないと思います。しかし、私のようにWeb担当の経験くらいしか売りモノがないおじさんは、この塹壕戦から逃れることが出来ません。

 正直、なぜフロントエンドをここまで複雑にしなければならないのか、未だに腑に落ちないところがあります。しかし時代の変化を嘆いていても仕方ないので、可能な限りキャッチアップしていきたいと思います。

 そんなわけで、更新が滞り気味な私の近況報告でした。

2020年代に独自ドメインは必要なのか?

 こんにちは、まさるです。

 このブログはcan.ne.jpというドメインに設置しています。
 whoisというサービスでドメインの登録情報を確認すると、このドメインを登録したのは1998年3月26日、と分かりました。
実に、23年1ヶ月前くらいです。なんと長い年月が過ぎてしまったのでしょうか……。

 この化石のようなドメイン、実は維持にけっこうお金がかかっています。
can.ne.jpは属性型JPドメインというもので、1年分の維持費が7700円もかかります。
 これとは別に、ドメイン預かりというサービスが年間3960円かかります。
合計で毎年11660円も払ってます。お財布に厳しいですね……。

 それでもドメインを維持しているのは、can.ne.jpという3文字のドメインが珍しいからです。しかし、今となっては、単なる自己満足でしかないような気もします。

 初めてドメインを取った1998年ごろは検索エンジンがまだ発達しておらず、ドメイン名を覚えてもらったりブックマークしてもらうことに価値がありました。しかし今は検索が全盛の時代です。わざわざURLを入力してサイトを訪問してくれる人はほとんどいません。

 今では昔ほど独自ドメインを取る価値が無いのは間違いありません。では、どのような時に独自ドメインを使う価値があるのでしょうか。

 独自ドメインを選ぶ最大の理由は、「コンテンツを置く先のサービスに振り回されたくない」ということです。外部サービスは終了してしまうことがありますし、ツイッターやフェイスブックのようにアカウントが特に理由もなく凍結されてしまうことがあります。そうした時に独自ドメインという『本拠地』があれば、心配した人がGoogleで検索して自分のサイトを確認してもらえる可能性があります。

 もうひとつの理由が、自由なソフトでコンテンツを公開出来ることです。このサイトもワードプレスというCMSで運営しているので、自分が好きなようにサイトをカスタマイズできます。また、サブドメインを作ることで自分のサイトであることを証明しながら複数のサイトを運営することも出来ます

 独自ドメインは、集客力では、どうしても大手サービスにはかないません。技術ブログであればキータなどで記事を書いた方が、たくさんの人の目にふれると思います。大手サービスのアドバンテージを理解した上で、よそに振り回されずにサイトを運営したい方は独自ドメインを検討する余地があると思います。

 このブログで紹介しているギットハブ・ページーズは無料で独自ドメインのサイトを作れます。もちろんドメイン自体の取得・維持にはお金がかかりますが、興味がある人はまずギットハブで試してみるのも悪くないと思います。

WordPressサイトを静的に出力してGitHub Pagesを作る

セキュリティが心配されるWordPress

 前にいた会社でキャラクター商品宣伝用のスペシャルサイトを作ることになりました。独自CMSを使っておりセキュリティ上の理由で制作会社さんからのCMS接続が難しかったため、系列会社のホスティングでサブドメインを作ることにしました。

 運営もコンテンツ修正が出来るようにしたかった(ベタHTMLで作ると都度制作会社さんに修正料金を払う必要がある)のでWordPressの導入を検討。しかし情シス担当者に「WordPressはセキュリティが弱いから止めてくれ」と言われ、制作会社さんにも「ベタHTMLにしてくれ(ないと面倒だし儲からない)」と言われ泣く泣くベタHTMLにしました。

WordPressのセキュリティが弱いのは動的CMSだから

  WordPressのセキュリティが多方面から懸念されてしまうのは、PHPという動的なサイト生成基盤を使っていることと単に「有名だから狙われやすい」のが理由です。「狙われたくないから情シスが勧めるマイナーなCMSを選ぶ」のは消極的であって根本的な問題解決になっていません。

 WordPressで作成した動的サイトを静的サイト(HTML+CSS+JavaScript)に変換して公開すれば、サイトが動的に生成されることによるセキュリティの問題を回避出来ます。

無料のGitHub Pagesで試してみる

 当サイトを無料のGitHub Pagesに静的サイトとしてミラーしてみましたので、やり方をメモとして残します。

https://masaru-kmt.github.io/

 もちろん、WordPressで静的サイトを作成したからといって大元のWordPressが安全になったわけではありません。セキュリティ上の観点からは、先にご紹介したLocalなど非公開のWordPressでサイトを作成し、GitHub Pagesなど公開環境に静的サイトをコミットするのが適切です。

 まず、GitHubでサイト名のレポジトリを作成します。

GitHubでGitHub Pages用のレポジトリを作成する

 次に、GitHubをまだ本格的に使っていない人はGitHub Desktopをダウンロードします(Windows/Mac)。私はUbuntuにはGitを入れていますが、Webサイトでは一度にたくさんのファイルをコミットするので今回はWindowsで試してみました。

GitHub DesktopならCLIが苦手な初心者でも安心()

WordPressから静的サイトを生成する『Simply Static』

Simply Staticは2か月前に更新されており無償なので安心

 WordPressから静的サイトを生成するプラグインは複数ありますが、検索上位のSEOサイト()で紹介されているプラグインは数年間更新が無かったり有料化されてしまったダメダメなものばかりです。結局WordPress内で検索して見つけた『Simply Static』を使うことにしました。

 Simple Staticの設定はパブリッシュ先のドメイン名くらいで簡単です。HTML化したサイトデータをzipファイルとしてダウンロードし、解凍してGitHub Desktopのレポジトリにドラッグ&ドロップし、GitHubにコミットします。

しばらく待たないと404 Not Foundのまま……

 コミットしましたがページが表示されません。「なぜ?」と焦って検索したところ、GibHubにコミットしたHTMLがWebページとして反映されるまでに時間がかかるのが理由、のようです。

HTMLを表示するには、1つずつGitHub上でコミットし直すか数10分待つ必要がある

 即時に更新を反映したいサイトには、GitHub Pagesは向かないようです。とは言え、無償でサイトを公開出来る(独自ドメインもOK)のは魅力的です。「少なくともGit (Hub)の存在くらいは知っている」という証明にはなるので検討の余地はあると思います。

 ちなみに、コンタクトフォームやコメントなどWordPressの動的な機能はミラー先の静的サイトでは無効になります。静的サイトにする際は「コンタクトフォームだけ他サイトのサービスを利用する」といった対策が必要となります。

Jupyter NotebookのセルをWordPressに貼り付ける

手作業でJupyter NotebookをWordPressに転記するのはツライ……

 ということでコピペする方法を探してみたところ、nbconvertを使う方法が良さそうです。

$ jupyter nbconvert --to html --template basic gstore-cust-revenue-prediction.ipynb

 生成されたHTMLから該当セルをWebブラウザーのインスペクター(F12キーを押下)でコピーし、WordPressのカスタムHTMLブロックに貼り込みます。

<div class="input">
<div class="prompt input_prompt">In [7]:</div>
<div class="inner_cell">
    <div class="input_area">
<div class=" highlight hl-ipython3"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span><span class="o">,</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span><span class="o">,</span> <span class="nn">seaborn</span> <span class="k">as</span> <span class="nn">sns</span>
<span class="kn">import</span> <span class="nn">json</span><span class="o">,</span> <span class="nn">re</span><span class="o">,</span> <span class="nn">gc</span>                              <span class="c1">#garbage collector</span>
<span class="kn">from</span> <span class="nn">sklearn.preprocessing</span> <span class="kn">import</span> <span class="n">LabelEncoder</span>
<span class="kn">from</span> <span class="nn">ast</span> <span class="kn">import</span> <span class="n">literal_eval</span>
<span class="kn">from</span> <span class="nn">sklearn.model_selection</span> <span class="kn">import</span> <span class="n">KFold</span>
<span class="kn">from</span> <span class="nn">sklearn.metrics</span> <span class="kn">import</span> <span class="n">mean_squared_error</span>
<span class="kn">from</span> <span class="nn">sklearn.model_selection</span> <span class="kn">import</span> <span class="n">GridSearchCV</span> <span class="c1">#Experimented hyperparams a bit with this</span>

<span class="kn">from</span> <span class="nn">catboost</span> <span class="kn">import</span> <span class="n">CatBoostRegressor</span>
<span class="kn">from</span> <span class="nn">xgboost</span> <span class="kn">import</span> <span class="n">XGBRegressor</span>
<span class="kn">import</span> <span class="nn">lightgbm</span> <span class="k">as</span> <span class="nn">lgb</span>

<span class="k">for</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">filenames</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="s1">'/home/masaru/data/kaggle_google_analytics'</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">filenames</span><span class="p">:</span>
        <span class="nb">print</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">dirname</span><span class="p">,</span> <span class="n">filename</span><span class="p">))</span>
        <span class="k">pass</span>
<span class="n">gc</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span>
<span class="n">sns</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">style</span><span class="o">=</span><span class="s1">'whitegrid'</span><span class="p">,</span><span class="n">palette</span><span class="o">=</span><span class="s1">'deep'</span><span class="p">,</span><span class="n">font_scale</span><span class="o">=</span><span class="mf">1.1</span><span class="p">,</span><span class="n">rc</span><span class="o">=</span><span class="p">{</span><span class="s1">'figure.figsize'</span><span class="p">:[</span><span class="mi">8</span><span class="p">,</span><span class="mi">6</span><span class="p">]})</span>
<span class="n">pd</span><span class="o">.</span><span class="n">set_option</span><span class="p">(</span><span class="s1">'float_format'</span><span class="p">,</span> <span class="s1">'</span><span class="si">{:f}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">)</span>     <span class="c1">#to display full numbers in dataframe and not just exponentiated form </span>
</pre></div>

    </div>
</div>
</div>

<div class="output_wrapper">
<div class="output">


<div class="output_area">

    <div class="prompt"></div>


<div class="output_subarea output_stream output_stdout output_text">
<pre>/home/masaru/data/kaggle_google_analytics/test_v2.csv
/home/masaru/data/kaggle_google_analytics/submission.csv
/home/masaru/data/kaggle_google_analytics/deep-learning-keras-ga-revenue-prediction.ipynb
/home/masaru/data/kaggle_google_analytics/gstore-cust-revenue-prediction.ipynb
/home/masaru/data/kaggle_google_analytics/ga-customer-revenue-prediction.zip
/home/masaru/data/kaggle_google_analytics/test.csv
/home/masaru/data/kaggle_google_analytics/sample_submission_v2.csv
/home/masaru/data/kaggle_google_analytics/GoogleAnalytics_Customer_Revenue_EDA_and_Prediction.ipynb
/home/masaru/data/kaggle_google_analytics/sample_submission.csv
/home/masaru/data/kaggle_google_analytics/train_v2.csv
/home/masaru/data/kaggle_google_analytics/train.csv
/home/masaru/data/kaggle_google_analytics/.ipynb_checkpoints/gstore-cust-revenue-prediction-checkpoint.ipynb
/home/masaru/data/kaggle_google_analytics/.ipynb_checkpoints/GoogleAnalytics_Customer_Revenue_EDA_and_Prediction-checkpoint.ipynb
</pre>
</div>
</div>

</div>
</div>

WordPressのテーマにNotebook用のCSSを追加する

 続いて、WordPressの『外観⇒カスタマイズ⇒追加CSS』でJupyter Notebookセル用のCSSを追加します(リンク先ページのソースを参照のこと)。

In [7]:
import numpy as np, pandas as pd, os, matplotlib.pyplot as plt, seaborn as sns
import json, re, gc                              #garbage collector
from sklearn.preprocessing import LabelEncoder
from ast import literal_eval
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV #Experimented hyperparams a bit with this

from catboost import CatBoostRegressor
from xgboost import XGBRegressor
import lightgbm as lgb

for dirname, _, filenames in os.walk('/home/masaru/data/kaggle_google_analytics'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
        pass
gc.enable()
sns.set(style='whitegrid',palette='deep',font_scale=1.1,rc={'figure.figsize':[8,6]})
pd.set_option('float_format', '{:f}'.format)     #to display full numbers in dataframe and not just exponentiated form 
/home/masaru/data/kaggle_google_analytics/test_v2.csv
/home/masaru/data/kaggle_google_analytics/submission.csv
/home/masaru/data/kaggle_google_analytics/deep-learning-keras-ga-revenue-prediction.ipynb
/home/masaru/data/kaggle_google_analytics/gstore-cust-revenue-prediction.ipynb
/home/masaru/data/kaggle_google_analytics/ga-customer-revenue-prediction.zip
/home/masaru/data/kaggle_google_analytics/test.csv
/home/masaru/data/kaggle_google_analytics/sample_submission_v2.csv
/home/masaru/data/kaggle_google_analytics/GoogleAnalytics_Customer_Revenue_EDA_and_Prediction.ipynb
/home/masaru/data/kaggle_google_analytics/sample_submission.csv
/home/masaru/data/kaggle_google_analytics/train_v2.csv
/home/masaru/data/kaggle_google_analytics/train.csv
/home/masaru/data/kaggle_google_analytics/.ipynb_checkpoints/gstore-cust-revenue-prediction-checkpoint.ipynb
/home/masaru/data/kaggle_google_analytics/.ipynb_checkpoints/GoogleAnalytics_Customer_Revenue_EDA_and_Prediction-checkpoint.ipynb

 無事、表示出来ました。

WordPressをローカル環境と同期する『Local』

Webサイトはバックアップしないといずれ「死ぬ」

 私が初めて独自ドメインでWebサイトを立てたのは20年も前になります。しかし2010年代以前に作ったサイトで今も残っているものはひとつもありません。このcan.ne.jpドメインにもかつて別のブログがありましたが、もはやデータ復旧もままなりません。

 不慮の事故、モチベーション喪失、技術の変化……。さまざまな理由でサイトは「死ぬ」のです。AdobeのFlash廃止でも話題になりましたが、多くのサイトが更改しなければ表示も難しくなり、いずれWebからひっそりと消えてなくなります。

 WordPressというオープンソースCMSのデファクトスタンダードが確立したことで、少なくともサイトデータのサルベージは行い続けられる可能性が高まりました。個人サイトは次々と書き捨てられていく商業サイトと異なり「生きた証」でもあるので、なるべく『生涯現役』を続けたいところです。

個人サイトのバックアップはローカルで分散

 商業サイトではクラウド上にバックアップを取るのが一般的です。複数のクラウドに分散することはあっても、ローカルへの手動バックアップを前提としているサイトは少ないでしょう。

 しかし個人サイトはクラウドの契約解除や無償ストレージ終了などでバックアップも消えてしまうのが常です。消える直前で対策すればサルベージや移行は可能ですが、仕事や生活の多忙で断念するケースも多いのが現実。やはり最終的には物理的に手元にもデータを置いておくのが一番です。

ローカルでWordPressサイトをミラー出来る『Local』

 『Local』はPC上にLAMPやLEMPなどにWordPressを加えたスタックをウィザード形式で構築し、クラウドからバックアップしたデータでローカルサイトとして表示、管理出来るソフトです。無償版があるので個人サイト向きです。Windows、Mac、Linuxの主要OSに全て対応しているのも魅力的です。

 当サイトを実際にローカルでミラーしてみましたので、メモを残します。

まずはバックアップ

 Local自体にはクラウド上のWordPressサイトをバックアップする機能はありません。WordPressにはバックアップ用のプラグインが複数ありますが、今回は『BackWPup』を使います。

 まず、ジョブを作成します。バックアップ対象は『DBバックアップ』『ファイル』『プラグイン』の3つ、宛先は『フォルダー』、圧縮形式は『zip』にしました。

  バックアップファイルはまずクラウド(LightSail)上に作られますので、『バックアップ』サブメニューから『ダウンロード』を選んで取得します。

バックアップのLocalへのインポート

Localを起動し、『Import Site』でzipファイルを読み込む

 バックアップデータを取得したらLocalを起動し、『Import Site』でzipファイルを読み込みます。

LAMPのバージョンはクラウドと合わせる

 LocalはWebサーバにApacheとnginxを選べるなどスタックの選択肢が充実しています。サイトの再現性の観点から、なるべくクラウド上のスタックとバージョンを合わせた方が良いそうです。

クラウド上のWordPressサイトがほぼローカルで再現

Ubuntu上のLocalでWordPressサイトが表示された

 ご覧のとおり、ローカルドメイン(can.ne.jp.local)でWordPressサイトが表示されています。テーマやプラグインまで再現されているので、クラウド上のサイトイメージそのままで見られるのは嬉しいところです。

管理画面もローカルで動く

 管理画面もローカルで動いています。この環境を複数のPCやMac上に持っておけば不測の事態でも生き延びた端末から復旧が出来そうです。Web分野のトレンドが大きく変わってしまったときは、Localで過去のサイトデザインを確認しながら、どのように移行させるか検討することになるでしょう。

Webデザインのトレンドをアップデートしたい

 最近は趣向を変えてWebデザインの本を読んでいます。

 WordPressの標準テーマが過去に仕事で携わってきた企業サイトやブログサイトと大きく異なるので、時差を埋めるのが目的です。モバイルファースト、レスポンシブのサイト設計でトレンドは大きく変わりましたが、従来型サイトも残っているようです。

 カラーマネジメントやワイヤーフレームなどは従来あまり意識していなかったので勉強中です。

https://color.adobe.com/ja/create/color-wheel