いぇいいぇい

ASP.NET、PHPを中心に書いています

英語キーボードでCtrl+Backquote(`)を機能させる

英語キーボードでのIME切替キーはバッククオートキー

Windows10で英語キーボードを使う場合、バッククオート(`)キーが半角/全角キーの代わりになり、IMEの切り替えに使われます。具体的にはAlt+`Ctrl+`がIME切替のショートカットになります。

※IME切替はIMEをオン/オフするという意味で使っています。

IME切替ショートカットキーは簡単に変更できない

しかし、このIME切替ショートカットをユーザーが簡単に変更することはできません。コントロールパネルから簡単に変更できればよいのですが残念ながらできません。

VSCodeでCtrl+`を使いたい!

私が困ったのが、この問題によりVSCodeでCtrl+`を使えないということです。 同じ問題を抱えている方もおられるようです。

Windows上でVisual Studio Codeを使う際に英語キーボードのCtrl+backquote(`)が認識されない問題の対策 - Qiita

個人的にはIMEの切り替えにはAlt+`しか使っておらず、Ctrl+`は使っていません。Ctrl+`というかなりアクセスしやすい位置にあるキーが死にキーになってしまっています。できればCtrl+`を使えるようにしたい。

PowerToys Keyboard Managerで簡単にキーマップを変更する

Windows10のIME切替ショートカットを変更するには、おそらくレジストリをいじる必要があると思いますが、できればそこまでの手間は掛けたくありません。

そこで見つけたのがMicrosoftが最近リリースしたKeyboard Managerです。PowerToysの機能のひとつになっておりレジストリを直接変更せずに簡単にキーをリマッピングできるという便利ツールです。

github.com

こちらを使って以下のように設定することで無事Ctrl+`を使えるようになりました。

f:id:sumi2:20200805112130p:plain
Keyboard Manager Settings

設定後はOS再起動が必要でした。またVSCode側では、workbench.action.terminal.toggleTerminalCtrl+F1を割り当てています。

PowerToysを常駐させておけば、VSCode以外のアプリケーションも含めてCtrl+`を使えるようになります。 PowerToysには他にもColor Pickerなど便利なツールが含まれています。いらない機能は機能毎に無効化することもできます。

PowerToys v0.25.0で動作確認済み。

ASP.NET Core Razor Pagesの第一印象

いまさらながらASP.NET Core Razor Pages(以下Razor Pages)で小さなサイトを作ってみました。MSはRazor Pagesを推奨しているようですが、実際に使ってみてMVCから乗り換えるに値するものなのか判断できればいいなーと思っています。まだまだ分かっていない部分があるので間違っていたらご指摘頂ければと思います。

使用環境

今回作成したRazor Pagesのプロジェクトと比較対象のMVCプロジェクトは以下のようなかんじです。.NET Coreバージョンの違いによる問題をPagesの問題と勘違いしている可能性があります。開発環境はVisual Studio 2019 16.4.2です。

プロジェクト .NET Core Version ページ数
Razor Pages 3.1 ~20
MVC 2.1 100+

メリット

ViewModelクラスを定義しなくていい

MVCではページごとにViewModelクラスをいちいち作るのが面倒くさいです。ViewDataやViewBagもいまいちです。Pagesではcshtml.csに定義されるページ専用コントローラー兼ビューモデルであるPageModelクラスが自動で作成されます。これをそのままViewModelにすることができます。ビューに渡したいデータがあればPageModelクラスに新しいプロパティを追加してそれに値をセットすればビューから参照できます。もちろん型情報付きです。

つまりRazor Pagesはコントローラーをビューモデルと共用しているわけです。これは単一のビューに限定されたクラスだからできることだと思いますし、実際便利だと思いました。MVCではコントローラークラスは複数ページ共用なのでこれをやれないんですね。

ルーティングが分かりやすい

Razor Pagesではページ毎に1ファイルというかんじになるので、フォルダ構造がそのままルーティング構造になります。Razor PagesではこのURLならこのファイルだなとすぐ分かります。

デメリット

ページを持たないアクションは結局MVCで作る

例えばAjaxアクションなどはページビューがないためRazor Pagesでは表現できないため、MVCで作ることになります。なのですべてのアクションをRazor Pagesに移行できるわけではないです。

独自のルーティングルールがある

Razor Pagesには独自のルーティングルールがあります。リンクもUrl.ActionではなくUrl.Pageで作ります。すべてのアクションをRazor Pagesにするのであれば問題ありませんが、MVCと混在することを前提とした場合、ルーティングルールが2つあるというのはちょっと負担になります。

複数ページでコードを共用しにくい

Razor Pagesでは1ページ毎に専用コントローラーになるので、MVCのように複数アクションでメソッドを共用したり、部分ビューを共用することがやりにくいです。

例えばCRUDのCreateとUpdateでフォームの部分ビューを作って共用したい場合、MVCでは各コントローラーのViewsフォルダ内に置けばよいですが、Razor PagesではSharedフォルダに置くことになります。Sharedフォルダが混雑しそうです。

またメソッド場合は、PageModelの継承クラスを定義したりすることになるのでしょうか。

Visual Studioが不安定

デバッグ実行とデバッグなし実行を切り替えたり、ビルドを実行したりなどのタイミングで、ブラウザ更新をかけるとページが表示されない状態になります。MVCではそんなことありませんでした。

Visual Studioが不安定2

使用していると、変数名やクラス名を変更したりなどするタイミングでVSが応答なしになることがあります。復帰までに1分くらいかかります。1度この状態になると頻繁にこの現象が発生するのでVSを再起動しています。すると数時間は持ちます。アナライザが何やらしているような感じです。MVCではそんなことありませんでした。

*.cshtmlの変更だけでコンパイルが走る

MVCでは*.cshtmlだけの変更であれば1秒未満で反映できていましたが、Razor Pagesでは*.cshtmlだけの変更でも2秒ほどかかります。PageModelもコンパイルしているのでしょうか。

ただ、*.cshtml.csやその他の*.csの変更によるコンパイル時間はMVCより早いように感じます。MVCはプロジェクト全体をコンパイルしている感じで、Razor Pagesは関連するページだけコンパイルしている感じです。間違っていたらすみません。

所感まとめ

  • Razor PagesはMVCに置き換わるものではなく、共存可能な独自の仕組み
  • すべてのアクションをMVCからRazor Pagesに乗り換えることは出来ない
  • ビューへの値渡しを気軽にできるのはよかった
  • 現状、VSが不安定すぎて使えない。(Razor Pagesに依存する問題かどうかは分からないが)
  • *.cshtmlの変更だけでコンパイルするのはやめてほしい。

V6プラスでMTUを最適化してみた

1ヶ月前にネット回線をV6プラス(MAP-E)に変更した話を書きましたが、2020/02/05現在、問題なく運用できています。 今回は、V6プラスでMTUを最適化した話を書きたいと思います。

MTUとは

MTUとは、IPパケットを送信する際の最大サイズです。このサイズより大きなデータを送信する場合はデータが分割されます。これをフラグメントといいます。

フラグメント(データ分割)が発生すると余計なコストがかかるため通信速度が落ちます。フラグメントが発生しないようにMTUを調節すればより効率的に通信できるわけですね。

MTUの一般的な値は1500ですが、最適な値はネット回線の種類などで変わります。なんで変わるかというと通信方式によってパケット内に追加のデータが必要になったりするからです。追加のデータが必要な場合は、ネットワークの下位にいるクライアントはその分を差っ引いたMTUにしなければフラグメント化してしまいます。なので。最適なMTUは環境によって異なるため実際に計測して確認することが重要になります。

使用環境

今回使用した環境は以下になります。

  • ISP: enひかり(V6プラスオプション, ひかり電話はなし)
  • Router: Buffalo WSR-1166DHPL/N (192.168.1.1)
  • Client: Windows10
  • 接続は [Internet] - [ONU] - [Router] - [Client]となっています。
  • ルーターのMTUは既定値の1500になっています。

MTUの最適値の求め方

Windowsメニューでcmdと打って管理者として実行をクリックしてコマンドプロンプトを開きます。

コマンドプロンプトでping google.com -f -l 1500を実行します。 以下の結果になると思います。

C:\WINDOWS\system32>ping google.com -f -l 1500

google.com [172.217.25.238]に ping を送信しています 1500 バイトのデータ:
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。

172.217.25.238 の ping 統計:
    パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)、

-fはデータ分割しない、-lはパケットサイズを指定するオプションです。パケットサイズを1500にするとフラグメント化してしまう結果になっています。

あとはパケットサイズをフラグメント化しない値になるまで小さくしていきましょう。フラグメント化しない値で最も大きな値を探しましょう。

私の場合は1424になりました。

C:\WINDOWS\system32>ping google.com -f -l 1425

google.com [172.217.161.206]に ping を送信しています 1425 バイトのデータ:
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。
パケットの断片化が必要ですが、DF が設定されています。

172.217.161.206 の ping 統計:
    パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)、

C:\WINDOWS\system32>ping google.com -f -l 1424

google.com [172.217.161.238]に ping を送信しています 1424 バイトのデータ:
172.217.161.238 からの応答: バイト数 =68 (1424 を送信) 時間 =8ms TTL=55
172.217.161.238 からの応答: バイト数 =68 (1424 を送信) 時間 =8ms TTL=55
172.217.161.238 からの応答: バイト数 =68 (1424 を送信) 時間 =8ms TTL=55
172.217.161.238 からの応答: バイト数 =68 (1424 を送信) 時間 =8ms TTL=55

172.217.161.238 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 8ms、最大 = 8ms、平均 = 8ms

なぜ1424になるか

なぜ1424になるのか考えてみます。

---- Internet ----
+40 IPv6 Header
+ 8 encaplimit
---- Router ----
+20 IPv4 Header
+ 8 ICMP Header
---- Client ----

おそらく上記のように各種追加データが追加されるためではないかと思います。 1424+28+48=1500というわけですね。 Clientが28バイト追加して、Routerがさらに48バイト追加すると。 というわけで、1424に28を足した値1452がMTUの最適値になります。

Windows10でMTUを変更する

では早速MTUを変更してみます。

まずは現在のMTUの設定状況を表示してみます。同じくコマンドプロンプトで以下を実行します。

C:\WINDOWS\system32>netsh interface ipv4 show interface

Idx     Met         MTU          状態                 名前
---  ----------  ----------  ------------  ---------------------------
 27        5000        1500  connected     vEthernet (Default Switch)
  1          75        1500  connected     Loopback Pseudo-Interface 1
 18           1        1500  disconnected  VPN - VPN Client
 10          25        1500  connected     vEthernet (bridge)
  9          15        1500  connected     vEthernet (DockerNAT)

私の環境ではいろんなネットワークがありますが、、設定変更する対象は271109の4つになります。

以下のコマンドを実行します。

C:\WINDOWS\system32>netsh interface ipv4 set interface 27 mtu=1452
OK
C:\WINDOWS\system32>netsh interface ipv4 set interface 1 mtu=1452
OK
C:\WINDOWS\system32>netsh interface ipv4 set interface 10 mtu=1452
OK
C:\WINDOWS\system32>netsh interface ipv4 set interface 9 mtu=1452
OK

最後に再度設定状況を表示して確認します。

C:\WINDOWS\system32>netsh interface ipv4 show interface

Idx     Met         MTU          状態                 名前
---  ----------  ----------  ------------  ---------------------------
 27        5000        1452  connected     vEthernet (Default Switch)
  1          75        1452  connected     Loopback Pseudo-Interface 1
 18           1        1500  disconnected  VPN - VPN Client
 10          25        1452  connected     vEthernet (bridge)
  9          15        1452  connected     vEthernet (DockerNAT)

これでMTUの設定を変更できました。

まとめ

MTUの最適値を探してWindows10に設定してみました。 ただここまで書いておいて変更の効果は体感できていません。またMTUの最適値が本当にこの値でよいのかも確信がありません。もし間違っているよ~とか、最適値を確認する方法があるよ~とかあればぜひ教えて下さい。

OpenWrtでv6プラス(MAP-E)接続する手順

ここ1年ほどネット回線が夜になると劇遅になるのでフレッツ光をPPpoEからIPoEに変えたいとずっと思っていたが、面倒くさいという思いがあって先延ばしにしていた。この度やっと重い腰を上げて乗り換えることにした。 が、当初思ったよりOpenWrtの設定がかんたんではなかったのでここに記録を残すことにした。まだまだPPpoEの契約者も少なく、OpenWrtを使っている人はさらに少ないだろうが、ネットにまとまった手順がなかったので記録せねばと3年ぶりに記事を書くことにした。みなさんお久しぶりです。口調が変わっていても気にしないで。3年も経てば色々忘れてる。

我が家の環境

  • ISP: enひかり(V6プラスオプション)
  • Router: Buffalo WZR-HP-AG300H(OpenWrt 18.06.5 r7897-9d401013fc)
  • フレッツ光からコラボ光へ転用(訪問工事なし)
  • ひかり電話はなし
  • 旧ISPはまた解約しておらずIPv4 PPpoEで接続できる状態
  • OpenWrtのインターフェースはLAN,WAN,WAN6があり、WANは旧ISPで接続中、WAN6は停止中の状態

なお今回解説する手順はJPNEが提供する「v6プラス(MAP-E)」を提供するISPであればどこでも同じになるはずである。

開通日に何が起こるか

当初開通日に接続が勝手に切れると思っていたからそのつもりで仕事を調整していたのだが、結局何も起こらなかった。なので翌日の午後に時間をとって新しい設定をすることにした。

この時点で本当に開通しているのかどうかもわかっていなかった。もしかすると手違いが発生しているのかとか思ったりした。後から調べてわかったのだが、開通はNTT側の設定が切り替わるだけで旧ISPの接続が切れるわけではないのでこちらではいつ開通したのか設定してみないとわからないようだ。DHCPで発行されたIPv6アドレスがJPNEのものであれば開通済みということらしい。

ネイティブIPv6接続

まずはネイティブIPv6接続してみる。

基本的には下記の記事を参考にした。ありがとうございます。 blog.misosi.ru

手順1-1) WAN6インターフェースを作成する

  1. OpenWrt の Web UI にログインする
  2. [Network] – [Interfaces]
  3. WAN6というインターフェースがあるので[Edit]をクリック。(WAN6がなければ作る)
    • General Setup
      • Protocol: DHCPv6 Client
    • Physical Settings
      • Bridge interfaces: off
      • Interface: eth1
    • Firewall Settings
      • Create / Assign firewall-zone: wan
  4. [Save & Apply]

手順1-2) RA や DHCPv6 を LAN 側にリレーするように設定する

  1. OpenWrt に ssh でログイン
  2. /etc/config/dhcpを以下のように編集
> vi /etc/config/dhcp
  :
config dhcp 'lan'  
  :
        option dhcpv6 'relay'
        option ra 'relay'
        option ndp 'relay'
  :
config dhcp 'wan6'  
        option dhcpv6 'relay'
        option ra 'relay'
        option ndp 'relay'
        option master '1'
  :
> uci commit /etc/config/dhcp

自分の場合はlanoption master 1がついていたので、その行を削除しなければならなかった。

手順1-3) WAN6を接続

  1. [Network] – [Interfaces]
  2. WAN6で[Connect]をクリック

接続確認

  1. https://test-ipv6.com/にアクセスし、IPv6 アドレスで通信できていることを確認。

MAP-EでIPv4接続

MAP-EとはIPv4パケットをIPv6パケットにカプセル化してJPNEに送り、JPNEがカプセル化を解いてインターネットに送る、受信も同じくIPv6にカプセル化されたものをJPNEから受信する、という仕組みのようだ。参照

つまりMAP-EとはクライアントとJPNEの間でトンネルを貼るということみたい。なんとなくわかった。

手順2-1) MAP-Eの手動計算

ちょっとここでMAP-Eのマッピングルールを計算してみる。急ぐ人は飛ばしてください。

計算方法は下記サイトを参考にした。ありがとうございます。(2409240bの誤りと思われる)

gato.intaa.net

ここまでで以下の値が手元にある。正しく設定するとこのIPv4アドレスで接続できるはずだ。

名前 説明
IPv6Prefix DHCPv6で発行されたIPv6プレフィックス。WAN6のIPv6アドレスの欄に表示される。 240b:253:cccc:dddd:eeee:ffff:gggg:hhhh/64
IPv6Addr DHCPv6で発行されたIPv6アドレス。 240b:253:cccc:dddd:iiii:jjjj:kkkk:llll
CE MAP-Eで計算したCEアドレス 240b:253:cccc:dddd:mmmm:nnnn:oooo:pppp
IPv4Addr MAP-Eで計算したIPv4アドレス xxx.xxx.xxx.xxx

手順2-2) MAP-Eの各パラメータを計算

下記の計算ツールでMAP-Eの各パラメータを計算する。「IPv6 プレフィックスかアドレスを入力」にIPv6Prefix(WAN6のIPv6アドレス)を入力して計算ボタンを押す。

http://ipv4.web.fc2.com/map-e.html

計算した結果は次項で使う。

手順2-3) mapパッケージのインストール

opkg update
opkg install map

余談だが以下のコマンドを実行するとIPv4アドレスなどを計算できる。値は各自置き換えること。

LEGACY=1 mapcalc * type=map-e,ipv6prefix=240b:252::,prefix6len=31,ipv4prefix=xxx.xxx.xxx.xxx,prefix4len=15,psidlen=8,offset=4,ealen=25

手順2-4) map.shの編集

  1. OpenWrt に ssh でログイン
  2. /lib/netifd/proto/map.shをviで開き#export LEGACY=1のコメントを外す

手順2-5) MAP-Eインターフェースを作成

MAP-E用のインターフェースを作成する。

  1. OpenWrt の Web UI にログインする
  2. [Network] – [Interfaces] - [Add new interface...]
    • General Setup
      • Name of the new interface: wan6_map (何でも良い)
      • Protocol of the new interface: MAP / LW4over6
  3. [Submit]
  4. [Network] – [Interfaces] - [WAN6_MAP] - [Edit]
    • General Setup
      • Type: MAP-E
      • BR / DMR / AFTR: 2404:9200:225:100::64 (JPNEのBRアドレス)
      • IPv4 prefix: 上記サイトで計算したoption ipaddrの値
      • IPv4 prefix length: 上記サイトで計算したoption ip4prefixlenの値
      • IPv6 prefix: 上記サイトで計算したoption ip6prefixの値
      • IPv6 prefix length: 上記サイトで計算したoption ip6prefixlenの値
      • EA-bits length: 上記サイトで計算したoption ealenの値
      • PSID-bits length: 上記サイトで計算したoption psidlenの値
      • PSID offset: 上記サイトで計算したoption offsetの値
    • Advanced Settings
      • Tunnel Link: wan6
    • Firewall Settings
      • Create / Assign firewall-zone: wan
  5. [Save & Apply]

手順2-6) WAN6の設定

  1. OpenWrt の Web UI にログインする
  2. [Network] – [Interfaces]
  3. WAN6というインターフェースがあるので[Edit]をクリック。(WAN6がなければ作る)
    • General Setup
      • Request IPv6-prefix of length: disabled (automaticだとログにeth1: link downとか出て通信が定期的に途切れる。意味はよくわかってない 間違い。詳細は最後に記載した。結局これの必要性は分からないが設定している。)
    • Advanced Settings
      • Custom delegated IPv6-prefix: WAN6のIPv6アドレスと同じ値か、mapcalcの計算結果のRULE_1_IPV6PDの値に/56を付与した文字列(例:240b:253:cccc:dddd::/56)を設定。どちらでも問題ないと思うが自分は後者を設定した。
  4. [Save & Apply]

手順2-7)option encaplimitの設定

  1. OpenWrt に ssh でログイン
  2. /etc/config/networkを以下のように編集
> vi /etc/config/network
  :
config interface 'wan6_map'
  :
        option encaplimit 'ignore'
  :
> uci commit /etc/config/network

※これで5時間ハマった。これがないとWAN6_MAPのTXはカウントされてもRXはカウントされないという状態になる。おそらくパケットが壊れた状態になると思われる。下記の記事と同じ現象と思う。

自力でIPv4 over IPv6をした(Tunnel編) – 愚行録 the Next Generation

手順2-8) WAN6_MAPを接続

いよいよ接続です。

  1. [Network] – [Interfaces]
  2. WAN6_MAPの[Connect]をクリック

接続確認

  • WAN6_MAPの状態のIPv4の項にMAP-Eで計算したIPv4アドレスが表示されることを確認
  • OpenWrtのインターフェース一覧に、トンネル用のインターフェース(Virtual dynamic interface (Static address))が自動的に追加され、IPv6の項にCEアドレスが表示されることを確認
  • https://test-ipv6.com/にアクセスし、管理されたトンネルメカニズム(6RD)を使用してIPv4経由でIPv6を転送しているようです。と表示されることを確認。
  • https://kakunin.net/kun/にアクセスし、プロバイダー名に*****.enabler.ne.jpと表示されることを確認。

システムログにLocal address not yet configured!と出る

推測だが、Local address not yet configured!は接続開始時にまだローカルアドレスが定まっていないために起こる一時的なエラーと思われる。接続後は発生しないようだ。

設定ファイル

最後に自分の設定ファイルからwan6wan6_mapの設定値を貼っておく。

/etc/config/network

config interface 'wan6'
        option proto 'dhcpv6'
        option reqaddress 'try'
        option ifname 'eth1'
        option reqprefix 'no'
        option ip6prefix '240b:253:cccc:dddd::/56'

config interface 'wan6_map'
        option proto 'map'
        option type 'map-e'
        option peeraddr '2404:9200:225:100::64'
        option ipaddr 'xxx.xxx.xxx.xxx'
        option ip4prefixlen '15'
        option ip6prefix 'aaaa:bbbb::'
        option ip6prefixlen '31'
        option ealen '25'
        option psidlen '8'
        option offset '4'
        option encaplimit 'ignore'
        list tunlink 'wan6'

速度計測

下記サイトで夜9時半に90Mbps程。いままではこの時間は3Mbpsとかだったので劇的改善である。しかもレイテンシも15msとか上出来な値。昼間は100Mbps。旧ISPでは昼間は250Mbpsとかでてたので負けてるが、最大速度よりも一定速度が常に出るほうがありがたい。

インターネット回線の速度テスト | Fast.com

MAP-Eはルーターへの負荷が高いとのことなので、完全に推測だがルーターの処理能力がボトルネックになっているのかもしれない。

SNATを240ポート使えるように調整

しばらく使っているとポート不足により通信できない状態になった。

iptables -t nat -L -vで見ると最初のポートブロックしか使われていない。 下記サイトの通り設定するとうまく分散できた。

OpenWrt map-e (JPNE v6plus) において、割当ポート240個をちゃんと使わせるための設定。 · GitHub

手順3-1) iptables-mod-ipoptをインストール

opkg update
opkg install iptables-mod-ipopt

手順3-2) firewall.userを編集

  1. /etc/firewall.userを以下のように編集
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  0 -j MARK --set-mark 10
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  1 -j MARK --set-mark 11
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  2 -j MARK --set-mark 12
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  3 -j MARK --set-mark 13
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  4 -j MARK --set-mark 14
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  5 -j MARK --set-mark 15
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  6 -j MARK --set-mark 16
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  7 -j MARK --set-mark 17
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  8 -j MARK --set-mark 18
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet  9 -j MARK --set-mark 19
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet 10 -j MARK --set-mark 20
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet 11 -j MARK --set-mark 21
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet 12 -j MARK --set-mark 22
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet 13 -j MARK --set-mark 23
iptables -t nat -A PREROUTING -m statistic --mode nth --every 15 --packet 14 -j MARK --set-mark 24

iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  0 -j MARK --set-mark 10
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  1 -j MARK --set-mark 11
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  2 -j MARK --set-mark 12
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  3 -j MARK --set-mark 13
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  4 -j MARK --set-mark 14
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  5 -j MARK --set-mark 15
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  6 -j MARK --set-mark 16
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  7 -j MARK --set-mark 17
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  8 -j MARK --set-mark 18
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet  9 -j MARK --set-mark 19
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet 10 -j MARK --set-mark 20
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet 11 -j MARK --set-mark 21
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet 12 -j MARK --set-mark 22
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet 13 -j MARK --set-mark 23
iptables -t nat -A OUTPUT -m statistic --mode nth --every 15 --packet 14 -j MARK --set-mark 24

手順3-3) map.shを編集

  1. /lib/netifd/proto/map.shを以下のように編集
#$ diff -c /lib/netifd/proto/map.sh.orig /lib/netifd/proto/map.sh

*** 135,140 ****
--- 135,141 ----
          json_add_string snat_ip $(eval "echo \$RULE_${k}_IPV4ADDR")
        json_close_object
      else
+       local mark=10
        for portset in $(eval "echo \$RULE_${k}_PORTSETS"); do
                for proto in icmp tcp udp; do
            json_add_object ""
***************
*** 142,152 ****
--- 143,155 ----
              json_add_string target SNAT
              json_add_string family inet
              json_add_string proto "$proto"
+             json_add_string mark "$mark"
                    json_add_boolean connlimit_ports 1
                    json_add_string snat_ip $(eval "echo \$RULE_${k}_IPV4ADDR")
                    json_add_string snat_port "$portset"
                    json_close_object
              done
+             mark=`expr $mark + 1`
        done
      fi
      if [ "$type" = "map-t" ]; then

確認

iptables -t nat -L -vですべてのポートブロックが使われていることを確認。

雑感

IPoEによる混雑の解消効果は素晴らしいし満足しているが、OpenWrtでのMAP-E設定はかなり敷居が高い。OpenWrtによるMAP-Eの動作はまだ自分の中で不安があるので、しばらく様子を見たいと思う。

2019/12/20 追記 eth1: link downが頻発する問題

しばらく運用していると、以下のようなログが出てリンクが切れることが頻発した。頻度としては通信しているとランダムに発生し、数時間発生しないときもあれば連続で発生するときもある。ただし再現性がないので確実に発生させる手順は不明。

eth1: link down
ar71xx: pll_reg 0xb8050014: 0x110000

発生すると数十秒で復帰するが、正直通常使用できる状態ではない。ネットを探しても同じような問題はいくつかあるが解決しなかった。これは困った。 こりゃ素直にv6プラス対応ルーター買うかと思って実際にWSR-1166DHPL/Nを注文した。この問題で何日も費やすリスクを考えたら5000円出して金で解決できるなら安いもんだ。

注文した後、IPoEは使えないと思ってPPpoEだけで運用していたが、それでも発生することに気づいた!!これは。。。そういえば今回の作業にあたってOpenWrtを最新にバージョンアップしていたが、それが原因かもと思い、元のバージョンにダウングレードしてみることにした。1つ前の18.06.4にしてみたがダメ。たしかもともとは18.06.1だったと思いそれに戻すと見事に治った。いまのところ4日間元気に稼働中。

ということで新しいルーターは無駄になってしまった。。

2019/12/27 追記 新しいルーターに移行しました

6日目に再発しました。

どうやらハードに問題があるみたい。

forum.openwrt.org

回避するパッチは存在するようだが、手元に新しいルーターがある以上、これ以上コイツの問題に付き合うつもりはない。ということで新しいルーターに移行することにしました。

ここで新しいルーターの設定方法は説明しないが、設定の所要時間は3分だった。「v6プラスを使用する」を選んで設定保存するだけ。かんたんすぎる。。

改めて速度計測してみると、なんと昼間で300Mbps、夜9時頃でも260Mbps出る。レイテンシは15msと変わらず。やはりハードの性能がネックになっているのかもしれない。

新しいルーターはいまのところ3日間ほどネットも無線も問題なく稼働している。またなにかあれば報告する。

管理画面系サイトにSPAは必要か?

先日管理画面系のサイトをリニューアルするにあたり、SPAを検討しました。その過程で何を検討し、どう判断したのかを書いてみたいと思います。

検討するサイト

まずは検討対象となるサイトについて説明したいと思います。 いわゆる管理画面サイトで、左側はサイドメニュー、右側はコンテンツエリアの2カラムレイアウト。サイドメニューの項目をクリックすると各ページのコンテンツが表示されます。 ヘッダー部分にはログイン情報があります。

コンテンツ・機能としては、以下のような物があります。

  • 項目リスト表示
  • 項目詳細表示
  • 項目編集
  • 項目追加
  • ログ表示
  • カレンダー・タイムテーブルによるタスク管理
  • 設定画面
  • ダッシュボード

バックエンドはASP.NET MVC。タスク管理機能など動的な部分はjQueryで実装しています。 SEOは必要ありません。モバイルアプリもありません。モバイルではPC版のレイアウトで操作できればOKなのでレスポンシブでもありません。

SPA、MPAの定義

SPA、MPAという言葉を多用するので、言葉の定義をします。

Single Page Application(SPA)
1つのページだけで構成されるサイト。ページ遷移がない。すべての動的コンテンツはAJAXで取得し、DOM更新する。ブラウザのヒストリーAPIを使って「ページ切り替えのようなこと」はできるが実際にはページ遷移していない。
Multi Page Application(MPA)
複数のページで構成されるサイト。ページ遷移を行う。ページの一部をAJAXなどを使って動的に更新することはできるがページ遷移するとページ全体を再表示することになる。SPAの対比のために使う。

リニューアルの要件

サイトをリニューアルするにあたり、以下の要件がありました。

  • デザインを新しく作り直す。(かっこいくておしゃれなやつ!)
  • タスク管理等の現在jQueryで実装している動的な部分も、ReactやVue等の最新のビューライブラリで書き直す。

SPAを検討した背景

ここ1,2年で、SPAの記事を目にすることが増えてきました。この時点ではSPAサイトを自分で作ったことはありませんでした。自分の中でSPAに対して抱いていた印象や知識は以下の様なことです。

  • ページ遷移がないので通信量が削減できる
  • ページ遷移がないので通信が発生せず、デスクトップアプリのようにキビキビ動く
  • バックエンドからUIロジックを排除できる
  • PCサイトとモバイルアプリで1つのバックエンドを使いまわしできる
  • JSファイルが大きくなるので初回ロードが遅いかも
  • SEOが不安なのでメディアサイトには向かないかも
  • それなりのフロント技術を求められるよ!

この中で一番魅力を感じたのは2番目のデスクトップアプリのようにキビキビ動くという点です。通常のWebサイトではページ遷移すると通信が発生するので、コンマ何秒かの待ち時間が発生します。これがなくなるのは地味だけど、でも体感できる差です。大きな差別化要因になると感じました。

「デスクトップアプリのようにキビキビ動くこと」は要件にはないですが、もしそれが実現できればそれに越したことはありません。そこでSPAについて詳しく検討することにしました。

検討したこと

以下、それぞれ検討したことと、その結果になります。プラス1なら☆(+1)、プラス2なら☆☆(+2)、マイナス1なら★としてランク付けしていきます。合算して☆が多ければ費用対効果があります。

検討にあたっては実際にAngular2でSPAサイトを構築してみました。

☆ページ共通部分を更新しなくていいのでその分高速化できる

SPAではサーバーは「データ」、フロントは「UI」と役割分担しますから、フロント側では必要な部分のみUI更新することができます。そうするとページヘッダーやナビゲーション部分はページ切り替え時には更新する必要がなくなりますからその分のレンダリングや通信コストが削減できます。これは正しいのでしょうか?

例えばログページから設定ページに移動した場合を考えてみます。

MPAの場合、

  1. ブラウザからページHTMLを要求
  2. サーバーでページ全体をレンダリングしてHTMLとして返す
  3. 返ってきたHTMLをブラウザで表示

SPAの場合、

  1. ブラウザから設定値をAJAXで要求
  2. サーバーで設定値をJSONで返す
  3. 返ってきたJSONをクライアントでDOM更新

上記を考えてみると、MPAはページ全体を送っているのに対して、SPAでは設定値だけを送っています。たしかに通信量は削減できそうですね。しかし体感できる程の差かどうかは、ケースによるのではないでしょうか。

今回のサイトの場合、ページ全体のHTMLは30KB程です。設定値のJSONが1KBだったとして、その差は29KBになります。29KB削減の効果は疑問です。モバイル環境なら大きいでしょうが、モバイルは想定していません。でも削減できるのはいいことですね。効果は小さいかもしれませんがプラス材料です。星ひとつ。

☆ページ遷移がないので通信が発生せず、デスクトップアプリのようにキビキビ動く

SPAはデスクトップアプリのようにキビキビ動くのが魅力です!この最大の利点を得るためにSPAにするといっても過言ではありません。 では管理画面をSPAにするとどうなるでしょうか?

前項の例を見てください。

あれ?どっちも通信発生していますね。SPAってページ遷移がないから早いんじゃなかったの??

通信が発生するということはMPAと同じく通信待ちが発生するということです。デスクトップアプリのようにキビキビ動きません。

SPAではキャッシュするから2回目以降はAJAXも要らないということでしょうか?ん?設定値をキャッシュする?それじゃ設定値が最新のものじゃないかもしれなくなるってことだからダメだよね。

設定ページ以外ならどうだろう。ログも最新じゃないと困るし、他も全部最新がいい。でも例えばログのページ送り操作のケースを考えると、クライアントサイドでキャッシュしてくれてもいいような気がする。その場合は通信がなくなるのでキビキビ動きますね。そっか、コンテンツによってキャッシュすべきかどうか違うんだ。で、キャッシュできないものはゼロ通信にできないと。

結局、SPAにしたからといって一律にゼロ通信にはできない。これじゃあデスクトップアプリにはなれない。当初期待していた目論見が崩れました。考えてみれば当たりまえの話ですが、大きな勘違いをしていたようです。

とりあえず、キャッシュできる場面ではキビキビ動くということで星ひとつ。

★初回は全ページのテンプレートを取得するため大きくなる

SPAはJSファイルに各ページのテンプレート、UIロジック、スタイル情報なども含めるため、ファイルサイズが大きくなりやすい。

初回表示が遅いってやつですね。ファイルを分割するなど工夫が必要になるかもしれません。マイナス星ひとつ

★★★ページキャッシュが使えない(戻る・進む問題)

これは実際に作ってみて気付いたことですが、SPAはブラウザのページキャッシュ機能が使えないんですね。

どういうことかというと、例えばログページから設定ページに移動した後、戻るした場合を考えてみます。

MPAの場合、

  1. ブラウザのページキャッシュからログページHTMLをロードして表示

SPAの場合、

  1. ブラウザの戻るイベントを受け、クライアントJSがログページのテンプレートをロードして表示
  2. クライアントJSがログデータをAJAXで要求
  3. サーバーがログデータをJSONで返す
  4. 返ってきたJSONをクライアントJSでDOM更新

MPAでは最終的なHTMLがブラウザのページキャッシュとしてローカルに残っていますから、それを表示するだけです。しかしSPAの場合はこのページキャッシュ機能が使えないため、自前ですべて賄う必要があります。対策としてはログ結果を自前でキャッシュしておき、戻る・進むの場合はキャッシュを参照するようにすることだと思いますが、MPAではブラウザがすべて面倒をみてくれる点と比べると気が重くなります。

これは当初想定していなかったデメリットです。管理画面系サイトはページ切り替えが多く、戻る・進むは普通に使います。マイナス星みっつ。

まとめ

他にも検討課題はあったと思いますが、この時点で今回のケースではSPAの採用を取りやめました。一番期待していたデスクトップアプリのようなUXが得られないと分かったからです。

おそらくSPAの使いどころというのは以下の点が判断条件になってくるのではないでしょうか。

  • サーバーからのデータ取得伴わない操作が多いか
  • サーバーからのデータ取得をキャッシュしやすいか

SPAにしろ、デスクトップアプリにしろ、データがリモートにある以上、通信待ちからは逃れられません。データをローカルに置けるというのはUI速度という面では利点です。データをリモートで一元管理できるというのもまた別の利点です。どちらも、ということは簡単ではありませんね。

ということで、以上となります。いかがでしたでしょうか?少しでも参考になれば幸いです。

Cloud Serviceがpagefile.sysを永続ディスクに配置するので固まる問題と回避法

Azure Cloud ServiceのVMでページングファイルがCドライブ(永続ストレージ)に配置されている件でトラブった経緯と調査、解決のために行ったことについて記載します。

運用環境

Azure Cloud Serviceで、ExtraSmallのVM2個で運用しています。osFamily、osVersionは未指定です。

OSはWindows Server 2012 R2 Datacenter。メモリは768MBです。

VMで処理が固まる

1年ほど運用してきましたが、最近処理が増えたのか、処理が途中で止まる現象が発生し出しました。

具体的には、

  • メモリを多く使うような箇所でログが途切れている
  • 物理メモリの使用率は常に100%近くで、仮想メモリも1~2GB使っている

ここで思ったのは、「ExtraSmallはそろそろきついか。。」、「もしかしてメモリリークしてる??」でした。メモリリークについてはメモリ使用量が増え続けてないので原因候補から外しました。

「じゃあ、性能不足か。予算が、、」、しかしメモリ使用量は物理、仮想合わせても3GB程度、仮想メモリは4GB確保しているので、まだ余裕はあるはず、この状況で処理が止まるなんてことあるだろうか?う~ん。

ここでふと、処理負荷時にディスク使用率が100%に張り付いているのを見つけました。HDDをそんなに酷使するような処理は行っていないので、なにがそんなに負荷を与えるの?と思いました。そういえばAzureのVMには永続ストレージと一時ストレージがあって、永続ストレージはものすごく遅いんだっけ、もしかして永続ストレージを使っているから重い?、とさらに調査してみることに。

VMにリモートデスクトップでログインしてパフォーマンスモニターで処理負荷時の様子を眺めました。すると以下のことがわかりました。

  • CPU負荷は問題ない
  • メモリ使用量もまだ余裕ある
  • Cドライブのディスク負荷がヤバイ

処理が止まる原因はCドライブの過負荷のようです。しかもCドライブに負荷をかけているのはpagefile.sys。

「そうか、物理メモリが少ないから常にページングファイルでガリガリやってる状態で、処理が立て込むとついには固まる、と。」

状況がわかってきました。

各ドライブの速度測定

一方で、VMには3つのドライブがあって、Cドライブ以外の負荷はまったくないような状況です。ここで疑問に思ったのはCドライブは永続ストレージ?それとも一時ストレージ?ということでした。もし永続ストレージなら改善の余地があるかもしれません。そこでネットを調べたところ、AzureのVMはDドライブが一時ストレージという記載がありますが、Cloud ServiceのVMにも当てはまるのか、情報が少なくはっきりしません。そこで実際に計測してみることにしました。

以下が実際のベンチマーク結果です。

Cドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :    43.511 MB/s
  Sequential Write (Q= 32,T= 1) :    56.066 MB/s
  Random Read 4KiB (Q= 32,T= 1) :     1.225 MB/s [   299.1 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :     1.082 MB/s [   264.2 IOPS]
         Sequential Read (T= 1) :    74.436 MB/s
        Sequential Write (T= 1) :    68.354 MB/s
   Random Read 4KiB (Q= 1,T= 1) :     0.547 MB/s [   133.5 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :     0.811 MB/s [   198.0 IOPS]

  Test : 500 MiB [C: 34.8% (6.9/20.0 GiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:07:01
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

Dドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :  1397.509 MB/s
  Sequential Write (Q= 32,T= 1) :   880.550 MB/s
  Random Read 4KiB (Q= 32,T= 1) :    68.634 MB/s [ 16756.3 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :    92.339 MB/s [ 22543.7 IOPS]
         Sequential Read (T= 1) :  1697.640 MB/s
        Sequential Write (T= 1) :  1372.134 MB/s
   Random Read 4KiB (Q= 1,T= 1) :    27.629 MB/s [  6745.4 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :    28.783 MB/s [  7027.1 IOPS]

  Test : 500 MiB [D: 34.0% (10.9/32.0 GiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:13:56
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

Eドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :   424.361 MB/s
  Sequential Write (Q= 32,T= 1) :   351.871 MB/s
  Random Read 4KiB (Q= 32,T= 1) :    78.930 MB/s [ 19270.0 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :    71.282 MB/s [ 17402.8 IOPS]
         Sequential Read (T= 1) :   365.320 MB/s
        Sequential Write (T= 1) :   334.481 MB/s
   Random Read 4KiB (Q= 1,T= 1) :    13.171 MB/s [  3215.6 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :    18.136 MB/s [  4427.7 IOPS]

  Test : 500 MiB [E: 41.4% (635.0/1534.9 MiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:23:47
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

この結果からDドライブが一時ストレージであることがわかりました。

なぜ永続ドライブに配置するのか

「そもそもなぜページングファイルを永続ストレージに配置するのか。ページングファイルは揮発性の情報だから、永続化する必要はないはずなのに。。単なるミス?それとももともとこのような仕様?」

MSに問い合わせてみようと思いましたが、インシデント代がもったない。。不具合という確証もないし。。

そこでMSDNフォーラムに質問してみました。

Cloud ServiceのExtraSmall VMで、pagefile.sysがCドライブに配置されている件

ページングファイルの場所をDドライブにしてみる

試しに手動にてページングファイルの場所をDドライブにして様子を見てみると、メモリ使用量は相変わらずですが、ディスク負荷はほぼ解消しました。

OK。これで解決しそうです。

ページングファイルの配置変更を自動化できる?

しかし、Cloud ServiceのVMはOSが自動更新されます。これによりページングファイルもCドライブに戻ってしまいます。OSが自動更新されてもページングファイルをDドライブにできるような方法はないものか。。

質問の回答ばかりに頼っていてもしょうがないので、自分で回避方法を探してみることにしました。

Cloud Servieにはスタートアップタスクという仕組みがあり、アプリケーション配置時にバッチスクリプトなどを実行することができます。既にNewRelicのインストールなどを行っていますし、ここでページングファイルの配置変更ができれば解決しそうです。

しかしページングファイルの設定変更にはOS再起動が必要です。できるのか。ネットで調べてみたところ、スタートアップタスクでOS再起動している人がいました。これはできるかも!やってみよう!

ページングファイルの配置変更スクリプト

やってみた結果、できました。

以下がページングファイルの配置変更スクリプトをスタートアップタスクとして登録する手順です。

1) ServiceDefinition.csdefにスタートアップタスクを登録します。

<ServiceDefinition ...>
  <WorkerRole ...>
    <Startup>
      <Task commandLine="changePagefileLoc.cmd" executionContext="elevated" taskType="simple"/>
    </Startup>
  </WorkerRole>
</ServiceDefinition>

2) changePagefileLoc.cmdをロールプロジェクト直下に追加します。

「出力ディレクトリにコピー」を「常にコピー」にします。 文字コードはASCII。

@echo off
DATE /T >> "%TEMP%\StartupLog.txt" 2>&1
TIME /T >> "%TEMP%\StartupLog.txt" 2>&1
ECHO Starting up changePagefileLoc.cmd. >> "%TEMP%\StartupLog.txt" 2>&1

PowerShell -ExecutionPolicy Unrestricted .\changePagefileLoc.ps1 >> "%TEMP%\StartupLog.txt" 2>&1
EXIT /B 0

3) changePagefileLoc.ps1をロールプロジェクト直下に追加します。

「出力ディレクトリにコピー」を「常にコピー」にします。 文字コードはUTF-8(BOM付き)。

Write-Output "==================="
Write-Output "Change pagefile location start"
$CurrentPageFile = Get-WmiObject -Query "select * from Win32_PageFileSetting"
Write-Output "C drive page file: $CurrentPageFile"
if ($CurrentPageFile -and $CurrentPageFile.Name.ToLower() -eq "c:\pagefile.sys") {
    $computer = Get-WmiObject Win32_computersystem -EnableAllPrivileges
    $computer.AutomaticManagedPagefile = $false
    $computer.Put()
    $CurrentPageFile.delete()

    Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{name="d:\pagefile.sys"; InitialSize = 0; MaximumSize = 0} -EnableAllPrivileges | Out-Null
    $PageFile = Get-WmiObject Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ d:'"
    $PageFile.InitialSize = 4096
    $PageFile.MaximumSize = 4096
    [Void]$PageFile.Put()

    $PageFile = Get-WmiObject Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ d:'"
    Write-Output "D drive page file: $PageFile"

    Write-Output "The computer will restart for the changes to take effect."
    Restart-Computer
}
Write-Host "Change pagefile location end"

まとめ

当面の回避策が見つかってよかったです。しかしなぜこのような仕様になっているのかは謎のままです。

ネットを探しても出てこない症状。可能性があるとすれば最近発生した不具合?、もし元々このような仕様だったとしたら、Cloud Serviceを使っている人はずっとこのような構成のOSを使わされていたということになります。

物理メモリが多いVMサイズではあまり問題になる状況はないかもしれませんが、ひとたびメモリ使用量が物理メモリを超えると、ページングによりCドライブが過負荷になり、しまいにはディスクアクセスエラーになり処理落ちするわけです。

ExtraSmallのように常にメモリ不足のような場合は致命的な問題です。まぁExtraSmallは開発・テスト用とされていますからね。素直に上のサイズ使えということですかね。

MSの人はこのことに気付いているのか、何か理由があってそうしているのか、聞いてみたいです。そういう窓口ってないんですかね。

gulp-watchで新規ファイルが認識しない問題が解決

以下のようにgulp-watchでファイル監視しているとき、ふと新規ファイルやリネームしたファイルが認識しないことに気づきました。

gulp.watch('./sass/**/*.scss', ['sass'])

調べて見たところ、解決策があったのでここで紹介します。

gulp-watchで新規ファイルを認識させる方法

gulp-watchではファイル監視にGazeを使っています。

Gazeでは、作業ディレクトリ(cwd)というパラメータがあります。規定値はprocess.cwd()となっており、最初の例では規定値が使われます。

どうもGazeではファイル監視に問題を抱えているようで、パラメータの指定の仕方にコツがいるようです。(参考:#41)

以下のようにcwdパラメータを指定して、監視パスから./をなくすと、新規ファイルでもうまく認識できるようになりました。

gulp.watch('sass/**/*.scss', { cwd: './' }, ['sass'])

これはGazeのバグかと思いますが、もう何年も放置されているようですね。。

GazeはGruntでも使われているようなので同じ問題があるかもしれませんね。

stackoverflow.com

github.com