プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

書籍『サーバーレスシングルページアプリケーション』を試してみました

書籍『サーバーレスシングルページアプリケーション』を読んで、試してみました。

サーバーレスシングルページアプリケーション ―S3、AWS Lambda、API Gateway、DynamoDB、Cognitoで構築するスケーラブルなWebサービス

サーバーレスシングルページアプリケーション ―S3、AWS Lambda、API Gateway、DynamoDB、Cognitoで構築するスケーラブルなWebサービス

いやー、この本いいですねー。実践的でかつ分かりやすい。最近は、FaaS (Function as a Service) っていうんですか?流行りのようですので、興味ある人はぜひ一度試してみるといいと思います。

さて、本の書評はネットを検索するといろいろと出てきます。

dev.classmethod.jp

yoshiyoshifujii.hatenablog.com

他にもいくつか目にしたのですが、忘れました(すいません)。

ということで、私などがあれこれいうのもおこがましいので、ここでは、実際にチュートリアルを試した際に気になったことやつまづいたことなどを、雑多にメモにしておきたいと思います。 似たようなところで、はまってしまう人の参考になればと思います。

ちなみに、はまったところを書いておられる方もいらっしゃいました。

kakakakakku.hatenablog.com

こちらもご参考にしてください。

さて、あれこれ書き始める前に、今回のチュートリアルを試した環境を書いておきます。

  • Windows 10 Pro 64bit, 1703 (Creators Update)
  • Bash on Ubuntu on Windows, 16.04.2LTS
  • sspa のリポジトリは 2017/7/31 現在のものをfork
  • 書籍:初版第1刷(2017/6/26発行)

BoW 便利ですねー。何の(大きな)問題もなく試せました。

第1章

p.12, sspa スクリプトが失敗する

で、早速、勇んで始めたら、第1書 p.12 のsspaスクリプトの実行で早速躓いてしまいました。

mor@DESKTOP-RLA4CF1:~/learnjs$ ./sspa server
Can't find Python. You need Python 2.7 or later to use this.

調べてみると、sspa スクリプトはpython2.7以上が必要とあり、スクリプト内部では python コマンドで呼び出していました。

で、Bash on Ubuntu on Windows の場合 python3 がデフォルトで入っているのですが、pythonコマンドでは起動できない(python3で呼び出すようです)。 なので、デフォルト状態では、pythonがないと判断されて実行できないとうおちでした。

解決方法は、シンボリックリンクを作成するだけです。

cd /usr/bin
sudo ln -s python3 python

これで無事に動きました。

p.17, AWS CLI のセットアップ

BoW なら Ubuntu と同じようにすれば、本のとおりでも(たぶん)うまくいくと思いますが、

sudo apt-get install python-pip

で pip をインストールしてみると、 python2.x に関連するパッケージもインストールされてしまい、気持ち悪かったです。

なので、今回は AWS CLI のインストールガイドのページを参考にインストールスクリプトをダウンロードし

curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python ./get-pip.py
sudo pip install awscli

としました(sudo経由でルート権限で実行すると、ホームディレクトリではなく、システム全体にpipをインストールしてくれる)。

ちなみに、同じawsの説明ページでもこちらにある手順だと、ユーザー環境へのインストールになるのでご注意を(場合によってはこれのほうがいい場合もあるかな)。

p.19, AWS CLI のセットアップ

リージョンとして ap-northeast-1 を指定してみましたが、以後特に問題なく使うことができました。 第6章だったかな?コード内にリージョン名を記入するときには、自分が指定したものを使うようにします。

第3章

テストの書き方がわからない

第3章以降は本文中ではテストの書き方に言及しなくなっています(まあ、紙面の都合上仕方ないでしょうね)。 なので、著者のサイト の Resources からソースコード一式をダウンロードして、自分が今読んでるソースコード(learnjs/xxxx/public.app.js)に該当する番号(章・節番号を元にしているようです)内のテストコードなどを参考に、自分で書いて試すが一番よさそうです。

p.57, コード

マイナス記号が抜けてます。オライリーの正誤表のページにも記載されています。

p.58, 3.2.2『データモデルを成長させる』

個人的には、この節のようにすればフォーマッターを適用できますよ、という紹介のように受け取れました。 ダウンロードしたコードにも実装がないようですしね。

あれか?自分でフォーマット用のコードを書けばすんなりチュートリアルとして使えるということなのかな? いずれにしても、チュートリアルとしては若干補う必要があるので、場合によっては飛ばすのもありかと思います。

第4章

p.84, 4.2.1 節

./sspa create_pool conf/cognito/identity_pools/learnjs

このまま実行すると

support/jsed.py

でエラーになります。

理由は、support/jsed.py 16行目の print 文がpython3 に対応していないためです。

ということで、ここを print関数に修正します。

mor@DESKTOP-RLA4CF1:~/learnjs/support$ diff jsed.py.org jsed.py
16c16
< print search(doc, keys)
---
> print( search(doc, keys) )
mor@DESKTOP-RLA4CF1:~/learnjs/support$

diff とるとこんな感じです。

p.91, refresh 関数はどこで使う?

ここを読んだだけでは、このrefresh 関数の使われ方がわからなくて、しばし戸惑ってしまいました。 読み進めると、p.113 の 5.4.1『ファイルセーフのデータアクセス関数』のところで、実際に使われており、なるほどと納得できました。 第4章では使われてないので、気にせず読み進めるのが良いのではないか?と思います。

第6章

Node.js のセットアップはどうする?

下記の記事にまとまっているように、

[http://qiita.com/7tsuno/items/e666223d6be22d657542:embed:cite]

BoW の場合 (付録でも触れらている) nvm を利用するのが良さそうです。

インストール方法は、 nvm のサイトに書いてあるので、それに従います。

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash

一度ログアウトして再ログイン後、インストールします。

nvm install 4.3

※ node.jsのバージョンは 4.3 にしました。今だと、Lambda が 6.10 もサポートしているのですが sspa スクリプト内で固定的に 4.3 が指定されているので、それに合わせています。

ちなみに、最初はrootユーザーでインストールしようとしたんだけど、

stackoverflow.com

このコメントに『rootでインストールして共有して使うのを想定していない』とあったので、やめました。

細かいですが、BoWだと zip もなかったので、ついでにインストールしておきます。

sudo apt-get install zip

p125, 6.1.3 のLambdaの課金単位

本文中では『GB/秒』とかいてあるけど、AWS Lambda の料金のページ等を見ると『GB・秒』の表記のほうが正しいと思います。

6.3の『プロジェクト』と6.3.1の『プロジェクト』は同じ意味?

確証はないですが、個人的には多分違うんじゃないかなと思ってます。 6.3節では『プロジェクト』がモノリシックな状態を作り出すものを意味しているのに対して、6.3.1節ではプロジェクトがLambda関数をまとめるファイルのような意味合いで使われているように思われます。

こういう時、原著を読むと分かったりするのですが、実際のところどうなんでしょうかね?

p137, このコラムはどうやって試せばよい?

自分でCLIで試すということじゃないかと思ってます。試したのをメモとして残しておきます。

p.138 の popularAnswers で dynamoDB を使った処理
mor@DESKTOP-RLA4CF1:~/learnjs$ aws --profile admin dynamodb scan --table-name "learnjs" --filter-expression "problemId = :probId" --expression-attribute-values '{":probId": {"N":"1"}}'
{
    "ScannedCount": 2,
    "Count": 1,
    "ConsumedCapacity": null,
    "Items": [
        {
            "userId": {
                "S": "ap-northeast-1:abcdefgh-1234-5678-ijkl-xxxxxxxxxxxx"
            },
            "problemId": {
                "N": "1"
            },
            "answer": {
                "S": "true"
            }
        }
    ]
}
mor@DESKTOP-RLA4CF1:~/learnjs$
lambda関数呼び出しの場合
mor@DESKTOP-RLA4CF1:~/learnjs/public$ aws --profile admin lambda invoke --function-name 'popularAnswers' --payload '{"problemNumber": 1}' tmp
{
    "StatusCode": 200
}
mor@DESKTOP-RLA4CF1:~/learnjs/public$ cat tmp
{"true":1}

p138, コード中の リージョン名

第1章で指定したリージョンに合わせて編集しましょう。

p138, コード中の byCount 関数

引き算のマイナスが抜けているみたい。たぶんこうなるはず。

function byCount(e1, e2) {
  return e2[0] - e1[0];
}

p.139, 6.4がチュートリアルとして扱いにくい

仕方ないのかなー?

6.3.4のビューおよびp.140のポリシーのサンプルなどもあると分かり易いんだけど、これがないので、若干試しにくいんだと思います(原著者のサイトやダウンロードしたものにも含まれてないようですしね)。

ということで、簡単ですが自分で試してみました。 popularAnswer はログインしており、かつ、解答が正解した場合に、次の問題へのリンクの下に、『Show Popular Answer』というリンクが表示され、それをクリックすると、その問題について多い正解が表示されるという形にしました。

popularAnswerのビューの例

public/index.html にビュー(popular-view)をtemplatesに追加します

        

また、リンク表示用の要素も追加します。

        
popularAnswer 表示処理

public/app.js に addPopularLink 関数および popularView 関数を追加します。

learnjs.addPopularLink = function(parentElem, problemNumber) {
  var link = learnjs.template('popular-link');
  link.find('a').attr('href', '#popular-' + problemNumber);
  parentElem.append(link);
}
learnjs.popularView = function(data) {
  var problemNumber = parseInt(data, 10);
  var view = learnjs.template('popular-view');
  var popular = view.find('.popular-list');
  learnjs.popularAnswers(problemNumber).then(function(items) {
    var objs = JSON.parse(items.Payload);
    for (var key in objs) {
      popular.append($('

').text('answer: ' + key + ', count: ' + objs[key])); } }); view.find('.title').text('Popular Answer for Problem #' + problemNumber); return view; }

既存の buildCorrectFlash 関数を修正して、認証が通っている場合に、リンクを追加するようにします。

learnjs.buildCorrectFlash = function(problemNum) {
  var correctFlash = learnjs.template('correct-flash');
  var link = correctFlash.find('a');
  if (problemNum < learnjs.problems.length) {
    link.attr('href', '#problem-' + (problemNum+1));
  } else {
    link.attr('href', '');
    link.text("You're Finished!");
  }

  learnjs.identity.done(function() {
    learnjs.addPopularLink(correctFlash, problemNum);
  });
  return correctFlash;
}

showView 関数に popularView を表示するためのルートを追加します。

learnjs.showView = function(hash) {
  var routes = {
    '#problem': learnjs.problemView,
    '#profile': learnjs.profileView,
    '#popular': learnjs.popularView,
    '#': learnjs.landingView,
    '': learnjs.landingView
  };
  var hashParts = hash.split('-');
  var viewFn = routes[hashParts[0]];
  if (viewFn) {
    learnjs.triggerEvent('removingView', []);
    $('.view-container').empty().append(viewFn(hashParts[1]));
  }
}

Lamda ポリシーの追加

p.140 にあるようにCognitoロールにLambda関数の呼び出しを許可するポリシーを追加します。 今回はコンソールで実行しました。

ブラウザで AWS のコンソールにログインして、 IAM -> ロール -> learnjs_cognito_authenticated を選択します 『インラインポリシー』の『ロールポリシーの作成』をクリックします。 今回は、Policy Generator を使い、下記の様なポリシーを追加しました。

(Lambda関数の呼び出し, invoke を許可するポリシー)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1502980684000",
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                Lambda関数のARN
            ]
        }
    ]
}

一応、これで手元の環境ではうまく動きました。 テストファーストを押しているチュートリアルですが、テストには手が回りませんでした・・・

p.140, FunctionName は ‘learnjs_popularAnswers’ ではなく ‘popularAnswers’ では?

Lambda の設定内容次第ですが、本文に従っているなら、多分間違いだと思います(popularAnswers が正しいと思います)。

第7章

p.152, 7.3.2 を実装したが、どうテストを書けばよい?

Webワーカーを導入した場合、テストも修正しないといけないのですが、ダウンロードしたコードには変更が反映されていないようです。 自分で書かないといけないのですが・・・(略)。

p.161, 7.6.1 にある CloudFront を試したい

ということで、CloudFront を試してみました。CloudFront の一般的な話は下記などを参考にしてください。

dev.classmethod.jp

設定方法自体は少しネットを検索すれば出てくると思うので、詳しくは触れませんが、S3のバケット名を指定して、下記画像のように独自ドメイン名を指定することぐらいで設定完了でした。

f:id:junichim:20170818133228p:plain

その他として、この本で作っているアプリで扱うときは、

  • ドメイン名(/終わり)でアクセスするには Default Root Object に index.html などを指定しておく

とよいかと思います。(細かい設定は全然見てませんが)予想以上に、この本で使ったS3の公開用バケットに対して CloudFront を設定して公開するのは簡単にできるな、と思いました。

Restrict Bucket Access の設定方法について

本文中でも直接S3のバケットにアクセスできないように設定しましょう、とあるので試してみました。

設定手順について簡単に示すとだいたい次の通りになります。

  1. 作成した distribution を選択し、Origins タブを表示し、 Edit を実行
  2. Restrict Bucket Access を Yes にする
  3. Origin Access Identity, Comments, Grant Read Permissions on Bucket が表示される
  4. Origin Access Identity は Create a New Identity を選択
  5. Comments は適当に
  6. Grant Read Permissions on Bucket は Yes, Update Bucket Policy を選択
  7. Yes, Edit ボタンを押して、変更を有効にする

ちなみに、Origin Access identity はCloudFrontからS3へアクセスする際のIdentityを新たに作るか既存のものを利用するかを指定するものです。 新規の場合は、Createでよいらしい。

Grant Read Permissions on Bucket は S3 のバケットポリシーを自動で編集するために読み取り許可を与えるか否かを指定するものです。Noを選んだ場合は、自分でS3のバケットポリシーを編集しないといけないので、Yesを選択したほうが楽なようです。

なお、Restrict Bucket Access の設定方法は下記記事を参考にさせていただきました。

S3の特定バケットへのアクセスを特定のCloudFrontからのみ許可する。 - Qiita

AngularJSで作ったSPAをAWS上の「S3+CloudFront」でお手軽ホスティングして、クラウドサービスってやっぱ素晴らしいなと思った話 | I am mitsuruog

オリジンアクセスアイデンティティを使用して Amazon S3 コンテンツへのアクセスを制限する - Amazon CloudFront

CloudFront 経由のみのアクセスに限定?

で、これで、S3で公開しているURLではアクセスできないかと思いきや、全然できてしまいます。 なんでだ?

というので調べてみると、sspaスクリプトがS3にアップロードする際に、ACLとして public-read を与えていたためのようです。これは、S3 の static web hosting を行う際には、誰でも読み込み権限を与える必要があるために、わざわざ設定しているようです(AWS のドキュメントにも説明がありますね)。

docs.aws.amazon.com

ちなみに、ネットでS3の静的ホスティングの記事などを見ると、バケットポリシーを設定するという記事(例えば、これとか)などが良くありますが、これも同じことで、ACLで指定するか、バケットポリシーで指定するかの違いだけのようです。

なので、CloudFront 経由のみにアクセスを限定したい場合は、上記の Restrict Bucket Access に加えて、public-readのACLを削除する必要があります。 やり方はいろいろあると思いますが、今回でしたら、一旦公開バケットの中のファイルをすべて削除して、コマンドラインから

aws --profile admin s3 sync public/ s3://learnjsバケット名 --acl private

のように private で再アップロードすればOKです。

なお、ついでに、 static web hosting も無効にしておきます(有効でも公開されてないのでアクセスできないのですが)。 これで、CloudFront 経由のアクセスのみになりました。

最後に

あれこれ書きましたが、一言でいうなら、おすすめできる本です。

若干チュートリアルとして扱いにくいところもありますが、まあ、新しいものに触れる際は、自分で試行錯誤しないといけないので、ま、その訓練と思えばいいんじゃないでしょうか。

改訂されることがあれば、チュートリアルとしてより扱いやすい本になることを期待しています!

にしても、AWS 使えば使うほど、amazon にベンダーロックインされてる感がすごいなー。まさにこの本の第一章で書かれている状態に近づきつつあるや。ま、これはこれとして楽しみましょう。

Bash on Ubuntu on Windows でまともにコピー&ペーストできる端末を使う

最近、Bash on Ubuntu on Windows (以後、BoW と略します)をよく使うようになってきました。これよくできてますね。Windowsでありながら、Ubuntu(bash)上であれこれ操作できてしまうので、VirtualBox に Ubuntu のせて・・・なんててんでやらなくなりました。

Creators Update 以後の BoW だと日本語表示・入力も問題ないですしね。

とはいえ、まったく標準のBoW環境に不満がないというわけでもなくて、文字列のコピー&ペースト操作がとにかくやりづらい。 多分、元々のコマンドプロンプトの流れを汲んでいるせいなんでしょうが、個人的には現時点でこれが唯一にして最大の不満です。

ということで、あれこれ調べてみると、同じように使いやすいターミナル環境を求めている人は多いようで、今回はこちらの記事

qiita.com

を参考にさせていただいて、wsltty をインストールしました。

まあ、前述の記事さえ読めばよいのですが、自分用のメモということで、インストール&初期設定時にやったことを書いておきます。

インストール

インストールは全然難しくなく、公式サイトからインストーラをダウンロードして、実行するだけです。 インストール時点のバージョンは 1.7.9 でした。 あ、Windows と BoW は

  • Windows 10 Pro 64bit版 1703
  • Ubuntu 16.04.2 LTS

でした。

設定

インストール後スタートメニューから起動できるようになります。

  • WSL % in mintty
  • WSL ~ in Mintty
  • WSL -l in Mintty

3つメニューが追加されていますが、どれでも起動はできます。違いは、

  • % のものが Windows のユーザーディレクトリ(C:\Users\ユーザー名 など)で起動
  • ~ はubuntuのホームディレクトリ(/home/ubuntuユーザー名 など)で起動
  • -l がログインシェル

とのことです。

ターミナルを起動後、ターミナル上で右クリックするか、ウィンドウの左上のアイコンをクリックするとオプションメニュー『Options』が表示されるので、それを選択します。 オプション画面が表示されたら、あとは好きなように設定します。デフォルトから変更したものは次のものです。

端末の色

f:id:junichim:20170815170708p:plain

Options -> Terminal -> type

を選び

xterm-256color

に変更します。これで色付きで表示されます

.bashrc にて端末種類が xterm-color や *-256color の時に色付きにしているためです

日本語化

Window -> UI language

を選び、

@ Windows language @

に変更します。 これで、 wsltty 再起動後、オプションメニューの表記が(Windows に合わせて)日本語になります。

フォント変更

テキスト -> フォント

を選択して、

フォント種類を Lucida Console から MS ゴシック にして、フォントサイズを 9pt -> 12 pt にしました。 文字が小さいと辛くなってきたので、これでだいぶ見やすくなったかな。

コピーアンドペースト

特に設定を変更しなくてもデフォルトで

  • 選択でコピー
  • 中マウスボタンでペースト

になっています。これだと随分使いやすいなー。 右ボタンでペーストしたいとかあれば、設定変更できるようです。

他にもいろいろと設定すると快適に使えそうですが、とりあえずはこれで試していきます。

CoderDojo伊勢 第3回 へ参加してきました

2017年8月5日(土) 雨上がりの蒸し暑い天気の中、CoderDojo伊勢の第3回へメンター(教え役のスタッフ)として参加してきました。

今回の会場はハートプラザみその 駐車場がいっぱいあって車で来ても楽ですね(近所なんで自転車ですが・・・)。

今日はこんな雰囲気でした。

f:id:junichim:20170805215306p:plain

まだ、ブログで紹介したことなかったので、この機に書いてみたいと思います。

CoderDojoとは?

CoderDojo を知らない方のために、簡単に説明すると、子供を対象としたプログラミング道場、というもので、ボランティア活動で運営しています。 CoderDojo Japan のサイトには、

CoderDojo は7〜17歳の子どもを対象にしたプログラミング道場です。2011年にアイルランドで始まり、世界では69カ国・1,250の道場、日本では全国に90以上の道場があります。

と書かれています。

よくあるプログラミング教室との一番の違いは、CoderDojo は子供が自分のペースで自由に取り組める場を提供することを目的としているものです。 わかりやすく、子供向けのプログラミングクラブといったりもします。

なので、先生がいて、何かのカリキュラムがあって、それに従って教える、というスタイルではなく、参加者が自分でやりたいことを考え、その過程でわからない点を他の参加者やメンターに尋ねるというスタイルになります。個人的にはこのスタンスが気に入ってます。

下記のスライドにまとめていただいているので、これを見るほうがもっとわかりやすいかもしれないですね。

三重県初

三重県でも、子供向けのプログラミング関係のイベントなどがいろいろと増えてきました。 ワークショップ主体でやったり、学校でイベントやったり、教室だったり・・・ 調べるといろいろ出てくると思います。

実は、CoderDojo伊勢 は三重県内で初のDojoです。というか、このブログを書いている時点で CoderDojo Japan を見てみたら、全国に90の道場がありますが、県内ではまだ伊勢だけです。そろそろ県内にも Dojo が増えないかな、と期待しているところです。

(あ、誤解を招くといけないので補足ですが、県内でも CoderDojo ではありませんが、似たような趣旨の活動を行っているところもあります。)

雑感など

第3回になり、メンターも初参加の方が増えてきました。大学生が2名参加してくれていたのがうれしかったですね。 参加者もCoderDojoは初参加だけど、家でやってます、という方もちらほら見かけるようになった印象です。 また、チャンピオン(CoderDojo伊勢の代表者の事)に聞いたところ、募集開始をして、その当日に定員が埋まってしまったとのことです。

なんとなくですが、だんだん世間が『プログラミング』という単語に反応してきているような印象です。

今回以前も参加していたので、それと比較すると、いままでは初心者が多くて、先生対生徒、というスタイルがメインだったようなのですが、今回からは少しでもScratchをやったことのある子どもは経験者として扱うようにしたためか、参加者が自ら作ろうとしている姿勢のようなものを感じました。こういう雰囲気が増えてくるといいなと思っています。

まだ、Scratch しかやってませんが、他の言語もやりたいという子供が増えてきてほしいですね。

最後に

CoderDojo伊勢では メンター・運営に協力してくれる方・寄付などを随時募集しているとのことです。

CoderDojo伊勢 支援のお願い - CoderDojo伊勢 (コーダー道場伊勢) -三重県伊勢市のプログラミング道場-

少しでもご興味を持った方は、ぜひお気軽にコンタクト取ってみてください。

リモートアクセスVPN を QNAP 上で動かす

今まで、こちらの記事にあるように、 SoftEther を利用して自宅LANへのリモートアクセスを実現していました。

しかし、SoftEtherVPN の Server を動かすために動かしていたマシンを廃棄したくなってきたため、これを QNAP 上に移動できないか検討したので、その際のメモをまとめておきます。

QNAP の機種は下記の TS-251+ を使ってます。

www.qnap.com

SoftEther VPN Server 立ち上げ

QNAP TS-251+ だと、 Virtualization Station と Container Station があり、仮想マシンとdockerコンテナを動かすことができます。 dockerコンテナで動かしたいなと思ったのですが、NIC周りでトラブルのが嫌だったので、とりあえずは Virtualization Station (KVM) 上で仮想マシンを立ち上げ、そちらで動かしてみました。

OS は、 Ubuntu 16.04.2 (64bit, サーバー)としました。 SoftEther VPN Server は現時点の最新版を使います(Ver 4.20, Build 9608, rtm)。ダウンロードページはこちらになります。

インストール手順としては、ほぼこちらの記事と変わりないのですが、若干違いがあったところだけ、追記しておきます。

起動方法

Ubuntu は 16.04 からスタートアップ処理が SysVinit から systemd に変更になったそうです。 これに伴い、 /etc/init.d にスタートアップスクリプトを書くスタイルから、 /etc/systemd/system に起動用設定ファイルを書くスタイルになったそうです。

で、ネットを探すと、そのものずばりのサイトがありました。

Systemd用SoftEther設定ファイル - blog.204504byse.info

qiita.com

実際に作成したスクリプトは下記になります。

[Unit]
Description=SoftEther VPN Server
After=network.target network-online.target

[Service]
Type=forking
ExecStart=/usr/local/vpnserver/vpnserver start
ExecStop=/usr/local/vpnserver/vpnserver stop

[Install]
WantedBy=multi-user.target

リンク先サイトまんまですね。 これを、 /etc/systemd/system/vpnserver.service として保存しておきます。

OS再起動後、vpnserverプロセスが立ち上がっていたらOKです。

(参考)

systemd 一般の話のリンクも載せておきます。

eng-entrance.com

qiita.com

qiita.com

NIC の追加とプロミスキャスモード

SoftEther VPN Server を使って、リモートアクセスを可能にするためには、ローカルブリッジを作る必要があります。

以前の環境だと、 ESXi 上で動かしていたため、ポートグループを作成して、そちらでプロミスキャスモードを設定するという方法を取っていました。 詳しい手順などは、下記を参考にしてください。

blog.mori-soft.com

QNAPの場合、仮想スイッチがあり、物理NICと仮想マシン上の(仮想)NICがこの仮想スイッチを経由して接続されているようです。 でも、仮想スイッチの設定に『プロミスキャスモード』みたいなものがありません。

どう設定すればいいのだろうか? と思って、あれこれ調べたのですが、これといった決め手が見つかりませんでした。

ただ、仮想スイッチは大きく3つの動作モードを持っているようで、仮想マシンと物理NICを直接つなげるような使い方でできるようです(外部専用ネットワーク)。

www.qnap.com

これを使えば、実現できそうな気がします。

ということで、とりあえず、今回は下記のように対応することにしました。

  • 外部専用ネットワークの仮想スイッチを作成する
  • TS-251+ の物理NICが1つ余っているので、これを作成した仮想スイッチにつなげる
  • 仮想マシンに(仮想)NICを追加し、この仮想スイッチに接続する
  • OS側の設定で、追加した仮想NICに対して、IPアドレスを 0.0.0.0 として設定する

外部専用ネットワークの仮想スイッチは、仮想スイッチの作成画面で、

f:id:junichim:20170710161302p:plain

『IPアドレスを割り当てない』を選択すると作れるようです。

設定後の TS-251+ のネットワークの設定画面をみると、こんな感じになります。

f:id:junichim:20170710160613p:plain

ちょっとわかりにくいですが、下側の Virtual Switch 4 というのが外部専用ネットワークで作成した仮想スイッチで、ここにつながっている仮想NICが上記で追加したものです。

/etc/network/interfaces の設定内容はこんな感じです。

mor@ubuntu-vpn:/etc/network$ cat interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto ens3
iface ens3 inet dhcp

# nic for SoftEther VPN Server
# added by Junichi MORI
# 2017/7/9
auto ens4
iface ens4 inet static
address 0.0.0.0

mor@ubuntu-vpn:/etc/network$ 

ens3 が一つ目のNIC、ens4が追加した二つ目のNICになります。

ちなみに、今回は起動時に rc.local とかで ifconfig ens4 promisc としていないのですが、ifconfig で設定内容を表示させると、ens4 に対して PROMISC が表示されていました。 以前セットアップした際は必要かと思っていたのですが、必要ないのかな? Ubuntu 16.04 だからなのか、Linuxは一般にこうなるのか、IPアドレスが 0.0.0.0 だからなのかまでは、ちょっとわからなかったです。

(参考)nicのデバイス名

Ubuntu 15.10 からnicのデバイス名の命名方法が変わっているようです。 eth0 や eth1 じゃなくて、 ens3 や ens4 となっています。

CentOS7 の記事ですが、命名規則がわかりやすかったです。

www.atmarkit.co.jp

お気を付けください。

その他

以前の方法だと、SoftEther VPN Server の設定として VPN Azure を使っていたのですが、結構おもい印象でした(いろんな要因があるので、これだけではないとは思います)。

今回はVPN Azure のよくある質問ページにも書いてある NATトラバーサル(NAT-T, 一般的には UDP ホールパンチング)を使うように変更しました。

とはいっても、クライアントとして SoftEther VPN Client を使えば接続時に自動的に試してくれるので、特別な設定は必要ないです。

SoftEther VPN Client の設定

NAT-Tを使うため、クライアント側では、 Softether VPN Client を使う必要があります。

Windows の場合

下記の手順に従って、インストール、設定をすれば完了です。

8.2 Windows へのインストールと初期設定 - SoftEther VPN プロジェクト

初回起動時に仮想LANカードが作成されます。接続設定として

  • VPN サーバー名(DDNS名)
  • ポート番号
  • VPN接続ユーザー名とパスワード

を入力すればOKです。

特に設定が間違っていなければ、これで接続できるはずです。

Linux の場合

SoftEther VPN Client はLinux版もあります。設定用のGUIツールがないので、やることはWindows版と同じですが、vpncmd で設定することになります。 設定時に使う主なコマンドとしては

  • NicCreate, 仮想LANカードの作成
  • AccountCreate, 新規接続設定の開始
  • AccountPasswordSet, 認証情報の設定

  • AccountConnect 接続

  • AccountStatusGet 接続状態の確認
  • AccountDisconnect 切断

かと思います。必要に応じてhelpコマンドで調べてください。

また、上記設定だけだと、VPN接続が成功しても、仮想LANカードにIPアドレスが割り当てられません。 なので、仮想LANカードがDHCPからアドレスを取得できるように /etc/network/interfaces に設定を書いておきます。

例えばこんな感じです。

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

allow-hotplug vpn_vpn0
iface vpn_vpn0 inet dhcp

ここでは仮想LANカード名を vpn0 としました。

これで接続を試して、問題なく接続できれば、OKです。

おわりに

これで問題なく QNAP 上で運用できるようになると、今まで使っていた古いサーバーを処分できるので、部屋が少しは涼しくなりそうです。 当面は問題が起こらないか様子見ですね。

あと、Linux 上で VPN Server を動かす場合 tap デバイスを使うのが定番のような印象を受けています。おいおい、このあたりも調べて試してみようと思います。

map フォーマットファイルを自分で作成する方法について

Androidアプリ『避難所検索@伊勢』では、オフラインマップを使っています。 このオフラインマップでは、 Mapsforge というライブラリを利用して地図を表示しており、 Mapsforge では OpenStreetMap のデータを map フォーマットというファイル形式で読み込み・表示します。

map フォーマットのファイルは、

Mapsforge Download Server

このサイトからダウンロードできます。明記はされていませんが、定期的に更新しているようです。

ただ、こちらのサイトの map ファイルは国または都市単位で、残念ながら日本は国単位でしかないので、サイズが非常に大きくなっています(japan.map で約 800MB!)。

スマホのストレージはそんなに潤沢じゃないので、これだと、アプリに同梱するにせよ、オンライン時にダウンロードするにせよ、扱いが難しくなってきます。

そこで、『避難所検索@伊勢』では、伊勢市近辺のみのオフライン地図データを作成して、それをアプリに同梱しています。

ここでは、メモがてら、オフラインマップファイルの作成の際にやったことをまとめておきます。

Webサービス

最初にWebサービスで任意の範囲の mapファイルをダウンロードできるものがないか探しました。

すると、BBBike というサイトが OpenStreetMap のファイルのダウンロードサービス(正しくは抽出サービス、というのかな?)を提供してくれています。

extract.bbbike.org

このサービスでは、指定した範囲に含まれる OpenStreetmap のデータを抽出(ダウンロード)することができます。また、ダウンロードできるファイル形式には、.map を始め、 .pbf など多種多様なものがあります(.pbf というのが、標準的な OpenStreetMap データのバイナリ形式フォーマットのようです)。

ということで、このサイトにアクセスして、ファイル形式を指定して、抽出完了通知を受け取るメールアドレスを入力して、ダウンロードしたい範囲を指定します。

しばらくすると、入力したメールアドレス宛に、ダウンロード準備ができたよ、と通知が来るので、メールに記載のリンクをクリックして、データを落とします。 zipファイル形式になっているので、解凍すると、中にお目当ての.mapファイルがあります。

なお、BBBike の抽出サービスについての詳細は下記サイトなどをご参考にしてください。

Help | BBBike extracts

OpenStreetMapの地図情報からタイル画像を生成する - GiBlockラボ

(参考)BBBike について

あと、元々の BBBike というサイトは、サイトのトップページに

Welcome to BBBike, your cycle route planner! We’ll help you find a nice, safe and short bicycle route in your city and around. Please choose your city:

とあるように、自転車向けに最適なルートを提供するというサービスのサイトのようです。元々、ベルリンを対象に始まり、それが他の都市にも広がったもようです。 日本だと、東京ぐらいしか入ってないですね。伊勢も入れてくれないかなー。

実は

実はこれで問題ないかと思いきや、このファイルを使って mapsforge で表示させると、なんと、海が真っ白になってしまいます。

f:id:junichim:20170707221930p:plain

最初はよくわからなかったのですが、いろいろ調べてみると mapsforge のやり方として、海岸線のデータを個別に持ったりしているのではなく、レンダリング時に海をうまいこと描画しているようです(ちょっと中途半端な理解なんでこれ以上なんともいえないです)。

なので、海へ対応するためには、それなりの手順を踏む必要があり、そのことが mapsforge のサイトに記載されていました。下記ページにあるような手順が必要とのことです。

mapsforge/MapCreation.md at master · mapsforge/mapsforge · GitHub

また、この際に、map-writer というのを使って書き出すようです。

mapsforge/Getting-Started-Map-Writer.md at master · mapsforge/mapsforge · GitHub

『こんなややこしいのやるの嫌だなー』と思いつつ、もう少し調べると、既に mapsforge のサイトに答えがありました。 mapcreator というツールが用意されており、これを使うと、上記ページに記載されている手順を python スクリプトで自動的に実行してくれるようです。

github.com

ということで、この mapcreator を使ってみたので(わかる範囲で)使い方を書いておきます。

mapcreator

準備

mapcreator のページ(一番下あたり)見ると、いろいろと準備が必要とあります。 詳細な手順は覚えていないのですが、 Windows10 (64bit, Anniversary Update) 上の BoW (Ubuntu 14.04.5 相当)で環境構築した際には、以下のものをインストールしています。

mapcreator

git clone https://github.com/mapsforge/mapsforge-mapcreator.git

Osmosis

wget http://bretth.dev.openstreetmap.org/osmosis-build/osmosis-latest.tgz
mkdir osmosis
mv osmosis-latest.tgz osmosis
cd osmosis
tar xvfz osmosis-latest.tgz
rm osmosis-latest.tgz

map-writer, Osmosisのプラグインとして動作

mkdir ~/.openstreetmap/osmosis/plugins
cd ~/.openstreetmap/osmosis/plugins
wget http://search.maven.org/remotecontent?filepath=org/mapsforge/mapsforge-map-writer/0.8.0/mapsforge-map-writer-0.8.0-jar-with-dependencies.jar

Osmosisのプラグインとして設定するには、特定のディレクトリに配置すればよいようです。詳細は下記リンク先等を参考にしてください。

mapsforge/Getting-Started-Map-Writer.md at master · mapsforge/mapsforge · GitHub

Osmosis/Detailed Usage 0.45 - OpenStreetMap Wiki

GDAL, ダウンロードページのUbuntuを参照, こちらのページに行きついた

sudo apt-get install python-software-properties
sudo add-apt-repository ppa:ubuntugis/ppa

sudo apt-get install python-gdal
sudo apt-get install gdal-bin

Shapely

sudo apt-get install python-pip
sudo apt-get install python-software-properties
sudo apt-get install libgeos-c1v5
sudo pip install Shapely

その他(ヘッダファイルなど)

sudo apt-get install python-dev
sudo apt-get install libgeos-dev

設定ファイル

入力となる .pbf ファイルですが、 mapcreator のサイトには、地球全体の.pbfファイル(planet.osm に対応する.pbf)を使って試しなさい、とありますが、数十GBもあるので、とてもじゃないけど扱いたくありません。 なので、.pbfファイルは抽出したい領域より少し大きめにとったものを BBBike のサイトからダウンロードしておきます。

ちなみに、.pbf ファイルはBBBikeからダウンロードするほかに、 Geofabrik 社のダウンロードサイトからも取得することができます。こちらのデータは定期的に更新されており、国別・地域別に提供されています。日本だと、地方(関東や関西など)別に提供されています。ちなみに関西で、約130MBありましたので、こちらのデータを使う場合はデータサイズにお気を付けください。

次に設定ファイルを編集します。基本的には、mapcreatorのページに説明があるので、それに従います。 あれこれいうより、実際に設定したxmlファイルを示します。

ise_config.xml



    
    


このファイルの意図としては、伊勢市周辺のデータを抽出して、.mapファイル(のみ)を作成するというものです。設定方法によっては.pbfファイルも一緒に作成できるようですが、今回は使っていません。

上記のxmlファイル中にパスを指定するタグ(xml中の~pathのタグ)がいくつかありますが、それぞれの意味は次の通りのようです。

  • osmosis-path, インストールしたOsmosis のbinフォルダ中の osmosis ファイルを指定
  • map-staging-path, 作業途中でmapファイル保存に使うフォルダ
  • pbf-staging-path, .pbf ファイルを保存するフォルダ。またスクリプト動作時に自動的に土地データ(http://openstreetmapdata.com/data/land-polygons)をダウンロードおよび展開する際に使用するフォルダ
  • polygons-path, 設定ファイルの part タグの name 属性に対応するポリゴンデータを格納するフォルダ
  • map-target-path, 最終的に.mapファイルが出力されるフォルダ(map-staging-pathと同じだと実行時に、コピーできないと怒られます)
  • logging-path, ログフォルダ
  • initial-source-pbf, 処理対象となる.pbfファイル名

ここで、 polygons-path に格納するポリゴンデータ(ポリゴンファイル)ですが、このファイルに定義した範囲が抽出の対象となるようです。 今回は下記の様に矩形で定義しました。

ise.poly

polygon
1
   136.517   34.585
   136.928   34.585
   136.928   34.377
   136.517   34.377
   136.517   34.585
END
END

なお、抽出元の.pbfファイルは、地図の北を上側にして矩形の左下と右上を、(経度、緯度)で表すと

(136.517, 34.377) - (136.928, 34.585)

の範囲を対象としたものになっています。

また、設定ファイル(上記の例だと ise_config.xml)の part タグの map-start-lat, map-start-lon はこのポリゴンで指定される領域内を指す必要があるそうです。

なお、ポリゴンファイルは、設定ファイルの part タグの入れ子の階層およびname属性と対応している必要があるので、注意してください。まあ、mapcreatorをcloneした状態で、xml/example-config.xml ファイルをみれば大体想像がつくとおみます。

上記の例の場合は、polygons-pathフォルダの直下に ise.poly ファイルを配置しています。

実行

上記の設定ができれば、さっそく実行してみます。

python mapcreator.py -c xml/ise_config.xml

実行が始まると、土地データ(http://openstreetmapdata.com/data/land-polygons)のダウンロードが始まります。全世界のデータっぽくて、このときで約460MBのサイズがありました。 無事にダウンロードが終わると処理が行われていきます。ちなみに、このファイルのことを調べると、ここでも『ポリゴン』という用語が出てきますが、先ほどのmapcreatorの設定ファイルで出てきている polygons-path のポリゴンとは別のものを指していると思いますので、ご注意ください。

内部の処理は詳細に追いかけていませんが、ログファイルを見て、特にエラーが出力されていず、終了しており、mapファイルが作られていればOKなのかと思います。

出来上がった map ファイルをmapsforgeで表示すると、今回は正しく海が表示されました。

おわりに

範囲を狭くしたmapファイルを作ることがこんなに大変だと思いませんでした。ちょっとやられた感があります。

とはいえ、OpenStreetMap のデータをあれこれ調べ始めると、結構楽しそうだな、とも感じてます。 どっかで仕事に絡めて調べてみたいなー。