Quantcast
Channel: 文系プログラマによるTIPSブログ
Viewing all 140 articles
Browse latest View live

低スペック環境とプログラマの関係

$
0
0

密接な関係です。
f:id:treeapps:20161022211353p:plain

いきなりですが、

プログラマに低スペックな環境を与えてはいけない

と私は考えています。

逆に、もしプログラマを苦しめたいなら、低スペックPCや貧弱なネット回線を与えると、簡単に苦しめる事ができます。

低スペック環境がもたらすこと

低スペック環境の定義

厳密な低スペックの定義など無いのですが、今回は仮に以下を低スペックと定義します。

  • ストレージがSSDではない。
  • メモリが16G以下である。
  • モニタが24インチ以下である。
  • ネットワークの回線が貧弱である。
  • 無線LANがプツプツ途切れる。
  • 机にパーティションが無い。

SSDでないことによる弊害

OSの起動やアプリケーションの起動などが非常に遅いので、「ま〜だ起動終わりませんかね〜?」と、「ボーッと画面を眺める時間が増える」ことになります。これが非常に勿体無い。

ボーッと眺めている間に別の作業をしたら?と考えると思いますが、別の作業をすることで更に負荷がかかり、トータルすると個別に処理が完了するのを待つ時間よりも多くの時間がかかってしまう事になりかねません。そうすると、すこしでも生産性を上げようとしたことが逆に生産性を下げる原因になってしまうことさえあります。

SSDの性能ですが、最近のSSDは昔のSSDのようにプチフリしたり寿命が極端に短い事も少ないし、読み込み・書き込み速度も飛躍的に向上しているので、快適になることは間違いありません。

メモリが16G以下であることの弊害

8Gで十分じゃない?という環境もあるかもしれませんが、8Gって結構ギリギリだと感じています。IDEやアプリケーションサーバに少し多くメモリを割り振ったら使い切ってしまうくらいの容量です。最低16Gあれば、プログラマが色々な事にチャレンジする事ができると思います。

例えば、ローカルでDockerやVagrantで仮想環境を構築しようとすると、それだけでドカッとメモリが奪われます。一番最悪なのは、「DockerやVagrantを使いたい。でもメモリが足りなくてそれができない。だから新規案件でもDockerやVagrantを採用しにくい。」という事が起きてしまう事です。

メモリが少ないという理由で生産性や利便性を向上するための手段が奪われ、可能性が潰されている。」というのは最悪極まりないですね。こういう状況に置かれている人って、結構いるのではないかと想像しています。声を上げないだけで、潜在的には沢山いそうに思えます。

他にも、macを使っている方で、ローカルでVirtualboxやVMWareFusionで、modern.IEの仮想イメージを使ってIEやEdgeで動作確認したい、でもメモリが足りなくてそれができない、という例もあります。
Free Virtual Machines from IE8 to MS Edge - Microsoft Edge Development
modern.IEは割り当てるメモリ容量が仮想環境上のOSのパフォーマンスにダイレクトに影響するので、2G程度の割当ではWin10の起動時間にかなり時間がかかってしまうので、最低4Gのメモリを割り当てたいところです。

モニタが24インチ以下であることの弊害

画面が狭いと表示できる情報が減るので、Control + tab等でのウインドウの切り替えの回数が増えます。一瞬で切り替わるじゃん?と考えると思いますが、問題なのはその回数です。塵も積もれば山となります。1回の動作が数秒だとしても、それが数千・数万回行われれば、それなりの時間の浪費になります。

また、ブラウザ+開発者ツール、タブ分割したターミナル、slackやhipchat等のチャットアプリ、など、ウインドウの切り替え無しで全ての情報が閲覧可能な状態にあると、生産性が向上しやすくなっています。

ネットワークの回線が貧弱であることの弊害

ファイルのダウンロード速度や、アップロード速度が低いと、やはり待ち時間が増えてしまうので、ボーッと眺める時間が増えます。

それが社内だけでの話で収まるならいいのですが、社外に及ぶと最悪です。例えば「社内にビルドサーバがあって、そこから本番環境やS3等にファイルをアップロードする」という場合は、社内からインターネットへのアップロード速度が遅いと、デプロイする時間に影響します。お客さんから「なんかデプロイ時間長くない?何にそんな時間かかってるの?」と言われてしまった時に、「いや〜、実は転送速度がくっそ遅くて、デプロイ時間の95%は転送時間なんですよね〜 hahaha !!」なんていったら大変なことになってしまうでしょう。

無線LANがプツプツ途切れることの弊害

これはストレス溜まりますね。

気持ちよく開発しているかと思ったら、プツッ。

無線LANがプツッっと途切れると、プログラマの集中もそこでプツッと途切れます。

有線LAN最強説を唱えたいところですが、ケーブルに足を引っ掛けたりする可能性が無いとはいえないし、物理的にモノが減った方が管理するモノが減って皆嬉しくなるように思えます。

机にパーティションが無いことの弊害

よく「パーティションが無い方が周りとコミュニケーションが取りやすい」と言う方がいますが、私の個人的な意見を言うと、「左右にパーティションは無くてもいいけど、正面にはあって欲しい」と思っています。正面の人や斜めの人達と視線があったりする事が非常に嫌だし、想像以上に集中力に影響を与えます。左右はなかなか視線が合うものでもないし、隣の人とは体の向きを変えて直接口頭で話す機会は沢山ありますね。

例えば、↓この環境で1,2の人達が3,4の人達と話そうとするのではなく、

人1人2
人3人4

↓3,4の人は5,6の人と話す事になるような座席にすると、視線云々で集中力が途切れる事も減りそうなんですが、どうですかね。

人1人2

////////////////// ← パーティション

人3人4

 

人5人6

////////////////// ← パーティション

人7人8

確証は無いのですが、ネット上にはパーティションが無い職場は生産性が落ちる傾向がある、といった情報を沢山見るし、私自身も前の席の人達と視線が合うと嫌ですね。今の環境がパーティションが無くて、どうしてもパーティションが作れない場合は27インチディスプレイ等をパーティションの代用とするしかなさそうです。

どうやって低スペックを見分けるか

簡単です。ボーッと画面を眺めて止まっている時間が長いかどうかで見極める事ができます。

例えば、「いや〜、負荷で画面がフリーズしちゃって、今操作受け付けないんですよ〜♪♪♪」等と、奴隷の鎖自慢のような自虐を言い始めたら危険です。

他にも、「いや〜、APサーバの起動に時間がかかるので、なるべく再起動しないよう工夫してるんです〜」等と、本質的な工夫ではない事を言い出してもアウトです。

プログラマの中にはこういった事を自虐的に喜々として語る人がいたりするので、笑ってないで解決に導くべきですね。

奴隷の鎖自慢、ダメ。ゼッタイ。自慢するのは凄いモノを開発できた時や、何か問題を解決できた時にしましょう。

雑感

この記事を書いた理由なのですが、以下の記事を読んだ事がきっかけとなっています。
togetter.com
この記事、とても面白いです。集中力が2倍に上がったことをどう判断してるんでしょうね。集中力が2倍になると、何故生産性が2倍になるんでしょうね。不思議な世界があるんですね。


↑これも面白いです。多分ちゃんと計算した事が無いのだと思います。。。

従業員が100人いたとすると、
人数が20%減る → 100 * 0.8 = 80
20%向上させる → 80 * 1.2 = 96

100%に戻ってませんね。こういう人は例えば株価に対しても「株価が50%減になってしまった。50%増にして元に戻そう!」とか言いそうで怖いです。

現在の株価を100円とすると、
50%減少 → 100 * 0.5 = 50円
50%増加 → 50 * 1.5 = 75円

50%減少したなら、現状から100%増えないと(2倍にならないと)元に戻りません。 この頭脳だと「50%増くらいなら現場の工夫で達成できる筈だ!」とか言いかねませんね。本人は50%増のつもりなのに、従業員は現在の2倍の成果を求められ、耐えかねた従業員が次々逃亡してしまう、と。その結果、「一体何故なんだ。何故目標が達成できないんだ。私についてこれる人間が中々いないという事か。フッ・・・」のように考え、それを延々と繰り返している例もありそうで怖いです。


話は戻りますが、もしプログラマの生産性を上げたいなら、給料を上げるよりも、単純に環境を向上させると上がりやすいと思います。従業員が全体的にスキルアップする事による生産性向上が理想ではありますが、それは時間がかかるし向上する保証がありません。環境の向上であれば、100%生産性が上がる保証が有るというわけではありませんが、少なくとも各人の能力に関係無く待ち時間を減らす事は確実にできます。

「集中力を2倍高めて生産性を2倍にする」と言っている人達や、算数ができない人達を雇うことやめれば、そういった費用の捻出もしやすいでしょうし、今一度本当に必要なものと必要でないものの整理が必要になりそうです。


DockerとSeleniumGridとNightwatchでE2Eテストを始めよう

$
0
0

Dockerで簡単に始められますよ〜
f:id:treeapps:20161124012643p:plain

皆さんはE2E(end to end)テスト、してますか?

今回はDocker、Selenium grid、Nightwatchを使ったE2Eテスト(ブラウザテスト)の環境構築からテスト実行、VNCでテスト実行の様子を確認するところまでやってみようと思います。

技術要素

  • Selenium Grid v3.0.1
  • Nightwatch v0.9.9
  • docker v1.12.1
  • node.js v7.2.0

なぜNightwatchなのか

沢山あるE2Eテストフレームワークの中で今回Nightwatchを選択したのですが、理由は以下の通りです。

  • javascriptでテストを記述できること。
  • 使い方が簡単であること。
  • 環境構築が楽であること。
  • 単独で動くこと。
  • assertの仕組みが内蔵されていること。
  • 高機能でなくていいので、コード記述量が少なく済むこと。

ざっくり言うと、「簡単に環境構築できて、簡単に動作すること」です。

まず、記述言語はjavascriptに限定しました。以前業務でjava + selenium webdriverでゴリゴリしていましたが、辛いです。あれは辛いです。いちいちコンパイルが必要だったり、環境面も優しくありませんでした。もっとカジュアルに記述できて、コンパイル無しで動くものにしました。

Protractorもいい感じに見えたのですが、Angular.jsのために作られたものだそうで、個人的には特定のFWに依存していたりする事が嫌だったのと、nightwatchと比較するとコード記述料が多いため、見送りました。

nightwatchであれば、nightwatch本体もchromeドライバーもfirefoxドライバーもselenium-standaloneも、全て「npm install」一発で環境を用意する事ができます。テストもjsでさっくり記述でき、コンパイル無しで即実行可能なので、相当楽に動かす事ができます。

環境構築

Dockerが無い時代はSelenium Gridの環境構築は非常に難しく、メンテナンスコストも高いものでした。

しかしDocker時代となった今、大変簡単に環境構築ができてしまいます。

dockerとnode.jsさえあればすぐテスト実行できちゃいます。

docker

docker for mac か docker for windowsをインストールして下さい。

[mac] https://docs.docker.com/docker-for-mac/

[windows] https://docs.docker.com/docker-for-windows/

node.js

macならnodebrew、windowsならnodist等を使い、node.jsをインストールして下さい。

for mac
nodebrew install-binary v7.2.0
nodebrew use v7.2.0
nodebrew alias default v7.2.0
for windows
nodeist stable v7.2.0
nodeist use v7.2.0
nodeist alias default v7.2.0

テストの準備

nightwatchを実行するための準備をします。

dockerコンテナ

最初はmac上でdocker-machineを使わずにdocker-compose upしてみたのですが、以下のように127エラーが起きてしまいました。linux(Amazon linux)上ではdocker-machine無しで上手くいきました。

tree:test tree$ docker-compose ps
                 Name                             Command             State          Ports
------------------------------------------------------------------------------------------------------
seleniumgridnightwatchexample_chrome_1    /opt/bin/entry_point.sh   Exit 127
seleniumgridnightwatchexample_firefox_1   /opt/bin/entry_point.sh   Exit 127
seleniumgridnightwatchexample_hub_1       /opt/bin/entry_point.sh   Up         0.0.0.0:4444->4444/tcp

エラーが出てしまうので、ちょっと嫌ですがdocker-machineで専用ホストを作成します。

$ docker-machine create -d virtualbox selenium
$ eval$(docker-machine env selenium)

vmwarefusionを持っている場合、-dの引数を「vmwarefusion」に変えて下さい。vmwarefusionの方が1.5倍程高速です。

git clone

例によって既にすぐテストできるプロジェクト一式をgithubにアップしています。

git clone https://github.com/treetips/selenium-grid-nightwatch-example.git

github.com

node_moduleの依存

package.jsonを用意してあるので、以下のコマンド一発で依存モジュール類が揃います。今回はnightwatchはglobalにインストールせず、ローカルにインストールする形にしました。

$ npm install

nightwatchの設定

nightwatch.conf.jsを開き、「selenium_host」にdockerのホストを設定して下さい。ホストは以下のコマンド確認することができます。

tree:selenium-grid-nightwatch-example tree$ docker-machine ip selenium
172.16.53.136

docker-hub

docker-hubのイメージを使用するので、アカウントが無い方はアカウントを作成して下さい。
https://hub.docker.com/

続いて、初回docker pull実行時はログインを求められるので、先に以下のようにdocker loginでログインしておきます。

tree:test tree$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.Username (treetips): treetipsPassword:Login Succeeded

ログインが成功すると、認証情報が以下の場所に保存され、以降はdocker loginは不要になります。

tree:test tree$ ll ~/.docker/config.json
-rw-------  1 tree  staff   188B 112319:48 /Users/tree/.docker/config.json

Nightwatchでテストを実行する

ローカルでテストする場合

ローカルの場合はいちいちVNC経由で動作を確認したくないので、selenium-standaloneを使ってローカルにインストールされたchrome,firefoxを直接実行したいですね。

そういう場合は以下のように実行して下さい。

$ npm run local

もしchrome、firefoxを単独で実行したい場合は以下のようにして下さい。

# test for chrome
$ npm run local-chrome

# test for firefox
$ npm run local-firefox

Selenium Gridで並列テストする場合

Selenium Gridを使って並列で一気にテストする場合は以下のようにして下さい。

dockerコンテナの起動

まず、Selenium Gridのdockerコンテナを起動します。

$ docker-compose up -d
Selenium Gridの動作確認

ブラウザから起動を確認します。
http://${dockerホスト}:4444/grid/console

以下ように起動しているノードが全て表示されます。
f:id:treeapps:20161124001158p:plain

Nighwatchを実行する

$ npm run grid

これで、Docker上のSelenium Gridに対して、chromeとfirefoxのブラウザテストを並列実行する事ができます。

Seleniumの実行状況をVNCで確認する

ローカルの場合はローカルにインストールされたchromeとfirefoxが起動するので、実行している様子を確認する事ができました。しかしSelenium Gridの場合は、linux等のサーバ上のchrome・firefoxが起動されます。サーバ側で実行されているchromeとfirefoxを閲覧するため、VNCを利用します。

幸い今回使用するchromeとfirefoxのdockerイメージは、最初からVNCサーバがインストールされているものを使っているので、VNCクライアントさえあればすぐ確認できます。

macの場合は、Finder ->メニューバーの「移動」 ->「サーバに接続」をクリックすると、以下のように表示されます。
f:id:treeapps:20161124002454p:plain

テキストボックスに以下の2つをそれぞれ入力し、接続先を追加します。
vnc://${dockerホスト}:4501 (firefox用)
vnc://${dockerホスト}:4502 (chrome用)
(4501, 4502は、docker-compose.ymlのports要素で指定しています)

接続ボタンをクリックするとパスワードが聞かれます。パスワードは「secret」です。
f:id:treeapps:20161124003317p:plain

起動すると、以下のように表示されます。この状態でnightwatchでテストを実行すると、ここにブラウザが表示され、画面遷移する様子が視覚的に確認できます。
f:id:treeapps:20161124002950p:plain

実際にnightwatchを実行し、VNCでそれを確認した様子は↓こんな感じです。
f:id:treeapps:20161124004154g:plain

コンソールには、以下のようにカラフルに結果が表示されます。
f:id:treeapps:20161124005647p:plain

VNCクライアントを2つ(firefox用とchrome用)起動しておけば、両方同時に確認する事ができます。

もしnightwatchの実行時にエラーが発生した場合は、「selenium-grid-nightwatch-example/screenshots/test/Display-yahoo-->-search-->-assert_FAILED_Nov-24-2016-004001-GMT+0900.png」のようにスクリーンショットが保存されます。

雑感

Selenium Gridを初めて使ってみたわけですが、以外な程あっさり環境構築できました。Nightwatchについても環境構築は簡単でした。

しかしSeleniumでブラウザを操ると、ローカルでは問題無いのに、Gridの場合だけ特定ブラウザだけクラッシュしたりする事があったりしました。もしかしたらlinux版のfirefox・chromeの問題なのかもしれませんが、定かではありません。正直seleniumは扱いが難しく職人芸的な面もあるので、あまりやりすぎるとメンテできなくなって使われなくなる(実体験)ので、程々にするとよいです。

ちなみに私の場合は正直ほとんどメンテしたくないと考えているので、とりあえずTOPから目的の画面までの画面遷移のみしかテストしていません。

こういうテストってカジュアルさが重要なので、java + WebDriverでゴリゴリするのに抵抗がある方は、まずはDocker on Selenium Grid + NightwatchでライトにE2Eテストしてみると、どれくらいメンテが簡単・大変なのかの感覚が掴めるので、是非試して見る事をおすすめします!

GAE/GOでgoapp serveした時に起きるThere are too many filesに対応する

$
0
0

ファイル数が多すぎるとエラーになるんですよね〜
f:id:treeapps:20160521191008p:plain

GAEで開発している時に、node.jsを使いたい時ってありますよね。
普通にnpm init してnpm installして開発していくわけですが、例えばGAE/GOで開発中にgoapp serveすると以下のエラーが発生する場合があります。

tree:tree-maps-go tree$ goapp serve
INFO     2016-12-0512:41:32,635 devappserver2.py:756] Skipping SDK update check.
INFO     2016-12-0512:41:32,731 api_server.py:205] Starting API server at: http://localhost:62066
INFO     2016-12-0512:41:32,734 dispatcher.py:197] Starting module "default" running at: http://localhost:8080
INFO     2016-12-0512:41:32,736 admin_server.py:116] Starting admin server at: http://localhost:8000
/usr/local/go_appengine/google/appengine/tools/devappserver2/mtime_file_watcher.py:129: UserWarning: There are too many files in your application for changes in all of them to be monitored. You may have to restart the development server to see some changes to your files.
  'There are too many files in your application for '

何が起きているかというと、goapp serveによってファイル監視が行われるわけですが、監視するファイル数が多過ぎるため、警告が表示されています。

特にnpm installすると、node_modules以下に大量の依存モジュールが配置されてしまい、goapp serveがすぐギブアップしてしまいます。

現象が起きる環境

状況が再現するSDKバージョンは以下の通りです。

tree:tree-maps-go tree$ goapp version
go version go1.6.3 (appengine-1.9.46) darwin/amd64

このSDKバージョンは最新に近いバージョンなのですが、このバージョンでもこの警告が発生します。

解決法

まず、警告が出ているファイルはmtime_file_watcher.pyなので、mtime_file_watcher.pyを修正します。

修正対象ファイル

$APPENGINE_HOME/google/appengine/tools/devappserver2/mtime_file_watcher.py

修正前

for dirname, dirnames, filenames in os.walk(self._directory,
                                                followlinks=True):

      if self._quit_event.is_set():

修正後

for dirname, dirnames, filenames in os.walk(self._directory,
                                                followlinks=True):

      if'/node_modules/'in dirname: continueif self._quit_event.is_set():


これでnode_modulesフォルダの監視がスキップされ、次回goapp serveした際に警告が表示されなくなります。

もしnode_modules以外で同様の現象が起きてしまう場合、or句で複数条件を記述して下さい。

node_modulesをデプロイ対象から外す

これでgoapp serveできるようになりましたが、このままではgoapp deployした時にnode_modulesフォルダもデプロイ対象に入ってしまいます。GAEはデプロイできるファイル数に上限があるので、これはいただけません。

そんな場合は、app.yamlに以下の記述を追加すると、node_modulesディレクトリをデプロイ対象から外す事ができます。

skip_files:- ^(node_modules/.*)

FF15の初週69万本の悲しみと旧作の振り返り

$
0
0

全盛期のスクエニを知っている自分としては非常に悲しいですね (´・ω・`)
f:id:treeapps:20161208184051p:plain
www.4gamer.net

FF15が発売され、ついに販売数が出ました。

今回はまじめな話ではなく、私にとってのFFの昔と今を思い返す記事となります。

気になる販売数は・・・!?

気になる販売数は、 690,471本とのことです。ミリオン割れのようです。

ちなみに過去作の販売数は以下のようです。

FC FF1 52万本 
FC FF2 76万本 
FC FF3 140万本 
SFC FF4 144万本 
SFC FF5 245万本 
SFC FF6 255万本 
PS FF7 初週202万本 累計328万本 
PS FF8 初週250万本 累計369万本 
PS FF9 初週195万本 累計282万本 
PS2 FF10 初週183万本 累計248万本 インタ版+約30万本 291万出荷 
PS2 FF12 初週184万本 累計231万本 インタ版+約13万本 
PS3 FF13 初週151万本 累計193万本 ベスト版含 
PS4 FF15 初週69万本

直近のFF13と比較してFF15は販売数が大幅減

FF13 2009年12月17日発売 初週151万6532本
FF13が発売された時のPS3週間売り上げ 24万5406台
PS3は2009年12月13日に本体売上400万台突破らしい

FF15 初週69万471本
FF15が発売された時のPS4週間売り上げ 11万318台
PS4は2016年11月に本体売上350万突破したらしい

これはとあるまとめサイトに書かれていたもので信憑性の程は解りませんが、もし本当だとして、本体を1人1台所有し、1人1本購入するものとする場合以下のようになります。

FF13の場合

本体4000000台 ÷ ソフト1516532本 = 2.63
本体所有者のうち、2.63人に1人はFF13を購入しています。

FF15の場合

本体3500000台 ÷ ソフト690471本 = 5.06
本体所有者のうち、5.06人に1人しかFF15を購入していません。


単純計算すると、FF15はFF13と比較して、2倍買われなくなった事になってしまいますね。これは結構厳しい結果です。本体を持っているにも関わらず、購入に至らないケースが増加してしまっています。

何故売れなかったのか

正直私はマーケティングだとか、その手の話には疎いので難しい事は解りませんが、ぱっと思いついたものをいくつか書いてみます。

キャラクター面

  • 黒尽くめのホスト集団に感情移入できなかった。
  • ホスト達がチャラ過ぎた。
  • ホスト達の会話が寒すぎた。
  • キャラクターが喋ることに違和感を感じた。
  • キャラクターと声優のミスマッチを感じた。
  • リアル過ぎて違和感を感じた。
  • 腐女子向け?と嫌悪感を感じた。

ストーリー・システム面

  • ストーリーが良くなかった。
  • ゲームシステムが良くなかった。
  • オープンワールドを活かせてなかった。
  • やりこみ要素が少なすぎ・多すぎた。
  • バグが多すぎた。
  • FF13のせいでFFブランドのイメージが損なわれた。

ハード・ソフト面

  • 発売されるのが遅すぎて興味が薄れた。
  • ソフトの価格が高かった。
  • PS4を持っていなかった。
  • もう据置型ゲームの時代ではないと思ったから。

ユーザ層

  • 小中高大学生がPS4を購入していないから。
  • デカイ据え置き機を所有する事が恥ずかしいから。
  • スマホゲーに夢中だから。
  • 映画のような映像クオリティは求めてないから。

間違っているものも多々あると思いますが、ざっとこんな感じの事が想像できます。

本体販売数はFF13とFF15で大きな違いはないのに69万本しか売れなかった、さて、本当の原因は一体何なのでしょうね。FFシリーズはPlaystationの代名詞とも言えるタイトルで、売上も1、2を争うものなので、FF15が69万本となると、PS4は頑張っても初週69万本しか売れません、という事に繋がりかねませんね。

PS4の限界はここまで、という事にならないように、原因の究明などを頑張って欲しいですね。(なんだかバグの調査みたい・・・・)

私のFFプレイ歴と振り返り

そんなことどうでもいいよ、という話ですが、一応どのシリーズをどれくらいプレーしたのかを書いてみます。

FF1

社会人になってからPS VITAでプレイ。普通に1回クリアして、目茶苦茶簡単だなあと思いました。

FF2

小学生のころにプレイ。パンデモニウムが突破できませんでした。デスライダー+ミスリルゴーレムの悪夢が蘇る・・

その後社会人になってPS VITAで再度プレイ。簡単でした。HPも敵の攻撃力も100倍くらいあってもいいと思いました。最終的にアルテマ16にするくらいまではやりました。

FF3

小学生の頃にプレイ。その時点で結構簡単にクリアできました。

その後社会人になってPS VITAで3D版のものをプレー。やはり簡単でした。その時の感想とか雑感は以下に書きました。鉄巨人は撃破しています。
www.bunkei-programmer.net

FF4

小学生の頃にプレイ。音質がやたら良かったのを覚えています。最終ダンジョン以外は簡単でした。

その後社会人になってDS版をプレイ。今までとは異次元な難しさに驚愕しつつ、難しくて逆に非常に楽しめました。個人的にはあれくらい難しいのが標準でもいいですね。フレイムドッグのほのおで壊滅寸前に追い込まれたり、透過レーザーで全滅するなんて、もう最高ですね。

さらにその後、ff4 complete collectionを購入し、隠しダンジョンを全て攻略するくらいはやりました。

ちなみにサウンド面がやたら良かったのは、裏話があるのです。
www.famitsu.com

FF4 the after years

社会人になってPS VITAでプレイ。忍者編がちょっときつかったくらいで、特に印象に残っていません。しっぽは数種類集めて満足して終了しました。

FF5

中学生くらいの時にプレイ。全ジョブマスターするくらいはやりました。まあまあ面白かったです。ゲームシステムが良かったためか、今でもやり込む人は多いですね。

FF6

中学生くらいの時にプレイ。この頃から私のFFに対する印象がちょっと変わりました。恐らく今までのFFと画風がちょっと変わったから?なのかもしれません。

FF7

高校生の時に友達に借りてプレイ。この頃から以下が進行していきました。

  • ダメージの強烈なインフレ
  • 召喚獣の演出の長期化
  • ディスク枚数の増加(FF7は4枚組)
  • サブクエスト(進行に直接関連の無い寄り道イベント)の増加

ナイツオブラウンドとか超究武神覇斬とか、もうなんだこれ、という感じですね。しかもナイツオブラウンドなんて1分40秒くらいじっと見ていないといけないほど長いです。

今見ると酷いグラフィックですが、当時は革新的に見えました。FF6からいきなりあのグラフィックでしたからね。ストーリーも以外とちゃんとしていたし、敵・味方共にキャラクターも魅力的でした。旧作のFFとはいい意味で一線を画するものだと思えました。FF6 -> FF7は恐らくFF史上最大の革新があったのではないでしょうか。

FF8

未プレイです。個人的にリアル路線のキャラクターに魅力を感じなかったのと、戦闘システムが激変してしまった事が原因で、やる気がおきませんでした。今もやりたいとは思いません。

FF9

FF8の印象が悪かったので、未プレイです。しかし後から見返すと、キャラクターがFF8のリアル路線からデフォルメ路線に戻ったので、ニコニコ動画でプレー動画を全てみました。が、ボスキャラがあまりにも魔道士だらけで「あれ・・?」と違和感を覚えたのと、シリーズ屈指のロード時間の長さに閉口しました。

FF10

ここからPS2です。一気にグラフィック・サウンド共に向上しました。

大学生の頃に友達に借りてプレイしました。プレイ当初はキャラが喋りまくる事に違和感を感じましたが、ティーダがアホキャラだったせいか、あの見た目でも違和感なく楽しめたと思います。

オーケストラ調のBGMなど格好いいBGMが増え、ビジュアル面も演出が格好よくなりました。キャラチェンジ等、戦闘システム面でも色々なキャラを使う(使わざるを得ない場面もあり)機会がありました。

最終的には限界突破という、ダメージが9999以上になる新しい概念の登場により、FF7とは比較にならないダメージの爆発的なインフレを生み、HPが1千万もある隠しボスが登場したりもしましたが、やりこみ要素も多く、中々楽しめました。

以降のFF

全くやっていません。

各シリーズの革新的だと思った点

FF3ジョブの概念と、明確なクリスタルの世界。
FF4(SFC)グラフィック、サウンド面の大幅向上。ゲーム容量の大幅向上。
FF5ゲームシステムの向上。
FF7(PS1)グラフィック、サウンド面の大幅向上。ゲーム容量の大幅向上。
FF10(PS2)グラフィック、サウンド面の大幅向上。ゲーム容量の大幅向上。
FF13(PS3)グラフィック、サウンド面の大幅向上。ゲーム容量の大幅向上。

こんな感じです。大まかに、ハードの進化の後を追うように進化しているように感じます。ハードの進化により、表現できなかったものが表現できるようになり、容量増加によりサブクエストやストーリーの長期化等もされました。

ではFF15はどうかというと、そもそもPS3とPS4で、今までのSFC -> PS1 -> PS2 -> PS3 程のハードの進化が無いと私は思っているので、ハードの進化による恩恵があまり無かったと思っています。そういった革新性の不足がFF15の衰退に繋がったという理由も考えられそうですね。(まるでアップルのような・・・)

何故私はFF10以降をプレイしなかったのか

過去作をプレイして私がFFに求めていたものはリアルなグラフィックではなく、強力な武器・防具・魔法を収集・吟味しつつ苦しみながらボスキャラを倒す事であり、グラフィックを楽しむ事ではありません。私がこういう趣向なもので、勿論ロマサガシリーズも大好きなわけです。(というかFFよりロマサガの方が圧倒的に好きです)

FF10以降はグラフィックを楽しむゲームと化しているという先入観がどうしても拭えず、素直に楽しめそうもないなあ、と思い込んでしまって、プレイする気にならないのです。その先入観がある状態であのホスト達を見てしまうと、ますますプレイ意欲が無くなりますね。。。

まあそう言わずにやって見ろよと言われると、

  • PS4を持っていない。
  • PS4を置く物理的なスペースを確保したくない。
  • 発熱・消費電力が気になる。
  • 他にやりたいゲームが中々出てこない。

といった理由で、まずハードウェアを購入する事が億劫になってしまうのです。なら(将来的に)PC版が出るよ!という意見もあると思いますが、PC版ではやれグラフィックボードが〜、等とハードウェア要件が気になりだしたりして、結局購入する気になりにくいです。

要はモノを減らしたいのです。デカイ本体を置いてケーブル地獄になりたくないという気持ちがあります。既にPC周りでコンセント・ケーブル周りは酷い事になっているので。

私が好きなFFシリーズ

1位FF4のDS版
2位FF10
3位FF2

FF4 DS版

FF4のDS版はあの難易度が非常によかったです。FFシリーズはほとんどが低難易度で、町から次の町へ行くのに対して装備を整えなくても楽勝、ボスも弱い、というのが通例なのですが、FF4 DS版だけは別格です。

突如現れた雑魚敵フレイムドッグにほのおで全員7割以上削られ、キマイラのブレイズで焼かれ、フリーズビーストに凍らされ、レッドドラゴンの熱線で焼かれ、ルゲイエのリバースガスで初見殺しされ、防衛システムの透過レーザーでオーバーキルされ、超強化されたミジンコのビッグバーンで壊滅し、散々な目にあいます。

だがそれが面白い。そこを攻略するのが楽しいのです。変態マントの紳士に盗むで火炎竜を阻止したり、対策して完封したりする過程が楽しいのです。(ぶっちゃけデカントという新システムのおかげで結構簡単に攻略できます)最近のユーザは難しい事を好まず、ほぼ何も考えずにボタンをポンポン押すだけでストーリーが進んでしまうような低難易度化が進行していて、とても受け入れられないでしょうね。(ダークソウル等の難しいゲームがヒットする事例もありますが)

私は基本的に難しいゲームが好きなのです!

FF10

キャラもストーリーも中々総合的に楽しめます。

FF2

完全に思い出補正ですが、FF2ってなんか何回もやりたくなってしまうのですよね。ストーリーも長くないし、攻略法も解りきっているので、魔法縛りとか、ついついやってしまいたくなります。

雑感

はやくゲームから本体という概念がなくならないかな〜

ゲーム機の本体はデータセンターにあって、プレーヤー側はそのゲーム機の画面をリモート表示する、という感じになりませんかね。そうすれば物理的な配置スペース、発熱、消費電力、売却、ソフトをDLするためのストレージ容量等の面倒な事を考えなくてもよくなります。まあ恐らくリモート表示する際のデータの転送量の問題で、コマ送りのような表示になってしまうのかもしれませんね。

なんというか、そろそろハードウェアの向上だけではなく、ゲーム機のあり方自体を一変して欲しいな、という気持ちがあります。スマホゲーはその今までのゲームのあり方(でかい本体の所有)を変えたので、次は本体自体を無くして欲しいです。

世間ではサーバーレス!サーバーレス!と騒いでいるので、そのサーバーレスの力で是非ゲームから本体の概念を無くして下さい!(他力本願)



というかですね・・・・



ロマサガの新作はよ

GAEを東京リージョンに変更して高レイテンシの呪縛から解放されよう!

$
0
0

大分乗り遅れた感が強いですが、東京リージョンに変更しました。 GAE/Jのサイトでもかなり速くなりましたよ〜
f:id:treeapps:20160521191008p:plain

私は個人でGoogle App Engineを使って以下のサイトを運営しています。GAE/Java+素のServletで作っています。

www.tree-maps.com

今回リージョン変更の作業を行ったので、ざっくりとしたリージョン変更手順や効果等を書いてみます。

昔のGAE

昔はGoogle App Engineはアメリカのリージョンしか存在せず、通信するだけで200ms程度必ずかかってしまっていました。物理的にデータセンターが遠いので、通信だけで時間がかかってしまいます。各リージョンでどれくらいのレイテンシが発生するのかは以下の記事をご覧下さい。

qiita.com

このレイテンシによって、ただでさえスピンアップに時間がかかるのに加え、高レイテンシによる通信の遅延も発生していたので、GAE=遅いという印象が強く植え付けられていると思います。実際私はus-centralリージョン+GAE/Javaで運用していましたが、遅さに辟易していました。

GAE/Jのスピンアップが遅い + 高レイテンシ + フレームワークの初期化が遅い、という3重苦により、ついこの前まで酷い有様でした。GAE/Jを使う限りスピンアップ問題の解決はちょっと難しいし、レイテンシはどうにもならない。ならせめてフレームワークの初期化の時間をどうにかしよう、という事で以前以下の記事を書きました。

www.bunkei-programmer.net

このような問題があったため、GAEといえば遅いわ制限キッツイわで萎える開発者が多かったことでしょう。

今のGAE

そしてついに 2016/11/08 に、GCPの東京リージョンができました!

cloudplatform-jp.googleblog.com

この時期と全く同じか不明ですが、GAEはGCP(Google Cloud Platform)の一部なので、GAEにも東京リージョンが追加されたのです!

これで今まで泣く泣くus-centralリージョンで200msのレイテンシを受け入れていたのが、今後はレイテンシ問題から解放されるのです!

という事で早速tree-mapsもus-centralからasia-northeast1に変更しましたので、ざっくり手順を書いてみます。

us-centralからasia-northeast1への変更手順

目標

  • GAEを使う。
  • 独自ドメインを適用する。
  • 独自ドメインに対してLet's encryptでSSL証明書を発行し、適用する。

前知識

現在のGCPでは、リージョン変更は簡単に行えません。

新たに東京リージョンで新規プロジェクトを作成し、そちらにリクエストが向くよう変更しないといけません。

しかし、その手間をかけてでもリージョン変更する価値はあります。

リージョンが選択できない場合は新規にgoogleアカウントを作成する

早速ですが、注意点があります。

私がGAEのアカウントを作成した際は、今のGoogle Cloud Platformの管理画面ではなく、以下のような古い管理画面でした。

f:id:treeapps:20161214182019p:plain

この時期に作った古いアカウントのせいか、Google Cloud Platformの管理画面で新しいプロジェクトを作成しようとしても、以下のようにリージョンが選択できませんでした

f:id:treeapps:20161214182245p:plain

Googleのサービスは往々にしてこういったこと(アカウント作成時期により機能が限定される)があるので、ここは素直に新規にGoogleアカウントを作成する事で、以下のようにリージョンの変更が可能な状態になりました。

新規GCPプロジェクトを作成する

GCP管理画面を開き、メニューの左上の部分を選択し、プロジェクトを作成します。
f:id:treeapps:20161214184051p:plain

作成するプロジェクト名は、移行前のus-centralで作ったプロジェクト名と同一で問題ありません。ここで旧アカウントのユーザは馴染みが無い事が起こります。

昔のプロジェクト作成時はプロジェクト名=プロジェクトIDだったのですが、今は異なります。プロジェクト名を入力すると、ユニークなプロジェクトIDが自動で振られます。デプロイする際のプロジェクトIDは、これを入力する必要があります。

f:id:treeapps:20161214185105p:plain

GCPプロジェクトを作成後、GCP管理画面の左上のハンバーガーメニューからGAEを選択し、GAEプロジェクトを作成します。ここで何やら見慣れないチュートリアルを強制されますが、頑張って下さい。。。チュートリアルでは、Google Cloud Shell上でgitでサンプルをcloneしてデプロイするデモを体験できます。最終的に何らかのプロジェクトをデプロイし、ブラウザで確認できる状態にして下さい。

チュートリアル完了後、めでたく見慣れたGAEのダッシュボードを拝む事ができます。ここまでできれば後はこの新アカウント・新プロジェクトIDに向けてデプロイしていきます。

EclipseのGoogleログインのユーザを変更する

Quick Start  |  Google Plugin for Eclipse  |  Google Developers
上記のpluginを使っている場合のお話ですが、今回Googleアカウントを新規に作成したので、以前まで使っていたアカウントでログインしている状態になっているかと思います。従って、まずデプロイの向き先(Googleアカウント)を変える必要があります。向き先の変え方ですが、ちょっと解りにくいのですが、Eclipseの右下から変更可能です。

f:id:treeapps:20161214183546p:plain

デプロイ先のプロジェクトIDの切り替え

デプロイの向き先のgoogleアカウントは変更できました。次はどのプロジェクトにデプロイするかの指定、プロジェクトIDを変更します。前項で確認したプロジェクトIDを、以下に入力して保存します。

f:id:treeapps:20161214183810p:plain

これでデプロイ準備完了です!

デプロイする

eclipseプロジェクトを右クリック -> Google -> Deploy to App Engine でいつものようにデプロイしましょう。これで新アカウント・新プロジェクトに向けてデプロイできます。

デプロイは完了してブラウザからも画面表示を確認できますが、独自ドメインからのアクセスは以下のようになっているので、まだユーザは旧サイトを閲覧している状態です。

リクエスト -> www.tree-maps.com(独自ドメイン) -> GAEの旧プロジェクト

旧アカウントのGAEプロジェクトのカスタムドメインを削除する

www.tree-maps.comからのリクエストを、このGoogleアカウントのこのGAEプロジェクトにプロキシするぞ〜、という設定が残ったままなので、これを削除します。

新アカウントで再びカスタムドメインを登録する

新アカウントにはまだ独自ドメインが登録されていない状態なので、ざっくり以下のような感じで登録していきます。旧アカウント作成時に行った作業と全く同じですね。

f:id:treeapps:20161214190619p:plain

tree-maps.comでドメイン追加し、tree-maps.comに対するエイリアスとしてwww.tree-maps.comを設定します。途中でドメインの所有権の確認が入るので、レジストラ側のTXTレコードに、指定された値を入力して保存し、TXTレコードの反映を待った後、ドメイン所有権の確認を通過させます。所有権が確認できた後、そのままウィザード通りに選択し、カスタムドメイン定義が完了します。

ここで「Aレコード、AAAAレコード、CNAMEレコードをこう設定しろ」という指示が表示されますが、これは旧アカウントで既にレジストラに入力したものと全く同じなので、レジストラ側の変更は不要です。レジストラ側の変更は、所有権確認のTXTレコードの追加のみです。

SSL証明書の反映

私の場合は旧アカウントで作成してあったLet's encryptのSSL証明書定義が既にあったのでそれを新プロジェクトに反映するだけでした。もし証明書が無ければ、以下の記事を参考にSSL証明を作成して反映して下さい。

www.bunkei-programmer.net

トラフィック割当の変更

GAEのチュートリアルでデプロイしたプロジェクトがデフォルトとして登録されてしまっています。

GCP管理画面 -> GAE ->バージョン と選択し、トラフィックを移行し、チュートリアルプロジェクトに向いているのを、新サイトの方に向き先を変えます。向き先変更後、チュートリアルのバージョンを削除してしまいましょう。

移行完了

おめでとうございます!これで移行完了です!

今回は個人サイトだったのでサイト停止時間はあまり気にしませんでしたが、事前にSSL証明書だけ新アカウントに登録しておいたりし、停止時間を減らす事は可能だと思います。

リージョン変更の効果

chrome上で計測

us-centralの場合

TTFB(Time To First Byte) は 319.63ms です。
f:id:treeapps:20161214200230p:plain

サイト全体の画面表示速度は以下の通りです。
f:id:treeapps:20161214194202p:plain

asia-northeast1の場合

TTFB(Time To First Byte) は 48.14ms です。
f:id:treeapps:20161214200239p:plain

サイト全体の画面表示速度は以下の通りです。
f:id:treeapps:20161215162930p:plain

大分違いますね。画像やjsやcss等も1リクエスト飛ぶので、そのリクエスト毎に高レイテンシを背負っていたのが、低レイテンシ化した事で、全体の通信が高速されています。

アプリには全く手を入れず、リージョンの変更でサイトの高速化をする事ができました。

これでもう「GAEって高レイテンシなんでしょwwww」なんて事は言えなくなります。

スピンアップとフレームワーク初期化遅い件は?

GAE/J+素のServletでこれだけ速度が出るようになりましたが、GAE/GOにする事で更なる高速化が望めます。

GAE/GOの驚異的なスピンアップ速度(GAE/Javaと比較して約12.6倍高速)を誇ります。
www.bunkei-programmer.net

Golang自体が超高速なのもあり、試しにフレームワークとしてginを使用した場合、画面にjsonを返すのに300〜600ms程度(この時はus-centralで計測した)だったので、大体1秒以内に画面表示まで完了させる事が可能になりそうです。ここまで高速ならGAEでSPAしても問題にならない領域になりそうです。

おまけ

ここまできたら東京リージョンでGAE/GOにしたらどれくらい速度出るかきになりますよね。せっかくなのでちょっとだけ試してみました。

東京リージョン+GAE/GOの驚異的な潜在能力

リージョンasia-northeast1
ランタイムgolang
ランタイム詳細go version go1.6.3 (appengine-1.9.48) darwin/amd64
フレームワークecho

上記環境でjsonpをシンプルに返却するURLを用意し、計測した結果は以下の通りです。


f:id:treeapps:20161215030943p:plain


・・・・んん??見間違いかな?

なんか 19msとかいうトチ狂った数値が見えますね。これは驚異的な速度ですね!これがGAEの真の力なのだ・・・!!

us-central + go + ginの場合

f:id:treeapps:20161215030926p:plain

f:id:treeapps:20161215030937p:plain

asia-northeast1 + go + echoの場合

f:id:treeapps:20161215030943p:plain

f:id:treeapps:20161215030950p:plain

雑感

GCPの東京リージョン追加は結構インパクト大きいですね。GCE方面でも、GCEがus-centralにあるせいでCloudSQLもus-centralに置かざるを得なかったのが、asia-northeast1に置けるようになったので、全体の高速化ができそうです。


巷ではコンテナの次のステージとして、サーバーレスが挙がっていますね。AWS lambdaの場合はどうやらamazon独自のコンテナ技術を使い、リクエスト時にコンテナを起動する事をしているようです。
d.hatena.ne.jp

これ、よく考えてみて下さい。GAEのスピンアップと似ていませんか?似ているというか、恐らく同じ事をしていますね。初回呼び出し時はコンテナが無いのでコンテナを作成、暫くアクセスが無いとコンテナは自動削除され、スピンダウン状態になる。完全にGAEじゃないですか。もしかしたら、


2008年4月に登場したGAEは、実は相当未来的なサービスだった


と言えるかもしれませんね。AWS lambda等と異なり、普通にjavaプロジェクトを用意する必要がある等の手間こそありますが、それ以外の仕組みはGAEと似ており、AWS lambdaはGAEをコンパクトにしたもののようなものに感じています。

しかし残念な事に昔のGAEは前述した通り、制限が厳しい(今もですが)、ランタイムがjavaとpythonのみだった(今はflexible environmentの登場でdockerで何でもできる)、東京リージョンが無い故の高レイテンシ、という問題があったため、中々日の目を見る事がありませんでしたが、それらを克服しつつある今、再度GAEを見直す時期なのかもしれません。

個人的には、無料でできる事が大きい(月間500万PVを捌く事例もあり)し、オートスケールやセキュリティスキャンの定期自動実行も完備され、OpenSSLの脆弱性地獄に悩む事も無いので、下手にVPS借りたりAWSやGCPでウェイウェイするよりも、GAEにした方が遥かに低価格で規模の大きいものを作る事ができるのではないかと思っています。

GAE/GOのデプロイ時のOAuth2認証後にlocalhostに遷移してしまう時の対処法

$
0
0

東京リージョンにGAE/GOをデプロイしようと思ったらこれだよ!
f:id:treeapps:20160521191008p:plain

前回の記事で東京リージョンのGCPプロジェクトを作ったので、早速GAE/GOをデプロイして速度確認しようとしたところ、ちょっと問題がありました。いつものように「goapp deloy」すると、以下のような事になりました。

tree:tree-maps-go tree$ goapp deploy
02:28 AM Application: tree-maps; version: go-echo
02:28 AM Host: appengine.google.com
02:28 AM Starting update of app: tree-maps, version: go-echo
02:28 AM Getting current resource limits.
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&response_type=code&client_id=550516889912.apps.googleusercontent.com&access_type=offline

If your browser is on a different machine then exit and re-run this
application with the command-line parameter

  --noauth_local_webserver

こんな感じのコンソールがダラダラ流れてブラウザ起動してgoogleのoauth認証が始まるのですが、oauth認証の許可ボタンをクリックしたら、なんとlocalhost:8080に遷移してしまったではないですか!!

本来であれば、許可した後にverification codeが発行され、その値をコンソールに入力するとデプロイが続行しますよね。残念がらlocalhostに遷移してしまい、verification codeを取得する事ができませんでした。上のコンソールをよく見ると、「ブラウザで以下のURLを閲覧しろよ」の下のURLの「redirect_uri」パラメータの値が「localhost」になっているのです。

これ自体の原因は不明ですが、解決策は↑にヒントが書いてあり、以下のようにコマンドを実行すればlocalhostにならずにoauth認証され、正常にデプロイできるようになります。(ドットの部分はプロジェクトのパスを設定してもOKです)

tree:tree-maps-go tree$ appcfg.py update . --noauth_local_webserver
02:36 AM Application: tree-maps-152415; version: go-echo02:36 AM Host: appengine.google.com
02:36 AM Starting update of app: tree-maps-152415, version: go-echo02:36 AM Getting current resource limits.
Go to the following link in your browser:

    https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&client_id=550516889912.apps.googleusercontent.com&access_type=offline

Enter verification code: hogehogehogehoge
Authentication successful.
02:37 AM Scanning files on local disk.
02:37 AM Cloning 1 static file.
02:37 AM Cloning 135 application files.
02:37 AM Uploading 81 files and blobs.
02:37 AM Uploaded 81 files and blobs.
02:37 AM Compilation starting.
02:37 AM Compilation: 108 files left.
02:37 AM Compilation completed.
02:37 AM Starting deployment.
02:37 AM Checking if deployment succeeded.
02:37 AM Deployment successful.
02:37 AM Checking if updated app version is serving.
02:38 AM Completed update of app: tree-maps-152415, version: go-echo

「--noauth_local_webserver」オプションですが、これはgoappコマンドには無く、appcfg.py側にあるオプションなので注意です。

SEOなんて気にせず気持ちよくブログで記事を書こう

$
0
0

疲弊するのはよくないです。
f:id:treeapps:20161225220110p:plain

先日とあるSEOについて書かれた記事が結構なブックマーク数を集めていたので読んでみたのですが、どうなのかなあ・・・・と思ったので、SEOについて書いてみようと思います。

お前は誰なんだ

私はブロガーではありますが、会社でB to B to CのWEBサイトの、中〜大規模の開発をしている者です。所謂どこにでもいる老害窓際プログラマーです。

ブログでほそぼそと記事を書き、個人でGAEやGCSを使った動的サイトを複数開発・運営し、業務で動的サイトを沢山開発・保守しています。一応個人ブログが持つ側面と業務サイトが持つ側面の両方を少しだけ知っているつもりです。本当に少しだけ・・・

ブログとSEOと

さっそく本題のSEOについて。サイトのSEOではありません。個人ブログのSEOについてです


ブログのSEOなんて考えてないで記事を書いたり文章力を鍛えた方がいい


これが私の意見です。勿論正解ではありません。(後述しますがSEOに正解なんてありません)

私が見たサイトには、ググって頑張って探してきたであろうTIPSが書かれていました。そして「グーグルがこう言っている」という言葉も添えられていたりしました。まあそれ自体はそうなんだろうし、いいと思います。やろうとしている方向は合っているのかもしれません。しかし私は別にそんな事しなくていいと思っています。

私は業務でそのブログが対象にしているであろう記事数の100〜1000倍のコンテンツ量のサイトを開発をしており、そこそこ知名度のあるSEO業者から「こういう目的でこういう施策をこの期間でやりましょう」と日々依頼され、それを日々実装しています。

業務サイトで行うSEO施策の例

まず、仕事でどんな感じのSEO施策をしているのかをざっくり書いてみます。個人でググった程度のSEOではなく、SEO業者が提案するSEO施策内容についてのお話です。

ページ数を減らす施策

タイトルがどうとか、キーワードがどうとか、まあ確かにSEOの施策として存在します。

しかし業務で開発するサイトでは、コンテンツ量を調整する類の施策の方が重要度が高い気がしています。それは何故か。答えは「ページ数が多過ぎるから」です。業務サイトはほとんどが動的なサイトなので、検索条件を組み合わせると、簡単に数千万URLできてしまいます。多言語化すると、更に数倍になります。

検索エンジンも馬鹿ではないので、「このページとあのページは恐らく同じ」という事を察知して、本当にユニークになるコンテンツを検索結果として表示しようと努力します。しかしそれにも限界があるので、canonicalタグでページをまとめたり、alternateタグで多言語を調整・誘導したり、コンテンツ量の少ないページをnoindexしたり、GETだったページをPOSTにしてURLパラメータを除去したり、明示的に検索エンジンにそういった命令をし、ページ量を調整します。

ページ量を調整する施策をしないと、検索エンジン側で頑張ってページ内容を把握しようと頑張っても、その量が多すぎるが故、コンテンツが正しく認識される時間が遅くなってしまいます。なので、できるだけ不用意なインデックス化は避け、必要なページを必要なだけインデックスさせるような事を行います。

勿論某キュレーションサイトみたいなものはNGで、真実に基いた記事で検閲されたものが公開されないといけません。

ページ数を増やす施策

S3やFTPやSSIを使い、開発会社以外が自由にサイトにコンテンツを追加可能にできる仕組みを構築し、更にそれらコンテンツのURLを自由に設定できる施策なんかもよくあります。(格安の)ページ制作会社にページの制作を発注し、大量に追加コンテンツを作って貰う感じです。その際にページのヘッダー・フッターの共通化等でSSIを使ったり、何らかのテンプレート化をする事もあったりします。

他にも、開発会社が異なる複数のサイトを統合する事になり、ドメイン統合作業が発生する事もあります。各社でURLが被らないようにうまくディレクトリを調節し、且つドメイン統合の影響を最小限に抑えるため、リバースプロキシで頑張って統合するケースも多々あります。

最近ではスマホに力を入れだしているので、ドメインを統合しつつUAでスマホサイトにリダイレクト、しかもPCサイトとSPサイトでURLが異なるのに頑張ってURL変換してSPサイトにリダイレクトするなんて力技もあります。(これは地獄なのでできればやりたくない施策です・・・)

圧倒的物量で押す

大体業務で行うSEO施策はこんな感じのものが多いです。確かにタイトルタグやdescriptionの精度を上げるような、よくはてなブログに上がる経験に基づいたSEO施策TIPS記事みたいな事もやるのですが、そんなに沢山やりません。本当は凄く大事な事なのですけどね。

実際はそういった細かな調整よりも、重複しないページ・コンテンツをドカーンと増やす施策の方が結果が出る場合が多いようです。これが良い事なのか悪い事なのか、正直どっちも当たっていると思うので何とも言えません

ブログのSEOは

ここでブログに話を戻します。

記事の冒頭でブログのSEOは気にしなくていいと書きました。これは私が日々業務で個人ブログとは比較にならないページ数のサイトを開発しているからというのもありますが、他にも理由があります。それらをいくつか書いてみます。

各社のブログは最初からある程度SEOが考えられている

もちろんフルスクラッチで開発する業務サイト程の自由度はブログには無いので、「ある程度の施策」です。私ははてなブログを使っているのではてなブログの事しか言えませんが、各種SNS連携も設定一つで簡単にできるし、自動的にはてなコミュニティに記事が拡散します。

たかがSNS連携だって、OGPに関するタグをツラツラと、しかもfacebookやtwitterで内容が異なるようなタグも自動で設定してくれているのです。普通にこれをスクラッチしようとすると結構面倒です。各社の対応OGPを調べて実装する必要があるし、テストするのも大変です。

ユーザビリティとか

サイトのテンプレートも用意され、しかもレスポンシブ対応やモバイル特化のテンプレートも存在し、それもテーマを選ぶだけで簡単に行えます。最初からユーザビリティを考慮したサイト設計がなされたテーマもあるので、単にテーマを適用するだけで、ある程度のユーザビリティを獲得する事ができます

勿論ユーザビリティと言っても、色々な種類のユーザビリティが存在するので、抜けはあるとは思います。

ブログのページ数はどうだろう

大半のブログが1〜2000ページ程度じゃないでしょうか。重複を除外すると500〜1000ページくらいですかね。個人ブログだとそのくらいのページ数しか作れない場合がほとんどです。人間一人が作る記事数には限界があります。

ページ数を考えると、私を含む恐らく素人の経験則に基づいたSEO施策を1000ページに施しても、正直たかが知れています。タイトル調整やキーワード選定やページ更新を頑張る事で、確かに効果は出るとは思いますが、その効果が現れるであろう影響範囲が圧倒的に不足しているように思えます。

繰り返しになりますが、それら施策に意味は絶対に、必ず、100%有ると思っています。が、その影響範囲は小さい、と私は考えています。労力に見合う結果は正直出にくいのではないでしょうか

最初からブログがある程度のSEO施策を行ってくれているのに加え、SEO業者が組織的に調査したものでもなく個人で調査したレベルの施策になるので、益々その効果は薄くなるように思えます。

SEO施策でライバルに勝てるかもしれないじゃないか

確かに可能性はありますね。キーワード頻出率を計算し、タイトルの左から何文字目までに云々、等をした方がいいかもしれません。が、その程度で他ブロガーに勝てるんですかね。検索エンジンってそんな馬鹿じゃないので、もしライバルに勝っているとしたら、単に内容で勝っているのであって、SEO施策で勝っているわけではないのではないでしょうか。

ちょっとググった程度の施策で勝ってしまう闇の時代は大分前に終了しています。「SEO施策で勝った気になっているだけ」です。本当は内容で勝っているんです。

検索エンジン

検索エンジンの目指すところ

検索エンジンは、最終的に自分たちの利益を生み出す事が目的です。利益を多く生み出すには、規模を大きくする必要があります。規模が大きくなると、各種ルールを設定しないと、混沌とし、コントロール不能に陥り、利益予測ができなくなってしまいます。

グーグル等の検索エンジンは、自分たちが最大限力を発揮できるルールを利用者である私達に強います。そうする事で検索エンジンは力を発揮しやすくなり、利益を生み出す事ができ、更に機能追加・改善もしやすくなり、お互いにメリットが生まれます。どんなルールを設定すればいいのかを考える必要がありますが、その時代のトレンドによって姿を変え続け、ルールは日々変わります。

日々細かいルールが変わったり増えたり廃止されたりしていくのですが、それは検索エンジンが主導で行われ、ユーザへの通知無しに変わる事も多々あります。それに頑張って個人が追従しようとするのは、中々厳しいものがあります。

検索エンジン側としては、文字コードが今時SJISだしSEOについて全く何も解っていないけど、非常に良い事を言っている記事を埋もれさせたくありません。そういう記事を拾うことができれば、検索エンジン側の利益にも繋がるし、ユーザ側も検索エンジンに現れやすくなり、お互いにメリットがあります。

検索エンジンはルールを設定してはいますが、ITリテラシーが低いユーザにそれは伝わりません。ユーザ側が自分達の都合の良いルールに従う事を口を開けて待つよりも、自分達でSEO下手な人達が何を伝えようとしているのかを解釈しようと日々努力しています。

その常軌を逸した努力は日々精度を上げていき、徐々に昔流行ったSEO技は駆逐されていき、残ったTIPSは、「良い文章を書きましょう」「インチキしないようにね」「セキュリティ高めようね」等、割りと普通?のTIPSだけになりました。結局健全なWEBが最も自然で扱い易く、検索エンジンにもユーザにもメリットが増えるという事なのでしょうかね。

SEOに明確な答えは無い

SEOの話をすると、必ず根拠の無い、自分の経験則だけのオレオレ持論を持ち出してくる人がいます。そうなってしまうのは、そもそもSEOには答えが無いためです。googleのサイトを見ても、本当に具体的な事は書いてくれず、ちょっと曖昧にかかれています。

これは推測ですが、明確な答えを書いてしまうと、悪用されやすいからだと私は考えています。世の中には検索エンジンを容易に欺く頭脳を持ち、それを悪用しようとする人達が沢山、それはもう沢山います。ほんの少しでも手の内を見せてしまうと、googleでさえ付け込まれます。実際にブラックハットSEOという形で、悪意の歴史は続いていますね。

他にも、敢えて明確に書かない事で、少しだけユーザに自分の頭で物事を考えて欲しい、という側面もあるのかもしれません。ユーザが自立してくれれば、ルールに従わせるのも容易になって検索エンジンの機能・精度の向上がしやすくなり、ユーザ側としても検索エンジンが使いやすくなります。

曖昧な世界で正解を求めて彷徨う

結局検索エンジンを作っている人達しか正解(実装)を知らないので、実はSEO業者がやっている事にも根拠はありません。彼らは検索エンジンの敷いたレール(ルール)に脱線しないように乗り、仮定を作り、実践し、KAIZENしていっているだけです。正解が解らないので、恐らくこれで合っているだろうと仮定して施策しています。

そんな曖昧なSEOの世界で、組織でもなく個人で一人で書いているブログで、明確な根拠もないTIPSに右往左往して振り回されるよりも、適切な言葉で、伝えたい事を伝える事ができる文章を書くよう努力する方が、絶対に精神衛生上良いと思います。

では一体何をしたらいいか

SEOの事なんか一旦忘れて下さい。

もっとページ数が多いサイト・ブログを扱う事になったり、ブログでご飯を食べていく段階になってから考えて下さい。今は増えすぎたURL数の調整やクロール導線の構築なんて考えなくていいです。各社ブログも馬鹿ではないので、自動で導線が作られたり、最初からある程度カバーしてくれている事が多いです。

各社はSEOをある程度標準装備する事でユーザの記事が検索エンジンに現れやすくなり、最終的に広告収益等が増え、会社側の利益に繋がる+ユーザのブログの人気が向上する事に繋がるので、各ブログは日々機能追加をしています。

文章力

タイトルの文字数が云々よりも、文章の構成能力を鍛る事を検討すべきです。私は文章の専門家ではないので、どうやったら鍛えられるのかは他サイトを参考にして下さい。起承転結とか、正しい言葉・正しい表現方法・段落などなど、学校で勉強したような事です。

キーワードの選定云々、カテゴリ云々も確かに効果はあるとは思いますが、まずは土台となる文章、伝えたい事を伝えたい人に簡潔に伝える、といった事に纏わる勉強をするべきだと考えています。

現実世界の例を挙げると、パワポのプレゼンを思い浮かべて下さい。あれです。結論が書かれていなかったり、スライドの順番と文章の脈絡がちぐはぐだったり、スライドが長すぎて読む気が起きなかったり、あれですあれ。あれが上手くできる人は、限られた表示領域で、限られた時間で、伝えたい事を伝えたい人に伝える事ができる人です。それと同じ力があれば、ブログは成功し易いと思っています。

レイアウトとかUXとか視線移動とか

ヘッダの位置だとか、ここにこの要素があるとUX的にどうとか、人間の視線移動的に云々とか、スクロール量と視認領域とか、小難しい事はSEO要素として存在します。

完全ではありませんが、これらもある程度テーマが解決してくれます。ユーザは文章だけ書けば、カテゴリリンクの位置はヘッダー・フッター等は自動的に、人間工学に基いて(希望的観測)設計してくれているので、テーマだけ変えればまあまあ対応できたりします。

別にレスポンシブに対応したテーマを選ばなくても、モバイルのサイトはデフォルトで用意されているので、どちらでも構いません。SEOだのUXだのを考えず、自分の好みのテーマを選んじゃって下さい。微妙に気に入らなければ、独自cssで拡張するのもアリです。

画像サイズとか

画像の縦横の大きさは、最近のブログはレスポンシブ対応される場合が多いので、縦横が大きい画像でもまあ大丈夫です。というか、高解像度スマホを考えると、ある程度大きめの方がいいかもしれません。

画像のファイルサイズについては、数メガバイトにもなる画像を作る方が逆に難しいので、そんな心配いらないかあと思います。はてなブログの場合は画像はCDN通っているのでキャッシュされますし。(あまり巨大な画像はそもそもアップロードする時点で弾かれた気がします)

1点気をつけないといけないのは、最近問題になっていますが、著作権ですね。ネットでフリー画像を探す際は、画像の使用許諾・ライセンスを読みましょう。出典を明記してリンクする必要があったり、画像を改変する場合は権利者の許可が必須だったり、何もかも自由だったり、色々ありますので。

揉めるのが怖かったり自信が無ければ、素直にPIXTA等で購入するか、画像ソフトで自分で0から作りましょう。

雑感

業務でも根拠のない、結果が出るのかも解らないSEO施策に振り回されて疲弊しているので、せめてブログでは疲弊したくないなあ、と思い、記事を書いてみました。

ブロガー兼開発者の私としては、SEOとか面倒な事は基本的にブログに全部お任せで、ユーザは記事を気持ちよく書く事だけに集中したいですね。SEOとかに振り回されて文章が疎かになるよりも、SEO完全無視で良い文章が書いた方が絶対楽です。勿論それが最高の結果になるかと言われたらNOで、施策した方がいいに決まっています。その施策に効果があるのか、いつまで効果があるのか、解らないまま何となく彷徨のは精神衛生上よろしくない、ということです。

既にブログをやっている方には解ると思いますが、いざ記事を書いてみると、自分の伝えたい事ってユーザに全く伝わらないんですよね。「そんな事どこに書いてあるんだ」と言いたくなるような事をブコメされたり、「いや、それ書いてるじゃない。見てないのか・・・」と言いたくなる程文章を見ずにブコメされます。

そうなってしまうのも、文章の構成能力が低いためです。勝手な想像をされてしまう程言葉足らずだったり、逆に文章が長すぎて重要な部分に辿り着く前に離脱してブコメされたり。しかしそれを避けるために理論武装するのも嫌ですね・・・そういう細かい理論武装は仕事でお腹一杯です。

私としては人がやりたがらない事を頑張ってやろうとするよりも、せめてブログではそんな面倒な事考えずに気持ちよく文章を書いて欲しいと思っています。変に奇を衒ってブルーオーシャンを狙ってる風な姿勢を見せて敷居を上げようとせず、どんどん参入者が増えていった方がいいと思います。業界が盛り上がらないと、衰退あるのみですから。


こんな感じで細々としたSEOに右往左往されるよりも、そんな事は一旦全部忘れて記事を頑張って書く方に力を入れた方が、絶対楽だし長続きすると思っています。勿論業務サイトだったり生活がかかっている方は、血眼になって検索エンジンを追いかけましょうね ^^

tree-mapsの開発ブログをスタートします

$
0
0

なぜ始めてしまうのか。
f:id:treeapps:20160521191008p:plain

今年はあまりブログを更新していなかったのですが、単に会社のお仕事が忙しかったのと、現在運営中のtree-mapsのリニューアルをしようと画策していたためです。

最初はこのブログでtree-mapsについて書いたりしましたが、開発の経過を綴る専用のブログを用意しました。リニューアルするにあたって、GAEを使うわけですが、ランタイム変更したり、大幅に構造を変更するため、どこにどう苦労してるとか、こんな技術要素だとか、色々書ける事があるので、専用ブログにする事にしました。

まだ記事は書けていませんが、こちらです。↓↓↓

tech.tree-maps.com

私がやろうとしていること、考えている事に直接コメントして頂ければ、それについて実装できればするつもりです。

なお、現在tree-mapsのリニューアルに向けてアンケートを実施中ですので、よろしければ回答お願いします。

docs.google.com


今後はtree-mapsについては開発ブログの方に投稿していきます。慣れないWordPressを使ったブログなので、色々整備できていない部分が多々ある状態ですが、徐々に整備していきます。


心機一転、テーマを変更してみました

$
0
0

あけましておめでとうございます。今年もよろしくお願いします。

f:id:treeapps:20170104180018p:plain

ということで、新年1件目の記事となります。

テーマの変更

このブログの読者様は解るかもしれませんが、ブログのテーマを一新しました。

何故テーマを変更したのか

  • 新年になったので、丁度区切りがついた感がある。
  • 既存のテーマに飽きた。
  • テーマにカスタムcssを適用しすぎて訳が解らないレベルで改造してしまった。
  • レスポンシブテーマにしたかった。

最大の理由は「レスポンシブ対応」です。

皆さんご存知の通り、検索エンジンも完全にスマホに舵を取っているので、そろそろレスポンシブ対応しないとな〜、と思い変更しました。レスポンシブにする事で、PC・スマホの両者でhtmlの内容が一致するので、より安全で検索エンジンにも優しくなります。

あと、変更前のテーマをcssであまりにも魔改造してしまっており、もう元のテーマの原型を留めない程にカスタマイズしてしまいました。あまりにもやり過ぎてしまったので、描画が少し重く、カスタマイズcssもあまりもの行数が増えてしまい、cssの修正が大変になっていました。

そこで今回は以下のテーマを適用し、マテリアルデザイン風に少しcssをいじりました。

CONTENTS - テーマ ストア

テーマのカスタマイズで意識したこと

  • マテリアルデザイン風にすること。
  • ドロップシャドウを画像に適用する。
  • 狭すぎるマージンは避ける。
  • adsenseは全部レスポンシブにする。
  • ごちゃごちゃしすぎないようにする。

こんなところです。このテーマは元々フラットデザインなのですが、やはり個人的にマテリアルデザインのドロップシャドウが好きなので、主に画像に適用してみました。いい感じに立体的になり、おもわずクリックしてみたくなるようにしました。

レスポンシブなので、マージンを多めに取り、スマホからでもリンクやボタンがタップしやすいように、マージンが狭すぎないように、文字が小さすぎないようにしてみました。

キャッチコピー画像

キャッチコピー画像ですが、ドロップシャドウを適用した時に綺麗に見えるように、ちゃんとキャッチコピー画像を作っています。昔の記事はキャッチコピー画像を作っていないので、順次作成していっています。頑張ってPhotoshop Elementsで作成していますが、プログラミングより時間かかりますね。。。

雑感

最近以下のブログを始めました。WPblogでWordPressなのですが、結構色々思う所がありますね。
tech.tree-maps.com

WordPressは何をするにしてもpluginのインストールが必要で、初期状態の機能は実は貧弱で、プラグインは必須です。そのプラグインも種類が沢山あるうえ、プラグイン同士の機能が被ってしまい、あのプラグインでもこの機能があるし、そのプラグインにも同じ機能がある、という事が多いです。

さらに、プラグインがテーマに影響を与えることもあり、プラグイン同士の相性問題、テーマとプラグインの相性問題、に遭遇します。上記ブログはマテリアルというテーマですが、Jetpackをインストールするとコメント機能に影響を与えてしまいました。ソースコードをいじれば回避できますが、WordPressのバージョンアップでソースコードの回収が上書きされて元に戻る可能性があります。

一方はてなブログ等のレンタルブログの場合、プラグインこそありませんが、標準機能が非常に高機能なので、今はてなブログで記事を書いていて、本当に楽だと感じています。WordPressの方がプラグインを駆使すれば確かにそこそこ色々できますが、やはりこのはてなブログの方が圧倒的に楽に記事が書けます。

ということではてなブログとWordPressの両方を運営していくことになりましたが、今後WordPressでブログはやりたくないかなあ、と思っています。唯一WordPressでいいなと思ったのは、テーマが多いことですね。

GoogleMapでMarkerとGeoJSONのプロット速度をゆる〜く比較する

$
0
0

うーむ、まあ、そうですよね・・・
f:id:treeapps:20170111000706p:plain

現在新tree-mapsの開発中なのですが、折角なのでGoogleMap以外を使ってみようとか、プロットの方式を変えてみようとか、色々試行錯誤しています。

※ この記事はゆる〜く計測するものです。完全で間違いの無い計測を目指したものではありません。

地図の選択肢

まず最初にGoogleMapとOpenStreetmapでマーカー生成速度を比較してみたのですが・・・・・OpenStreetMapのマーカー生成処理速度、おっそ・・・・これは使えません。信じられない程の遅さです。

OpenStreetMapが遅いのでBingやYahooの地図も試したいところですが、今回は割愛します。

GoogleMap一択になったので、まずは普通にMarker APIでマーカーを生成するのと、GeoJSON APIで一気に座標を読み込んでマーカーを生成するのは、どれくらいの速度が出るのか、比較してみました。

計測で使うGeoJSONファイル

こんな感じです。

{"type": "FeatureCollection",
  "features": [{"type": "Feature",
      "properties": {},
      "geometry": {"type": "Point",
        "coordinates": [139.0274727702,
          35.720855461]}},
    {"type": "Feature",
      "properties": {},
      "geometry": {"type": "Point",
        "coordinates": [139.0456545884,
          35.720855461]}},
・・・以下略・・・

Marker APIによるマーカー生成

ソースコード

こんなクソコードでいい筈ありませんが、何となくの速度計測はできます。碁盤のように縦横整列した状態でピンが綺麗に生成されます。

縦200個、横300個、合計6万マーカーの生成を行います。

<!doctype html><html><head><metacharset="UTF-8"><style>html{height: 100%;}body{height: 100%;}#map{height: 97%; border: 1pxsolidgray;}</style></head><body><inputtype="button"value="マーカーを追加"id="add-marker"/><inputtype="button"value="マーカーを削除"id="delete-marker"/> マーカー数=<spanid="marker-num"></span>件 処理時間=<spanid="result">0</span><divid="map"></div><scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script><scripttype="text/javascript"src="https://maps.google.com/maps/api/js?key=${APIキー}"></script><scripttype="text/javascript">var map = null;var markers = newArray();var ROW_COUNT = 200;var COL_COUNT = 300;$('#marker-num').text(ROW_COUNT * COL_COUNT);google.maps.event.addDomListener(window, 'load', function(){var mapOptions = {        zoom: 8,        center: new google.maps.LatLng(36.72085546100, 141.8274727702),        mapTypeId: google.maps.MapTypeId.ROADMAP,        scaleControl: true};    map = new google.maps.Map(document.getElementById('map'), mapOptions);});$('#add-marker').on('click', function(){var startTime = newDate();for(var row = 0; row < ROW_COUNT; row++){for(var col = 0; col < COL_COUNT; col++){var lat = 35.72085546100 + (row / 100);var lng = 139.0274727702 + (col / 55);var latlng = new google.maps.LatLng(lat, lng);            markers.push(new google.maps.Marker({position: latlng, map: map}));}}    $('#result').text((newDate() - startTime) / 1000);});$('#delete-marker').on('click', function(){    jQuery.each(markers, function(){this.setMap(null);});});</script></body></html>

実行結果

処理時間の計測完了からマーカー表示が遅いですが、これはGoogleMapApiの処理は完了済みだが、マシンが頑張って大量のマーカーをレンダリング(表示)する処理に時間がかかっています。つまり処理自体は高速ですが、マーカーの描画の時間がかかっているわけです。

f:id:treeapps:20170110232304g:plain

GeoJSON APIによるマーカー生成

GeoJSON

geoJsonという汎用空間データフォーマットがあり、ここ最近GoogleMapはこのGeoJSONに対応しました。

データレイヤ  |  Google Maps JavaScript API  |  Google Developers

ということで早速どれくらいの速度なのか、計測してみましょう。

ソースコード

例によって、こんなクソコードでいいのかは棚上げし、こんな感じで準備しました。

<!DOCTYPE html><html><head><metacharset="UTF-8"><metaname="viewport"content="initial-scale=1.0, user-scalable=no"><style>html,body,#map-canvas{height: 100%;margin: 0px;padding: 0px}</style></head><body><inputtype="button"value="マーカーを追加"id="add-marker" /><inputtype="button"value="マーカーを削除"id="delete-marker" /> 処理時間=<spanid="result">0</span><divid="map-canvas"></div><scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script><scripttype="text/javascript"src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=${APIキー}"></script><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script><script>var map;google.maps.event.addDomListener(window, 'load', function(){    map = new google.maps.Map(document.getElementById('map-canvas'), {        center: {lat: 36.72085546100, lng: 141.8274727702}, zoom: 8
});});$('#add-marker').on('click', function(){var startTime = newDate();    map.data.loadGeoJson('/assets/html/coordinates.geojson', null, function(features){        $('#result').text((newDate() - startTime) / 1000);});})
</script></body></html>

実行結果

f:id:treeapps:20170110233758g:plain

あれ、遅い。。。

GeoJSONファイルのパース処理の時間は追加される筈ですが、パース後は何か特別なマーカー生成をしてくれると期待して、Marker APIより高速になるかも!?なんて思ったらこれです。

件数が少ないのが悪いのかと思って、試しに縦400、横500の計20万件で試したのですが、結果はやはりGeoJSONの方が遅いというか、Marker APIはちゃんとプロットできましたが、GeoJSON APIは500エラーで取り込めず。

GeoJSONはポリゴンとか、マーカー以外の方が強そうなので、マーカー生成は素直にMarker APIでゴリゴリした方がいいのかもしれませんね。

おまけ

OpenStreetMapにMarker APIで2500件プロットすると、どれくらい時間がかかるでしょうか。

ソースコード

leaflet.jsを使っています。

<!doctype html><html><head><metacharset="UTF-8"><linkrel="stylesheet"href="https://unpkg.com/leaflet@1.0.2/dist/leaflet.css"/><style>html{height: 95%;}body{height: 100%;}#map{height: 97%;border: 1pxsolidgray;}</style></head><body><inputtype="button"value="マーカーを追加"id="add-marker"/><inputtype="button"value="マーカーを削除"id="delete-marker"/>マーカー数=<spanid="marker-num"></span>件 処理時間=<spanid="result">0</span><divid="map"></div><scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script><scriptsrc="https://unpkg.com/leaflet@1.0.2/dist/leaflet.js"></script><scripttype="text/javascript">var ROW_COUNT = 50;var COL_COUNT = 50;var ZOOM = 10;    $('#marker-num').text(ROW_COUNT * COL_COUNT);var map = L.map('map').setView([36.02085546100, 140.5274727702], ZOOM);    L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png', {            id: 'osmmap',            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);var markers = newArray();    $('#add-marker').on('click', function(){var startTime = newDate();for(var row = 0; row < ROW_COUNT; row++){for(var col = 0; col < COL_COUNT; col++){var lat = 35.72085546100 + (row / 95);var lng = 139.9274727702 + (col / 45);var marker = L.marker([lat, lng]).addTo(map);                markers.push(marker);}}        $('#result').text((newDate() - startTime) / 1000);});    $('#delete-marker').on('click', function(){        jQuery.each(marker, function(){map.removeLayer(marker)});});</script></body></html>

実行結果

f:id:treeapps:20170110235251g:plain

うーん、たった2500件なんですけどね。もしかしてleaflet.jsの処理速度が遅いとかですかね?それとも実装の仕方が間違っているとか。

雑感

何か根本的に私は何かを間違えているような気がして仕方ありません。

ちなみにGeoJSONファイルは、一旦緯度・経度のcsvファイルを用意し、以下のnode_modulesを使ってcsv -> GeoJSONに変換しました。
github.com

とりあえず普通にMarker APIでプロットした方がよさそうという事が解ったので、新tree-mapsの大量プロット機能はまたGoogleMapにしようと思います。

プログラミング関連技術は進化が速く書籍が発売されてもすぐ陳腐化する現状

$
0
0

書籍という形態がもうマッチしなくなってきているのかもしれませんね。

f:id:treeapps:20170114053420p:plain

最近思うのですが、「◯◯についての書籍が欲しい」と思ってamazon等で探してみても、全然見つかりません。見つかったとしても、新発売の書籍なのに古いシンタックスで書かれていて、本当に知りたい事・知っておいた方が嬉しい事が書かれていないことが増えています。

そうなってしまうのも、プログラミングに関する技術の進化が最近異様に早くなってきている気がしていて、その速度に書籍という形に落とす速度が間に合っていないのではないか?と思いつつあります。

最近書籍を購入していない現実

特にjavascript界隈はそれはもう凄い速度で進化しており、たった数ヶ月で激変してしまったりする事もあります。最近Reactの書籍を購入しましたが、購入した時点で既に2段階くらい古い記述方法でかかれており、より短くエコな記述方式でないため、書籍に書かれたやり方ではトレンドから外れてしまいます。(トレンドに追従する事が良い事ではありませんが)

コンテナ関連技術も進化速度が速く、シンタックスに後方互換はあるものの、結構な勢いで変化しており、より便利でエコな最新機能について書かれていない場合があったりします。

書籍を見ていると「最新版ならもっと短く簡単に書けるのに」というパターンが結構あるのですよね。書籍のみに頼ると、最新でエコなシンタックスを知らずに進んでしまう事もあるので、ちょっと怖いですよね。

書籍という形態

どうなんでしょうね。書籍という形態はこれからも維持できるのですかね。

以前からですが、日本初の技術が少なくなってきており、欧米初の技術が世界を席巻しています。

「欧米でとある技術が人気になる」 → 「日本でも話題になる」 → 「日本で使う人が増える」 → 「書籍化」 → 「既にメジャーバージョンが上がっている」

そうなると当然↑のようなフローになってしまい、書籍化の時点で周回遅れになりがちです。いざ書籍化しようとした時点で既にメジャーバージョンが結構上がったりしていたりするので、本を作るのと並行して、最新バージョンの新機能解説をおまけ程度に追加する形になりますよね。

java・C#・Ruby・Python・PHP等のプログラミング言語はバージョンアップが遅い(速いものもありますが)のでまだ最新バージョンに追従しやすいと思いますが、フレームワーク・ライブラリとなると進化速度は結構速いので、追従するのはかなり厳しい、というか無理なのではないかと思えます。

もし自分が本を書く側になるとしたら・・・・ちょっと恐ろしいですね。追従できる気がしません。

書籍の変わりになる?ものの登場

最善なのは公式サイトのリファレンス(大体英語)を読む事ですが、頭の中で英語を日本語に翻訳する際に、精密に翻訳できずに異なる見解のまま進んでしまったりするので、やはり自国の言葉で書かれたものがあると、認識の齟齬が起きにくかったりしますよね。

そういった問題を解決するため、Githubに書籍に書かれそうな知見を集約し、皆で修正するような流れがあったりします。単純にGithubにテキストをアップする場合もあるし、Node.js製のGitbookを使ってより情報を閲覧しやすくするような技術が登場したり。

Gitbook等の技術を見ていると、技術進化の速度に追従するには、変更があった差分のみを随時更新し、コンテンツ自体は誰でも追加・変更可能、という分業が向いているのかなあと思ったりします。

ただしそれにも弱点があって、優れた人達が集まってそれが更新されなければ、そのコンテンツの品質はどうしても上がりにくくなります。一方紙やkindleの書籍の場合は大抵著名な方が書かれる場合が多いので、最初から一定の水準はクリアされている場合が多いです。ここが恐らく最も難しい問題になると私は思います。

雑感

最近プログラミング関連の書籍を購入する機会は本当に減りました。紙媒体も電子書籍もあまり購入していません。購入しても「ああ、古いシンタックスで書かれている・・・」と落胆する事も多いです。年末に自宅にある書籍を確認したのですが、もう古すぎて役に立たないものが多かったので、大部分を売却してしまいました。

そうなってしまう場合が多いので、最近は「公式サイトの英語のリファレンスしか見ない」という方も多いのではないでしょうか。その場合は一定の英語力が無いと、「あれ、この文章、微妙に何言っているのか解らないぞ」という残念な事になります。「プログラミングやるなら英語は重要」とよく言われますが、本当にそうですね。英語を話せない・書けないのはまだしも、読む事ができないと、もう本当に損をするというか、周回遅れになりやすい状況です。

とはいえ何もかも英語のドキュメントを読もうとすると翻訳等で効率が下がるので、どうしても最初に日本語で解説された記事を探したいですよね。qiita等にはスキルの高い方々が欧米の最新技術についての記事が沢山投稿されますが、qiitaですら「この記事凄く良いのにバージョンが古い」と思う事が結構あります。「最新のシンタックスの記事はどこだ〜」と探し回るのですが、中々見つからなかったり。

そこで日本語サイトを諦めて言語未指定で検索すると、知りたい最新の情報が書かれている海外の個人ブログやStackOverFlowがすぐ見つかったりして、「ああ、やはり今より英語が読めるようにならないと本気でマズイ」と痛感する機会が増えました。


書籍の話から大分脱線してしまいましたが、最近の技術の進化速度を見ると、プログラム関連書籍の将来が心配になりますね。今後どうやって進化の速度に追従していくのか、追従していくのを諦める人が増えて書籍が衰退していくのか、Githubに知見が集約されていくのか、さあ、どうなるのでしょうね。

tree-mapsをフルリニューアルしました!

$
0
0

やっとお披露目できる状態になりましたよ〜

f:id:treeapps:20170211214804p:plain

今までのtree-mapsも内容的にそれほど悪くなかった(と思っている)のですが、流石にjQuery + Knockout.jsを何とかしたいのと、GAE/Javaのスピンアップ速度の遅さを何とかしたいと思っていました。

そこで去年くらいからこそこそと勉強していたのですが、ようやく今回リリースできそうな段階まで持っていく事ができたので、リリースしてみました。多分動くと思ったので、リリースしてやりました!

f:id:treeapps:20170211215301g:plain

※ 今回はかなり長文です。
※ 技術要素をつらつら書いていますが、精密な言葉は使っていません。あまり突っ込まないで下さい。

新tree-maps

↓↓↓↓↓↓

tree-maps: 地図のWEB TOOLの事ならtree-mapsにお任せ!

↑↑↑↑↑↑
こんな感じです。GAEにはバージョンという概念があり、旧tree-mapsのアプリもいつでも動かす事ができます。

もし旧tree-mapsを使いたい場合、トップページの下部か、左サイドナビの「旧tree-maps」リンクから使う事ができます。

インフラ

インフラはGAE(Google App Engine)のまま、変更はありません。

が、しかし、ランタイムを変更しました。今まではJavaでしたが、今回はGolang(GO言語)にしました。

Golangは何を解決したのか

Golang選択の理由は非常にシンプルで、Javaよりも高速で、Javaよりもコンテナのスピンアップ速度が遥かに高速だからです

どれくらい高速なのかは以前以下の記事を書きましたので、合わせてどうぞ。
www.bunkei-programmer.net

今回Golangをランタイムとして使ってみて、以下の感想を持ちました。

ランタイム書きやすさスピンアップ速度処理速度
Java☓☓☓
Golang

本当はJavaで書きたかったのですが、Javaのスピンアップの速度の遅さが半端ではなく、他のメリットを全て帳消しにする程のものだったので、Golangを採用しました。JavaはIDE支援が超強力なので、目茶苦茶書きやすいのです(完全に個人の主観ではありますが)。

スピンアップが遅いとコンテナが起動するまでの速度が遅い=画面が表示されるまでが遅いという事になるので、ユーザの離脱率が跳ね上がるわけです。Golangをランタイムにする事で、それを大幅に軽減する事ができます。単純に動作速度もトップクラスの速度なので、Ajaxでjsonを取得するリクエストをガンガン投げても高速にレスポンスを返すこともできます。

スピンアップ・スピンダウンとは

念のため説明しておくと、GAEのインフラは、一定時間サイトにアクセスが無いと、なんと(仮想)サーバが削除されます。これはリソース(物理的なメモリ消費量
ディスク使用量・電力・通信量)を出来る限り節約するためです。このサーバごと削除される事をスピンダウンと言います。

スピンダウンしている状態からサイトにアクセスされると、Google内部ではサーバの構築・アプリケーションやフレームワークの起動が行われます。このGoogle内部のサーバ構築をスピンアップと呼びます。サーバはフルマネージドで恐らくVMWare的な仮想環境上で管理されており、プログラマブルにサーバのインスタンスの生成・削除が行われます。

この考え方は今AWSで流行っているAWS Lambdaと同等です。GAEがアプリケーション単位なのに対し、AWS Lambdaは関数単位という違いはあります。

バックエンド

まず、リニューアル前のバックエンドについて説明します。

フレームワーク

No使用フレームワークの変遷
1Seasar Slim3
2Play framework v1
3Servlet v2.6

実は3回フレームワークを変更しています。Slim3はすぐやめたので実質カウントする必要はないですけどね。

最初はPlayのv1系で頑張ってGAEを開発していたのですが、途中で開発が全くされなくなり、音沙汰もなくなってしまい、終いにはデプロイコマンドが失敗する状態にまでなってしまいました。GAE上でスピンアップからPlayの初期化速度も非常に遅く嫌気が指していたので、デプロイ失敗を機に素のServlet、しかもv2.6系に回帰しました。

Servletに回帰した事でスピンアップ後のアプリの起動完了までの速度は大分速くなりました。が、フレームワークの利便性が無くなり、開発は厳しくなりました。

そしてリニューアルです。使用言語はGoogleのGolangなので、Golangのフレームワークを選定する事になります。前述のスピンアップ速度調査記事ではGin(ジン)を選択していましたが、今回新tree-mapsではecho(エコー)フレームワークを選択しました。

echo選択の理由は、Ginよりも更にシンプルで高速(多分)で、何よりGithub上のコミット数がGinよりも多く、これは開発が活発に行われているな!と確信したので、echoにしました。まあGinもechoも対して機能の違いは無いので、どちらを選んでもいいと思います。

とまあフレームワークのお話をしましたが、正直バックエンド側はルーティング以外何もしていません。。。今回バックエンドが行っているはなんと以下だけです。

  • ルーティング
  • リダイレクト(実はプロットをprotとtypoしていたのは内緒)
  • htmlテンプレート(divが一個あってgooglemapのjsとadsenseのjsをロードする程度)

.goファイルの数はなんと2ファイルだけです。しかもトータルで300行で収まるくらいです。最近の開発ではサーバ側はjson生成器みたいなものなので、これでよいのです。下手にhtml生成をサーバ側で頑張らない方針なのです。

ビルド

単にgoapp deployのみです。変に頑張ってはいません。

フロントエンド

カオスを極めるフロントエンド

さて、問題のフロントエンドです。バックエンド重視からフロントエンド重視にトレンドは推移し、未熟極まるフロントエンドがここ数年で常軌を逸した進化をしており、沢山のフレームワークが現れ、あっという間に廃れていきます。それは今も尚続いています。

javascript界隈

javascriptのフレームワークの栄枯盛衰は以前より少し収まり、以下で落ち着いたと思われます。

※ FW = フレームワーク(framework)

FW名学習コストメリットデメリット
Reactビューのみなので他の技術要素との組み合わせがし易い。FWではないので他機能の組み合わせが必要。
Angularフルスタック。全部入りなのでライブラリの組み合わせで迷わない。angular wayの縛りの強さ。
Vue他FWのいいとこ取りで学習コストが低い。小中規模に強い。大規模は厳しい?
jQueryデザイナが片手間にできるほど簡単。もう時代に則さない。大規模は地獄。

大体こんな感じではないでしょうか。他FWも沢山ありますが、メインはこれらに絞られた感がしています。ReactとAngularについては正直結構学習コストは高いです。Reactはビューのみなので学習コストは低い!という声もありますが、アプリはビューだけでは済まないので、他ライブラリ等を総合すると、結局学習コストは高いと思います。

Angularについては、Angular v1系から言われていますが、依然として学習コストは高いです。が、フルスタックなので、Reactほど他ライブラリとの組み合わせに疲弊する機会は少ない???ので、学習したなりのリターンはありそうです。

一方Vue.jsですが、javascript界隈の壮絶で地獄の栄枯盛衰を横目に、最近非常に注目を浴びて急上昇しています。ReactやAngularよりも学習しやすく、小〜中規模に優しいため、参入障壁が低めなので、人気が爆発してきています。最近ようやくレガシーIEの排除も本格的に進み、今までレガシーIEのせいでVue.jsの採用を見送り、Knockout.jsを選択していた人もいると思いますが、その呪縛も薄れ、Vue.jsを採用しやすくなったのも、人気上昇の要因の一つだと思われます。

jQueryは、デザイナとの協業を考えると、根絶する事は不可能に近いと思います。特に外部にデザインを発注する場合、jQuery必至になりがちです。jQueryプラグインの資産も膨大で、jQueryに依存し過ぎてしまっているので、jQueryの脱却はほぼ無理なのでしょうね・・・とはいえjQueryのお手軽さは非常に優秀で、ReactやAngularやVue.js等と違って開発環境構築は不要で、jquery.jsをロードするだけでサクッと開発が始められます。ちょっとしたデモなら、jQueryが活躍する場面は多いです。

CSS界隈

一方CSSは静かだと思いきや、SCSS、LESS、Stylus、SASS等が登場したり、BEM等の記法論争があったり、実は水面下で激しい論争が繰り広げられていました。それらも少し落ち着きつつあるようで、どうやらメインストリームはPostCSSになりそうな感じですね(賛否両論あると思います)。

ビルド界隈

フロントのビルドについては、Gruntは廃れ、Gulpとwebpackの2強の状態はそれほど変わっていないと思われます。強いて言えばGulpを排除してwebpackのみで完結するパターンが増えていると思いますが、まだまだgulpも使われているようです。

AltJS

残念ながらCoffeeScriptはHubotスクリプト以外ではほぼ見なくなりました。元々AltJSは歴史が浅く、論争も少ない方なので、CoffeeScriptの衰退以外はほぼ変わっておらず、メインストリームは以下だと思われます。

名称メリット
Babel静的型付け無しで、利用例は多くお手軽に導入できる。
TypeScript静的型付け有りで、Microsoftが開発しているので採用しやすい。

その他にはGoogleのDartもありますが、これはメインストリームとは言い難い状況のようです。とりあえずBabelかTypeScriptのどちらかを選択しておけば間違いは無い感があります。

UIフレームワーク界隈

flexboxベースのUIフレームワークが増えてきていますが、依然としてBootstrap、Foundationが多いと思われます。Material Design Liteはスクロール周りにバグ?を抱えていたりして採用しづらいです。最近ではReactやAngularにベッタリなUIフレームワーク、例えばmaterial-ui、Blueprint、Angular Material、OnsenUI、ionic等も続々登場しています。

新tree-mapsではどうしたか

さて、論争を説明したところでようやく本題に戻ります。(それだけフロントエンド界隈が激しいので技術選定も悩みました)

新tree-mapsでは大まかに以下を選定しました。

種別名称
アーキテクチャシングルページアプリケーション
ビルドwebpackのみ。
AltJSBabel。ES2015,ES2017
FW・ライブラリreact, react-router, axios
CSSPostCSS, sugarss
UIフレームワークmaterial-ui

reacを選択しました。はい。

Angularと迷ったのですが、私が迷っていたタイミングではまだAngular2が正式リリースされておらず、タイミングが微妙だったのと、リリース当初のAngura2は微妙な空気があったので、Reactとなりました。Vue.jsも迷いましたが、会社の業務の採用例的にも今回はReactに軍配が上がりました。reactなので、今回はSPAにしています。

UIについては、最初はFlexboxベースのbulmaを使っていたのですが、欲しいコンポーネントが足りないので見送り、マテリアルデザイン全開で、採用例も多いmaterial-uiにしました。コンポーネントも多く、これは素晴らしいです。

CSSについては、そもそもmaterial-uiがデザインのほとんどを吸収してくれるので、ちょっとしたCSSが書ければ何でもいい、生cssでも全然問題無いのです。まあPostCSSが流行ってきているので、折角なのでPostCSS+sugarssにしました。残念ながら今回cssはたった1ファイル、しかも140行しか無いのですけどね。。。

苦労した点

props???

今回React + Babelなわけですが、jsx記法は思ったよりすんなり入れました。キモい感はしますが、別にいいんじゃない?程度の感覚です。jsxのキモさよりも、reactのpropsを理解するのに少し時間がかかりました。stateは単純に現状の状態、例えばテキストボックスの入力値やラジオボタンの選択値がstateに当たるわけですが、propsとは???と中々理解が進みませんでした(今もですが)。

this???

以下は実際のtree-mapsのジオコーディングするボタンのコンポーネントですが、「は?何このthis。どこに対するthisなんだよwwww」「this.handleSubmitのhandleSubmitって何だよ。関数なのかプロパティなのか解らねーよ!」というような感じで、どこの事を指しているのか、どこの何を呼ぼうとしているのか、がさっぱり解らず、かなり苦労しました。。。賢い方たちはこれを瞬時に理解できるようで、羨ましいです。

<RaisedButton
    label={'ジオコーディングする'}
    secondary={true}
    icon={<FontIcon className="muidocs-icon-custom-github"/>}
    onTouchTap={this.handleSubmit}
    disabled={this.state.buttonDisabled}
    style={{width: '100%'}}
/>
bind地獄

bindについても最初は「はあ!?何だこれ。bindだらけになるじゃねえか!!!」と思っていました。

this.handleFunc1 = this.handleFunc1.bind(this);
this.handleFunc2 = this.handleFunc2.bind(this);
this.handleFunc3 = this.handleFunc3.bind(this);
・・・以下thisを使う関数毎に書く・・・

実際にplot画面では20個近くの↑こんな感じのbindが並びました。で、java脳な私は「こんなのアノテーション一発で済ませろよボケェ!」と思っていたら、ありました。ボケェなのは自分でした
github.com
今回は自分だけが開発するアプリなので、class定義に@autobindしてしまい、bind地獄から解放されました。

redux

reactのpropsやbindなど、基礎の理解が全然進まず、FBの公式チュートリアルをこなしても、いざ「これを作ろう!」と思った時にチュートリアルでやった事が全然活かせず、四苦八苦していました。

結局state管理については各コンポーネント内で行っており、reduxは使いませんでした。reduxを使うとバケツリレーを避けられるというメリットを知りつつ、react-routerを動かすのに苦労したりして、結局勉強する間もなかった(言い訳)ので、採用には至りませんでした。

今になって思いますが、あると凄く嬉しいですね。。。たかがメニューの選択値をAppBarに表示するのだって、reduxが無いとバケツリレーを凄く頑張らないといけないし、グローバルな状態管理は無いと恐らく破綻するなあ、という予感が今頃しています。

しかし今まさに行われているreduxのmiddleware論争は熾烈を極めつつあり、結構ガッツリ変わりそうな予感がしているのですよね。この辺は今丁度ホットな戦場なので、好きな方は是非論争に参加してみて下さい。。。

material-ui

最初はBootstrap的に扱えるのかなあ〜、とか楽観視していたのですが、違いました。

書き方が解らねえ・・・解らねえぞ!!!

たかがボタン一つ表示する方法も解らない。最初は絶望しました。今となっては理解できて便利に使っていますが、最初の絶望感は半端ではありませんでした。

<RaisedButton
    label={'ジオコーディングする'}
    secondary={true}
    icon={<FontIcon className="muidocs-icon-custom-github"/>}
    onTouchTap={this.handleSubmit}
    disabled={this.state.buttonDisabled}
    style={{width: '100%'}}
/>

↑前述の例ですが、「RaisedButtonって何?どこでどうやって定義してるの?」「アイコンどうやって表示するの?」「イベント貼るのどうやるの?」「何かスタイルの定義が効かないんだけどなんで?」等、様々な困難が待ち受けていました。

結局、RaisedButtonはmaterial-uiが事前に用意してくれている、htmlを出力するコンポーネントで、それをimportすると使えるようになります。

import RaisedButton from 'material-ui/RaisedButton';

iconの行を見ると一瞬「は?ぶちのめすぞ!!」等と思いますが、iconというpropsに、FontIconというコンポーネントを渡しているだけなのです。javaで言うと、RaisedButtonというクラスに対してsetIcon(FontIcon fontIcon) しているだけなのです。

イベント定義についてはもう「リファレンス見ろ」なわけですが、thisというのはReact.Componentクラスを継承したクラス、つまり今自分がいるクラスの事で、javaでいうと、自分のクラスのメソッドを呼んでいるだけです。handleSubmitはプロパティではなくメソッドです。

classNameでスタイルを定義しても効かない問題ですが、これは単純で、material-uiのコンポーネントの中にはインラインでcssを定義しているものがほとんどなので、classNameよりも優先されるインラインcssに負けてしまい効かない、という事でした。これは抜け道が用意されていて、propsにstyleを使うと、initialStateより優先してstyleを使ってくれるので、結果的にデフォルトのインラインcssに勝つ事ができます。react-sidebarではこの問題に嵌まりました。。。嵌ったおかでで色々学べたのですけどね。

SPA???

もう全然解らん。意味解らん。というのが最初の感想でした。

簡単に言うと、昔frameでhtmlを切り替える手法(古すぎるか・・)がありましたが、あれです。ajaxで各ページを読み込んでいるのではないです。コンポーネントを切り替えてもhttpリクエストは走りません。しかしこれだけだと、ブラウザのアドレスバーのURLは変わりません。

そこでPushStateを使います。PushStateによって「ブラウザのアドレスバーのURLを変える」「ブラウザの閲覧履歴を追加する」という2つの事を行う事ができます。実際はhttpリクエストせず画面遷移もしていませんが、擬似的に画面遷移した時と同じ事をしているのです。

PushStateでURLを変える事は解りました。という事は、サーバサイド側でそのURLを受け付ける事ができるルーティングが必要になります。ここでまた困惑します。サーバサイドのルーティングですが、対象ページのURLのルーティング設定はしますが、全て同じhtmlを返却します。「/」でアクセスされても、「/plot/」でアクセスされても、全て同じhtmlを返します。結局のところhtml内ので「id="hoge"」に対してreactがコンポーネントをガッツリ描画するので、同じhtmlを返せばいいのです。

サーバサイドのhtmlテンプレートエンジンで頑張っていた部分の大部分は無くなり(headタグ内は必要)、全く同じhtmlを返す事になります。そこから先はreactがhtml生成を頑張ります。つまり、サーバサイド側は本当にやる事が無くなります。

これでメニュー変更が画面遷移もhttpリクエストも無く実行する事ができ、PushStateでアドレスバー更新・履歴追加も行い、/plot/等でアクセスされてもサーバサイドのルーティングによってindex.htmlが返る。つまりこれがSPAなわけです。実際サイトを使って頂くと解ると思いますが、「くっそ動作が軽い」「一体どこでhttpリクエストしてるんだ!?」と思う程軽快です。時間がかかるのはせいぜいjsの初期ロード程度です。

webpackによって複数のjsは1ファイルにまとめられ、cssはjs内に隠蔽され、最終的にhtmlのbodyタグ内はdivタグ1個と、bundleされたjsを1ファイル読み込むだけになりました。当然bundle.jsはファイルサイズが大きめになりますが、deflateしてgzip圧縮転送したり、エッジキャッシュを効かせればそれを軽減できます。

データソース

所謂データベース等ですが、全く未使用です。制限が厳しすぎるが性能劣化が無いDatastoreがありますが、今回未使用です。

現状では特に使う部分はありませんね。

新tree-mapsはどう変わったか

技術要素の刷新によって一体tree-mapsはどう変わったのでしょうか。

高速になった

高速になるというのは、言うのは簡単ですが、実際にそれを行うのは非常に難しいです。処理速度は技術要素に依存する面も強いので、古い技術を使っているとそもそも高速になりにくい部分もあります。今回は以下を解決する事で、サイトの高速化を図りました。

スピンアップ

ランタイムをGolangに変更したい事で、スピンアップが激遅問題は解決しました。これでいつアクセスしても常に超高速です。

SPA

SPAにした事で画面遷移が無くなり、サーバサイド側で行う事が減ったので、画面が表示されるまでの速度と画面上の操作は非常に高速になりました。

bundle

webpackによって1ファイルのjsに全て押し込めた事で、ロードするjsの量が減り、更にdeflate + エッジキャッシュによってjsの読み込みも高速化しています。httpリクエスト数も減ったので、キャッシュが有効ならhttpリクエスト数も非常に少なくなります。

機能追加のし易さ

reactもある程度使っていると、「あ、これjQueryでは厳しいけど、reactだとできるわ」という部分が出てきます。特に「チェックボックスがこういう状態で、テキストエリアがこういう状態の時、ボタンは非活性になる」等の、複雑な状態が絡む処理はreactは圧倒的に楽です。jQueryと違ってdomの状態を気にする事も無く、単にstateの値だけで判断でき、stateの状態が全てのイベントとリンクしてくれるので、これはバグも起きにくいなと感じました。

モバイルファースト

material-uiを使っていると強制的にモバイルファーストになります。旧tree-mapsではTwitterのbootstrap v3で一応レスポンシブ対応していましたが、モバイルは全然意識していませんでした。今回はmaterial-uiなので、Androidのスマホを使っている方には見慣れたUIだと感じると思います。

Googleもこのモバイルの波を意識し、ついにPCサイトよりモバイルサイトの方に軸を変更してきているので、例えそのサイトがモバイルで使われる類のものでなくてもモバイルファーストでなくてはならないのです。

これってtree-mapsのようなどう考えてもPCでしか使われないサービスにとってはモバイルの存在は邪魔でしかなく、モバイルファーストの強制も何だかなあ、という感じがします。実際GAで見ても、PCが95.6%で、残りがモバイル等です。これでモバイルファーストを強制されるのはねえ・・・

新機能について

ジオコーディング・逆ジオコーディング

ジオコーディング or 逆ジオコーディング実行後、入力・変換された緯度経度を使って即座にgoogle mapに描画するようにしました。

プロット画面

マーカーの色を選択できるようにしました

以前から要望がありましたが、googlemap標準の薄い赤のピンだけでなく、黄・緑・白・青・紫を選択できるようにしました。

通常の赤いピン以外の表示ですが、google earth等のgoogleのアイコンを利用する事で、赤いピンと同等の描画速度を実現しました。google製のピン以外では処理速度が遅く、正直使い物になりませんでした。

中心点を選択できるようにしました

入力された緯度経度の1行目を中心点とみなすか・みなさないかを選択可能にしました。更に中心点のアイコンをgooglemapでお馴染みにのピンではなく、独自の家マークアイコンに変更する事ができるようにしました。更に、中心点はプロット時にぴょんぴょんアニメーションするようになり、より目立つようにしました。

中心点から半径nメートルの円を描く

他のサイトで円を描く機能を持つサイトがあるので、パクってみました。

この機能の使い道としては、中心点を基準に円を描くので、中心から見て半径nメートルに含まれるピンの位置を視覚的に確認できる、といったものを想定しています。

ちなみに円の色と円の半径を変更可能です。

元の位置に戻る

プロットしていて迷子になった事はありませんか?沢山の緯度経度をプロットしていて、地図をスクロールしていると「あれ?最初の位置ってどこだっけ、戻りたいんだけどなあ」と思う事ってあると思います。そんな時、「元の位置に戻る」ボタンをクリックしてみて下さい。クリックすると、最初にプロットした時の位置・ズームの状態が復元されます。

クラスターに対応

これは旧tree-mapsにもありましたが、使い方がいまいち解らないという声がありました。

簡単に説明すると、現実世界に例えると、例えばビルがあってそのビル内の部屋毎に緯度経度を入力したとします。それをプロットしまうと、ビル内の部屋は全て同一の緯度経度なので、完全に同じ位置に複数のピンが重なって表示される事になります。

すると、見た目はピンが1件しか無いように見えるのに、実際は100件重なってました〜、なんて事がおきます。それを回避するため、一点の範囲に存在するピンをまとめ、その範囲に含まれるピン数を表示する、というピンのまとめ表示をする事で、重なってしまう問題を解決します。

これは地図のズームを引いた場合、例えば日本全体を表示した場合に如実に現れます。東京都にピンを1万件配置しても、日本全体が見えるくらいまで引いてしまうと、ピンが目茶苦茶重なってしまいますね。その時クラスター表示なら、まとめて表示してくれるので、ちゃんと視覚的に(数値で)確認する事が可能になるわけです。

クラスターのサイズ、ピンをまとめる範囲を変更できるようにしていますが、デフォルトの40pxが最も扱いやすいまとめ範囲だと思ったので、とりあえず40pxのまま使うとよいと思います。範囲を狭めるとクラスター同士が重なってしまったりするので、40pxというのは絶妙な範囲なのです。

ヒートマップに対応

これはtree-maps開発ブログの方で既に告知しましたが、ヒートマップに対応しました。

クラスター表示より沢山の緯度経度を扱う事ができます。試しに20万件の緯度経度で試してみましたが、地図自体は軽快に動作しました(但し地図描画するまでが遅い・・・)。

使い道は・・・ユーザにおまかせします。

バグ等の解決していないこと

隠さずに全部書いてしまいますよ。

スクロール位置が持続される

例えばトップページで下の方までスクロールします。その状態で左サイドナビで他のメニューを選択します。するとあら不思議、選択したメニューのスクロール位置がトップページのスクロール位置になっています。スクロール位置まで記憶されているのです。

以下を参考にしみたのですが、効かないのです。。。
github.com

使用しているreact-routerはv3.0.0なのですが、どなたか解決方法をご存知の方がいれば教えて頂きたいです。。。

SNSのタイトルがおかしい

厳密には、URLは正しいがタイトルは一つ前の画面のものになっている、です。

react-document-metaを使って、メニュー切替時にtitleタグ・meta descriptionを差し替えているのですが、差し替え後のtitleタグが取れていません。綺麗な解決方法は思いついていませんが、reduxを導入し、メニュー切替時にstateにtitleタグを保存し、snsボタン描画時はそのstateからtitleタグを取得する、みたいにすれば上手くいきそう?と模索中です。

DMS => DEG変換機能

特定のフォーマットに従っていないと変換できません。

以前はjavaでゴリゴリしていたのですが、golangであのゴリゴリをするのが面倒だったので、ライブラリを使ってごまかしています。。。優先度低めでいずれ直します。

サーバサイドレンダリング(SSR)

多分やりません。少なくともGolangではやりたくありません。。。node.jsだったら、これくらいの規模のアプリならやってもいいかな?と思います。

これから開発するかもしれない機能

グループプロット

これはアンケートをした時にも書いていますが、プロットをグループ単位で行えるようにしたいと考えています。

例えば、とあるイベントで、男性グループと女性グループがいるとして、人間全てに緯度経度データを持っているとします。その時、視覚的に「男性の場合はピンを青色に」「女性の場合はピンを赤色に」というグルーピングをしたい場合、現状のtree-mapsではできません。

男女だけでなく、「今日の緯度経度」「昨日の緯度経度」を同じ画面で確認したい場合も、グループを分けて色が異なるピンでプロットする事ができれば、視覚的に確認する事ができます。

これは個人的に非常に有用な機能だと思っているので、開発する予定です。

入力値の保存・復元

例えばtree-mapsのプロット機能を使う際に、必ず中心点を使う、更に色も毎回同じにしている、等という場合、毎回のその選択するのは煩わしいですね。それを解決するため、ローカルストレージに状態を保存し、次回画面表示時に使えるようにしようと考えています。

プッシュ通知

chromeでプッシュ通知ができるので、新機能通知機能ができるかな?等と適当に考えています。

アプリ化

アンケートで「webアプリかネイティブアプリかどっちがいい?」と聞いたのですが、ほぼwebアプリでした。理由は「環境構築が不要」でした。という事で旧tree-mapsと同様に今回もwebアプリとしています。

もしネイティブアプリであればもっと沢山の緯度経度を扱えると思いますが、環境構築(アプリのインストール)が鬼門の場合があります。会社でtree-mapsを使う場合にネイティブアプリだと、「許可されていないアプリのインストールは許可されない」等の壁が立ちはだかります。するとそもそもtree-mapsを使う事すらできない場合があるので、webアプリだと助かる場合が多いようです。

とはいえアプリ化すると嬉しい人もいると思うので、Electronでアプリ化できるかな〜?、等と安易にアプリ化を模索しています。

GAEで欲しいもの

GAE/node.jsが切実に欲しい

です。正直Golang+echoでは開発がし辛い事は間違い無いので、多少の速度を犠牲にしてもnode.jsにしたいです。

一応flexible environmentでnode.jsは動かせますが、それはもうStandardなGAEではなく、GCE上で動くGAEで、有料です。私が望んでいるのはflexible environmentではない真のGAE、フルマネージドなnode.jsランタイムなのです。これからの開発ではnode.jsが増え続けると思われるので、もしあれば皆使うと思うのですけどね。

あと、一応ですが、去年GCPに東京リージョンが出来たので、GAEで東京リージョンが選択できるようになりました。東京リージョンに変更する事で得られるメリットについては以下の記事をご覧下さい。
www.bunkei-programmer.net

雑感

もうね、FacebookのザッカーバーグCEOの以下の言葉通りです。
f:id:treeapps:20170211215301g:plain
(本当は「完璧を目指すよりまず終わらせろ」という意味です。。。」

グループプロット機能を開発してからリリースしてもよかったのですが、それだとリリースするタイミングが遅くなります。それに、色々と開発してみたい機能はあるので、いつまで経っても正式リリースに至らない事だってあります。

特に技術要素については顕著で、次から次へと有用なフレームワーク・ライブラリが登場してしまうので「よーし、今回はこの機能を使うぞ!」→「ん?なんと!こんな凄いライブラリが出たのか!」等とやっていると、本当にいつまで経っても完成に至りません。以前以下の記事を書きましたが、プログラムを書く人にとって技術要素は甘い罠なのです。
www.bunkei-programmer.net

これはビジネスにも言えると思います。新しい技術要素を使ったり導入する事が目的となってしまい、利益を生み出す事の優先度が下がってしまう事がよくあります。確かに最新機能を使う事でインフラ維持費が減ったり保守コストが下がったりしますが、手段を目的としたくはありませんね。単にreactやangularが流行っているから導入するのではなく、それを使って何が解決できるのか、何を解決しようとしているのか、それらが自分達が抱えるどんな問題を解決してくれるのか、を考えて技術選定したいですね。


そして今回サイトの高速化と利便性向上と開発のし易さを解決するためreactを勉強しましたが、私のナメクジレベルの脳みそでは学習コストはかなり高いと感じました。もし自分がreact未経験者のチームを抱える事になったら、結構厳しいなと感じています。また、デザイナとの協業は難しいなと感じました。そもそもwebpackの使い方等のレクチャーが必要だったり、react以前の壁が高いのです。

reactをやるためにはjsxが動かないといけない、jsxを動かすにはbabel等が必要になる、babelを使うにはwebpack等が必要になる、webpackを動かすにはnpm・yarnを使う必要がある、npm・yarnを使うためにはnode.jsのインストール(nodebrewなど)が必要になる、node.jsを扱うには、ローカルにインストールするかdockerなどを使う・・・というように、あれをするためにはこれが必要になる、という事がフロントエンドには多く、そもそもやりたい事をやるところまで辿り付くのが大変です。

勿論webpackを使わない等の省略する選択肢はありますが、現実的にそれらを省略するのはトレンドから明らかに外れてしまう恐怖等があったりして、結局世間で言われているベストプラクティスのようなものに頼る事になります。すると学習コストがどんどん積み上がり、結局採用には至らない、なんて事も多いのではないかと思います。

結局今回私はその学習コストを支払ったのですが、「ああ、これが数年前に言われていた日本は遅れているという事か」という事が今頃はっきりと解りました。jQueryの世界とreactの世界は全く異なり、開発スタイルも大きく異なります。jQuery(しかもv1系くらいの頃の)の開発では如何に無駄で効率の悪い開発をしていたのか、如何にバグを誘発する開発をしていたのか、を身をもって知る事になりました。

ReactやAngular未経験者はいつかこれらを学習する必要があると思いますが、学習する際は詳しい人が側にいると非常に捗ると思います。特に両者は関連する技術要素が非常に多く多岐にわたるので、協力者がいると全く学習効率が違うと思います。私はサーバサイド側の人間で一人でこそこそ勉強していたわけですが、沢山嵌まり、沢山ググりました。そのコストは大きかったです。とはいえ問題にぶつかり、それを何とか解決に至らせるという能力を養う事には繋がりましたが、フロントエンドに関してはそれはあまりおすすめしませんね・・・それだけフロントエンドの世界がカオスなのです。

かといってカオスな世界を避けてしまうと、気づいた頃には自分の持つ技術要素が時代遅れで取り返しが付かない状況になったりする恐怖があります。もういっそ技術を捨て、SEやマネジメント方面へ行く選択肢もありますが、「自分の所有スキルが化石化してしまったので、仕方なくSEやマネジメントを始める」なんて動機でキャリアチェンジするのも怖いですよね。



・・・・などという事をボケーっと考えながら勉強していたのですが、真実を話すと、react採用の一番の理由は別に「あれを解決したい」とかじゃないんですよね・・本当は「世間の、それも日本ではなく世界のトレンドから取り残されて化石化するのが怖い」というのが一番の理由です。フロントエンドだけでなく、docker周りも避けてしまうとマズイなあという事も認識しています。

まだまだ勉強が足りていませんが、おっさんになったからといって勉強を避け、化石にならないようにしたいと思っています。これからIT業界はますます既存のSEの存在意義等が問われていくと思っていて、技術を知らない=存在価値の低下は必至だとも私は思っているので、頑張りたいですね。

なんだかtree-mapsの話とかけ離れてしまいましたが、こんな事を考えながらtree-mapsの開発をしていました。今回大きな告知なのでこっちのブログに書きましたが、移行は以下のtree-maps開発ブログに書いていきますので、今後ともよろしくお願いします!
tech.tree-maps.com

Google Maps Apiで標準の赤以外のマーカーを高速にプロットする

$
0
0

まあ別に高速というほどではないのですけどね。

f:id:treeapps:20170111000706p:plain

tree-mapsをリニューアルした際に、標準の赤以外のマーカーの実装をしました。その時、どのように高速にマーカーを配置するのかについて、備忘録的なメモを残しておきます。

使用するAPI

赤以外のマーカーの画像配置は、google.maps.MarkerImageを使用します。

MarkerImageなので、画像のURLをオプションに指定する必要があります。

では軽い画像、CDN等でキャッシュされていて、大量アクセスが飛んでもびくともしないマーカー画像はあるでしょうか?

MarkerImageで使う画像

新tree-mapsでは、以下をマーカー画像として使いました。

sites.google.com

kml4earth.appspot.com

google mapには赤い画像しか無いので、他のgoogleのサービスの画像を使ってしまえばいいのです。ドメインを見ると解りますが、両者ともgoogleが提供しているマーカー画像集となっています。(片方はgoogle earthのマーカー画像です)

これらのマーカー画像は If-Modified-Since をきちんと返してくれるので、更新が無ければ304 not modifiedとなり、画像をリクエストしにいかなくなります。従って画像の取得は非常に高速になります。

例えば緑のマーカーを表示する場合は、以下のようにします。これで標準の赤マーカーと同等の速度でプロットできます。

new google.maps.Marker({
    map: map,
    position: latLng,
    icon: new google.maps.MarkerImage('http://maps.google.co.jp/mapfiles/ms/icons/green-dot.png')
});

※ map, latLngは適当に変数に当てはめて下さい。

別のマーカー画像

以下のサイトでもマーカー画像を配布しているようです。

www.googlemapsmarkers.com

こちらのサイトはマーカーのカラーをURLで指定可能で、マーカー内にリテラルを埋め込む事もできるようですが、リテラルを埋め込むのはやめたほうがいいですね。

恐らくリアルタイムに画像を生成されていますが、行番号等を埋め込んでしまうとキャッシュが効かない状態に陥ってしまい、マーカー画像のhttpリクエストが物凄い沢山飛んでしまい迷惑をかけてしまいます。

外部のサービスを利用する際は迷惑がかからないようにしたいですね。

雑感

正直、最初はまさかgoogle earthのマーカー画像をgoogle mapで使う発想はありませんでした。やはりstack overflowさまさまですね。

google提供の画像であれば、いくらいじめてもまあ大丈夫でしょう。きっと。。

例のアレの影響?ではてなブログ+独自ドメインのアクセス減が結構酷い件

$
0
0

独自ドメインとか関係ないんじゃ〜
f:id:treeapps:20170104180018p:plain

お金さえ稼げれば嘘情報やオカルト情報を垂れ流しても構わない、等と考える企業に鉄槌を下すフィルタが最近実施されましたね。
jp.techcrunch.com

当ブログではどうだったのか

あちこちで経過報告等がされていると思いますが、私も簡単にしておこうと思います。

結果から言うと、フィルタ適用前と比較して月間15%程度減少し、その状態が継続中なのであります。

これはなかなか痛いですね。

恐らく過去の記事がオリジナリティが低く、低品質なのだと思います。多分。

皆さん過去の記事をリファクタしていってるのですかね。

独自ドメインについて

当ブログは、はてなブログPRO + 独自ドメイン構成ですが、カッチリ影響を受けたっぽいです。

ここで私の推測を言うと、

  • 独自ドメインだからといって影響を逃れる事はできない
  • 独自ドメインの元となるドメイン、treeapps.hatenablog.comのサブドメインがはてなブログだから、実はそっちが悪い可能性も?
  • ひょっとしてはてなブログやめてWordPressにしても関係無いのでは?

と思っています。Googleはおバカではないので、このブログがはてなブログである事は解っている筈なので、単純に独自ドメインにしても駄目ですね。

また、WordPressにしたからフィルタの影響を受けにくい!というのは、今は仮にそうだったとしても、Googleがそれを放置すると思えません。目標は「健全なWEB」なのですから、はてなブログだろうがWordPressだろうが、等しく品質フィルタを適用しないと、公平ではないし、いつかバレて袋叩きにされるでしょう。

なので、安易に別ブログに映ったりWPを運用してみたりするのは一旦注意して再考した方がいい?と思っています。もしはてなブログのせいではなかったら、単に今までの検索エンジンからの評価を一旦全て失うだけという、どうしようもない事になってしまいますからね。

ブログ以外はどうか

私はGoogle App Engineを使ったサイトを2つ、WPblogを1つ、GoogleCloudStorageをWEBホスティングしているサイトを1つ運営していますが、今回減少が見られたのは当ブログのみです。このブログだけ明らかな減少が観られました。

ブログというのは自分でコンテンツを積み上げていくものなので、上記のような動的サイトと比較すると、やはり影響を受けやすいのは仕方ないですね。

tree-mapsはどうか

www.bunkei-programmer.net

この記事でtree-mapsという、地図のWEBサービスをリニューアルした事を告知しました。

その後のアクセス状況ですが・・・・かなりアクセスが増えました

リニューアルして間もないから、皆興味本位で色々なメニューをポチポチしている影響が大きいとは思いますが、やはりアクセス増のポイントはSPA、シングルページアプリケーションにありそうです。

とにかく読み込みが速く、というか画面の切り替えにHTTPリクエストしていないので速いのは当然ですが、効果は絶大ですね。

まだ1ヶ月経過していないので不確定ですが、恐らく1.5倍〜2倍のアクセス増となりそうです。

WEBサービスなのでページ内のテキストは少ないですが、他サイトには無い独自の機能を提供しているせいか、品質フィルタの影響は全くありませんでした。よかった。

↓tree-mapsという地図に関するWEBサービスを開発・運用しています!↓

www.tree-maps.com

今後の当ブログについて

このままいくと、当ブログのアクセス数がtree-mapsに負ける!?という自体が現実味を帯びてきそうで、今ちょっと怖い時期なのですよ。それだけ当ブログのアクセス減とtree-mapsのアクセス増が進行してしまっています。

tree-mapsは広告収益はほとんど考えていない(tree-mapsは私が自信を持って第三者に見せる事ができるサイトとして作っている)ので、利益的には未だに当ブログが95%を占めていますが、この先ジリ貧になりそうな予感がしています。

まだ様子を観て事を急がないつもりで、今まで通り全く変わらない形で当ブログの更新をしていきます。が、やはりブログではなく動的サイトをGAEで増やしていきたい気持ちもあるので、ブログの更新自体は更に減る可能性があります。

最近リアルのお仕事で勉強しないといけない事が多かったり、フロントエンドの勉強を兼ねてtree-mapsのリニューアルをしてみたり、ブログが後回しになるケースが増えています。更に、今回のような品質フィルタが出てきたので、気持ちよく適当にスラスラ〜っと記事を書くことがもうできないという気持ちが先行してしまい、「こんな事を記事にしたいけど、google先生の顔色伺わないといけないし、正直疲れるから面倒臭くなる・・・」という事が増えています。

今回のフィルタによって、悪質サイトを目にする機会が減りましたが、それと同時に今まで呑気に日記のような記事を書いていた人達は、もう通用しないという事になります。(よほどのオリジナリティが無ければの話です)

なので、単に「悪質サイトが減ったぞ!ヒャッホーイ!」等と言ってられなくなります。これからはブログやサイトに強いオリジナリティ、明確に役立つものを厳選して、真撃に記事を書かねばならないという事です。それ自体はWEBのあるべき姿といえますが、以前のような気軽さはもう通用しないので、ブログへの参入の敷居は確実に上がったと思われます。

やっている事は正しいし、健全な方向へ向いつつあるとは思いますが、個人がWEBへ参入する敷居は上がったので、徐々に個人は減り、企業サイト・企業ブログメインになっていくのかもしれない?と私は思っています。一見するとそれも正しいように見えますが、それはつまり「個人はいらない。但し有能なもののみ許す」という事を助長しそうで怖いのですよね。

今回の対応が行き過ぎてしまうと個人が消え去り、情報を発信する人口が大幅に減る事に繋がり、最終的にgoogleの広告収益の激減を招いたりしないのかね?とか考えているのですが、どうなのでしょうね。

真偽の程は定かではありませんが、品質フィルタ適用後に悪名高いNAVERまとめが上昇した等という話が出たりしているので、天下のgoogle先生がAIを駆使しても尚、本当に良いものと悪いものが正しく区別できていないので、この問題はとても判定基準が難しいのだろうなあ、と私は生暖かい目で傍観しています。

GAE/GOでgoapp serveした時に起きるThere are too many filesに対応する:解決編

$
0
0

やっと負荷問題が解決しましたよ〜

f:id:treeapps:20160521191008p:plain

www.bunkei-programmer.net

前回上記記事で、node_modulesフォルダが有る場合のgoapp serve問題について書きました。

今回はその記事の続きとなります。

おさらい

一応おさらいしておきます。

GAE/Golangアプリケーションを起動するコマンドに「goapp serve」というものがあります。これを実行するとGAEアプリケーションが起動します。

しかし、昨今npmモジュールとwebpackやbrowserifyを使ってjsでウェイウェイする開発が一般的ですね。goapp serveは、npmのwatchのようにファイルの変更を監視してホットデプロイする機能があります。このファイル監視がnode_modulesフォルダ内まで観てしまい、監視ファイル数オーバーを引き起こしてしまっていました。

冒頭の記事で、とりあえずnode_modulesでエラーがでないようにし、とりあえずgoapp serveで起動する事はできました。しかし実は完全に解決していませんでした。

実は問題が有った

あのやり方ではnode_modulesのエラーが出ないだけで、監視自体はガッツリ行われているっぽかったのです。その証拠として、npm installするとgoapp serveがnpmモジュール追加に逐一反応している(エラーにはならない)し、CPUのコアを1つ使いきるくらいの負荷(CPU使用率的には50%オーバー)で、結構酷い状態なのでした。

今回はnode_modulesの監視を完全に止める方法がようやく解ったので、書いてみます。

修正方法

goランタイム

go_appengineのバージョンによって変更するファイルパスが違うようです。今回は v1.9.48 の修正方法となります。

$ goapp version
go version go1.6.3 (appengine-1.9.48) darwin/amd64

変更するファイル

修正するのは以下のファイルです。
${GO_APPENGINE}/google/appengine/tools/devappserver2/watcher_common.py
ネットで調べてみると、go_appengineのバージョンによって watcher_common.py のパスが違うようなので、見つからない場合はfind等で自分で探してみて下さい。

修正内容

変更前
def ignore_dir(dirpath, dirname, skip_files_re):
  """Report whether a directory should not be watched."""
  return (dirname.startswith(_IGNORED_PREFIX) or
          skip_files_re and skip_files_re.match(os.path.join(dirpath, dirname)))
変更後
_IGNORED_DIRS = ('node_modules',)

def ignore_dir(dirpath, dirname, skip_files_re):
  """Report whether a directory should not be watched."""
  return (dirname.startswith(_IGNORED_PREFIX) or
          skip_files_re and skip_files_re.match(os.path.join(dirpath, dirname)) or dirname in _IGNORED_DIRS)

これで、node_modulesの監視自体をしないようになり、且つ、goapp serveした際の「There are too many files」も起きなくなり、一件落着となります。

雑感

バージョンアップ毎に手動でgo_appengineのモジュールを手動で修正する事になるので、正直嫌ですね・・・

環境変数で_IGNORED_DIRSが設定できれば凄く楽なんですけどね。

という事で、ちょっと面倒臭いとはいえこれで夜中CPUがブン回って暑くて起こされたりしなくなりますので、皆さんも夏に備えてnode_modulesの監視回避はして下さいね!


Mac環境でElectronを使ってMacとWindowsをパッケージングして実行可能ファイルを生成する

$
0
0

Windowsのビルドは依存が多くて実は結構大変です
f:id:treeapps:20170310234605p:plain

最近tree-mapsをリニューアルしたのですが、WEB版だけでなくデスクトップアプリも欲しかったので、Electronに手を出してみる事にしました。

ビルド設定

node_modues

以下の2つのモジュールをインストールします。yarnを使っていない方はnpm install -Sに置き換えて下さい。

yarn add electron-prebuilt electron-packager --dev

electron実行用js

以下は実際にtree-mapsで使っているものを少し整形したものです。
解りやすいように electron.js としてファイルを保存して下さい。

const{app, BrowserWindow, Menu, session} = require('electron');

let mainWindow = null;
app.on('window-all-closed', function () {
    app.quit();
});

app.on('ready', function () {
    mainWindow = new BrowserWindow({
        width: 1000,
        height: 600,
        webPreferences: {
            defaultFontFamily: {
                standard: 'Noto Sans JP',
                serif: 'Noto Sans JP',
                sansSerif: 'Noto Sans JP',
                monospace: 'Noto Sans JP'}}});
    // WebView的なロードを行う
    mainWindow.loadURL('https://www.tree-maps.com/');
    mainWindow.on('closed', function () {// アプリ終了時にキャッシュをクリアする
        session.defaultSession.clearCache(() => {})
        mainWindow = null;
    });

    var template = [{// ショートカット
        label: "Application",
        submenu: [{label: "About Application", selector: "orderFrontStandardAboutPanel:"},
            {type: "separator"},
            {
                label: "Quit", accelerator: "Command+Q", click: function () {
                app.quit();
            }}]}, {// コピペを有効化
        label: "Edit",
        submenu: [{label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:"},
            {label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:"},
            {type: "separator"},
            {label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:"},
            {label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:"},
            {label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:"},
            {label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:"}]}];

    Menu.setApplicationMenu(Menu.buildFromTemplate(template));
});

icnsファイルの生成

electronアプリの実行ファイルに設定するアイコン画像は、icnsという特殊な形式のものを使います。macでicnsを生成するには「iconutil」を使います。

iconutilでiconsを作るわけですが、16px, 32px, 64px, 128px, 256px, 512px, 1024px のpngファイルが必要なので事前に用意しておいて下さい。

それらを以下のような構造にまとめます。

.
└─ ${アプリ名}.iconset
       ├── icon_128x128.png
       ├── icon_128x128@2x.png
       ├── icon_16x16.png
       ├── icon_16x16@2x.png
       ├── icon_256x256.png
       ├── icon_256x256@2x.png
       ├── icon_32x32.png
       ├── icon_32x32@2x.png
       ├── icon_512x512.png
       └── icon_512x512@2x.png

tree-mapsの場合は「tree-maps.iconset」というフォルダを作り、その中に画像を配置しました。
この状態で、以下のように実行すると、icnsファイルが生成されます。

iconutil -c icns tree-maps.iconset

成果物として「tree-maps.icns」というファイルが生成されます。これを次項で使用します。

アプリ実行・ビルド等のタスク設定

package.jsonのmainとscriptsセクションを以下のようにします。この例ではwebpackを使用するので、「yarn add webpack --dev」等でインストールして下さい。

"main": "electron.js",
  "scripts": {"build": "NODE_ENV=product webpack --progress --color",
    "app": "electron .",
    "package": "NODE_ENV=product electron-packager . tree-maps --out=dist --platform=darwin,wind32--arch=x64 --icon=tree-maps.icns --overwrite"
  },

※ この記事ではビルドは64bit版で行うものとします。

appタスクはパッケージングせず、直接electronアプリを実行します。主にデバッグ用途で使用します。

electronアプリを配布する際は、まずbuildタスクを実行して各種成果物を生成し、その成果物をpackageタスクでパッケージングします。

electron-packagerには沢山の引数がありますが、それぞれ以下のようになります。

outパッケージングの出力先パス
platformdarwinはmac、win32はwindowsです
arch32bitか、64bitか
icon実行ファイルのアイコン
overwrite上書きする

mac版のビルド手順

yarn build && yarn package

macの場合はこれだけで、distフォルダ配下に実行可能ファイルが生成できます。非常に簡単です。

windows版のビルド手順

実は、macでwindowsのビルドの行うのはちょっと手間がかかります。

macでwindowsのパッケージングをするには、おおまかに「homebrew」「xquartz」「wine」のインストールが必要になります。

homebrewのインストール

以下の「インストール」にワンライナーのインストールコマンドがあるので、インストールします。
brew.sh

xquartzのインストール

$ brew cask install xquartz
Uninstalling brew-cask... (5,904 files, 3.2MB)
Warning: The default Caskroom location has moved to /usr/local/Caskroom.

Please migrate your Casks to the new location and delete /opt/homebrew-cask/Caskroom,
or if you would like to keep your Caskroom at /opt/homebrew-cask/Caskroom, add the
following to your HOMEBREW_CASK_OPTS:

  --caskroom=/opt/homebrew-cask/Caskroom

For more details on each of those options, see https://github.com/caskroom/homebrew-cask/issues/21913.
==> Downloading https://dl.bintray.com/xquartz/downloads/XQuartz-2.7.11.dmg
######################################################################## 100.0%==> Verifying checksum for Cask xquartz
==> Running installer for xquartz; your password may be necessary.
==> Package installers may write to any location; options such as --appdir are ignored.
Password:
==> installer: Package name is XQuartz 2.7.11
==> installer: Installing at base path /
==> installer: The install was successful.
🍺  xquartz was successfully installed!

wineのインストール

非常に依存モジュールが多く、インストールが長く、インストールする依存の中には503エラーでタイムアウトするものもあります。503の場合は時間を置いて再度brew installしてみて下さい。

tree:certbot tree$ brew install wine
==> Installing dependencies for wine: libpng, freetype, jpeg, libtool, libusb, libusb-compat, fontconfig, libtiff, webp, gd, libgphoto2, little-cms2, cmake, jasper, libicns, makedepend, openssl, net-snmp, sane-backends, libtasn1, gmp, nettle, libunistring, libffi, p11-kit, gnutls
==> Installing wine dependency: libpng
==> Using the sandbox

・・・snip・・・

By default Wine uses a native Mac driver. To switch to the X11 driver, use
regedit to set the "graphics" key under "HKCUSoftwareWineDrivers" to"x11"(or use winetricks).

For best results with X11, install the latest version of XQuartz:
  https://xquartz.macosforge.org/
==> Summary
🍺  /usr/local/Cellar/wine/2.0: 4,297 files, 461.9MB

パッケージングしてみる

yarn build && yarn package

パッケージングのコマンド自体はmacと同様(というかmacとwinを同時にビルドするよう前項でpackage.jsonを設定しました)です。

が、ビルド中に、ここから更に「mono」「Gecko」のインストールを求められます。monoは、macでwindowsのバイナリを生成するためのものでお馴染みのアレです。

パッケージング中に以下のダイアログが自動で起動するので、全てインストールしちゃって下さい。

f:id:treeapps:20170310231438p:plain

f:id:treeapps:20170310231504p:plain

f:id:treeapps:20170310231510p:plain

3つ目のダイアログの Install をクリックすると、パッケージング処理が続行して、無事exeを生成する事ができます。

macとwindowsの64bit版をパッケージングすると、以下のような成果物が出力されます。

f:id:treeapps:20170310232139p:plain

macの方は「アプリ名.app」単体で起動しますが、windowsはフォルダの中のdll等は全て必要なので、exeファイル単体で配布せず、ディレクトリごと配布して下さい。

エラー集

Make sure that the "wine" executable is in your PATH.

yarn packageする時に、wineがインストールされていない場合に発生します。wineをインストールしましょう。

$ yarn package
yarn package v0.20.3
$ NODE_ENV=product electron-packager . tree-maps --out=dist--platform=win32--arch=x64--icon=tree-maps.icns--overwrite 
Packaging app for platform win32 x64 using electron v1.4.13
Could not find"wine" on your system.

Wine is required to use the app-copyright, app-version, build-version, icon, and 
win32metadata parameters for Windows targets.

Make sure that the "wine" executable is in your PATH.

See https://github.com/electron-userland/electron-packager#building-windows-apps-from-non-windows-platforms for details.
error Command failed with exit code 1.

wine: XQuartz is required to install this formula.X11Requirement unsatisfied!

wineのインストールをするには xquartz のインストールが必須のようです。xquartzをインストールしましょう。

tree:certbot tree$ brew install wine
Updating Homebrew...
wine: XQuartz is required to install this formula.X11Requirement unsatisfied!

You can install with Homebrew-Cask:
  brew cask install xquartz

You can download from:
  https://xquartz.macosforge.org
Error: An unsatisfied requirement failed this build.

curl: (22) The requested URL returned error: 503 first byte timeout

brew install中に以下のように503エラーが発生する場合があります。これはhttpステータスコード通り、サーバが混み合っているだけですので、少し時間が経ったら再びbrew installして下さい。

==> Installing wine dependency: sane-backends
==> Downloading https://fossies.org/linux/misc/sane-backends-1.0.25.tar.gz
######################################################################## 100.0%==> Downloading https://raw.githubusercontent.com/Homebrew/formula-patches/6dd7790c/sane-backends/1.0.25-missing-types.patch

curl: (22) The requested URL returned error: 503 first byte timeout
Error: Failed to download resource "sane-backends--patch"
Download failed: https://raw.githubusercontent.com/Homebrew/formula-patches/6dd7790c/sane-backends/1.0.25-missing-types.patch

雑感

今回はmacとwindowsのデスクトップアプリを作るのが目的でelectronを採用しましたが、electronって目茶苦茶簡単ですね。とりあえず起動すればいい程度なら、前述の electron.js のみで起動しちゃいます。windowsのパッケージングが色々不便なので嫌ですが、mac版のパッケージングはシンプルで非常に良いですね。

なお、electronはgoogle先生のchromiumが利用されており、chromeとして起動するので、「IEだけデザイン崩れする!」等が起きないので、デザイン周りでも非常に楽です。しかし、chromium自体のファイルサイズが大きいので、生成されるバイナリ・その他諸々のファイルサイズがどうしても大きくなってしまうのが難とも言えます。

ちなみにですが、electronを使用しているアプリには「slack」「atom」「VisualStudioCode」等、有名ソフトでも使われています。

今回はパッケージング時にコードサイニングを行っていないので、システム環境設定 ->セキュリティとプライバシー ->一般タブ ->ダウンロードしたアプリケーションの実行許可から許可する必要があるので注意して下さい。

おまけ

私はtree-mapsというサイトを開発していて、ReactでSPAで作っています。今回Electronを使ってアプリ化してみたので、よかったらダウンロードしてみて下さい。

www.tree-maps.com

Google Maps Apiで地図に色々な地理院タイルを適用してみよう!

$
0
0

久しぶりの投稿です〜

f:id:treeapps:20170111000706p:plain

最近tree-maps開発ブログの方ばかりに投稿していてこっちのブログに投稿してなかったので、久しぶりの投稿になります。

先日tree-mapsのプロットページのスタイル適用機能に、沢山の地理院タイルを追加しました。
www.tree-maps.com

この地理院タイルについて、適用方法をまとめてみます。

地理院タイル一覧

国土地理院はGoogle Mapに適用できるタイルを実は用意してくれています。
地理院地図|地理院タイル一覧

このタイルをGoogle Mapに被せると、昔の航空写真等を適用する事ができたりしてしまうのです。専門的なタイルも沢山あり、地図に関わる特定の方々が喜ぶタイルもあると思いますよ!

地理院タイルの適用

es2015で、しかも中途半端にしか書きませんが、以下のようにmap(new google.maps.Mapオブジェクト)に対して、以下のように後付でオプションを設定してあげると、地理院タイルを適用する事ができます。

// 地図の指定const tileType = 'std';
const tileExtension = 'png';
const supportZoomMax = 21;
const supportZoomMin = 0;

const getMapOption = (tileType, tileExtension, zoomMax, zoomMin) => {return{
        name: "地理院地図(GSI Maps)",
        tileSize: new google.maps.Size(256, 256),
        minZoom: zoomMin,
        maxZoom: zoomMax,
        getTile: (tileCoord, zoom, ownerDocument) => {const img = ownerDocument.createElement("img")
            img.id = 'gsi-map-layer-image'
            img.style.width = "256px"
            img.style.height = "256px"const x = (tileCoord.x % Math.pow(2, zoom)).toString()
            const y = tileCoord.y.toString()
            img.src = 'http://cyberjapandata.gsi.go.jp/xyz/' + tileType + '/' + zoom + '/' + x + '/' + y + '.' + tileExtension
            return img
        }}};

map.setMapTypeId('GsiMaps');
map.mapTypes.set("GsiMaps", getMapOption(tileType, tileExtension, supportZoomMax, supportZoomMin));

上記の「地図の指定」の4つのconstの値を変えると、簡単に様々の地理院タイルを適用する事ができます。

例えばtree-mapsでは実際に、constの代わりに以下のようにjsonで定数を用意し、対応するズーム値等をまとめています。

styleMap: {anaglyphmapGray: {value: 'anaglyphmapGray',
        label: '[地理院タイル] アナグリフ グレー',
        roadMap: false,
        mapTypeControl: false,
        tileBaseUrl: 'http://cyberjapandata.gsi.go.jp/xyz/anaglyphmap_gray',
        tileExtension: 'png',
        supportZoomMin: 2,
        supportZoomMax: 16,    },
    hillshademap: {value: 'hillshademap',
        label: '[地理院タイル] 陰影起伏図',
        roadMap: false,
        mapTypeControl: false,
        tileBaseUrl: 'http://cyberjapandata.gsi.go.jp/xyz/hillshademap',
        tileExtension: 'png',
        supportZoomMin: 2,
        supportZoomMax: 16,    },},    

実装時の注意点

mapTypeIdが変わってしまう

通常の地図だと

map.setMapTypeId(google.maps.MapTypeId.ROADMAP)

というように、ロードマップが適用されます。
しかし地理院タイルの指定時は「GsiMaps」とユニーク値を指定しているので、tree-mapsのように地図のスタイルを変えたり元に戻す必要が有る場合、通常の地図に戻す場合は上記のようにロードマップに戻してあげる必要があります。

対応しているズーム値

最初嵌ったのですが、沢山ある地理院タイルは、それぞれ対応しているGoogle Mapのズーム値が決められています。

例えばズームレベルの項目に「9~11」と書かれていたら、zoomMax: 11, zoomMin: 9 のみ対応しています。それ以外のズーム値は画像が用意されていないので、必ず対象の地図にかかれているズームレベルを確認して設定する必要があります。

必ずしも0〜21の値が指定できる訳ではないので注意しましょう。

対応している地域

「提供範囲」という部分が画像を配信している地域です。これが「日本全国」であれば、日本の画像のみが表示でき、海外を表示しようとするとタイル画像が404になったりします。

例えば「西之島付近噴火活動 標高タイル(2013年12月17日)」のタイルがありますが、ここの提供範囲は「西之島付近」とだけ書かれているので、それ以外の地域ではタイル画像は404になります。相当範囲が狭いので、汎用的な地図サービスで使うにはちょっと難しいですね。

ズームレベルが不正確?

例えば「アナグリフ」の「グレー」のタイルですが、ズームレベルを見ると2〜18に対応してそうに見えますが、私が試してみたところ、どうも「2〜16」しか対応していませんでした。

もしかしたらズームレベルの17〜18は特定の地域しか配信されていないのかもしれませんね。

タイル画像にはpngだけでなくjpgもある

これはよく見ていないとタイル画像が404になり、「は?」と思ってしまう点です。

タイルにはpngとjpgの2種類があるので注意する必要があります。

雑感

最近ひたすらtree-mapsの開発をしているのですが、google maps apiで今まで使った事が無かったAPIを大量に使いました。

特に地物の強調は「あ、こんな機能あるのか、マジか〜」と思いました。ただ、地物の指定は使い方が非常に煩雑で、「これの場合はあのオプションしか効かない」「あれの場合はこっちのオプションしか効かない」等、条件分岐が大変多いのですが、それらについてリファレンスに書かれていません。。。

なので、自分で試してみて「あれが有効」「これが有効」を調べる必要があり、なかなか大変です。

ルート機能についても同様で、あのオプションはこのオプションしか効かない、この機能はアメリカでしか動かない等があり、こちらもかなり厄介です。

バカ正直に地物やルート機能を実装するとUIが大変難しくなってしまうので、本当はもっと機能があるけど、敢えて操作が簡単になるように解りやすいUIにしています。本当はもっと細かく地物の強調を設定できたりしますが、UIが複雑過ぎて駄目なのですよね。

まだまだtree-mapsで追加したい機能があるので、これからもtree-mapsをよろしくお願いします!!
www.tree-maps.com

dockerとnginxで静的リソースをgit環境無しで閲覧できる環境を構築しよう!

$
0
0

デザインhtmlをgit管理すると、どうしても営業やテスターが困っちゃうんですよね〜
f:id:treeapps:20170501215041p:plain

最近会社で「未だにsvnを使ってる悪い子はいねがぁ〜〜!?」と、社内をなまはげが闊歩しているような、していないような感じなので、デザインhtml関連をgitに移行してやりました。

しかしここで問題が発生しました。

デザインhtml

唐突に「デザインhtml」という言葉を出してしまったので説明します。WEBサイトの開発を行う際は、以下のフローで開発が行われます。

  1. デザイナがjpgやpng形式でサイト全体を画像化してお客様に見せる。
  2. お客様からデザインOKが出たら、画像だったものをhtmlファイル化する。これをデザインhtmlと呼ぶ
  3. エンジニアはデザインhtmlを元に、動的なサイトを実装する。

エンジニア以外の方とgitについて

営業「うむむ・・・gitに移行したのはいいんだけど、これ、最新ファイルを取得するツールとか入れないと駄目だよね?svnより簡単?」

テスター「私、あまりgitに詳しく無いので、ツールの使い方教えて貰えますか?」

デザイナ「私はgit使えますが、ローカルでhtmlコーディングしているので、今pushされている状態を見せるのにいちいちstashしたりするの面倒なんですよね…」

はい。エンジニア以外から「ツール(SourceTree)の使い方教えろや」「pushされている状態のhtmlを今すぐ見せろや」等といった要望が挙がりました。

GHEやGitbucketを使っている場合、画像やcssの中身は確認できますね。しかし、htmlファイルを開こうとしても、表示されるのソースコード(html)です。chromeのアドオン等でhtmlをプレビューできるものもあるようですが、全員にそれを強要するもの残念です。

かといって全員にSourceTreeの使い方と、gitについてレクチャーするもの大変です。新規メンバーが参入したらまた一からレクチャーしないといけません。

それらを考慮し、何とかしてみる事にしました。

ミッション

  • git環境無しに、デザイナが作成したデザインhtml(静的リソース)をブラウザで確認できる事。
  • 既存のサーバを可能な限り汚さない事。
  • 他サーバへの引っ越しがし易い事。
  • 他のエンジニアが比較的容易にカスタマイズしやすい事。
  • 下手に頑張り過ぎず、例えばapacheのDirectory Index(nginxだとautoindex)で済ませてしまう事。
  • その仕組を捨て去る事になっても泣かない事。

これらを考慮し、以下の構成にしました。

  • docker + docker-compose
  • Alpine Linuxベースのnginx
  • nginxのautoindexを使ってしまう。

最初はnode.js系のDirectory Indexの仕組みを使ってみたりもしましたが、どうしてもexpress等のjsのコードを書かないといけなかったり、面倒だったのです。使用するnode_modulesが用意しているcallbackを頑張ってチマチマ修正しても、モジュールが提供していない機能が欲しくなったりして面倒かも?と思ったので、ならいっそ皆扱える(筈)nginxにしちゃえ!と考え、そうする事にしました。

何故Dockerを使うか

既存のサーバを汚したくない

まあnginxだったらインストールしてもいいかな?とも思いますが、nginx自体のバージョンアップとか考えると面倒だし、そのサーバに既に別のwebサーバがインストールされていたりするとちょっと困ってしまいますね。既存のソフトウェアとのバッティングを避ける際はdockerを使うと楽です。

他のサーバへの引っ越しが楽

他のサーバに仕組みをゴッソリ引っ越したくなった場合、yum等でインストールしてしまうとアンインストールする必要があるし、アンインストールした事で既存環境に影響を及ぼしたり、ゴミが残ってしまったりすると面倒です。dockerであれば、Dockerfileやdocker-compose.yml等の少量のファイルさえあれば、大抵のサーバですぐ動きます。

モチベーション

地味に重要だと思うのですが、私は未だにオンプレ案件を多く担当していますが、オンプレ系プロジェクトはとにかく技術要素が古い場合が多く、モチベーションが上がりにくかったりします。それを少しでも解消するためにdockerを使ってみる、というものアリなんじゃないでしょうか。

軽量

https://hub.docker.com/_/nginx/

公式のAlpine linuxベースのnginxが用意されているので、これを使います。

イメージのサイズも15MByteと非常に小さく、イメージのダウンロードもすぐ終わります。

$ docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
nginx                                1.12.0-alpine       61055e9116d4        11 days ago         15.5 MB

nginxで作る静的リソースビューア

github.com

例によって完成形は作っておきました。git cloneして、docker-compose upすればすぐ起動します。

こちらをcloneし、必要な部分をカスタマイズして下さい。

dockerのインストール

手順はオフィシャルサイトに丸投げします。

docs.docker.com

使用するイメージ

https://hub.docker.com/_/nginx/

こちらです。公式のAlpineベースのnginxを使っています。

docker-compose.yml

version: '3'
services:
  webserver:
    build: .
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./htdocs:/var/www/html:ro
      - ./assets/:/etc/nginx/assets/:ro
    image: treetips/simple-static-file-viewer
    container_name: simple-static-file-viewer
    tty: true
    restart: always
    environment:
      - LANG=ja_JP.UTF-8
      - TZ=Asia/Tokyo
    ports:
      - "8081:80"
    command: "nginx -g 'daemon off;'"

これは最新バージョンのインタックスなので、v3に非対応の場合はversionを2に書き換えて下さい。

では、少しづつ解説していきます。

/etc/localtime:/etc/localtime:ro

このマウントは、タイムゾーンを設定するために行います。

一般的なlinuxのタイムゾーン変更であれば以下のようにしますね。

ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

しかしAlpine Linuxにはそもそも /usr/share/zoneinfo自体が無いので、dockerホストのlocaleをコンテナにマウントしてしまいます。こうする事で、dockerホストとコンテナが同一のlocaleとなります。

/default.conf:/etc/nginx/conf.d/default.conf:ro

公式のnginxイメージは、/etc/nginx/nginx.confの中に、include /etc/nginx/conf.d/*.conf; という形でファイルが読み込む記述があります。つまり、nginx.confを直接カスタマイズするのではなく、conf.d配下にファイルを配置してね、という事ですね。

何故「default.conf」という名前にしているのかというと、公式イメージに既に/etc/nginx/conf.d/default.confが存在するためです。そのdefault.confは大部分がコメントアウトされており、コメントされていない部分のみを抽出すると、以下のようになります。

/etc/nginx/conf.d # cat default.conf
server {
    listen       80;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

これだったら、もういっそこのファイル自体を上書きして、自分の自由にカスタマイズしたいので、default.confをマウントして上書いています。

./htdocs:/var/www/html:ro

nginxのautoindexを使うにあたり、htdocsが重要になります。このhtdocsにファイルが配置される事で、autoindexにファイル・フォルダが反映されます。rw(read only)と書いていますが、ファイル・フォルダをコピーしたり移動したりする事ができるので、ホスト側のhtdocs配下にファイルを配置すれば、ブラウザで即確認可能になります。

./assets/:/etc/nginx/assets/:ro

これはちょっと特殊です。nginx標準のautoindexは、以下のような表示になります。

f:id:treeapps:20170501210528p:plain

何だかガタガタして縦に綺麗に並ばないし、見難いのですよね。見難いのであれば、スタイルをいじってしまえばいいのです。

nginxでスタイルをいじるには、ngx_http_addition_moduleモジュールを使います。このモジュールはalpineベースのnginxイメージに含まれているので、コンパイル等をせず使う事ができます。

Module ngx_http_addition_module

使い方は以下のような感じで、add_before_bodyとadd_after_bodyを設定します。引数はファイルパス等ではなく、URIなのでご注意下さい。

    location / {
        add_before_body       /assets/nginx-before.txt;
        add_after_body        /assets/nginx-after.txt;
        autoindex             on;
        autoindex_localtime   on;
    }

/assets/というURIは、以下のようにhtdocsとは別に、aliasで作り出しています。/etc/nginx/assets自体は、docker-compose.ymlでマウントして用意したディレクトリです。

    location /assets {
        alias                 /etc/nginx/assets;
    }

ngx_http_addition_moduleですが、通常のautoindexは、以下のようなhtmlを出力します。

<html><head><title>Index of /</title></head><bodybgcolor="white"><h1>Index of /</h1><hr><pre><ahref="../">../</a><ahref="%E4%BB%8A%E6%97%A5%E3%81%AF%E3%81%84%E3%81%84%E3%81%8A%E5%A4%A9%E6%B0%97%E3%81%A6%E3%82%99%E3%81%99/">今日はいいお天気です/</a>                                       30-Apr-2017 18:57                   -
<ahref="Dockerfile">Dockerfile</a>                                         29-Apr-2017 14:57                  65
<ahref="README.md">README.md</a>                                          29-Apr-2017 15:36                2624
<ahref="default.conf">default.conf</a>                                       29-Apr-2017 13:55                1236
<ahref="docker-compose.yml">docker-compose.yml</a>                                 29-Apr-2017 14:07                 451
</pre><hr></body></html>

ngx_http_addition_moduleを使うと、以下のように、<pre>タグより前と後のhtmlを自由にカスタマイズする事ができるようになります。簡単に言えば、ヘッダーとフッターを自由に書く事ができるようになり、テンプレートエンジンのように共通のヘッダー・フッターを持つ事ができます。

<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8" /><metaname="viewport"content="width=device-width, initial-scale=1.0"><linkrel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"type="text/css" /></head><body><divclass="container-fluid"><divclass="row-fluid"><divclass="col-md-12"><html><head><title>Index of /</title></head><bodybgcolor="white"><h1>Index of /</h1><hr><pre><ahref="../">../</a><ahref="%E4%BB%8A%E6%97%A5%E3%81%AF%E3%81%84%E3%81%84%E3%81%8A%E5%A4%A9%E6%B0%97%E3%81%A6%E3%82%99%E3%81%99/">今日はいいお天気です/</a>                                       30-Apr-2017 18:57                   -
<ahref="Dockerfile">Dockerfile</a>                                         29-Apr-2017 14:57                  65
<ahref="README.md">README.md</a>                                          29-Apr-2017 15:36                2624
<ahref="default.conf">default.conf</a>                                       29-Apr-2017 13:55                1236
<ahref="docker-compose.yml">docker-compose.yml</a>                                 29-Apr-2017 14:07                 451
</pre><hr></body></html></div></div></div></body></html>

例えばこのように、bootstrapを適用するだけで、以下のような見た目になります。

f:id:treeapps:20170501211538p:plain

たったこれだけでレスポンシブ化して縦も揃って見やすくなりました(日本語の列はズレますが・・)。

<pre>タグが厄介ではありますが、<head>タグが解放されたという事は、cssやjsを適用し放題な訳ですね。

定期的にhtdocs配下にファイルを配置する

私の場合はデザインhtmlのファイル群をjenkinsでpullし、dockerホストのhtdocsを更新しています。デザインファイルがpushされるとホスト側のhtdocs配下に上書かれ、それがnginxのコンテナに伝搬してブラウザでそのファイル・フォルダを確認する事ができるようになります。

autoindexの注意点

autoindexには注意点があり、ngx_http_index_moduleに注意する必要があります。

Module ngx_http_index_module

「index index.html」等と書いてしまうと、http://localhost:8081/等とアクセスした際にindex.htmlが自動的に参照されてしまい、autoindexの一覧表示がされず、index.htmlを表示してしまいます。特にデザインhtmlであれば大抵index.htmlが存在するので、autoindexが全然開けない事態になります。

これを防ぐため、通常有用であるindexモジュールを敢えて効かないような設定にする必要があります。

何故Fancy Indexモジュールを使わないの?

Fancy Indexは残念ながら標準モジュールではなく追加モジュールです。従って、有効にするにはコンパイルオプションを指定してコンパイルしないといけません。そこまで頑張りたくないので、今回は見送りました。

h5aiは使わないの?

larsjung.de

まあ使ってもいいとは思うのですが、ちょっと、仕組みが重すぎるというか、重厚過ぎます。そこまでリッチなものは欲していないというか、カスタマイズが凄く難しそうというか。phpが必要な点も嫌ですね。

雑感

Directory Indexの仕組みを色々調べてみると、結構ヘビーな実装のものが多く、ちょっと敬遠してしまいます。

特にh5aiは面倒ですね。超シンプルでいい、もしくは自分で全部いじりたい、といった場合、今回のようにngx_http_addition_moduleでいじるのが丁度いいと思います。

今回私が用意しものはかなりシンプルなものですが、ここからリッチにしたい等があれば、是非いじってみて下さい!
github.com

Typescript, react-router v4, Redux対応時に起きたエラーの対応メモ

$
0
0

色々嵌ってかなりきつかったです。
f:id:treeapps:20170518023123p:plain

会社ではJDK1.6まみれですが、個人開発ではjsにどっぷりになりつつあるtreeです。

tree-mapsでは、babel(ES2017), react, redux, react-router(v3) の組み合わせだったので、今回はちょっと変えて、Typescript(v2.3), react, redux, react-router(v4) の組み合わせを勉強中です。

最初は「Babelと比較してTypescriptの環境構築目茶苦茶楽だわ〜〜」なんて考えてたのですが、Typescriptの書き方が全然解らないし、問題児react-router(v4)のおかがで、動作させるまでに恐ろしく手間がかかったので、色々メモしておこうと思います。

※ 今回はメモ書きレベルなので、精度は低いと思われます。

使用モジュール

package.jsonの抜粋です。

  • "@types/history": "^4.5.1",
  • "@types/react": "^15.0.24",
  • "@types/react-dom": "^15.5.0",
  • "@types/react-router": "^4.0.9",
  • "@types/react-router-dom": "^4.0.4",
  • "create-react-class": "^15.5.3",
  • "prop-types": "^15.5.10",
  • "react": "^15.5.4",
  • "react-dom": "^15.5.4",
  • "react-redux": "^5.0.4",
  • "react-router-dom": "^4.1.1",
  • "react-router-redux": "5.0.0-alpha.4",
  • "redux": "^3.6.0",
  • "redux-logger": "^3.0.1",
  • "typescript": "^2.3.2",
  • "webpack": "^2.5.1",

reduxをreact-router v4に対応させる

v4系に対応していない!?

react-router v4をreduxで管理させようと思ったのですが、2017/05/18現在react-router-reduxのv4系は残念ながらreact-routerのv4系に対応していません。

The next version of react-router-redux will be 5.0.0 and will be compatible with react-router 4.x. It is currently being actively developed over there. Feel free to help out!

https://github.com/reactjs/react-router-redux

なので、5.0.0-alpha.4を使う必要があります。以下のようにインストールします。

yarn add react-router-redux@5.0.0-alpha.4

react-router-reduxとreact-routerを連携

import * as React from 'react'import * as ReactDOM from 'react-dom'import{createBrowserHistory} from 'history'import{Provider} from 'react-redux'import{ConnectedRouter} from 'react-router-redux'import{Switch, Route, Redirect} from 'react-router-dom'import configureStore from './store/configureStore'import App from './container/App'import Hello from './components/Hello'const store = configureStore()
const history = createBrowserHistory()

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Switch>
        <Route exact path="/" component={App} />
        <Route exact path="/hello" component={Hello} />
        <Redirect from="*" to="/" />
      </Switch>
    </ConnectedRouter>
  </Provider>,
  document.getElementById('app')
)

ざっくりこんな感じです。

一応以下を見ながら書いてみたのですが、これで合ってるか全く自信ありません・・・
github.com

Routeの行で謎のエラーが!?

f:id:treeapps:20170518005946p:plain

こんな感じでエラーとなり、以下のようなエラーメッセージが表示されました。

(28,12): error TS2322: Type '{ exact: true; path: "/"; component: typeof App; }' is not assignable to type'IntrinsicAttributes & IntrinsicClassAttributes<Route> & Readonly<{ children?: ReactNode; }> & Rea...'.
  Type '{ exact: true; path: "/"; component: typeof App; }' is not assignable to type'Readonly<RouteProps>'.
    Types of property 'component' are incompatible.
      Type 'typeof App' is not assignable to type'StatelessComponent<RouteComponentProps<any> | undefined> | ComponentClass<RouteComponentProps<any...'.
        Type 'typeof App' is not assignable to type'ComponentClass<RouteComponentProps<any> | undefined>'.

これは2パターンの解決方法があるようです。
stackoverflow.com

anyを使う場合

修正前

<Route exact path="/hello" component={Hello} />

修正後

<Route exact path="/hello" component={Hello as any} />
RouteComponentPropsを使う場合
<Route exact path="/" component={App} />
import{RouteComponentProps} from 'react-router-dom'interface Props extends RouteComponentProps<any> {}exportdefaultclass App extends React.Component<Props, any> {

  constructor(props:Props) {super(props)
  }・・・略・・・


この2パターンの解決から察するに、Routeで指定するcomponentに型を指定しないといけない、という事のようですね。

未解決事項

browserHistory.pushはどうやる?

react-router v4からbrowserHistoryが無くなったので、記述方法を変更する必要があるようです。

qiita.com

こちらの記事を読むと、どうやら「browserHistory.push」を「this.props.history.push」と書き換えるそうです。

が、Typescriptでこれを書くにはどうしたらよいのか。。。これが解らない。バカ正直に「this.props.history」と書いても、historyが見つからないと言われるんですよね。Propsのinterfaceを切って、そこにhistoryを追加してやるのですかね。解りません。

雑感

最近googleでTypescriptが標準言語として承認されたり、Angular v2系からTypescriptが標準になったりして、世界的にTypescript熱が高まっているようですね。という事で流行りに乗って触ってみたわけですが・・・・もう本当に何が何だか解りません。。。

実際にTypescriptに変えてみて、babelでこう書いていたのをTypescriptで書くにはどうしたらいいのだろう?という疑問が噴出しております。

例えばbabelでReact.Componentと書けていたのに、TypescriptだとReact.Component<Props, any>等と、型指定を強要されます(当たり前ですが)。この時に、どんな型を指定したらいいのかを調べるのに大分苦労しています。

なんか、今のところTypescriptを使ってみて嬉しい点はwebpackの設定がちょっと楽なくらいです。恐らくビジネスロジックを沢山書くようになると型が力を発揮すると思うのですが、現状単にreact + react-router + reduxを連携させるのもヒーヒー言ってるくらいなので、早く型の恩恵を受けるところまでいきたいですね。


今回色々苦労しているのは複合している原因があるからなのですよね。

  • Typescriptを初めて使ってみた。
  • react-routerがv4になって破壊的なバージョンアップが行われた。
  • react-router-reduxがまだreact-router v4に正式対応できていない。
  • react v15.5でReact.PropTypes等が変更された。

これら問題が同時に起きて泣きそうなのに、react + redux + react-touterを連携するために必要なモジュールが沢山あるので、それも嫌な点の1つですね・・・

  • react
  • react-dom
  • react-redux
  • react-router-dom
  • react-router-redux
  • redux

関連モジュールが多い事で、ネット上の情報でどれを見たらいいのか解らない、情報が錯綜しているような事態に見舞われる点がキツイですね。

それぞれのgithubのドキュメントを見ろって話ではありますが、モジュール単独のドキュメントを見ても、各モジュールを全て組み合わせて連携した例は無いですし、やはり定番の組み合わせをした例が欲しいのですが、中々いい記事を見つける事ができず、エラーが出る→StackOverflow先生タスケテー!、という事の繰り返しになってしまいます。

多分、こういう関連モジュールの多さや、それぞれのモジュールが動作するバージョンの違いや、どのドキュメント見たらいいか解らない問題が、ここ数年で言われている「react嫌い」に繋がっているのではないかと思いました。

これはreactに限った話ではないと思いますが、みなさんどうやってこれら問題をスルスルと解決していっているのか、大変気になります。

復活したSchemaSpyでDB定義書を自動生成してnginxで確認するdocker環境を作ってみました

$
0
0

やってやった
f:id:treeapps:20170501215041p:plain

もしかしたら知っている方もいるかもしれないSchemaSpyですが、↓長らく更新が停止しており、皆静かに離れていきました。
SchemaSpy

この状況をに絶望した?人達が集まり、有志による開発がGithubで始まっていました。
github.com

以前は非常に簡素なデザインのDB定義書で、「ちょっとかっこ悪い」「(見た目が)イケてない」と思われたと思いますが、新たにデザインも刷新され、twitter bootstrapによって生まれ変わりました。

Chinook Database

折角なのでSchemaSpyを再び使ってみようとしましたが、Dockerに慣れてきた私達には、graphviz, javaをインストールしたり、JDBCドライバをダウンロードする事すら億劫です。

SchemaSpyによって生成したDB設計書をローカルに保存したくないので、サーバに定義書を配置して定義書の閲覧はサーバに配置されたものをブラウザで確認したいのです。apacheやnginxで確認用のwebサーバを立てるなんて絶対嫌ですね。

という事で、Dockerで一通りの環境を揃えてみました。

システム要件

  • Docker v1.13以上
  • docker-compose

技術要素

  • SchemaSpy v6.0
  • MySQL-server(バージョン問わず)
  • Dcoker v17
  • docker-compose v1.14
  • alpine linux v3.6
  • alpine linuxベースのnginx v1.13.1
  • alpine linuxベースのopenjdk v1.8

常駐するのはalpineベースの軽量なnginxのみで、schemaspyコンテナは実行する時のみしか現れないようにしたので、マシンリソースにも優しいと思います。

環境構築手順

例によって事前にgithubにpush済みなのです。

github.com

今回使った(作った)イメージのサイズですが、それぞれ以下のようになっています。

dtree:~ tree$ docker images
REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
treetips/schemaspy-mysql   latest              54c553615d38        20 hours ago        208MB
alpine                     3.6                 7328f6f8b418        2 days ago          3.97MB
nginx                      1.13.1-alpine       33aa78cbda15        10 days ago         15.5MB
openjdk                    8u121-jdk-alpine    630b87931295        7 weeks ago         101MB

treetips/schemaspy-mysqlはopenjdk:8u121-jdk-alpineのイメージをベースにして208MByteとなりました。

このopenjdkはalpine linuxベースなので、恐らくかなり小さいサイズだと思うのですが、それでもまだ大きいですね。208MBのうち101MBがopenjdkで占有しているのが泣けてきますね。

docker環境を用意する

docker for mac, docker for windows でさっくり環境構築をして下さい。必須要件はこれだけです。

Docker For Windows | Docker

Docker For Mac | Docker

git cloneする

git clone https://github.com/treetips/schemaspy-mysql.git

schemaspy.propertiesをいじる

schemaspy.propertiesにデータベースの接続情報を書いて下さい。

cd schemaspy-mysql
vi schemaspy.properties

コンテナを起動する

お馴染みの起動です。

docker-compose up -d

schemaspyのコンテナ起動時にDB定義書の自動生成をします。生成が完了すると、schemaspyコンテナはそっと消えます。

nginxコンテナだけデーモン起動します。

ブラウザでDB定義書を見てみよう

http://localhost:8081/

さて、どうでしょうか。見えたでしょうか。

自動生成が成功すれば、以下のような定義書をブラウザから確認できたと思います。

テーブル一覧

f:id:treeapps:20170630222012p:plain

テーブル詳細

f:id:treeapps:20170630222033p:plain

関連図

f:id:treeapps:20170630222022p:plain

中々いい感じです。ただしColumnsタブを開く際は注意がいるかもしれません。全テーブルの全カラムが表示されるので、100テーブルとかあると・・・

DB定義書を更新する

再度SchemaSpyを実行し、DB定義書を生成します。

停止中のschemaspyを叩き起こすのですが、--rmオプションを付けるので、実行が完了するとまた静かに眠りに付き、リソースを消費し続ける事はありません。

docker-compose run --rm schemaspy

本当はcron用コンテナでも用意してそこからSchemaSpyコンテナを叩こうと思ったのですが、再実行したいタイミングは人によって違うと思ったので、やめておきました。場合によってはCIサーバでDBマイグレーションが実行されたタイミングでピンポイントで実行したいかもしれませんし。

私はまだまだdockerを始めたばかりで全然ベストプラクティスが解ってないのですが、コンテナをバッチ的に起動するって、どうやるのがいいのでしょうね。

一番簡単なのは今回のようにホスト側のcrontabで叩いてしまえばいいのですが、ホスト側のcron設定を汚すのも微妙に嫌ですね。Jenkins等のCIでスケジューリングして叩くのも楽ではありますが、ちょっと格好悪い気もしています。

DBマイグレーション実行後のみ叩きたいケースや、手動で再生成したいケースを考えると、やはりホスト側から叩くのが良い気がしていますが、どうするのがよいのやら。digdagとかairflow的なワークフローコンテナ用意するとか?

実装するうえでハマった点

AWTFontDefaultChar: symbol not found

java.lang.UnsatisfiedLinkError: /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/libfontmanager.so:
 Error relocating /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/libfontmanager.so: AWTFontDefaultChar: symbol not found

このエラーはバグで、JDKのバージョンによって起きるようです。最新バージョンで試してもエラーが起きたので、openjdk:8u121-jdk-alpine を使いました。

FontConfigurationのNullPointerException

schemaspy_core | java.lang.NullPointerException
schemaspy_core | 	at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
schemaspy_core | 	at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219)
schemaspy_core | 	at sun.awt.FontConfiguration.init(FontConfiguration.java:107)

これはAlpine linuxで起きがちなエラーで、単にシステムフォントがインストールされていないだけです。

ttf-dejavu等を適当にインストールしちゃって下さい。

wget: can't execute 'ssl_helper': No such file or directory

Alpine linuxでwgetをインストールしてhttpsのURLを指定すると、以下のエラーが起きました。

wget: can't execute 'ssl_helper': No such file or directorywget: error getting response: Connection reset by peer

ca-certificatesを使う方法もあるようですが、私は面倒なのでlibresslをインストールして解決しました。(ダウンロード後は不要なのでdelします)

schemaspy.propertiesのschemaspy.portが反映されない

これは単純にschemaspyのバグっぽいです。何指定しても無視されます。コマンドラインパラメータでportを指定しても同様の結果になりました。

なので、現状は残念ながら3306固定だと思って下さい。(MySQL以外だと違うかもしれませんね)

雑感

最近開発支援のツール等をdocker-composeを使ってサクッと構築しています。

実装がクソですが、一応紹介してみます。

静的リソースビューワー

github.com
これは、gitにpushされたファイルを、git環境無しでブラウザから閲覧できるようにする支援を行うものです。

と言っても単にnginxコンテナを起動してhtdocsのボリュームをホストと同期し、CIで定期的にhtdocsにファイルをpullすると、デザイナや営業がSourceTree等をインストールしなくてもgitのファイル群を確認できるよ、というクソツールです。

SeleniumGrid + nightwatchのE2Eテスト支援

github.com
ほぼサンプルプロジェクトですが、Dockerコンテナ上にSeleniumGridを構築し、ChromeとFirefoxに対してNightwatchでE2Eテストするためのものです。ローカル環境で実行する場合はGrid起動せず、自分のPCにインストールされたブラウザを直接起動するnpmスクリプトも用意しています。

複数のMySQL・MariaDBデータベースを一括起動

github.com
業務で使うデータベースがほとんどMySQLで、案件毎にバージョンが異なるのがウザかったので、いっそMySQLもMariaDBもありったけ起動してやれ!的なノリで作りました。docker-compose up -dすると、MySQL5.5〜5.7、MariaDB 10.0〜10.3を全部一気に起動するという、細かい事は一切気にしない男らしいコンテナです。

ちなみに前述のスクリーンショットは、この男らしいコンテナにテストテーブルを作って自動生成したものをキャプチャしたものなのです。

テスター向けのphpMyAdmin

これはDockerfileが不要なのでgithubにもdocker-hubにもアップしてませんが、以下のようにdocker-compose.ymlを保存して実行するだけで、WEB版のphpMyAdminが起動できます。

version: '3'
services:
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    container_name: phpmyadmin
    environment:
      - LANG=ja_JP.UTF-8
      - TZ=Asia/Tokyo
      - PMA_HOST=127.0.0.1
    ports:
      - "8080:80"

強い権限を持つユーザでphpMyAdminにログインされると、テーブルの削除やカラム定義の変更等が行えてしまうので、select・insert・update・deleteのみが行える専用ユーザを用意すると、テスターが自分でデータをいじってテストする事ができて便利です。


こんな感じで最近dockerで簡単に用意できる便利ツール的なものを導入していってます。

↑では挙げてませんが、アプリケーション内の画像を、ビルド時に画像変換(圧縮)するものも作ってたりします。parallelコマンドとmogrifyコマンドで並列実行し、高速に一括で画像変換するものです。業務ではそれを使っているのですが、githubで公開するにはクオリティが低すぎるので、公開していません。

ImageMagickコンテナを作っていて思ったのですが、コンテナ内で変換した画像ファイルのowner・groupの権限設定って目茶苦茶面倒臭いですね。ホストとコンテナでユーザIDとグループIDを合わせたユーザで変換しないと駄目なんですよね。何も考えずにコンテナ内で変換してもroot:rootになるし、かといってuid・gidを揃えないとホスト側からは存在しないuid・gidのowner・groupになってしまうし。


こんな感じで今後も簡単に試せて簡単に捨てる事ができるdocker環境を増やして公開していきます!

Viewing all 140 articles
Browse latest View live