大乱闘障害復旧バトル2020/08/01-2020/08/02

ということで学科システムに障害が発生していました。具体的に言うと学科内から外に行けない、一部VMに学内からでもアクセスできないなどでした。

この障害は突然発生したものではなく琉大工学部で定期的に発生する計画停電によるものです。 原因としては3つあるL3スイッチのうち1つが故障。その結果としてネットワークが死んだという感じでした。 今回の計画停電はエレベーターも破壊したのでなにか良からぬ事をした可能性があります。

これはそのときの解決のログです(記憶力が無いので間違ってるかも...)

2020/08/02

8:50

停電発生

id:anatofuzが学科サイトを静的にダウンロードするツールをテストしていたところ、突如ホストの応答が無くなる。 mattermostに「停電したわw」的な内容を書き込む。

だいたい同時刻

id:unimarimoが悪い予感がしたらしく、停電対応でサーバー室に入る。 時既に遅しで既にサーバーは死んでいたらしい。この時に新規システムにつないでたケーブルを抜くのを忘れる。

9:40

mattermostにフラグを貼る

f:id:anatofuz:20200803205109j:plain

16:00

id:anatofuzが17時に復電しそうだなと思いサーバー室に到着。時既に遅しで既に復電しており、エアコンが付いていない30度の部屋の中サーバーが元気に稼働している。

基幹システムはa,b,c,dの4台あるが、bとcだけなぜか立ち上がっていた。新規システムはつないでいた2台が起動していた。

とりあえず新規システムを落としつつエアコンをいれる。このときサーバー室から変な音がする。

予備のエアコンも稼働して冷えたのでaとd起動する。起動した所livbirtdが立ち上がってないので一旦全員落とす。その後相変わらずlivbirtdが立ち上がってないのでsystemctlで立ち上げる。

16:25

kvmが立ち上がったのでVMを立ち上げていく。

立ち上げたが踏み台サーバーにssh出来ないなどの報告が出る。よくよく見ると学科のwebサイトも見れないわwifiが外に行けないわの症状が出ている。

8.8.8.8にpingを飛ばした所外に行けてなさそう。

17:00

id:unimarimoと合流。仲良くサーバーの調子を見る。 学内ネットからだと学科サイトが見れそう。内向きDNSは元気そう。

systemctlでstatusを確認していたらnetwrokが一部立ち上がってない。startしても変化が見られない。

ぐむ〜と言いながら色々試したがダメそう。エレベーターが動いてなかったので「電気が完全に復旧してないのでは?」みたいな話になる。工学部周辺が停電という話だったので総情(琉大のネットワークの大本を管理している場所)が停電していたら外にいけないよね〜と話し、一旦帰宅することに。

階段を降り1Fにつくとエレベーターに「故障しました」のガムテープが。ひょっとしたら電気は完全に復旧している?などと思いながらおなか空いたので一旦家帰ってご飯。

18:00

ご飯食べながら色々考えていたが、そもそも総情のオンプレで配信している総情のページは見えているのだからやはり電気は復旧している。電気がだめじゃなくて我々が悪そう.......。

外に行けないサーバーのことを「内向的な性格」などと呼びつつ大学に戻る。

18:50

内向きが解決できるかと思っていたがなんかアクセスできなくなっている。 大学戻った所4Fのwifiが死んでいた。

先日UTMのアップグレードをしたこともありUTMを疑うが、UTMは見る限り元気に稼働している。 VMの状態を確認するなら有線持ってくるか〜と言いながらUTMの下に目を向けると、L3スイッチ3つのうちの1つが息をしていないことが発覚。 f:id:anatofuz:20200803204750j:plain

ラックの後ろに回ると動いていないL3スイッチのランプが赤色に光っている… しかも焦げたような匂いが漂っている。

f:id:anatofuz:20200803204814j:plain

型番からググってマニュアルを見ると、PS OKが赤色の場合は出力が停止しましたとのことらしい。障害じゃん…。

念のために電源を抜き差ししても変わらず。こいつが原因そうだったので電源を抜いて業者さんとの連絡用のMLに投稿。翌日の9時から作業をすることに。

f:id:anatofuz:20200803205243j:plain

22:40

業者さんから「火災防止でスイッチのケーブル抜いといて」と言われる。抜いたか覚えてなかったのでid:unimarimoに頼んで抜いてもらう。

実は既に抜いていたのでただサーバー室まで歩いて行かせたムーブをした。

2020/08/03

9:10

業者さんとこんにちは。 コンソールケーブルは持ってきていただいた…。申し訳ない。

9:30~10:00

スイッチの電源を交換したりしたところ、相変わらず赤ランプだったので物理障害が確定。代替機の手配をしてもらいつつ今後の方針を決める。

L3スイッチ3つのうちに生き残った2つのスイッチに空いているポートがあったので、configを空いているポートに移す作業をすることになる。

その前に他のネットワーク機器が大丈夫かを調べた所、故障したスイッチに接続していた各研究室のフロアスイッチ及びEPS室に置いてあるスイッチがpingが通らないことが発覚。これらも治す必要がある。

またwifiアクセスポイントも赤色点灯しておりエラーが発生している。どうもwifiが全体的に調子が悪いらしい。

10:30?~

L3スイッチに接続していたフロアスイッチやルームスイッチの様子を見に行く。どうも自己防衛かなんかで落ちていたらしい。エレベーターやL3スイッチ、フロアスイッチまで障害が発生しているので計画停電で電気業者がなにかやらかした説が出てきた。一部スイッチの調子を見に行くためにid:unimarimoが脚立を借りに行く。ショムニっぽい。

各スイッチにはコンソール接続してconfigure terminalしたあとにshutdownno shutdownをl3に接属していたport-channleで実行して解決して回る。

研究室はこのコロナ騒ぎで人がいないのでマスターキーを借りる。しばらくしたらエレベーターの修理で使うらしいので別のマスターキーを借りに行くなどをした。

wifiアクセスポイントは再起動時はよくなったがしばらくするとエラーがでる。つらい

11:30~

スイッチの設定等は業者の方に頼みつつ、基幹システムの状況をリカバリしにいく。 radiusとかが立ち上がってないのを立ち上げていく。一部VMが立ちあがってもvirsh consoleでいけないので泣きながらshutdown。shutdownしても落ちないのでdestroy.........

wifiコンパネにアクセスしようとしたが相変わらずwifiが死んでいる。つらみ。

12:00~

疲れたのでお昼休憩。家帰ってご飯を食べる。

鮭を3切れ焼いた。

13::00~

頑張ってwifiアクセスポイントのwebコンパネにアクセスしようとする。 L3スイッチからVLANを切って有線でアクセスしようとしたが失敗する。

研究室のサーバーからアクセスしようとしたがこれも失敗。むむむ.....とか言ってたら突然wifiでアクセスできるようになった。webコンパネが表示しているスイッチなどの情報が少なすぎてウケるという感じ。まだどっかで障害が起きてそう。

基幹サーバー及びシステムともに、突然外部にいけるようになったりいけなくなったりする。 systemctlやnmcliなどを使っても原因が断定出来ない。基幹サーバーにtracerouteが入って無くて辛い気持ちになる

13:20~

業者さんが原因を特定。生き残ったL3スイッチ2台がともにActiveになっていたらしい。 いわゆるダブルアクト状態というもの。

L3スイッチは冗長化としてActive/Standbyの状態を持つ手法があるらしいが、両方Activeだとゲートウェイが2つ存在することになりコンフリクトするらしい。 この設定を正常にした所、ネットワークが改善され外に行けるようになった。

業者さんがスイッチの設定を書き換え、及び取り外しを行っている間に、相変わらずダメそうな各フロア/ルームスイッチを再起動して回る。

14:30

すべてのスイッチの設定を生存していたスイッチに移しスイッチ側の復旧は完了。

僕とid:unimarimoも基幹システムのmountやデーモンの障害を一通り解決し終了。 ネットワークでトラブっていたためか微妙にNFSが調子悪そう。まぁ治るか....。 代替機がきたタイミングで交換作業をしましょうという話になり今日は終了。

感想

ちょうど来月システム更新なんですが、システム更新前に障害が発生してウケるwという感じでした。 (今回代替機のL3スイッチは30日後に引き取られるスイッチになってしまった)

業者の方の対応力がすごくて、一緒にフロアスイッチを床から取り出す作業や、原因の特定、設定の書き換えまでしていただきました。ありがとうございます。

あとは一家に一台コンソールケーブルはあったほうがいいですね。シリアルケーブルも...。

あわせてよみたい

seeker-s-eye.blogspot.com

ie.u-ryukyu.ac.jp

流れに乗って @xtetsuji さんからの出題をやってみる

ということで流れに乗ってみました。

アルゴリズムそんな自信ないのでどうなんだろう.....。競プロerからなんか突っ込まれそう

gist.github.com

シグナルでプロセスが終了されるとぴえんするコマンド作った

最近YouTubeでPIENのプレイ動画をよく見るので作りました

pienの後に渡したプログラムがシグナルで死んだ場合はぴえんの歌が再生されます github.com

なんとなくRustの勉強も兼ねてRustで書いてます。

地味にbrewでもダウンロードできるようになってます。

$ brew tap anatofuz/pien
$ brew install anatofuz/pien

別途音源が必要なので音源配布サイトからダウンロードして、ホームディレクトリに.pien.mp3で置いておくと再生されます。

soundevotee.net

実装方法

音声を流すのはafplayを雑に叩くみたいな世界観です。そぼく.....

RustでのCLIの作り方は全然解ってなかったので、いいかんじのチュートリアルを読みながら勉強しました。

rust-cli.github.io

肝なのは外部コマンドの終了コードの取得ですね。 Rustの場合は外部コマンドの終了コードはいい感じにパターンマッチで取得できます。

doc.rust-lang.org

use std::process::Command;

let status = Command::new("mkdir")
                     .arg("projects")
                     .status()
                     .expect("failed to execute mkdir");

match status.code() {
    Some(code) => println!("Exited with status code: {}", code),
    None       => println!("Process terminated by signal")
}

この例だとNoneに分類されるやつらが全員シグナルで死んでるので、Noneの場合は音楽を再生すれば良いということになります。その為今回は入力で受け取ったコマンドの結果をパターンマッチし、シグナルで死んでたらafplayを実行するみたいな極めて単純な実装になってます。

こういう一発ネタみたいなコマンド作るのやはり楽しいですね。

apache -> nginx -> gitlab-ceのプロキシを止める

追記 2020/07/15

8080ポートはgitlabのrailsアプリケーションのポートなのだけど、ここに直接プロキシするのではなく、gitlabのHTTPのルーティングを行っているgitlab-workhorseの8181ポートに繋げないと駄目だった


自分が在籍している学科ではオンプレ上にgitlabを構築して運用している。

gitlab.ie.u-ryukyu.ac.jp

gitlabが動いているサーバーはオンプレ上に構築したKVMで動かしているVMなのだけど、歴史的理由でこのサーバーでは他にjenkinsやredmineなども動作している。

もともとredmine-> jenkins -> gitlabの順でサーバー上に構築されたこともあり、サーバー全体のwebサーバーはapacheで動いている。

問題がgitlabのinstallでgitlab-ceを使っていたのだけれど、gitlab-ceはデフォルトでgitlabにまつわるすべてのミドルウェアなどもインストールして構築してくれる。 具体的に言うとデータベースとしてPostgreSQL、webサーバーとしてnginxを同梱している。

現状の学科のgitlabは、このgitlab-ceに同梱されているnginxをポート8000番で起動させ、apacheからそのnginxにプロキシをしていた。つまりapache -> nginx -> gitlabの順でHTTPリクエストが伝搬 される。

具体的に見るとこういう感じだった。大変ですね。

$ lsof -i :8000
COMMAND   PID       USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
nginx    5769       root    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5770 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5770 gitlab-www   19u  IPv4 131528575      0t0  TCP localhost:irdmi->localhost:52234 (ESTABLISHED)
nginx    5770 gitlab-www   20u  IPv4 131524330      0t0  TCP localhost:irdmi->localhost:52192 (ESTABLISHED)
nginx    5771 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5771 gitlab-www   18u  IPv4 131526937      0t0  TCP localhost:irdmi->localhost:52196 (ESTABLISHED)
nginx    5771 gitlab-www   19u  IPv4 131530893      0t0  TCP localhost:irdmi->localhost:52244 (ESTABLISHED)
nginx    5772 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5772 gitlab-www   15u  IPv4 131530209      0t0  TCP localhost:irdmi->localhost:52230 (ESTABLISHED)
nginx    5773 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
httpd    7873     apache   16u  IPv4 131524319      0t0  TCP localhost:52186->localhost:irdmi (CLOSE_WAIT)
httpd    7874     apache   16u  IPv4 131527717      0t0  TCP localhost:52192->localhost:irdmi (ESTABLISHED)
httpd    8583     apache   16u  IPv4 131528766      0t0  TCP localhost:52198->localhost:irdmi (CLOSE_WAIT)
httpd    8831     apache   16u  IPv4 131525112      0t0  TCP localhost:52164->localhost:irdmi (CLOSE_WAIT)
httpd    9433     apache   16u  IPv4 131524337      0t0  TCP localhost:52196->localhost:irdmi (ESTABLISHED)
httpd   10272     apache   16u  IPv4 131524317      0t0  TCP localhost:52184->localhost:irdmi (CLOSE_WAIT)
httpd   11736     apache   16u  IPv4 131529561      0t0  TCP localhost:52244->localhost:irdmi (ESTABLISHED)
httpd   24102     apache   16u  IPv4 131528566      0t0  TCP localhost:52230->localhost:irdmi (ESTABLISHED)
httpd   24103     apache   16u  IPv4 131525592      0t0  TCP localhost:52188->localhost:irdmi (CLOSE_WAIT)
httpd   24111     apache   16u  IPv4 131530228      0t0  TCP localhost:52234->localhost:irdmi (ESTABLISHED)
httpd   24166     apache   16u  IPv4 131526971      0t0  TCP localhost:52200->localhost:irdmi (CLOSE_WAIT)

この構成であまり問題は無かったのだけれど、諸事情でgitlabのHTTPヘッダにAccess-Control-Allow-Originの設定をする必要が出た。現状の構成だとApache側でAccess-Control-Allow-Originを付与しておけば良いと思ったのだけれど、実際はapache -> nginxに伝搬される際に、nginx側でヘッダ情報が落とされることが判明した。さらにgitlab-ceが生成するnginxの設定ファイルでAccess-Control-Allow-Originを付与するのが結構めんどくさそうだった。

というわけでapache -> nginx -> gitlab-ceのプロキシを止めて、apache -> gitlab-ceに一本化したい。本来ならapacheやめてnginxにしたいのだけれど、redmineやjenkinsのプロキシ設定も変更する必要があり、やや工数的にめんどいので段階的にやることにした。

gitlab側の設定

まずはnginxを止めなければならない。

これらはgitlab側の設定ファイルgitlab.rbを編集することで停止できる。

若干違うけれど、gitlab公式の同梱されてないnginxを使う方法が参考になった。 docs.gitlab.com

とりあえずnginxの使用を止める

nginx['enable'] = false

この後に続く nginx['hoge']的な記述はすべてコメントアウトしておく。

末尾にこの2行を追加する(これは効果があるかどうかは不明)

gitlab_workhorse['listen_network'] = "tcp" 
gitlab_workhorse['listen_addr'] = "127.0.0.1:8181" 

apache

今まではこんな感じの設定だった

<VirtualHost *:443>
        ServerName gitlab.ie.u-ryukyu.ac.jp
        ProxyRequests off
        ProxyPass / https://127.0.0.1:8000/
        ProxyPassReverse / https://127.0.0.1:8000/
        SSLEngine on
        SSLProxyEngine on
        SSLProxyCheckPeerCN off
        SSLProxyCheckPeerName off
        SSLCertificateFile hoge.cer
        SSLCertificateKeyFile hoge.key
        SSLCertificateChainFile hoge.cer
</VirtualHost>

何も考えずにnginxにプロキシしていたが、apacheからgitlabに直接プロキシする場合はある程度設定を書き加える必要がある。

様々な人がブログエントリで設定例を書いていたが、最終的にはgitlab公式が出しているapacheの設定例が一番正しかった。

gitlab.com

httpsでやってることもあるので、この例の通りにやらないとリポジトリの中身が表示されないとか、LDAPログインした後にコールバックされないなどの悲しさが存在する。

ここまで設定したらgitrlabとapacheを再起動すればよい

gitlab-ctl reconfigure
gitlab-ctl restart
gitlab-ctl reconfigure
systemctl restart httpd

Akka実践バイブル読み始めた

追記

akkaとakkaHTTPを最新にしてみたら動いた

diff --git a/chapter-up-and-running/build.sbt b/chapter-up-and-running/build.sbt
index 1d6c95a..4e10c25 100644
--- a/chapter-up-and-running/build.sbt
+++ b/chapter-up-and-running/build.sbt
@@ -7,8 +7,8 @@ version := "1.0"
 organization := "com.goticks"

 libraryDependencies ++= {
-  val akkaVersion = "2.5.4"
-  val akkaHttpVersion = "10.0.10"
+  val akkaVersion = "2.6.6"
+  val akkaHttpVersion = "10.1.12"
   Seq(
     "com.typesafe.akka" %% "akka-actor"      % akkaVersion,
     "com.typesafe.akka" %% "akka-stream"     % akkaVersion,

最近Scalaやりたい感が強く、特にAkkaを使ってみたい気持ちがあったので「Akka実践バイブル」を図書館で借りて読み始めた。

Akkaが採用しているアクターモデルは今やっている研究にも応用できそうとかそういう意味合いもある。

Scalaあんま書いたことがないので、Scalaの勉強がてらみたいなところ。とりあえずsbtを使えば色々できるらしいということは解っている。

とりあえず第2章でサンプルコードがあったのでGitHubからcloneして動かした所見事に動かなかった。

日本語版のは本家のリポジトリをforkしており、それを実行すると次の様なエラーが出る。

github.com

[ERROR] Failed to construct terminal; falling back to unsupported
java.lang.NumberFormatException: For input string: "0x100"
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
        at java.base/java.lang.Integer.parseInt(Integer.java:658)
        at java.base/java.lang.Integer.valueOf(Integer.java:989)
        at jline.internal.InfoCmp.parseInfoCmp(InfoCmp.java:59)
        at jline.UnixTerminal.parseInfoCmp(UnixTerminal.java:242)
        at jline.UnixTerminal.<init>(UnixTerminal.java:65)
        at jline.UnixTerminal.<init>(UnixTerminal.java:50)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
        at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:166)
        at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:404)
        at java.base/java.lang.Class.newInstance(Class.java:591)
        at jline.TerminalFactory.getFlavor(TerminalFactory.java:211)
        at jline.TerminalFactory.create(TerminalFactory.java:102)
        at jline.TerminalFactory.get(TerminalFactory.java:186)
        at jline.TerminalFactory.get(TerminalFactory.java:192)
        at sbt.ConsoleLogger$.ansiSupported(ConsoleLogger.scala:123)
        at sbt.ConsoleLogger$.<init>(ConsoleLogger.scala:117)
        at sbt.ConsoleLogger$.<clinit>(ConsoleLogger.scala)
        at sbt.GlobalLogging$.initial(GlobalLogging.scala:43)
        at sbt.StandardMain$.initialGlobalLogging(Main.scala:116)
        at sbt.StandardMain$.initialState(Main.scala:125)
        at sbt.xMain.run(Main.scala:34)
        at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:111)
        at xsbt.boot.Launch$.withContextLoader(Launch.scala:130)
        at xsbt.boot.Launch$.run(Launch.scala:111)
        at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:37)
        at xsbt.boot.Launch$.launch(Launch.scala:119)
        at xsbt.boot.Launch$.apply(Launch.scala:20)

本家の方で動かすとぬるぽする

github.com

[info] Loading project definition from /Users/anatofuz/src/github.com/RayRoestenburg/akka-in-action/chapter-up-and-running/project
java.lang.NullPointerException
        at java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1770)
        at java.base/java.util.regex.Matcher.reset(Matcher.java:416)
        at java.base/java.util.regex.Matcher.<init>(Matcher.java:253)
        at java.base/java.util.regex.Pattern.matcher(Pattern.java:1134)
        at java.base/java.util.regex.Pattern.split(Pattern.java:1262)
        at java.base/java.util.regex.Pattern.split(Pattern.java:1335)
        at sbt.IO$.pathSplit(IO.scala:744)
        at sbt.IO$.parseClasspath(IO.scala:859)
        at sbt.compiler.CompilerArguments.extClasspath(CompilerArguments.scala:62)
        at sbt.compiler.AggressiveCompile.withBootclasspath(AggressiveCompile.scala:50)
        at sbt.compiler.AggressiveCompile.compile2(AggressiveCompile.scala:83)
        at sbt.compiler.AggressiveCompile.compile1(AggressiveCompile.scala:70)
        at sbt.compiler.AggressiveCompile.apply(AggressiveCompile.scala:45)
        at sbt.Compiler$.apply(Compiler.scala:74)
        at sbt.Compiler$.apply(Compiler.scala:65)
        at sbt.Defaults$.sbt$Defaults$$compileTaskImpl(Defaults.scala:789)
        at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:781)
        at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:781)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
        at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
        at sbt.std.Transform$$anon$4.work(System.scala:63)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
        at sbt.Execute.work(Execute.scala:235)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
        at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:830)
[error] (compile:compile) java.lang.NullPointerException

なんか初手で出鼻くじかれた感じあって辛ぽよですね。3,4年前くらいの本なのでしゃーない気もする。いい感じにパッチ当ててるリポジトリを探してみよう。

学科newsサイトのhugo移行への道のり 1

自分が在籍している学科(琉大工学部知能情報コース)は学科関連のお知らせをインターネット経由で流している。歴史的にはネットニュース(fj)時代から運用されており、その後wordpressに移行している。

現在はここから見れる

WordPressでの運用は元気に動いている間は良いのだけれど、数年前にplugin経由でGoogle検索結果を爆撃されるという事件があったりした。最近はDBのコネクションエラーが多発しており、ログを取って解析していても原因が追求できない。

更に追い打ちをかけるように学科ウェブはだいたいマルチサイト化されたWordPressで動かしているので、どこかでDBエラーなどが発生すると全滅するということになっていた。

まぁこれが辛いので静的サイト化するぞ!!!!! というのをid:unimarimoと話していたのだけど、最近のシステム更新のどさくさに紛れてシス管MTGで話したら河野先生も乗り気にできたので進めている。

静的サイト化!!!

静的サイト化としては色々方法があって、nuxt.jsを使う方法などを発見していた。 とはいえ学科のメンバーとwebサイトを管理しているシス管メンバーが毎年コンスタントにnuxt.jsのことが解るようになるかと言われると微妙なところがあった。

まぁこれはメンバーと主に僕の趣味で、バイナリをおいておけばどうにかなるhugoを使ってmarkdownから変換する形式にしていこうとなった。

静的サイトのつくりかた

ということで今考えている構成はこんな感じ

  • markdown化したnews-ieリポジトリを学科gitlabにたてる
  • 投稿したい人がなにかでリポジトリmarkdown等をpushする
  • jenkinsかgitlab ciが頑張ってhugoを動かしてhtmlを生成する
  • どうにかしてそのファイルをwwwサーバーに送りつける

ci移行のとこと投稿に関しては手探りではあるのだけど、なんとなく行けそうな気がする。

news-ieは他に学科アカウントが無いと閲覧できないという機能があって、今まではwordpressのpluginで管理していたのだけれど、これはnginx側で制御することにしたい。ついでに今はwwwサーバーはapacheなのでapacheからnginxへの移行もやってしまう。

手順?

基本的には現在のnews-ieのエントリをすべて回収してくる必要がある。 賢い人はいるみたいですでにwordpress pluginでhugoで使えるmarkdown形式に変換した上でzipダウンロードしてくれるやつがあった。

github.com

管理コンソールでこのpluginを使ったところ、本当に動いているか怪しいほど時間はかかった。 1分以上立った後で、zipが無事ダウンロードされたのだけれど、ちゃんと添付ファイル込でmarkdownにしていたのでかなり良かった。

とりあえずこれで今までの投稿のmarkdownが手に入ったので、後は頑張ってhugoでhtmlを組み立てていくフェーズとなる。続きはまた...

golangのldflagsに引っかかった

golangにはビルド時のリンク関連の制御のldflagsオプションがある。

ldflagsを利用するとビルド時に変数の埋め込みなどが出来るのだが、これで引っかかった。

やろうとしていたことは次の様なコード。

環境変数の代わりにldflagsで変数埋め込みを行い、実行する場合は環境変数の設定を無くしたかった。

package main

import (
    "fmt"
    "os"
)

var envtest string = os.Getenv("ENVTEST")

func main() {
    fmt.Println(envtest)
}

実際にこれを go build -ldflags "-X main.envtest=goodnight" ./main.goの様にldflagsを使って変数埋め込み行いビルドした。

環境変数を設定していない状態で、生成したバイナリを実行するとこの様な結果になる。

$ ./main

何も表示されなかった。つまりenvtestENVTEST環境変数をロードしにいっていることになる。 とはいえstringsコマンドをかけてやると普通に出てくる。どういうことなの。

$ strings ./main | grep goodnight
goodnight

え〜と思い代入文を外して実験した。

package main

import (
    "fmt"
)

var envtest string

func main() {
    fmt.Println(envtest)
}

これで全く同じ方法でビルドした。

$ ./main 
goodnight

埋め込まれてる!!!!!!!!!!

ネタバラシ

ドキュメントを見たところちゃんと書いてあった

-X importpath.name=value

Set the value of the string variable in importpath named name to value. This is only effective if the variable is declared in the source code either uninitialized or initialized to a constant string expression. -X will not work if the initializer makes a function call or refers to other variables. Note that before Go 1.5 this option took two separate arguments.

「This is only effective if the variable is declared in the source code either uninitialized or initialized to a constant string expression.」とのことだったので、ちゃんとドキュメントは読もうな.....

ちなみに開発環境では環境変数使って実行環境では使いたくないときはどうするべきなんだろう。。。 initで空文字列かどうかを判定して、空文字列だったら環境変数から取るとかにするのがいいのかな。