update.さくらのアドベントカレンダー(その2)をご覧の皆様、初めまして。一般のさくらインターネットユーザのPiroといいます。この記事では自分で自由に使えるLinuxなサーバーかPCがあるという事を前提として1、さくらのレンタルサーバーのライトプランで静的コンテンツだけのWebサイトを公開・運用する際のノウハウをご紹介します。SSH接続できない!自分は日経Linux誌でシス管系女子という漫画形式の記事を連載させて頂いているのですが、「せっかく本まで出したんだからプロモーション用のページ作りましょうよ!!絵とかバーンとでっかく貼ってかわいい感じのを!!」と言ってみたものの、「日経BP公式サイトのCMS上ではムリ(大意)」と言われてしまったため、自分で勝手にサイトを作って公開する事にしました。なるとどこかにサーバを借りる必要があるのですが、自分は個人的に10年来以上のさくらのレンタルサーバーユーザなので、「やっぱ信頼と実績のさくらだよね!」と。作りたいのはせいぜい1枚ペラの静的コンテンツだったことから、月額にして約129円の一番安いプランで必要十分だろうと判断して、サーバ上の一角をお借りする事にしました。ところが、ここに大きな誤算がありました。ライトプランではSSH接続は使えないのです......自分がそれまで個人的に愛用していたスタンダードプランは月額約429円で共用サーバの一角をお借りする物で、こちらはSSHが使えます。cloneした物を~/wwwに置き、そのままWebサイトとして公開していました。それと同じ要領で行くつもりだったのですが、月額300円をケチったばかりに目論見が総崩れです。SSHができなきゃrsyncでお手軽ミラーリングというわけにもいきません。いって今更Windows用のFTPクライアントを探す気にもなれないし。2016を立ち上げた結果、1日最低1回はサイトを更新する必要が生じてついに我慢の限界に達しました2。そこで運用開始から1年が経過してようやく、サイトの更新手順の自動化に本腰を入れて取り組む事にしました。静的コンテンツのアップロードは、rsyncを使えればそれが一番楽です。rsyncの--archiveオプションと--deleteオプションを組み合わせれば、ローカル側で更新されたファイルを転送し、リモート側に取り残されたファイルを削除する、という事(いわゆるミラーリング)を効率よく行えます。前述の通りライトプランではこの方法を採れません。考えてみれば、Linuxのコマンド操作とシェルスクリプトをテーマに解説記事を自分で書いてるわけだから、コマンドラインで使えるFTPクライアントがあればなんとかなりそうです。FTP」みたいなキーワードで検索してみたら、あっさり見つかりました。これ自体は対話的に動作するツールとの事ですが、引数で指示を与えれば完全な自動実行も可能で、しかもミラーリングのための便利な内部コマンドを持ってると。rsyncと対でlftp、と名前も覚えやすいですね!3これを使って、「実行すれば後はrsync感覚で更新されたファイルのコピーと取り残されたファイルの削除が完了する」というシェルスクリプトを用意しました。スクリプトがあるディレクトリにcdすることにする。exit"このスクリプトは、以下のような位置関係でファイルが存在している事を前提にしています。upload.shhtml(ディレクトリ)index.html.htaccess...lftpコマンドは、実行する内部コマンドを-eオプションで指定すれば非対話的に実行でき、;で区切って複数コマンドを列挙すれば複雑な操作も自動実行できます。この方法を使って、まずsetでTLSに関わる諸々のオプションを有効化して、セキュアな通信を行うようにする。その上で、openでログイン。ログイン後、mirrorでミラーリングを実施。終わったらexitで切断。いう操作を行うようにしました。パスワードは平文でスクリプトに書く必要がありますが、TLSを有効化していわゆるFTPSでの通信を確立してから認証するので、パスワードが平文のままネットワークを流れる事はありません。--verboseとを併せて指定しています。この状態でスクリプトを実行してみて、問題がない事を確認できてから改めて--dry-runを取り除くことにします。共通パーツの埋め込み前述の通り元々が1枚ペラのページだけに留めるつもりだったため、HTMLファイルには共通のヘッダやフッタ、各SNS向けのシェア用ボタンなどもそのまま静的に記述していました。ページ数が増えていくにつれて「これはいつか破綻する......」という危機感が増していき、アドベントカレンダー用に一気に何十ページも増やす事が確定したことでついに破綻が現実化してしまいました。ここまでの時点でファイルのアップロードを自動化する事はできました。ついでに、各ページのヘッダやフッタといった共通部分を別ファイルに分離しておいて、アップロードの直前にそれらをくっつけてからアップロードするようにしてみます。戦略としては、以下の要領です。【シス管系女子】特設サイト</title>...のように埋め込んでいく、という方針です。Include)を思い出した方もいると思います。SSI使えばええやんという話なのですが、SSIはWebサーバの機能なので、アップロード前の表示確認をするにはローカルにApacheか何かを立てないといけません。それは面倒で嫌だったので、SSIを使わずにそれっぽい事をやることにしたのでした。できたスクリプトはこんな感じです。スクリプトがあるディレクトリにcdすることにする。egrepコマンドのように使える「$esed」を定義しておく。アップロード用のファイルを用意する。前回の結果は削除してソースを丸ごとコピーし直すことにする。共通ファイルの埋め込み箇所の検出用正規表現。ファイル名部分は、後方参照で取り出せるように`()`で囲っておく。sedのコマンドライン引数を組み立てるための、sedのスクリプト。含まれている物を検索する。ファイルパスだけを取り出す。1ファイルの中で何カ所も見つかる事があるので、重複を取り除く。見つかった各ファイルに対して処理を行う。埋め込みマークを埋め込み対象のファイルの内容で置き換えるためのsedのコマンド列を組み立てる。最後に各行を区切っている改行文字を削除して1行に繋げる(tr)。sedで一気に内容を解決して、リダイレクトでアップロード用ファイルの位置に書き出す。evalを使ってコマンド列をシェルの記法で改めてパースさせる。最後に、テンポラリファイルを削除する。ファイルのパスと指定すると、そのパターンにマッチした行の後に指定ファイルの中身をそのままはめ込むということができる、というのがポイントです。これで指定ファイルの内容を埋め込んだ後、/マッチングパターン/dで埋め込みマークだけの行を消して、さらにs/マッチングパターン//で行中の埋め込みマークも消す、という風にして痕跡を消しています4。この辺の詳しい事は別の記事にまとめたので、そちらも併せてご覧下さい。sedのrはマッチ位置ではなくマッチした行と次の行の間に指定ファイルの内容を出力するため、これだけだと行の中にある埋め込みマークが期待通りに処理されません。*[^$])/\1"\\$'\n''\3/g'であらかじめ埋め込みマークの直後で強制的に改行するようにしています。awkを使えばもっとスマートにできるのかもしれませんが、awk使ったら負けかなと思ってる5のでこんな感じになりました。絶対パスを相対パスに「解決」する埋め込み用の共通のパーツとして切り出した内容はどの階層のファイルに埋め込まれるか分からないので、共通パーツ内のリンクは相対パスではなく、/から始まる絶対パスで書かないといけません。この絶対パスはHTMLファイルがWebサーバからコンテンツとして返されたときにだけ有効な物なので、アップロード前の表示確認をしようと思ってHTMLファイルをブラウザで開くとリンク切れになってしまいます。先に書いたようにWebサーバを立てたくなかったので、面倒さに目を瞑って今までは相対パスを書いていたのですが、埋め込み用の共通パーツには上記のような理由から相対パスを書けません。そこでこの際だからと、共通パーツの埋め込みと同時に絶対パスを相対パスに解決(って言っていいのか?)するようにしてみました。リンク先に絶対パスが書かれている箇所の検出用正規表現。そのまま残す部分を後方参照で取り出せるように`()`で囲っておく。除外して検索するようにする。「../../」の部分を作る。ここで変数に格納しておく。最後の書き出し直前に絶対パスを解決する。"$path.tmp"done絶対パスを相対パスにするということは、言い換えると、そのファイルのパスからドキュメントルートまで到達するのに必要な数だけ../を手前に付け足すということです。この../../の部分を求めるために、grepの結果から得たそのファイル自身のパスをsedで以下のように加工しています。html_resolved/path/to/dir/file.htmlの最後の部分を取り除いて、親ディレクトリまでのパスのpath/to/dirにする。各パートを..に置き換えて、../../../..という文字列にする。html_resolvedディレクトリの分だけ余計に上位を指しているので、最初の..を.に置き換えて./../../..という文字列にする。先頭の./..は冗長なので、../にする。3の手順は前述のファイル配置に依存してこうなっているので、違うファイル配置にする場合は適宜読み替えて下さい。していますが、これは以下のようなパターンを判別し分ける事を意図しています。→何もしない1番目と3番目を見分ける必要があるので、パスの最初の1文字の/だけでなく、その次の2文字目まで含んだ正規表現としています。変更が無かったファイルの処理をスキップするここまでの手順でアップロード前に共通パーツの埋め込みや絶対パスの解決を行うようにした結果、前処理にそこそこ手間がかかるようになってしまいました。毎回全ファイルに前処理を施しているとキリがありませんので、前処理を施す前のソースファイルに変更があったときだけ前処理をやり直すようにします。先のスクリプトではアップロード用のファイルを毎回すべて用意し直していましたが、前回用意したファイルに対してrsyncで差分更新を行えば、ソースが更新されたファイルだけ前処理をやり直す事ができるはずです。./html_resolved/...rsyncはローカルとリモートの間でのミラーリングに使う事が多いですが、実はこんな風にローカルのファイル同士にも使えるのでした。--archiveだけだと、ソースと前処理済みのファイルの最終更新日時が「違う」という理由で処理済みファイルが上書きされてしまいます。--updateを明示的に指定して「処理済みファイルの方が新しい場合は何もしない」「処理済みファイルの方が古い場合は、ソースの方が更新されたという事で、前処理をやり直すためにコピーする」という動作になるようにしています。ソースから消えたファイルをアップロード用ファイルから取り除くため--deleteも指定しています。先の手順で「変更があったファイルだけを対象に前処理を行う」ようになりました。前処理の仕方そのものに手を加えたり、埋め込み用のパーツの方に変更を加えた場合には、各ページのファイルの方には変更が無いため前処理がスキップされたままになってしまうという問題があります。一応、html_resolvedを削除すれば強制的に全ファイルに前処理をやり直せますが、画像などのファイルまでアップロードし直す事になってしまうので効率が悪いです。何か良い方法は無いでしょうか?先程「--archiveだけだと、ソースと前処理済みのファイルの最終更新日時が『違う』という理由で処理済みファイルが上書きされてしまいます(なので--updateも指定する)」と書きました。いう事は、--updateをrsyncに指定しなければ、ここで期待される「前処理済みのファイルだけをソースで上書きして、前処理をやり直す」という事が可能になります。いうことで、rm等のコマンドで一般的な-f(強制実行)オプションに倣って、この前処理用スクリプトにもgetoptsを使って-fオプションを実装してみます。既定の状態では、`--update`を指定する。`-f`が指定された場合、先の変数の値を空文字にする。`--update`を直接書く代わりに変数の値を使うようにする。./html_resolved/...もっと効率よくしたい場合には「変更があった埋め込み用パーツを参照しているソースファイルを検出する」という事をする必要があります。今の所は上記のやり方で必要十分なので、これ以上の事をするつもりはありませんが、興味のある方は挑戦してみてはいかがでしょうか。まとめ最終的に完成した前処理用のスクリプトとアップロード用のスクリプトは、以下の通りです。アップロード前に必ず前処理をやり直す。ソースの方ではなく、前処理済みのファイルの方をアップロードする。exit"ちゃんとしたCMSを使えばこういう事をわざわざ自分でやる必要も無いのですが、静的なページで済む物を月額129円でミニマムに公開するだけなら、こんな感じで管理の手間を軽減できますよ......という事例のご紹介なのでした。「シス管系女子」について最後にシス管系女子の事も改めてご紹介します。自分は2011年から日経Linux誌上で「シス管系女子」というケーススタディ形式の解説マンガ記事を連載させて頂いています。本編では主にSSH接続でLinuxのサーバーにリモートログインして操作する場面を想定して、各種コマンドの使い方を解説しています。その延長線上として、それらのコマンドの実行手順をシェルスクリプトにして自動処理するという事にも挑戦しています。動作のイメージを絵で表現するように心がけていますので、この記事でやっているような「いろんな手法を組み合わせて、やりたい事を的確に実現する」という事をする上で有用な、丸暗記ではない・より深いレベルでの理解を得られるはずです。良かったら、お近くの大きめの書店でシス管系女子の本や掲載誌を手に取ってチラ見して頂けましたら幸いです。それ以外にも、Twitterのみんとちゃんbotアカウントでイラスト6や本編に入りきらなかった小ネタを流したり、Webサイトの方にも連載や本では扱わなかったもっと基礎的な話の特別編を置いていたりします。Calendarも公開中です。いうことで、さくらのアドベントカレンダー(その2)の4日目でした。X)でもこの方法をそのまま使えるかもしれません。実際にそういう由来で名付けられたのかどうかまでは分かりませんでした。↩ただしこのままだとマッチングパターンの中にパス区切りの/があった時に破綻するので、それぞれのマッチングパターンを囲むのに/以外の文字を使うように気をつけています。rとdについては\;マッチングパターン;dのような要領で明示的に;を指定しています。↩そのうち何でもawkでやるようになってしまう、というのが怖いのです......あと、シス管系女子の内容の実践という事も意識しているので、なるべく本編で解説した技術だけでやりたいという思いもありました。upLoginユーザーは見つかりませんでした。