Docker for Macを捨てcolimaでやっていく

Docer for Macの問題点としてはとにかくI/Oが遅い点です。
nuxt build などフロントエンドのビルドをDockerマシーン上で行うと非常に遅く、プロジェクトの規模とマシンスペックによっては30分以上かかるケースがあります。

また、Docker for Macが有料化されたこともあり、Docker for Macを避ける会社も出てきているかと思います。

Colimaとは

ColimaLimaをラップしたコンテナランタイムで、最小の設定でLimaを通してdockerなどのコンテナを利用することができます。

そもそもLimaはWindowsにおけるWSL2のように、Linuxの仮想環境をMac上で実行するものです。Limaを使ってdockerを利用することもできますが、多くの設定をする必要があり煩雑でした。

Colimaを使うことで、仮想Linux環境上でDockerを利用することができ、多くの場合Docker for Macを利用するよりも高速に、特にI/Oについては大幅な改善が見られます。

Colimaのインストール方法については公式ドキュメントに任せますが、特殊な環境ということもありいくつかのtipsをまとめてみました。

Dockerコンテナ内からDNSが引けずにnpm installやcomposer installなどが失敗する

colimaで作成したdocker環境ではホストのDNSが引けず、DNS解決に失敗することがあります。たとえば npm install や composer install をした場合に「リポジトリが見つからないのでインストールに失敗した」というようなことがあります。

この場合は、下記のようにColimaの設定で明示的にDNSのIPを指定することで解決します。ここではパブリックDNSを指定する必要があります。(Google Public DNSの 8.8.8.8 など)

colima start --dns 8.8.8.8

Rosetta 2を利用する

ARM対応していないDockerイメージを利用することも多々あるかと思います。

ARM MacかつMacOS 13 (Ventura)以降であれば下記のオプションでRosetta 2が利用できます。(Rosetta 2の対応自体はLimaにて行われいます)

colima start --arch aarch64 --vm-type=vz --vz-rosetta

Docker環境がうんともすんとも言わなくなる

nuxt devなどを利用中にHMRが実行されなくなったり、レスポンスが全く返ってこなくなったり、することがあります。この場合、ColimaかLimaがハングしている場合があります。

columa restart をしDockerコンテナを立ち上げ直すことで解決します。

最後に

Colimaを利用することでnuxt buildの時間が半分以下になるケースもありました。よいDXの為には高速な開発環境が必要です。是非Colimaを利用してみてください。

LINE公式アカウントの消せないリッチメニューを消す方法

LINE公式アカウントのチャンネルを運用するには、LINE Official Account Manager(以下 Account Manager)を使う方法とサードパーティーや独自開発をしたアプリケーションを介してLINE Messaging API(以下 Messaging API)を利用する方法があります。
リッチメニューの仕様として、Account Managerで作成したリッチメニューをMessaging APIで操作したり、Messaging APIで作成したリッチメニューをAccount Managerで操作したりすることはできません。

ここで問題になるのが、リッチメニューの表示優先順位です。

ドキュメントによると(1. が最優先)

  1. Messaging APIで設定するユーザー単位のリッチメニュー
  2. Messaging APIで設定するデフォルトのリッチメニュー
  3. Account Managerで設定するデフォルトのリッチメニュー

という順で表示されるのですが、1. や 2. が設定されていると、Account Managerでリッチメニューを作成してもユーザーに表示されませんが、前述の通りAcocunt ManagerからはMessaging APIで作成したリッチメニューを削除することができません。

この場合Messaging APIを利用してリッチメニューを削除する必要がありますが、意外と簡単に削除することができます。

流れとしては

  • チャンネルアクセストークンを用意する
  • リッチメニューIDを取得する
  • リッチメニューIDを指定して削除する

の3段階です。

チャンネルアクセストークンを用意する

Messaging APIを利用するにはチャンネルアクセストークンが必要です。

チャンネルアクセストークンは LINE Developers のコンソール の「チャンネル設定」から確認することができます。

チャンネルアクセストークンがない場合は、Account Managerの右端の「設定」→左メニューの「Messaging API」のページのチャンネルID(Channel ID)とチャンネルシークレット(Channel secret)を確認できます。

そして、短期のチャネルアクセストークンを発行するAPIからチャンネルアクセストークンを発行できます。

リッチメニューIDを取得する

リッチメニューの配列を取得するAPIでMessaging APIで作成したリッチメニューのIDを取得できます。

{
  "richmenus": [
    {
      "richMenuId": "{richMenuId}", // <- これがリッチメニューID
      "name": "Nice rich menu",
      "size": {
        "width": 2500,
        "height": 1686
      },
      "chatBarText": "Tap to open",
      "selected": false,
      "areas": [
        {
          "bounds": {
            "x": 0,
            "y": 0,
            "width": 2500,
            "height": 1686
          },
          "action": {
            "type": "postback",
            "data": "action=buy&itemid=123"
          }
        }
      ]
    }
  ]
}

リッチメニューを削除する

リッチメニューを削除するAPIに先程取得したリッチメニューIDを指定して削除します。

このAPIを叩くことで「Messaging APIで設定するユーザー単位のリッチメニュー」や「Messaging APIで設定するデフォルトのリッチメニュー」も削除することができるので、Account Managerで設定したリッチメニューをユーザーに表示させることができます。

まとめ

Messaging APIが使えると、LINE公式アカウントのチャンネルをより深くカスタマイズできるようになりますが、相応の専門知識が必要です。

もしMessaging APIでお困りごとがあれば、弊社お問い合わせフォームから気軽にご相談ください。

Google Publisher Tag のよくあるエラー

googletag.openConsole() で表示される警告文の意味と主な対処方法の自分メモ

用語

GPT: Google Publisher Tag
GAM: Google Ad Manager

ドキュメント

⚠️ 広告ユニットが取得されませんでした

何が起きているか

defineSlotは行われたが、displayやrefreshを行っておらず、広告リクエストが走っていない状態

対処方法

displayやrefreshを実行する。

googletag.display('div-id');
googletag.pubads().refresh([slot]);

disableInitialLoad() が利用されている場合は、displayでは広告リクエストが走らなくなるのでrefreshを使う。

⚠️ 広告ユニットが埋められませんでした

何が起きているか

配信条件に一致する広告申込情報が見つからずに広告を埋められなかった

対処方法

GAMで該当の広告ユニットと広告申込情報の配信設定やターゲティングを見直す

MacでThunderboltブリッジ接続のネットの上りが遅いのを解決する

tl;dr

下記コマンドでTCP Segmentation Offload を無効にする。

sudo sysctl -w net.inet.tcp.tso=0

説明

(以下すべてmacOS Catalinaで検証しています。)
Mac同士をThunderbolt3ケーブルなどを繋いでThunberboltブリッジ接続しネットワーク共有をした場合に、アップロードの速度が1MB/s以下と異常に遅いのですが、それを解決する方法を見つけました。

Thunderbolt Ethernet Bridge very slow - Apple Community に答えが載っていました。
確かに下記2種類のコマンドのどちらかで解決しました。

sudo sysctl -w net.inet.tcp.tso=0
sudo sysctl -w net.link.generic.system.hwcksum_tx=0

後者の場合はファイルの書き込み速度が50%ほど遅くなる副作用がありましたので、前者をオススメします。

これらは実際のところ何の設定を変えるコマンドなのでしょうか?

我々はその謎を探るべくアマゾンの奥地へと向かった…

net.inet.tcp.tso=0 はTCP Segmentation Offload を無効にしています。
一方 net.link.generic.system.hwcksum_tx=0 は TCP Checksum Offload を無効にしています。

ではなぜ、TCP Segmentation Offloadを無効にしたりTCP Checksum Offloadを無効にしないと異常にネットの上りが遅いのか?
TCP Checksum Offloadを無効にすると書き込み速度が落ちるのか?

答えはわかりませんでした。

でも自分が何をしたか全く分からないよりはマシなので「TCP Segmentation Offloadを無効にした」とだけ覚えておくことにします。

スマートにiPhoneXの対応のためのpaddingを設定する

iPhoneXには、画面上部にセンサーや前面カメラが仕込まれているディスプレイの切り欠きがあります。

safariではviewportを下記のように指定することで、横持ちした際にこの切り欠きの周りまでレンダリング領域とすることができます。

<meta name="viewport" content="viewport-fit=cover">

この指定をすると、ディスプレイ全体に背景色等を引き伸ばせるため、より一体感のあるデザインにすることができるのですが、切り欠き部分がコンテンツと重なってしまうことが起こり得るため、CSS定数を使ってpadding等を余分に持たせてあげることで、これを回避してあげる必要があります。

設定できるCSS定数は以下の通り

constant(safe-area-inset-top)
constant(safe-area-inset-right)
constant(safe-area-inset-bottom)
constant(safe-area-inset-left)

しかし、このCSS定数で使われる constant 関数ですが、他のブラウザでは一切実装されていない機能になります。
そのため、既にpaddingが設定されている要素にcalc等を使って切り欠き分をプラスしてあげる方法だと、構文解釈ができずにpaddingが未設定とされてしまいます。
とはいえわざわざiPhoneXのために新しい要素で囲ってあげるのもバカらしい…。

そんなときは下記の様に同じセレクタ内でpaddingを複数記述するとスマートに解決することができます。

.container {
	padding: 10px 20px;
	padding-right: calc(20px + constant(safe-area-inset-right)); // for iPhone X
	padding-left: calc(20px + constant(safe-area-inset-left)); // for iPhone X
}

safariでは padding-rightpadding-left を上書きするように後述しているので、切り欠き分も余分に余白が適用されますが、それ以外のブラウザでは constant を使っている行は解釈されないので1行目のみ適用されます。

要は記述順が重要で、ベンダープレフィックスのような用法で使ってあげればよいのです。

Vue.jsでSSRする場合にv-forでエラーが出る

Nuxt.jsを含めて、Vue.jsの2.xを使ってSSR(サーバーサイドレンダリング)をする場合に、 v-for の箇所で下記のようなエラーが出る場合がある。

The client-side rendered virtual DOM tree is not matching server-rendered content.

再現したコードはこれ( items は単純な文字列の配列)。
ちなみにVue.jsのバージョンは 2.2.6

<template v-for="item in items">
  {{ item }}
</template>

回避するには、下記のように個々を要素で囲ってあげる。

<template v-for="item in items">
  <span>{{ item }}</span>
</template>

クライアントサイドのみではエラーは出ず、ちゃんとレンダリングされるので、サーバ側の実行ロジックにバグがあるのかもしれない。