2018年12月30日日曜日

同一分散仮想スイッチ上の同一分散ポートグループにいる VM 間で通信ができない場合の対処方法

概要

同一ホストであれば通信できるけど異なるホストに移動すると通信できない場合は分散仮想スイッチのアップリンクを確認してみましょう

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)

分散スイッチにアップリンクを追加する

vds_uplink2.png

アップリンクの部分が 1 以上になっていることを確認しましょう
またこれが各ホストで設定されていることを確認しましょう

VDS にホストを追加するときにアップリンク選択するところがあるのでそこで追加対象のホストが持つそれぞれのアップリンク選択してあげましょう

また大抵の場合は VMkernel で 1 つ使っているので VDS 用にもう 1 つ作成しておきましょう
vds_uplink1.png

VLAN の設定確認

それでもダメな場合は VLAN の設定確認しましょう
「VLAN なし」もしくは「VLAN トランク」にすれば疎通できるようになると思います
vds_uplink3.png

vds_uplink4.png

VLAN ID を設定しちゃダメなのか

自分の場合 VLAN ID を設定すると異なるホスト間で通信できませんでした
が、普通に考えればできるんじゃないかなと思います

物理のネットワーク側で対象の VLAN ID が振られている通信を許可する必要があります
Nested を使っている場合はとりあえずトランクなり VLAN ID なしにしておけば良いかなと思います
物理側は使用している機器や環境に応じてかなり変わってくるので自身の環境に合わせて適切に設定してください

参考サイト

2018年12月29日土曜日

マネックス FX PLUS で少し FX してみた (約定履歴あり)

マネックス FX PLUS で 1 ヶ月半ほどドル円の取り引きをしてみました
やってみた所感と実際に取り引きした約定履歴を掲載します

所感

スワップポイントが安い

マネックス FX PLUS を使っていて、これが一番微妙な点かなと思いました
2018/11/27 時点ですがドル円の場合、買いスワップが 18-20 円ほどしかありません
売りスワップは 20-30 円ほどです
ショートメインの人にはオススメかもしれませんがロングでスワップポイントメインの方には向いていない証券会社かなと思います

またマネックス FX PLUS は他の証券会社のようにスワップポイントカレンダーを公開しておらずアプリなどからしかスワップポイントを確認することができません
これもかなり辛いポイントかなと思います

入出金のタイミング

どこを調べてもなかったのですが実際にやってみたわかったので記載します
例えば 11/20 (火) に約定して決済した場合、その損益はすぐに証拠金には反映されますが出金するための口座には振り込まれません
実際に口座に振り込まれるのは「2 FX営業日後」でした
なので 11/22 (木) に振り込まれその後出金可能となります
土日祝日など市場が休みの場合には翌営業日に回される感じになります

また FX の口座からメインの MRF 口座というところに更に資金を移す必要があります
そうしないと実際の金融機関 (みずほや三菱UFJ など) に出金することができません
FX 口座から MRF 口座には即時反映されるので良いのですが MRF 口座から出金するには 1 営業日かかります
また実際に金融機関に振り込まれるのは金融機関の開始時間になるので朝 9:00 などがほとんどです
手数料は無料ですがすぐに出金することはできないようです

実は手数料がかかるケースがある

FX PLUS では最低 1,000 通貨から取り引きできるのですが実は 10,000 通貨以上でないと手数料がかかります
なので取り引きする際は最低でも 10,000 通貨 (1lot) から取り引きするようにしましょう
参考

ポジションの一括編集ができない

例えば 40,000 通貨で 5 ポジションもっている場合に 5 ポジションすべてで同じ指値の決済がしたい場合はポジションごとに指値を指定する必要があります
もちろんポジションを同時に決済することはできますが、その場合はクイック決済になるため指値はできません

ポジションごとに指値を変えたい場合にはそれで OK ですが同じ指値に変更したい場合などは同じことを繰り返さないといけないので面倒でした

Web 版のチャートがしょぼい

FX PLUS は Web でもスマホでもできるのですが Web 版のチャートがかなりしょぼく基本はスマホ (MonexTraderFX)で取り引きしていました
あとから気づいたのですが Web 版とは別に MonexTraderFX 自体の Web 版があるらしくこれを使えばスマホと同等の機能が使えるようになると思います

レバレッジ

25 倍固定です
もし 25 倍以下で取り引きしたい場合は実効レバレッジを使って自分でレバレッジを調整するしかありません
参考

スプレッド

ドル円の場合ですが原則 0.3 銭でした
たしかこれも最近になって 0.3 銭になりそれまでは 2 銭ほどあった記憶があります (今思えばよくそのスプレッドでやっていたなーと思います、、)
参考

おまけ: 約定履歴

実際に 1 ヶ月半ほど取り引きしてみたのでその履歴を紹介します
基本はスマホアプリで取り引きしました
ポジションを入れるときはクイックで決済するときは指値を基本的には入れています

10 月度

通貨ペア 取引区分 売買 約定数量 約定価格 注文日時 約定日時 決済損益 売買手数料
USD/JPY 新規 40,000 112.360 2018/10/29 20:44:44 2018/10/29 20:44:44 ¥ 0 ¥ 0
USD/JPY 新規 40,000 112.350 2018/10/29 20:41:43 2018/10/29 20:41:43 ¥ 0 ¥ 0
USD/JPY 新規 40,000 112.340 2018/10/29 20:39:00 2018/10/29 20:39:00 ¥ 0 ¥ 0
USD/JPY 決済 80,000 112.340 2018/10/29 12:36:56 2018/10/29 20:28:09 ¥ 15,776 ¥ 0
USD/JPY 決済 40,000 112.340 2018/10/29 12:37:06 2018/10/29 20:28:09 ¥ 7,888 ¥ 0
USD/JPY 新規 40,000 112.150 2018/10/26 12:51:21 2018/10/26 13:13:59 ¥ 0 ¥ 0
USD/JPY 新規 80,000 112.150 2018/10/26 13:09:33 2018/10/26 13:13:59 ¥ 0 ¥ 0
USD/JPY 決済 40,000 112.650 2018/10/25 07:36:21 2018/10/26 02:49:44 ¥ 21,680 ¥ 0
USD/JPY 決済 40,000 112.650 2018/10/25 07:36:27 2018/10/26 02:49:43 ¥ 19,216 ¥ 0
USD/JPY 決済 40,000 112.650 2018/10/25 07:36:34 2018/10/26 02:49:43 ¥ 13,536 ¥ 0
USD/JPY 新規 40,000 112.108 2018/10/25 07:28:39 2018/10/25 07:28:39 ¥ 0 ¥ 0
USD/JPY 新規 40,000 112.198 2018/10/23 21:13:00 2018/10/23 21:13:00 ¥ 0 ¥ 0
USD/JPY 新規 40,000 112.340 2018/10/23 16:22:06 2018/10/23 16:26:04 ¥ 0 ¥ 0
USD/JPY 決済 20,000 112.350 2018/10/23 11:44:42 2018/10/23 16:02:01 ¥ 2,864 ¥ 0
USD/JPY 決済 20,000 112.350 2018/10/23 11:44:51 2018/10/23 16:02:01 ¥ 9,832 ¥ 0
USD/JPY 新規 20,000 112.850 2018/10/22 18:16:55 2018/10/22 18:20:09 ¥ 0 ¥ 0
USD/JPY 新規 20,000 112.510 2018/10/20 02:25:49 2018/10/20 02:25:50 ¥ 0 ¥ 0
USD/JPY 決済 20,000 112.550 2018/10/19 21:41:00 2018/10/19 22:43:15 ¥ 8,744 ¥ 0
USD/JPY 新規 20,000 112.120 2018/10/18 23:29:11 2018/10/19 00:47:00 ¥ 0 ¥ 0
USD/JPY 決済 20,000 112.180 2018/10/18 17:20:25 2018/10/19 00:33:35 ¥ 2,920 ¥ 0
USD/JPY 新規 20,000 112.350 2018/10/17 08:07:12 2018/10/17 08:30:48 ¥ 0 ¥ 0
USD/JPY 決済 20,000 112.310 2018/10/17 07:55:02 2018/10/17 07:59:42 ¥ 2,072 ¥ 0
USD/JPY 新規 20,000 112.221 2018/10/15 08:43:41 2018/10/15 08:43:41 ¥ 0 ¥ 0
USD/JPY 決済 20,000 112.190 2018/10/15 08:36:55 2018/10/15 08:38:48 ¥ 300 ¥ 0
USD/JPY 新規 20,000 112.205 2018/10/15 08:30:10 2018/10/15 08:30:10 ¥ 0 ¥ 0
USD/JPY 決済 10,000 112.085 2018/10/12 20:05:40 2018/10/13 00:42:05 ¥ 500 ¥ 0
USD/JPY 新規 10,000 112.135 2018/10/12 09:11:04 2018/10/12 09:11:04 ¥ 0 ¥ 0
USD/JPY 決済 10,000 112.130 2018/10/12 08:49:57 2018/10/12 09:05:23 ¥ 440 ¥ 0
USD/JPY 新規 10,000 112.086 2018/10/12 08:47:59 2018/10/12 08:47:59 ¥ 0 ¥ 0
USD/JPY 決済 10,000 112.200 2018/10/11 22:44:47 2018/10/12 00:04:20 ¥ 830 ¥ 0

10 月合計: +106,598円

ここに記載はないのですが前半で 1,000 通貨単位の罠にハマり取り引きしていました、、
10 月度がプラスで終われたのは今考えれば完全ビギナーズラックだったかなと思います

11 月合計

通貨ペア 取引区分 売買 約定数量 約定価格 注文日時 約定日時 決済損益 売買手数料
USD/JPY 決済 200,000 113.100 2018/11/22 15:48:55 2018/11/26 10:31:20 ¥ 25,720 ¥ 0
USD/JPY 新規 200,000 113.000 2018/11/16 18:02:33 2018/11/16 22:40:19 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.300 2018/11/16 20:20:48 2018/11/16 20:22:09 ¥ 8,800 ¥ 0
USD/JPY 新規 200,000 113.256 2018/11/16 20:07:41 2018/11/16 20:07:41 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.320 2018/11/16 15:06:04 2018/11/16 15:06:04 ¥ 200 ¥ 0
USD/JPY 新規 200,000 113.319 2018/11/16 15:02:54 2018/11/16 15:02:54 ¥ 0 ¥ 0
USD/JPY 決済 40,000 113.330 2018/11/16 15:00:23 2018/11/16 15:00:24 ¥ -15,472 ¥ 0
USD/JPY 決済 40,000 113.330 2018/11/16 15:00:23 2018/11/16 15:00:24 ¥ -18,632 ¥ 0
USD/JPY 決済 40,000 113.330 2018/11/16 15:00:23 2018/11/16 15:00:24 ¥ -45,256 ¥ 0
USD/JPY 決済 40,000 113.330 2018/11/16 15:00:23 2018/11/16 15:00:24 ¥ -45,656 ¥ 0
USD/JPY 決済 40,000 113.330 2018/11/16 15:00:23 2018/11/16 15:00:24 ¥ -46,056 ¥ 0
USD/JPY 決済 200,000 113.353 2018/11/16 14:18:38 2018/11/16 14:18:38 ¥ 26,000 ¥ 0
USD/JPY 新規 200,000 113.483 2018/11/16 10:28:58 2018/11/16 10:28:58 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.310 2018/11/15 22:42:35 2018/11/15 22:42:35 ¥ 5,600 ¥ 0
USD/JPY 新規 200,000 113.338 2018/11/15 18:41:32 2018/11/15 18:41:32 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.613 2018/11/15 10:19:14 2018/11/15 10:19:14 ¥ 5,200 ¥ 0
USD/JPY 新規 200,000 113.639 2018/11/15 10:10:04 2018/11/15 10:10:04 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.569 2018/11/15 10:02:00 2018/11/15 10:02:00 ¥ 4,000 ¥ 0
USD/JPY 新規 200,000 113.589 2018/11/15 09:18:24 2018/11/15 09:18:24 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.790 2018/11/14 18:00:53 2018/11/14 18:00:53 ¥ 2,000 ¥ 0
USD/JPY 新規 200,000 113.800 2018/11/14 17:59:54 2018/11/14 17:59:54 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.908 2018/11/14 16:53:06 2018/11/14 16:53:06 ¥ 600 ¥ 0
USD/JPY 新規 200,000 113.911 2018/11/14 16:44:11 2018/11/14 16:44:11 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.885 2018/11/14 16:13:44 2018/11/14 16:13:44 ¥ 1,600 ¥ 0
USD/JPY 新規 200,000 113.893 2018/11/14 15:56:13 2018/11/14 15:56:13 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.931 2018/11/14 13:31:40 2018/11/14 13:31:40 ¥ 1,000 ¥ 0
USD/JPY 新規 200,000 113.936 2018/11/14 13:30:05 2018/11/14 13:30:05 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.776 2018/11/14 00:32:05 2018/11/14 00:32:06 ¥ 7,000 ¥ 0
USD/JPY 新規 200,000 113.811 2018/11/14 00:28:11 2018/11/14 00:28:11 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.901 2018/11/13 23:31:14 2018/11/13 23:31:14 ¥ 19,800 ¥ 0
USD/JPY 新規 200,000 114.000 2018/11/13 13:19:32 2018/11/13 13:19:32 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.713 2018/11/13 11:11:47 2018/11/13 11:11:48 ¥ 1,600 ¥ 0
USD/JPY 新規 200,000 113.721 2018/11/13 11:10:34 2018/11/13 11:10:34 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.600 2018/11/13 09:24:59 2018/11/13 09:25:17 ¥ 5,600 ¥ 0
USD/JPY 新規 200,000 113.628 2018/11/13 09:24:30 2018/11/13 09:24:30 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.858 2018/11/12 09:32:36 2018/11/12 21:15:33 ¥ 2,000 ¥ 0
USD/JPY 新規 200,000 113.868 2018/11/12 09:06:50 2018/11/12 09:06:50 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.850 2018/11/12 09:03:27 2018/11/12 09:04:00 ¥ 2,000 ¥ 0
USD/JPY 新規 200,000 113.860 2018/11/12 09:03:01 2018/11/12 09:03:02 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.850 2018/11/12 07:46:47 2018/11/12 08:17:39 ¥ 2,000 ¥ 0
USD/JPY 新規 200,000 113.860 2018/11/12 07:46:20 2018/11/12 07:46:20 ¥ 0 ¥ 0
USD/JPY 決済 200,000 113.834 2018/11/12 07:29:07 2018/11/12 07:39:04 ¥ 2,000 ¥ 0
USD/JPY 新規 200,000 113.844 2018/11/12 07:28:44 2018/11/12 07:28:44 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.900 2018/11/09 14:35:33 2018/11/09 16:00:28 ¥ 3,120 ¥ 0
USD/JPY 新規 120,000 113.926 2018/11/09 14:35:13 2018/11/09 14:35:13 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.915 2018/11/09 13:45:08 2018/11/09 13:56:55 ¥ 2,400 ¥ 0
USD/JPY 新規 120,000 113.935 2018/11/09 13:44:49 2018/11/09 13:44:49 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.880 2018/11/09 12:54:10 2018/11/09 13:35:28 ¥ 3,480 ¥ 0
USD/JPY 新規 120,000 113.909 2018/11/09 12:53:27 2018/11/09 12:53:27 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.900 2018/11/09 11:06:55 2018/11/09 11:31:52 ¥ 7,680 ¥ 0
USD/JPY 新規 120,000 113.964 2018/11/09 05:14:44 2018/11/09 05:14:44 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.700 2018/11/08 20:21:54 2018/11/08 20:41:56 ¥ 2,640 ¥ 0
USD/JPY 新規 120,000 113.722 2018/11/08 20:21:27 2018/11/08 20:21:28 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.680 2018/11/08 19:05:06 2018/11/08 20:02:14 ¥ 2,400 ¥ 0
USD/JPY 新規 120,000 113.700 2018/11/08 19:04:37 2018/11/08 19:04:38 ¥ 0 ¥ 0
USD/JPY 決済 120,000 113.680 2018/11/08 18:09:42 2018/11/08 18:42:59 ¥ 2,400 ¥ 0
USD/JPY 新規 120,000 113.700 2018/11/08 18:01:52 2018/11/08 18:01:52 ¥ 0 ¥ 0
USD/JPY 決済 40,000 113.216 2018/11/06 08:01:03 2018/11/06 08:01:03 ¥ 200 ¥ 0
USD/JPY 新規 40,000 113.221 2018/11/06 08:00:03 2018/11/06 08:00:03 ¥ 0 ¥ 0
USD/JPY 決済 40,000 113.195 2018/11/06 07:56:16 2018/11/06 07:56:16 ¥ 320 ¥ 0
USD/JPY 新規 40,000 113.203 2018/11/06 07:55:59 2018/11/06 07:55:59 ¥ 0 ¥ 0
USD/JPY 決済 40,000 113.196 2018/11/06 07:49:46 2018/11/06 07:49:46 ¥ 560 ¥ 0
USD/JPY 新規 40,000 113.210 2018/11/06 07:48:31 2018/11/06 07:48:31 ¥ 0 ¥ 0
USD/JPY 決済 40,000 113.197 2018/11/06 07:47:30 2018/11/06 07:47:30 ¥ 40 ¥ 0
USD/JPY 新規 40,000 113.198 2018/11/06 07:42:30 2018/11/06 07:42:30 ¥ 0 ¥ 0
USD/JPY 新規 40,000 113.050 2018/11/02 14:07:31 2018/11/02 14:07:32 ¥ 0 ¥ 0
USD/JPY 新規 40,000 112.971 2018/11/02 14:02:28 2018/11/02 14:02:29 ¥ 0 ¥ 0

11 月合計: -23,112円

実は 10 月の後半でなぞのショートを 20 lot ほど掴んでしまいずっと 11 月中に持っていたためにマイナススワップがすごいことになっていました
マイナススワップの仕組みを知らずずっと持っており損切りするにもできず一時期は中間選挙の影響でドル高もあり含み損で 40 万ほどありました
11 月の後半で米中貿易懸念やブレグジット懸念におりリスクオフとなり円高になり始めたタイミングで損切りしました
最終的には 112.60 あたりまで落ち込んだのでもう少し持っていれば 11 月度もプラスで終われたかもしれません
が、さすがに 1 ヶ月以上ショートポジションを持ち続けるのは精神的にも良くなかったので 113 円前半あたりで損切りしました

最後に

マネックス FX PLUS を少し使ってみて実際に取り引きもやってみたので備忘録をまとめてみました
約定履歴は参考程度に御覧ください

使用感的には非常によかったかなと思います
アプリも良くできていて特におかしな動きはありませんでした
入出金なども特に手間取ることもありませんでした

スワップポイントさえ悪くなければ続けてもよかったかもしれません
なお、現在は FX の取り引きにマネックス FX PLUS は使っておりません

2018年12月28日金曜日

IFTTT でドル円が指値になったら Slack に通知する方法

概要

Finance というサービスを使って実現します
株のチャートを取得するためのサービスですが裏側は Yahoo ファイナンスから情報を引っ張っているようで為替情報も取得できました
今回は指値までレートが上昇したらなったら Slack に通知してみます

環境

  • IFTTT (2018/12/11 時点)

this

New Applet から this を作成します
「finance」で検索すると出てきます
ifttt_finance1.png

「Price rises above」を選択します
ifttt_finance2.png

トリガーを設定します
ここでポイントですが「Ticker symbol」は「USDJPY=X」を入力します
レートは好きな値を入力しましょう
ifttt_finance3.png

指定できる為替の Ticker symbol は Yahoo ファイナンスで確認できる通貨であれば何でも OK です
例えばユーロ/円の URL は https://info.finance.yahoo.co.jp/fx/detail/?code=EURJPY=FX になりますがその場合は「EURJPY=X」と入力しましょう
(最後の部分は FX でも動作すると思います、、、確かめていません)

that

Slack に通知する設定をします
Slack サービスの設定をしていない場合は設定しておきましょう
ifttt_finance4.png

「Post to channel」アクションを選択します
ifttt_finance5.png

あとは通知するチャネルなど選択すれば OK です
一番下に「Create Action」があるので選択します
ifttt_finance6.png

完成

Finish をちゃんと押すようにしましょう
ifttt_finance7.png

実際に通知が来ると以下のような感じです (レートは変えているので上記とは違います)
ifttt_finance8.png

最後に

IFTTT の finance サービスを使ってみました
レートを変更したい場合は作成して Applet を編集してください
段階的に通知したい場合は Applet を毎度編集するか複数の同じ Applet を作成すれば良いと思います

また超えたら通知が来るので指定したレートあたりでうろうろしているような相場だと通知が大量に来るのでその場合もレートを調整するなり OFF にするなりしてください

あとは一瞬だけ超えた場合などはさすがに検知できないのでご注意ください

2018年12月27日木曜日

Ruby で C言語を扱う方法

概要

RubyInline を使います
他にも Rice や FFI といったライブラリもありますが今回は RubyInline を使います

環境

  • macOS 10.14.1
  • clang Apple LLVM version 10.0.0 (clang-1000.11.45.5)
  • Ruby 2.5.1p57

インストール

  • bundle init
  • vim Gemfile
gem "RubyInline"
  • bundle install --path vendor

gem の名前に注意しましょう
inline ではありません

Hello World

とりあえず printf でメッセージを表示するサンプルを書いてみましょう

  • vim test.rb
require 'inline'

class Test
  inline(:C) do |builder|
    builder.include '<stdio.h>'
    builder.c '
      void say(char *msg) {
        printf("%s\n", msg);
      }
    '         
  end
end

t = Test.new
t.say "hello"
  • bundle exec ruby test.rb

で「hello」と表示されます

builder を使ってヘッダファイルの読み込み (.include) や C言語の関数を登録 (.c) します
Ruby から参照する場合はクラスを作成して、そのクラスのメソッドとして呼び出します
Ruby の文字列は char のポインタで普通に渡せました

返り値がある場合

require 'inline'

class Test
  inline(:C) do |builder|
    builder.include '<stdio.h>'
    builder.c '
      int sum(int a, int b) {
        return a + b;
      }
    '         
  end
end

t = Test.new
ret = t.sum 1, 2
puts ret.class
puts ret

int は Ruby 側では Integer として扱われました

複数の include

require 'inline'

class Test
  inline(:C) do |builder|
    builder.include '<stdio.h>'
    builder.include '<math.h>'
    builder.c '
      double my_pow(double a, double b) {
        printf("%lf\n", a);
        printf("%lf\n", b);
        return pow(a, b);
      }
    '         
  end
end

t = Test.new
ret = t.my_pow 10, 2
puts ret.class
puts ret

単純に 2 度 .include をコールすれば OK です
ちなみに C言語の double は Ruby では Float でした

最後に

Ruby から C 言語をコールしてみました
もし C の共有ライブラリ (.so 形式) などでしか作られていない場合は使えるかなと思います
あとは RubyInline を使う理由に C で処理したほうが早いという理由もありあした
C 以外に C++ も使えるのでたしかに速度重視の場合は考慮してもいいかもしれません

2018年12月26日水曜日

go-flags を使ってみた

概要

golang にはデフォルトでコマンドラインパーサ用のライブラリが用意されています
しかし更に強力なライブラリがサードパーティで公開されています
今回は go-flags というライブラリを簡単ですが使ってみました

環境

  • macOS 10.14.1
  • golang 1.11.2
  • go-flags 5de817a

インストール

  • go get github.com/jessevdk/go-flags

Getting Startedサンプルコード

とりあえず動かしてみましょう

package main

import (
    "fmt"
    flags "github.com/jessevdk/go-flags"
    "os"
)

type Options struct {
    Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information" required:"true"`
}

var opts Options

func main() {
    _, err := flags.Parse(&opts)
    if err != nil {
        os.Exit(1)
    }
    fmt.Printf("%v\n", opts.Verbose)
    fmt.Println("end")
}

struct でオプションの情報を定義します
short, long, description などヘルプに表示できる情報を定義できます
定義した struct の変数を準備します

そして flags.Parse にその変数のポインタを渡すことで引数の情報が struct に合わせてバインドされます
err じゃない場合は各 struct フィールドを参照できるようになっています

実行は

  • go build github.com/hawksnowlog/gf_test
  • go install github.com/hawksnowlog/gf_test
  • $GOPATH/bin/gf_test -v

でできます
今回の場合 required:"true" オプションを指定しているので指定しない場合はエラー文言 the required flag '-v, --verbose' was not specified) が表示されます
struct で指定可能なオプションはこちらに記載があります

パーサをカスタマイズする

例えば -h 指定時の Usage メッセージを変更してみます
ちなみにデフォルトは gf_test [OPTIONS] と表示されます

package main

import (
    "fmt"
    flags "github.com/jessevdk/go-flags"
    "os"
)

type Options struct {
    Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information" required:"true"`
}

var opts Options

func main() {
    parser := flags.NewParser(&opts, flags.Default)
    parser.Usage = "[opts]"
    _, err := parser.Parse()
    if err != nil {
        os.Exit(1)
    }
    fmt.Printf("%v\n", opts.Verbose)
    fmt.Println("end")
}

NewParser を使ってパーサを作成します
そして Usage プロパティの値を変更することで文言を変更できます
NewParser の 2 つ目の引数でパーサーのデフォルトの動作を指定できます
今回は flags.Default を指定しています
ここ に指定可能なデフォルトの定数があります

関数を呼び出す

オプション定義時に型ではなくコールバック用の関数を定義できます

package main

import (
    "fmt"
    flags "github.com/jessevdk/go-flags"
    "os"
)

type Options struct {
    Verbose []bool       `short:"v" long:"verbose" description:"Show verbose debug information" required:"true"`
    Name    func(string) `short:"n" long:"name" description:"Name parameter"`
}

var opts Options

func main() {
    opts.Name = func(n string) {
        fmt.Println(n)
    }
    parser := flags.NewParser(&opts, flags.Default)
    parser.Usage = "[opts]"
    _, err := parser.Parse()
    if err != nil {
        os.Exit(1)
    }
    fmt.Printf("%v\n", opts.Verbose)
    fmt.Println("end")
}

Name func(string) という感じで Options を定義します
そして Options の変数 opts に対して opts.Name でコールバック用の関数を定義してあげます
上記のコードの場合 main 内でないとsyntax error: non-declaration statement outside function body になるので注意してください

サブコマンドを定義する

オプションではなくサブコマンドも定義できます

package main

import (
    "fmt"
    flags "github.com/jessevdk/go-flags"
    "os"
)

type SubCmd struct {
}

func (sc *SubCmd) Execute(args []string) error {
    fmt.Println("subcmd")
    return nil
}

type Options struct {
    SubCmd SubCmd `command:"subcmd"`
}

var opts Options

func main() {
    parser := flags.NewParser(&opts, flags.Default)
    _, err := parser.Parse()
    if err != nil {
        os.Exit(1)
    }
}

struct でオプションを定義する際に command:"subcmd" を指定します
「subcmd」の部分はサブコマンド名になるので好きな名前を指定してください
ポイントは SubCmd struct の Execute 関数です
この引数と返り値で定義することでサブコマンドが指定されたときに Execute 関数が自動的に呼ばれます

最後に

簡単ですが go-flags の使ってみました
基本はコマンドラインツールを作るときに使う感じです

他にも初期化の方法はいろいろあります
.ini ファイルを使って外部のファイルにオプション情報を定義してそこからパーサを生成することもできるようです

参考サイト

2018年12月25日火曜日

goroutine の並列処理の終了を待機する方法

概要

例えば並列で 5 つの goroutine を回す場合、すべての goroutine の終了を待ってからメインの処理に戻りたい場合があると思います
今回は sync パッケージと errgroup パッケージを使った 2 つのサンプルコードを紹介します

環境

  • macOS 10.14.1
  • golang 1.11.2
  • errgroup 42b3178

sync.waitGroup

sync パッケージはデフォルトで使えるライブラリです
これでも goroutine の並列処理の完了を待つことができます

  • mkdir -p go/src/github.com/hawksnowlog/eg_test/
  • touch go/src/github.com/hawksnowlog/eg_test/main.go
  • vim go/src/github.com/hawksnowlog/eg_test/main.go
package main

import (
    "fmt"
    "sync"
    "time"
)

func waiter(i int) {
    t := time.Duration(i)
    time.Sleep(t * time.Second)
    fmt.Println(i)
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            waiter(i)
        }(i)
    }
    wg.Wait()
    fmt.Println("end")
}
  • go build github.com/hawksnowlog/eg_test
  • go install github.com/hawksnowlog/eg_test
  • $GOPATH/bin/eg_test

これで 0 から 4 までのカウントが終了してから end が表示されると思います

まず sync.WaitGroup の変数を作成します
作成した waitGroup の変数に対して goroutine を開始する前に wg.Add(1) でインクリメントします
そして goroutine 内で defer を使って関数が終了した際に必ず wg.Done() をコールします
あとは同期したい部分で wg.Wait() することですべての goroutine が終了するまで待機します

errgroup インストール

  • go get -u golang.org/x/sync/errgroup

errgroup.Group

同じようなことを errgroup.Group を使ってもできます
こちらを使ったほうが簡潔に書けます

package main

import (
    "fmt"
    "golang.org/x/sync/errgroup"
    "time"
)

func waiter(i int) error {
    t := time.Duration(i)
    time.Sleep(t * time.Second)
    fmt.Println(i)
    return nil
}

func main() {
    eg := errgroup.Group{}
    for i := 0; i < 5; i++ {
        val := i
        eg.Go(func() error {
            return waiter(val)
        })
    }
    err := eg.Wait()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("end")
}

goroutine でコール関数が error を返却する必要があります
メイン処理では各 goroutine が error を返却しているかどうか判定することができるようになっています
また Add や Done の記述も不要になっています
ちなみに val := i で別の変数に格納してから goroutine に値を渡さないとすべて 5 で値が渡ってしまうので注意が必要です

最後に

goroutine とメイン側の処理を同期する方法を紹介しました
機能的には errgroup のほうがいろいろできそうです
単純な同期であれば sync で十分かもしれません

今回のサンプルは goroutine 内のタイムアウトを考慮できていません
チャネルなどと組み合わせるか goroutine で呼び出している関数側でタイムアウト時の処理を考慮するようにしましょう

参考サイト

2018年12月24日月曜日

VMKernel ネットワークで vMotion を有効にしないと vMotion できない

概要

vMotion できない場合は確認してみてください

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)

VMKernel の設定を確認

こんなエラーが出て vMotion できない場合は
vmkernel_vmotion_check1.png

対象のホストの VMKernel アダプタの設定を選択して「vMotion」の欄が有効になっているか確認してください
vmkernel_vmotion_check2.png

もしここが有効になっていない場合は設定から有効にしましょう

あとは他のホストに vMotion できない原因としては VM が配置されているストレージが ESXi のローカルストレージ (datastore1 など) になっている場合があります
ホスト同士が共有ストレージ (例えば nfs など) を使っている必要があり要するに異なるホスト間から対象の VM の vmdk や vmx ファイルを参照できる必要があるということです

2018年12月23日日曜日

VCSA6.7u1 で「サービス vpxd-svcs の開始中にエラーが発生しました」でセットアップに失敗する

概要

VCSA 6.7u1 をセットアップ中にタイトルのエラーが出て先に進めなくなりました
その場合の対処方法を紹介します

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)

背景

今回は VCSA に FQDN ではなく IP アドレスを振ることを想定しています
FQDN の場合 DNS サーバが必要になりそれが面倒なので IP で管理します
ただその場合に該当のエラーが発生しました

対処方法

IP アドレスの割り当てを static で振るようにしましょう
DHCP を使っている場合、該当のエラーが発生します
インストール時だと以下の画面での設定です
vpxd_svcs_not_working1.png

またセットアップ時にも気をつける点があります
同じようにネットワークを設定する項目がありその中の「マシン名」という項目にも同じ IP アドレスを設定しましょう
確かデフォルトだと「photon-machine」になっていると思いますがそれだと名前解決できずにエラーとなります

参考サイト

2018年12月22日土曜日

InstantClone_Task 時に IP がバッティングしないようにする方法

概要

過去に紹介した記事に InstantClone 時に IP がバッティングしてしまうという問題があることを紹介しました
vmware-tools を使った解決方法がわかったので紹介します

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)
  • Ubuntu 18.04 LTS (クローン元)

クローン元の VM で準備

事前に作業が必要です
スクリプトを配置するだけです

ネットワーク情報を更新するスクリプトを準備

配置する場所はどこでも OK です
まだ実行しないでください

  • vim my_customize.sh
#!/bin/bash
vmware-rpctool "instantclone.freeze"

echo "Updating MAC Address ..."
for NETDEV in /sys/class/net/e*
do
        DEVICE_LABEL=$(basename $(readlink -f "$NETDEV/device"))
        DEVICE_DRIVER=$(basename $(readlink -f "$NETDEV/device/driver"))
        echo $DEVICE_LABEL > /sys/bus/pci/drivers/$DEVICE_DRIVER/unbind
        echo $DEVICE_LABEL > /sys/bus/pci/drivers/$DEVICE_DRIVER/bind
done

echo "Updating Hostname ..."
hostnamectl set-hostname ic01
  • chmod 755 my_customize.sh

やっていることは Mac アドレスの unbind/bind です
これを実行することでクローン元で使っていたイーサネットカードの Mac アドレスをリフレッシュして本来あるべき Mac アドレスを割り当てます

  • echo "0000:03:00.0" > /sys/bus/pci/drivers/vmxnet3/unbind
  • echo "0000:03:00.0" > /sys/bus/pci/drivers/vmxnet3/bind

ちなみに今回の環境は DHCP なのでこれを行うだけで dhclient が動作して DHCP サーバから IP の取得も行ってくれます

スナップショットを取得しておこう (optional)

この後で先程配置したスクリプトを実行します
スクリプトは一度実行すると VM が何も受け付けない状態になってしまいます
再起動すれば直りますが毎回再起動するのは面倒なのでスナップショットを取得しておきましょう

スクリプトを実行しておく

準備ができたらクローン元の VM でスクリプトを実行しておきましょう

  • ./my_customize.sh

という感じでスクリプトを実行すると VM が何も受け付けない状態 (フリーズ) になります
スクリプト内に記述した vmware-rpctool "instantclone.freeze" が実行されフリーズ状態になります

IP などもなくなるので SSH している場合には通信が切れます
ちなみにこうなると復帰させる方法はスナップショットから元に戻すか VM を再起動するしかありません

InstantClone を実行

フリーズ状態になったクローン元の VM を使って InstantClone を実行しましょう
API からのみ実行できるのできるので mob や rbvmomi を使いましょう (参考)

動作確認

InstantClone した VM で以下を確認しましょう

  • IP アドレスがクローン元の VM と別のものが振られている
  • ホスト名が設定されている

特に IP アドレスに関しては VM が起動してすぐに別の IP が振られていることを確認してみましょう
(Web Client から見るとクローン元の VM が振られているように見えますが実際は別の IP になっていると思います)

おまけ: guestinfo を組み合わせる

例えばホスト名などは動的に決めたい場合があります
そんな場合は InstantClone 時に guestinfo を設定することで実現することができます
またスクリプトも guestinfo から値を取得するように変更します

  • vim my_customize.sh
#!/bin/bash
vmware-rpctool "instantclone.freeze"

echo "Updating MAC Address ..."
for NETDEV in /sys/class/net/e*
do
        DEVICE_LABEL=$(basename $(readlink -f "$NETDEV/device"))
        DEVICE_DRIVER=$(basename $(readlink -f "$NETDEV/device/driver"))
        echo $DEVICE_LABEL > /sys/bus/pci/drivers/$DEVICE_DRIVER/unbind
        echo $DEVICE_LABEL > /sys/bus/pci/drivers/$DEVICE_DRIVER/bind
done

echo "Updating Hostname ..."
HOSTNAME=$(vmware-rpctool "info-get guestinfo.ic.hostname")
hostnamectl set-hostname ${HOSTNAME}

そして InstantClone を guestinfo 付きで実行します

  • vim instant_clone.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('Datacenter') || fail('datacenter not found')
vm = dc.find_vm('u18_01') || fail('VM not found')

spec = {
  name: "ic01", location: "",
  config: [
    {key: "guestinfo.ic.hostname", value: "ic01"}
  ]
}
ret = vm.InstantClone_Task(spec: spec).wait_for_completion
puts ret

こんな感じでホスト名を設定できます

ちなみに今回は DHCP 環境だったので Mac アドレスの更新だけで良かったのですが IP を static で振る場合には guestinfo を使って実現することもできます
static IP を振る場合はこちらのスクリプトが参考になると思います

最後に

InstantClone_Task でクローン先の VM に同じ IP が振られてしまう現象に対応してみました
今回は Ubuntu18.04 に対するスクリプトなので別のディストリビューションや GuestOS であればそれ用のスクリプトを作成する必要があります

ちなみに InstantClone はクローン元の VM のコンソール情報もクローンします
なので InstantClone 前にコンソールでログインした状態にしてしまうとクローン後の VM もすでにコンソールでログインしている状態になってしまうので注意しましょう

参考サイト

2018年12月21日金曜日

InstantClone_Task 時に guestinfo を渡す方法

概要

前回 InstantClone をサクっと試してみました
実は InstantClone_Task 実行時に作成されたインスタントクローン VM に guestinfo を渡すことができます
gusetinfo は簡単に言えば GuestOS (例えば Ubuntu など) 上で参照することができる変数です

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)
  • Ruby 2.5.0p0
  • rbvmomi (2.0.0)

guestinfo を指定する方法

spec に config を key/value 形式で並べるだけです

<spec>
   <name>ic01</name>
   <location></location>
   <!-- ok -->
   <config>
      <key>guestinfo.ic.hoge</key>
      <value xsi:type="xsd:string">guestinfo.ic.hoge</value>
   </config>
   <config>
      <key>guestinfo.ic.fuga</key>
      <value xsi:type="xsd:string">guestinfo.ic.fuga</value>
   </config>
   <config>
      <key>guestinfo.iic.fuga</key>
      <value xsi:type="xsd:string">guestinfo.iic.fuga</value>
   </config>
   <config>
      <key>guestinfo.fuga</key>
      <value xsi:type="xsd:string">guestinfo.fuga</value>
   </config>
   <!-- ng -->
   <config>
      <key>ic.fuga</key>
      <value xsi:type="xsd:string">ic.fuga</value>
   </config>
   <config>
      <key>fuga</key>
      <value xsi:type="xsd:string">fuga</value>
   </config>
</spec>

少しポイントがあります
渡せる key に制約があるようで必ず guestinfo で始まっている必要がありました
もし制約違反していると値が GuestOS 側に渡りません
参照しても Invalid key name supplied になります
また、存在しない key を指定すると No value found になります

どうやって参照するの

vmware-tools の機能を使います
vmware-tools がインストールされていると vmware-rpctool コマンドが使えます
実際にインスタントクローンされた VM にログインして以下のコマンドを叩いてみましょう

  • vmware-rpctool "info-get guestinfo.ic.hoge"

すると設定した value 側の値が取得できると思います

おまけ: rbvmomi でやってみた

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('Datacenter') || fail('datacenter not found')
vm = dc.find_vm('u18_01') || fail('VM not found')

spec = {
  name: "ic01", location: "",
  config: [
    {key: "guestinfo.ic.hoge", value: "hoge"},
    {key: "guestinfo.ic.fuga", value: "fuga"}
  ]
}
ret = vm.InstantClone_Task(spec: spec).wait_for_completion
puts ret

spec の指定を mob で実行する spec に合わせるだけです

最後に

InstantClone_Task 時に gusetinfo を渡す方法を紹介しました
実はこれを応用すると前回紹介した InstantClone 時に IP アドレスがバッティングしてしまう問題を解決することができます

要するにネットワーク情報を guestinfo で渡してインスタントクローン VM が作成されたときにネットワークを設定するスクリプトを実行することでバッティングを解消するという流れになります

ちょっと力技感も強いですがこちらで紹介されていたのでほぼオフィシャルなのかなと思います
具体的なやり方も分かったら紹介したいと思います

参考サイト

2018年12月20日木曜日

vSphere 6.7 で InstantClone_Task を試してみた

概要

vSphere 6.7 から正式に InstantClone が使えるようになったので試してみました
Web Client からは使えないので mob と SOAP API (rbvmomi) から実行してみました

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)
  • Ruby 2.5.0p0
  • rbvmomi (2.0.0)

mob から実行する

対象の VM に移動して methods 内から InstantClone_Task を選択すれば OK です
条件としては対象の VM が起動している必要があります (停止中の VM に対して実行したところエラーになりました)
instant_clone1.png

最低限必要な spec は以下の通りです

<spec>
   <name>ic01</name>
   <location>
   </location>
</spec>

instant_clone2.png

これで Invoke すれば InstantClone で VM が作られます
location を指定すれあ別のホストに作成することもできます

rbvmomi から実行する

プログラムからも実行してみました

  • bundle init
  • vim Gemfile
gem "rbvmomi"
  • bundle install --path vendor
  • vim main.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('Datacenter') || fail('datacenter not found')
vm = dc.find_vm('vm01') || fail('VM not found')

spec = {name: "ic01", location: ""}
ret = vm.InstantClone_Task(spec: spec).wait_for_completion
puts ret
  • bundle exec ruby main.rb

これだけです
rbvmomi で実施する場合は VIM.connect する際に rev: '6.7.1' の指定が必要です
WSDL から InstantClone_Task を探すのですが rev: を明示的に指定しないと探すことができずエラーになってしまいます
またパワーオンになっていないと以下のエラーが発生します
InvalidPowerState: The attempted operation cannot be performed in the current state (Powered off). (RbVmomi::Fault) (edited)

ちなみに WSDL の URL は https://192.168.100.30/sdk/vim.wsdl という感じの URL で確認できます

最後に

InstanceClone_Task を試してみました
いろいろ経緯とかを調べてみるとコンテナの需要が高まったのと CI/CD で使えないかという要望があったららしいです
なのであくまでも用途としては使い捨て VM という感じで使うのだと思います

一つ気になったのはインスタンスクローン後にクローン元の VM と同じ IP が振られる現象が多々ありました
ネットワークのインタフェースの MAC アドレスクローン直後は同一に見えてしまうため DHCP も同じ IP を払い出しているんだと思います
再起動したり一定時間経過すると異なる IP が振られたのですが、直後はどう対処すれば良いか不明でした

P.S どうやら GuestInfo の仕組みを使って VM ないで IP のリフレッシュ作業を行う必要があるようです
https://www.virtuallyghetto.com/2018/04/new-instant-clone-architecture-in-vsphere-6-7-part-2.html
おそらくこの方法が確実だと思われます

参考サイト

2018年12月19日水曜日

AMP 超入門

概要

AMP は Google が推奨するモバイル用の Web ページの描画を高速化するための仕組みです
簡単に言えば使える JavaScript やタグを制限しキャッシュすることでページの高速化を実現しています
今回は AMP 対応したページの作成を公式のチュートリアルを元に試してみました

環境

  • Chrome 70.0.3538.110

AMP 対応のサンプルページ

まずは AMP 対応したページを実際に作って動かしてみましょう

  • mkdir -p /path/to/workspace
  • cd /path/to/workspace
  • touch index.html
<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <title>Hello, AMPs</title>
    <link rel="canonical" href="/index.html">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <script type="application/ld+json">
      {
        "@context" : "http://schema.org",
        "@type" : "NewsArticle",
        "headline" : "AMP のテストページです",
        "image" : {
          "@type" : "ImageObject",
          "url" : "/welcome.jpg",
          "height" : 400,
          "width" : 400
        },
        "publisher" : {
          "@type" : "Organization",
          "name" : "@hawksnowlog",
          "logo" : {
            "@type" : "ImageObject",
            "url" : "/welcome.jpg",
            "height" : 400,
            "width" : 400
          }
        },
        "author" : {
          "@type" : "Person",
          "name" : "hawksnowlog"
        },
        "datePublished" : "2018-12-17T00:00:00Z"
      }
    </script>
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    <style amp-custom>
      /* any custom style goes here */
      body {
        background-color: white;
      }
      amp-img {
        background-color: gray;
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <h1>Welcome to the mobile web</h1>
    <amp-img src="/welcome.jpg" alt="Welcome" height="400" width="400"></amp-img>
  </body>
</html>

welcome.jpg は適当にダウンロードして index.html と同じパスに配置します

  • wget 'https://pbs.twimg.com/profile_images/712848447569661952/ayfI9-77_400x400.jpg' -O welcome.jpg

動かす

  • cd /path/to/workspace
  • python -m SimpleHTTPServer

で localhost:8080/index.html にアクセスするとページが表示されます

本当に AMP 対応しているか確認する

ページを見るだけではただの Web ページです
このページが AMP 対応しているかどうか確認するには

  1. デベロッパーツールのコンソールを使う
  2. amp-validator という Chrome 拡張を使う

の 2 通りがあります
1 の場合コンソールを開いた上で http://localhost:8000/index.html#development=1 という URL にアクセスします
するとコンソールに以下のように表示されると思います
try_amp1.png

ここにエラーが表示される場合は AMP 対応できていません
例えば使えないタグを使っていたり必要なメタタグなどが不足している場合などです

2 の場合はツールバーにアイコンが表示されます
ページにアクセスして緑のアイコンが表示されれば AMP 対応が完了しているページになります

解説

では動かせたところで AMP 対応に必要なそれぞれのタグのについて紹介します

html タグ

まず上部にある HTML タグに AMP 対応であることの記載が必要です
amp ではなく雷マークでもいいようです

<html amp lang="en">

JavaScript

次に amp の JavaScript ライブラリを参照します
これも必須です

<script async src="https://cdn.ampproject.org/v0.js"></script>

AMP は基本的に上記以外の JavaScript は使えません
動的なコンテンツを表示したい場合それ専用の AMP タグがあるのでそれで代用します
将来的には好きな JavaScript が使えるようになるという話も聞きますが現状では厳しいようです (Twitter などを埋め込む場合はそれ専用のタグが用意されているようです)

rel=”canonical” の設定

簡単に言えば AMP 対応していない同一ページへのリンクを設定します
AMP 対応しているページだけであればその自身へのページのリンクを設定します

<link rel="canonical" href="/index.html">

AMP 対応するとよく言われるのが AMP 対応していないページ (モバイル用ではなく通常の Web 用のページ) を残すというケースです
その場合にはこの rel="canonical" を使って参照して上げることで Google に別のページがあることも教えてあげることで、そちらのページもインデックスさせることができます
またモバイル用のページが見づらい人がいる場合には Web 版に誘導することもできるようになります

viewport

必須のタグと属性です
基本はコピペで OK です

<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

viewport はレスポンシブ対応しているページでよく見かける属性です

JSON-LD

JSON-LD はページの構造やメタ情報を定義することができる機能です
AMP 用の JSON-LD スキームが決められており定義することが推奨されています
JSON-LD を定義することで Google の検索結果 (特にニュース記事など) でよく見る「カルーセル」に該当ページを表示されることができるようになります

<script type="application/ld+json">
  {
    "@context" : "http://schema.org",
    "@type" : "NewsArticle",
    "headline" : "AMP のテストページです",
    "image" : {
      "@type" : "ImageObject",
      "url" : "/welcome.jpg",
      "height" : 400,
      "width" : 400
    },
    "publisher" : {
      "@type" : "Organization",
      "name" : "@hawksnowlog",
      "logo" : {
        "@type" : "ImageObject",
        "url" : "/welcome.jpg",
        "height" : 400,
        "width" : 400
      }
    },
    "author" : {
      "@type" : "Person",
      "name" : "hawksnowlog"
    },
    "datePublished" : "2018-12-17T00:00:00Z"
  }
</script>

上記は @type NewsArticle の定義になります
他にもタイプの定義があるので公開するページのタイプに合わせて設定してあげると良いと思います (参考)

amp-boilerplate

AMP Boilerplate Code と呼ばれています
これも必須の要素です
viewport と同じで基本はコピペで OK (なはず) です

<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

余分なスペースなどは削除しておく必要があるようです

amp-custom

カスタム用のスタイルシートをここで定義します
むしろここ以外でスタイルシートを定義することはできません

<style amp-custom>
  /* any custom style goes here */
  body {
    background-color: white;
  }
  amp-img {
    background-color: gray;
    border: 1px solid black;
  }
</style>

amp-img

画像を表示する場合には img タグではなく amp-img タグを使います
amp-img のように AMP 対応したページでは使えるタグが限られています
既存の HTML タグにも使えるものがありますが基本的には限られたタグしか使えません

AMP で使える基本コンポーネントは以下の通りです
https://www.ampproject.org/ja/docs/reference/components

逆に使えないタグや属性は以下の通りです
https://www.ampproject.org/ja/docs/fundamentals/spec

一旦作成してみて validator やコンソールで確認してみて、エラーが出なくなるまでタグを置き換えたり削除したりするしかないかなと思います

実際に AMP 対応するには

ケースとしては 2 つが考えられると思います

  1. 既存のページを AMP 対応する
  2. 新規で AMP 用のページを作成する

1 は動的なコンテンツに依存していれば依存しているほど大変だと思います
また大規模なページになればなるほど大変なイメージもあります
やり方はいろいろありますがやはり前に紹介した通り validator を使って 1 つ 1 つエラーを解決していく感じだと思います

2 のほうが簡単だとは思います
が、2 を採用すると同一ページの管理が 2 つになってしまうため管理コストがあがります
うまく吸収してくれるようなツールもありそうですが単純に考えれば 2 倍になります
rel="canonical" 属性もあるようにそういったケース自体 AMP は想定しているので間違いではないです

個人的にはできれば 1 で進めて無理そうであれば 2 にするというのが良いかなーとは思います

当然ですがどちらにするにしてもモバイル向けのページデザインにする必要はあります
1 であればレスポンシブデザイン対応は必須になるので考えることが増えます
2 であればレスポンシブにする必要はなくなり完全にモバイル用の別ページを作れば良いだけになるので考えることは減ります

自分は実際に自分のページに適用したなどの経験はないのであくまでも推測にはなります

最後に

AMP 対応したサンプルページを作成して AMP に入門してみました
制限を設けることで最適化しているのでシンプルなページほど AMP に向いているなと思いました
AMP 用の JSON-LD の @type を見てもわかるようにニュース記事やブログ記事、レビュー記事など静的コンテンツを含んでいるページに向いているのだと思います

また JSON-LD を使うことで Google 検索のカルーセルに該当記事を表示させることもできるので SEO 的な効果もあるのかもしれません

同じではないですが PWA やレスポンシブデザインも意識したほうが良いかもしれません
最近は PWA + AMP (PWAMP (ぷわんぷ)) という取り組みをしているページもあるようです
PWA 自体は Service Worker を使った仕組みになるので仕組み自体は全然違いますがシナジーはあるので興味があれば調べてみると良いかなと思います

参考サイト