<?xml version='1.0'?>
    <feed xmlns='http://www.w3.org/2005/Atom'>
      <id>https://blog.comame.xyz/</id>
      <title>blog.comame.xyz</title>
      <link rel='alternate' href='https://blog.comame.xyz/' />
      <link rel='self' href='https://blog.comame.xyz/feed.xml' />
      <author><name>comame</name></author>
      <updated>2024-01-13T00:00:00Z</updated>
      
    <entry>
      <title>MetalLB が ARP 応答を返していなかった問題に対処した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2024-01-13/metallb-arp-not-work' />
      <id>https://blog.comame.xyz/entries/2024-01-13/metallb-arp-not-work</id>
      <updated>2024-01-13T00:00:00Z</updated>
      <summary>&lt;p&gt;MetalLB が ARP 応答を返してくれなくなったことにより、自宅サーバで動かしているすべてのサービスに一切接続できない状況になっていた。&lt;/p&gt;
&lt;p&gt;ロードバランサによって IP アドレスは割り振られるが、通信はできないという状況になるため、サーバへの通信が一切通らなくなってしまっていた。&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;直接的な原因は L2Advertisements を設定していなかったこと。
背景としては、MetalLB の設定方法を CRD を使うように変えたときに、きちんとドキュメントを見なかったこと。&lt;/p&gt;
&lt;h2&gt;作業ログ&lt;/h2&gt;
&lt;p&gt;L2 (ARP) で経路を設定している。
Service に IP アドレスは割り振られているので、speaker が仕事をしていないのだろうと推測。普段使いしている Windows マシンから &lt;code&gt;Get-NetNeighbor&lt;/code&gt; したところ、案の定経路がわからない状態になっていた。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://metallb.universe.tf/troubleshooting/#general-concepts-1 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://metallb.universe.tf/troubleshooting/#general-concepts-1&lt;/a&gt; に従って &lt;code&gt;kubectl describe svc &amp;lt;svc&amp;gt;&lt;/code&gt; をしてみると、イベントがない状態だった。
speaker が公告しない条件は以下であった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service に有効な Endpoint がない&lt;/li&gt;
&lt;li&gt;Service が &lt;code&gt;externalTrafficPolicy=local&lt;/code&gt; であり、speaker の Node で Endpoint がない&lt;/li&gt;
&lt;li&gt;L2Advertisements が speakers の Node にない&lt;/li&gt;
&lt;li&gt;Kubernetes API が &amp;#39;network not available&amp;#39; と言っている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上から順に確かめると、IPAddressPool は存在するが、L2Advertisements を設定していないことに気が付いた。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://metallb.universe.tf/configuration/_advanced_l2_configuration/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://metallb.universe.tf/configuration/_advanced_l2_configuration/&lt;/a&gt; を見ながら L2Advertisements を設定したところ、治った。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://github.com/comame/kubernetes-manifests/commit/1a8a8d503fe918d378b8939028538a30af5155a6#diff-835cacfccf39c08c0a6a286a16abc4b64583a196d5b84c7ce96a6b742d5d6120 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;設定ファイルの変更履歴&lt;/a&gt; を見ると、ConfigMap から CRD に変更するときに、L2Advertisements を作成せず、IPAddressPool だけ作成していることが分かった。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>2023年を振り返る</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-12-30/review-2023' />
      <id>https://blog.comame.xyz/entries/2023-12-30/review-2023</id>
      <updated>2023-12-30T00:00:00Z</updated>
      <summary>&lt;p&gt;今年なにをやったっけという振り返り。&lt;/p&gt;
&lt;h2&gt;開発&lt;/h2&gt;
&lt;h3&gt;Go 言語&lt;/h3&gt;
&lt;p&gt;今年に入ってから初めて使い始めて、手になじんだ道具として扱えるようになってきたような気がしている。GitHub のレポジトリを見ると、大体 20 レポジトリ程度を Go 言語で書いたことになるらしい。&lt;/p&gt;
&lt;h3&gt;自宅サーバー&lt;/h3&gt;
&lt;p&gt;自宅でシングルノードの Kubernetes を運用し始めて 2 年が経過した。&lt;a href=https://github.com/oauth2-proxy/oauth2-proxy target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;oauth2-proxy&lt;/a&gt; のようなものを導入したことによって、ちゃんとユーザーを識別してアクセス制御ができるようになり、ネットワーク境界によるアクセス制御をしている部分を大幅に減らすことに成功した。&lt;/p&gt;
&lt;h2&gt;DJ&lt;/h2&gt;
&lt;p&gt;Music Unity 2023 に行ったことをきっかけに、月に 1 回程度のペースで身内で DJ を練習するようになった。&lt;/p&gt;
&lt;p&gt;本当に幸運なことに、39noPops 5th Anniversary のオープニング DJ をやる機会に恵まれた。本当に楽しく DJ をさせてもらうことができ、今後もイベントなどで DJ をやる機会があるといいなという思いがある。&lt;/p&gt;
&lt;h2&gt;お絵かき&lt;/h2&gt;
&lt;p&gt;少しだけ練習して、しばらく放置している。&lt;/p&gt;
&lt;h2&gt;あそび&lt;/h2&gt;
&lt;h3&gt;スプラトゥーン3&lt;/h3&gt;
&lt;p&gt;大量にやる月とまったくやらない月があった。その結果、結局ウデマエは最高 S のままとなった。スシコラを握るようになり、対面力は多少鍛えられたと思う。&lt;/p&gt;
&lt;h3&gt;TRPG&lt;/h3&gt;
&lt;p&gt;今年はエモクロアで 8 セッション、CoC で 11 セッション参加した。自分が入り浸っている Discord サーバーで頻繁にセッションが行われており、片端から手を挙げていた結果かなりの回数になっていた。自分のプレイスタイルが確立されてきたような気がする。&lt;/p&gt;
&lt;h2&gt;2024 年はどうする？&lt;/h2&gt;
&lt;h3&gt;お仕事&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;引き続きがんばるぞい！&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;DJ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;多少背伸びが必要な目標として、3 つのイベントにのれるように頑張っていきたい。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;お絵かき&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上手くなりたいので、継続して練習できるようにしていきたい。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;自宅サーバー&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SoftEther で VPN を運用しているが、これを捨てられるようにしたい。開発サーバーへの接続は SSH が必要なため、うまいやり方を考える必要がありそう。&lt;/li&gt;
&lt;li&gt;HDD の速度面での辛さを実感することが増えてきたので、SSD を導入したい。&lt;/li&gt;
&lt;li&gt;現時点では PPPoE (IPv4) 前提で構築しているが、将来的にネットワーク構成が大きく変わる可能性がある。そうなったときにもインターネットに引き続き公開し続けられるような方法を模索したい。&lt;/li&gt;
&lt;li&gt;デプロイされているアプリケーションの数が増えてきた。ノード数を増やせるように、既存のアプリケーションの設定を見返す必要がある。&lt;/li&gt;
&lt;li&gt;ログ収集に問題があるので、ログを適切に収集・利用できる方法を模索したい。&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>39noPops 5th Anniversary</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-12-25/39nopops-5th-anniversary' />
      <id>https://blog.comame.xyz/entries/2023-12-25/39nopops-5th-anniversary</id>
      <updated>2023-12-25T00:00:00Z</updated>
      <summary>&lt;h2&gt;イベント&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://web.archive.org/web/20231224155813/https://twvt.me/39nopops_5th_anniv target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;39noPops 5th Anniversary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;12/24&lt;/li&gt;
&lt;li&gt;nagomix渋谷&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;セットリスト&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/img/2023-39pop/39nopops-5th-anniversary.png&quot; alt=&quot;セットリスト&quot;&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ロジクール K380S を買った</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-11-16/logicool-k380s' />
      <id>https://blog.comame.xyz/entries/2023-11-16/logicool-k380s</id>
      <updated>2023-11-16T00:00:00Z</updated>
      <summary>&lt;p&gt;&lt;a href=https://www.logicool.co.jp/ja-jp/products/keyboards/pebble-keys-2.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;ロジクール K380S&lt;/a&gt; を買ってしまった。普段は US 配列キーボードを使用しているが、見た目がよくて衝動買い。&lt;/p&gt;
&lt;h2&gt;感想&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ファンクションキーに謎の機能が割り当てられているが、&lt;code&gt;Fn + Esc&lt;/code&gt; で Fn ロックできた&lt;ul&gt;
&lt;li&gt;VSCode でファンクションキーを頻繁に使うので、Fn ロックできないと使い物にならないところだった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;キートップを見るとなぜか US 配列も印字されている&lt;ul&gt;
&lt;li&gt;ソフトウェアで US 配列として認識している用途を想定しているのか...&lt;/li&gt;
&lt;li&gt;そんなことあるんだ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ロジクールのキーボードは初めて購入したが、Bluetooth の接続が爆速で驚いた&lt;ul&gt;
&lt;li&gt;接続時のストレスが少なくてよさそう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;乾電池で動作する&lt;ul&gt;
&lt;li&gt;処分だったり充電だったりのことを考えると、個人的には内臓バッテリーより乾電池のほうが好き&lt;/li&gt;
&lt;li&gt;普段であれば乾電池はどこでも買えるので、急に切れてもまあ困らない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ところで自宅にはすでにキーボードがあるので、使いどころはあんまりないかもしれない...&lt;ul&gt;
&lt;li&gt;机にキーボードが2台鎮座することに&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>日本語配列のキーボードを英語配列設定で使っている</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-10-25/keyboard-jp-en' />
      <id>https://blog.comame.xyz/entries/2023-10-25/keyboard-jp-en</id>
      <updated>2023-10-25T00:00:00Z</updated>
      <summary>&lt;p&gt;自分は普段英語配列キーボードを使用しているが、英語配列キーボードを選べる Windows ノートパソコンの選択肢が少ないので、ハードウェアは日本語配列を選び、OS の設定で英語配列として認識させている。&lt;/p&gt;
&lt;h2&gt;運用・感想&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ctrl + Space で IME 切り替えをできるように設定している&lt;ul&gt;
&lt;li&gt;&lt;code&gt;半角/全角&lt;/code&gt; が &lt;code&gt;`&lt;/code&gt; で置き換えられてしまうため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\\&lt;/code&gt; の物理的な位置が英語配列と異なるため、打ち間違えることがある&lt;ul&gt;
&lt;li&gt;Enter キーが縦長なので仕方ない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enter キーが遠い、Backspace が小さい&lt;ul&gt;
&lt;li&gt;割と嫌だが、ノートパソコンのキーボードを使う頻度が比較的低いので、諦める&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;右 Shift が遠い&lt;ul&gt;
&lt;li&gt;それなりに多用するキーだが、あまり気にならなかった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多少の妥協をすることによって、ノートパソコンと一緒にキーボードを持ち運ぶ手間を捨てられた。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Kubernetes のログを DaemonSet で収集する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-10-16/kubernetes-log-collect' />
      <id>https://blog.comame.xyz/entries/2023-10-16/kubernetes-log-collect</id>
      <updated>2023-10-16T00:00:00Z</updated>
      <summary>&lt;p&gt;Kubernetes で動かしているアプリケーションのログを収集するものを DaemonSet で動かすようにした。今までは温かみのある手作業でノードにセットアップしていたので、大幅に改善された。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;kubeadm でセットアップしたクラスタ&lt;/li&gt;
&lt;li&gt;コンテナランタイムは Containerd&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ログを取得する&lt;/h2&gt;
&lt;p&gt;ログは &lt;code&gt;/var/log/containers/xxx.log&lt;/code&gt; に吐き出されるので、&lt;code&gt;tail&lt;/code&gt; などで内容を取ればよい。&lt;/p&gt;
&lt;h2&gt;DaemonSet で配置する&lt;/h2&gt;
&lt;p&gt;各ノードで 1 つずつ実行されてほしいので、DaemonSet で配置する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;apiVersion: apps/v1&lt;/code&gt;
&lt;code&gt;kind: DaemonSet&lt;/code&gt;
&lt;code&gt;metadata:&lt;/code&gt;
&lt;code&gt;  name: log&lt;/code&gt;
&lt;code&gt;spec:&lt;/code&gt;
&lt;code&gt;  selector:&lt;/code&gt;
&lt;code&gt;    matchLables:&lt;/code&gt;
&lt;code&gt;      name: log&lt;/code&gt;
&lt;code&gt;  template:&lt;/code&gt;
&lt;code&gt;    metadata:&lt;/code&gt;
&lt;code&gt;      labels:&lt;/code&gt;
&lt;code&gt;        name: log&lt;/code&gt;
&lt;code&gt;    spec:&lt;/code&gt;
&lt;code&gt;    containers:&lt;/code&gt;
&lt;code&gt;    - name: log&lt;/code&gt;
&lt;code&gt;      image: xxx&lt;/code&gt;
&lt;code&gt;      securityContext:&lt;/code&gt;
&lt;code&gt;        runAsUser: 0 # /var/log/containers へのアクセスが必要なため&lt;/code&gt;
&lt;code&gt;      volumeMounts:&lt;/code&gt;
&lt;code&gt;      - mountPath: /var/log/containers&lt;/code&gt;
&lt;code&gt;        name: containers&lt;/code&gt;
&lt;code&gt;      - mountPath: /var/log/pods&lt;/code&gt;
&lt;code&gt;        name: pods&lt;/code&gt;
&lt;code&gt;    volumes:&lt;/code&gt;
&lt;code&gt;    - name: containers&lt;/code&gt;
&lt;code&gt;      hostPath:&lt;/code&gt;
&lt;code&gt;        path: /var/log/containers&lt;/code&gt;
&lt;code&gt;        type: Directory&lt;/code&gt;
&lt;code&gt;    - name: pods&lt;/code&gt;
&lt;code&gt;      hostPath:&lt;/code&gt;
&lt;code&gt;        path: /var/log/pods&lt;/code&gt;
&lt;code&gt;        type: Directory&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>CRAZYBUS の BGM を生成する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-03-30/crazybus-bgm' />
      <id>https://blog.comame.xyz/entries/2023-03-30/crazybus-bgm</id>
      <updated>2023-03-30T00:00:00Z</updated>
      <summary>&lt;p&gt;ベネズエラの有名なゲーム &lt;a href=https://dic.pixiv.net/a/CRAZYBUS target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;CRAZYBUS&lt;/a&gt; の BGM を流す Web アプリを作った。Web Audio API を使っている。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://comame.xyz/crazybuz target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://comame.xyz/crazybuz&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;やり方&lt;/h2&gt;
&lt;p&gt;ソースコードは &lt;a href=https://github.com/comame/CRAZYBUZ/blob/main/app.js target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;GitHub&lt;/a&gt; にある。&lt;/p&gt;
&lt;h3&gt;十二平均律の周波数を求める&lt;/h3&gt;
&lt;p&gt;十二平均律では、半音の周波数比がすべて同じであり、1 オクターブで周波数が 2 倍になることから、半音の周波数比は&lt;/p&gt;
&lt;p&gt;\begin{eqnarray}
    r = 2^{\frac{1}{12}}
\end{eqnarray}&lt;/p&gt;
&lt;p&gt;となる。音程の基準を \( 440 \ \mathrm{Hz} \) として、上下 2 オクターブずつ周波数を算出する。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;OscillatorNode&lt;/code&gt; で矩形波を生成する&lt;/h3&gt;
&lt;pre&gt;
&lt;code&gt;const context = new AudioContext()&lt;/code&gt;
&lt;code&gt;const oscillator = context.createOscillator()&lt;/code&gt;
&lt;code&gt;oscillator.type = &apos;square&apos;&lt;/code&gt;
&lt;code&gt;oscillator.frequency.value = 440&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;のようにして矩形波を作成できる。&lt;/p&gt;
&lt;h3&gt;CRAZYBUS の BGM を作る&lt;/h3&gt;
&lt;p&gt;CRAZYBUS の BGM は、音程をランダムに 2 つ選び、BPM 360 で音程を変えながら混ぜればよい。&lt;/p&gt;
&lt;h2&gt;ちなみに&lt;/h2&gt;
&lt;p&gt;バスの走行音は A2、クラクションの音は G5 らしい。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Music Unity 2023</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-03-27/mu2023' />
      <id>https://blog.comame.xyz/entries/2023-03-27/mu2023</id>
      <updated>2023-03-27T00:00:00Z</updated>
      <summary>&lt;ul&gt;
&lt;li&gt;&lt;a href=https://mu2023.jp/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://mu2023.jp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://web.archive.org/web/20230326155521/https://mu2023.jp/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://web.archive.org/web/20230326155521/https://mu2023.jp/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Day 1 は羽田空港で、Day 2 は Twitch で見た。最高の 2 日間だったし、音楽って本当に楽しい。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ローカルで動かしている開発環境のポートをインターネットからアクセス可能にする</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-01-23/dev-port' />
      <id>https://blog.comame.xyz/entries/2023-01-23/dev-port</id>
      <updated>2023-01-23T00:00:00Z</updated>
      <summary>&lt;p&gt;開発マシンの 8080 番ポートに、インターネットから &lt;a href=https://dev-8080.comame.xyz target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://dev-8080.comame.xyz&lt;/a&gt; のような形でアクセスできるようにした。&lt;/p&gt;
&lt;h2&gt;やり方&lt;/h2&gt;
&lt;h3&gt;サーバの sshd の設定をいじる&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://linux.die.net/man/5/sshd_config#:~:text=GatewayPorts target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;GatewayPorts&lt;/a&gt; の設定をいじっておく。これによって、サーバの 0.0.0.0 で転送されたポートを待ち受けることができるようになる。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;GatewayPorts=clientspecified&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;ロードバランサーを設定する&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://github.com/comame/kubernetes-manifests/commit/8a9d189ed4a6f3158798531db015389cc0dc548c target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/comame/kubernetes-manifests/commit/8a9d189ed4a6f3158798531db015389cc0dc548c&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;サーバに対して SSH ポートフォワードをする&lt;/h3&gt;
&lt;p&gt;開発マシンからサーバに対して SSH 接続する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ ssh -R 0.0.0.0:8080:localhost:8080 &amp;lt;server&amp;gt;&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;感想とか&lt;/h2&gt;
&lt;p&gt;開発中のものにインターネットからアクセスする必要があったときにデプロイしなくて済むようになったので、開発体験がよくなった。ロードバランサが動いているマシンに対して SSH 接続をしないといけないので、スケールはしなさそう？&lt;/p&gt;
&lt;p&gt;アクセス制御はいずれやりたい&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Chrome のパスワード同期を直す</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2023-01-06/fix-chrome-password-sync-fail' />
      <id>https://blog.comame.xyz/entries/2023-01-06/fix-chrome-password-sync-fail</id>
      <updated>2023-01-06T00:00:00Z</updated>
      <summary>&lt;p&gt;Chrome のパスワード同期がされなくなる問題を解決した。環境は Windows 版 Chrome 108.0.5359.125 (Stable)。&lt;/p&gt;
&lt;h2&gt;手順を試す前に&lt;/h2&gt;
&lt;p&gt;手元の環境で解決した手順であり、すべての場合に適用できない可能性があることに気を付ける。&lt;/p&gt;
&lt;p&gt;ブラウザのデータをバックアップする。ブックマークのエクスポートは「ブックマーク マネージャ」から、パスワードのバックアップは &lt;a href=chrome://settings/passwords&gt;chrome://settings/passwords&lt;/a&gt; と &lt;a href=https://passwords.google.com target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://passwords.google.com&lt;/a&gt; の両方からエクスポートしておく。&lt;/p&gt;
&lt;h2&gt;発生していた問題&lt;/h2&gt;
&lt;p&gt;Chrome でパスワードが同期されず、パスワードマネージャが使えない状態になってしまった。&lt;a href=chrome://sync-internals&gt;chrome://sync-internals&lt;/a&gt; を見ると、&lt;code&gt;components/sync/driver/data_type_manager_impl.cc&lt;/code&gt; で &lt;code&gt;datatype error was encountered: Preexisting controller error on Sync startup&lt;/code&gt; のようなエラーが表示されており、パスワードの同期に失敗していた。&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;p&gt;同期時のフォーマットエラーなので、エラーになるパスワードを Google のサーバとローカルから削除すれば直るのでは、という発想。&lt;/p&gt;
&lt;h3&gt;インポートエラーになるパスワードを削除する&lt;/h3&gt;
&lt;p&gt;エクスポートしたパスワードの CSV を &lt;a href=chrome://settings/passwords&gt;chrome://settings/passwords&lt;/a&gt; でインポートし、エラーになる CSV の行を削除する。&lt;/p&gt;
&lt;h3&gt;同期データの削除&lt;/h3&gt;
&lt;p&gt;Google のサーバに保存されている同期データを削除する。&lt;a href=https://chrome.google.com/sync target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://chrome.google.com/sync&lt;/a&gt; から削除する。&lt;/p&gt;
&lt;p&gt;Chrome を使用しているすべての端末で、保存されたパスワードを削除する。手順は &lt;a href=https://support.google.com/chrome/answer/2392709?hl=ja target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;ヘルプページ&lt;/a&gt; に従う。&lt;/p&gt;
&lt;h3&gt;Windows を再起動する&lt;/h3&gt;
&lt;p&gt;Windows を再起動する。&lt;/p&gt;
&lt;h3&gt;パスワードをインポートして、同期を再開する&lt;/h3&gt;
&lt;p&gt;&lt;a href=chrome://settings/passwords&gt;chrome://settings/passwords&lt;/a&gt; でパスワードをインポートし、Chrome の同期を再開する。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>VRoid でアバターになろう！</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-12-02/become-avatar' />
      <id>https://blog.comame.xyz/entries/2022-12-02/become-avatar</id>
      <updated>2022-12-02T00:00:00Z</updated>
      <summary>&lt;h2&gt;どういうこと&lt;/h2&gt;
&lt;p&gt;VRoid Studio でアバターを作って、kalidoface 3D で動かそう！&lt;/p&gt;
&lt;p&gt;&lt;video src=&apos;/img/2022-become-avatar/preview.webm&apos; controls autoplay&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2&gt;必要なもの&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ブラウザ (少なくとも Chrome では動作確認済み)&lt;/li&gt;
&lt;li&gt;OBS&lt;/li&gt;
&lt;li&gt;VRoid Studio (アバターを自作するなら)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Kalidoface 3D を試そう&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://3d.kalidoface.com/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Kalidoface 3D&lt;/a&gt; とは、VRM 形式のアバターをブラウザで動かすことのできる Web ツールです。PC のカメラを使ったフェイストラッキング、フルボディトラッキングに対応しています。フェイストラッキングはかなり精度が高く、カメラに向かって正面を向いていれば、正確に瞬きや口の動きを捉えてくれます。サンプルアバターも用意されており、手軽に試すことができます。&lt;/p&gt;
&lt;p&gt;まず、ブラウザで &lt;a href=https://3d.kalidoface.com/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://3d.kalidoface.com/&lt;/a&gt; にアクセスすると、何もせずともサンプルアバターが表示されます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/2022-become-avatar/kalidoface-explain.jpg&quot; alt=&quot;Kalidoface 3D の説明&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;アバターの切り替え (サンプルアバターも選べる)&lt;/li&gt;
&lt;li&gt;設定ボタン (押すと画像左側の設定項目が表示される)&lt;/li&gt;
&lt;li&gt;左からフェイストラッキング、フルボディトラッキングの切り替え&lt;/li&gt;
&lt;li&gt;カメラの選択&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これでアバターを動かすための準備は完了です！&lt;/p&gt;
&lt;p&gt;次に、実際にアバターを動かし、さらに OBS にアバターを移すための準備をしていきましょう。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/2022-become-avatar/kalidoface-explain.2.jpg&quot; alt=&quot;Kalidoface 3D の説明&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;トラッキング開始ボタン&lt;/li&gt;
&lt;li&gt;背景の切り替えボタン (押すと画像左側のメニューが表示される)&lt;/li&gt;
&lt;li&gt;背景をグリーンバックにする&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上で Kalidoface 3D の準備は終わりです。次に、アバターをビデオ会議アプリに映すために OBS の設定をしていきましょう。&lt;/p&gt;
&lt;h2&gt;OBS の設定&lt;/h2&gt;
&lt;p&gt;アバターを表示させるためのソースとして、ウィンドウキャプチャでブラウザのウィンドウを取り込みます。このとき、「ウィンドウの一致優先順」は「ウィンドウのタイトルに一致する必要があります」を選択することをお勧めします。うっかり別のウィンドウが映りこんでしまう事故を防ぐためです。&lt;/p&gt;
&lt;p&gt;次に、グリーンバックを切り抜きます。追加したウィンドウキャプチャに対して、「クロマキー」フィルタを追加します。今回はグリーンバックなので、「色キーの種類」は「緑」です。アバターの色合いに合わせて背景色を変える必要があるかもしれません。&lt;/p&gt;
&lt;p&gt;あとは「仮想カメラ開始」を押し、好きなビデオ会議アプリで入力カメラを「OBS Virtual Camera」にするだけです。&lt;/p&gt;
&lt;h2&gt;VRoid Studio&lt;/h2&gt;
&lt;p&gt;VRoid Studio とは、3D モデリングの知識がなくとも、自分のアバターを作成することができるソフトです。&lt;a href=https://vroid.com/studio target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;VRoid Studio の公式サイト&lt;/a&gt; からインストールできます。&lt;/p&gt;
&lt;p&gt;VRoid Studio からは VRM 形式でアバターをエクスポートでき、Kalidoface 3D を含む様々なツールでアバターを使うことができます。自分で作ることが難しくても、&lt;a href=https://booth.pm/ja target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;BOOTH&lt;/a&gt; などで購入した服に着せ替えることができます。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;通話するときにカメラをつけるのが恥ずかしくても、アバターなら解決できます。自分のアバターを通じて、新たなコミュニケーションのきっかけになるかもしれません。&lt;/p&gt;
&lt;p&gt;サンプルアバターを動かしてみるだけでも、あるいはアセットを着せ替えるだけでも、それは立派な表現の一つです。ぜひ皆さんも試してみてください。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/2022-become-avatar/smile.png&quot; alt=&quot;アバターで笑顔を作るのは難しい&quot;&gt;&lt;/p&gt;
&lt;h2&gt;トラブルシューティング&lt;/h2&gt;
&lt;h3&gt;Kalidoface 3D でカメラが選択できない&lt;/h3&gt;
&lt;p&gt;接続しているカメラが選択できないときは、ブラウザの設定でカメラへのアクセス権を許可してください。&lt;/p&gt;
&lt;h3&gt;Kalidoface 3D のアバターが動かなくなる&lt;/h3&gt;
&lt;p&gt;Kalidoface 3D が動いているブラウザのタブが非アクティブになると、自動的に停止します。Kalidoface 3D のタブが常に見えるようにしてください。&lt;/p&gt;
&lt;p&gt;また、長い時間動かしたままにしておくと、なぜかアバターが動かなくなることがあります。諦めてタブを再読み込みしましょう。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Mastodon インスタンスを立てた</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-11-21/mastodon' />
      <id>https://blog.comame.xyz/entries/2022-11-21/mastodon</id>
      <updated>2022-11-21T00:00:00Z</updated>
      <summary>&lt;h2&gt;モチベーション&lt;/h2&gt;
&lt;p&gt;Twitter のスレッドや Zenn のスクラップのような書き心地で、適当に書き殴れるメモ帳が欲しかった。そういう気持ちがあったところに、Twitter 騒動で Mastodon の話題を耳に挟んだので、じゃあやってみるかの気持ちでインスタンスを立ててみた。&lt;/p&gt;
&lt;h2&gt;やったこと&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Mastodon のインスタンスを Kubernetes の上にデプロイした&lt;/li&gt;
&lt;li&gt;OpenID Connect でユーザー認証ができるようにした&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mastodon の構築&lt;/h3&gt;
&lt;p&gt;Mastodon の GitHub レポジトリにおいてある、&lt;a href=https://github.com/mastodon/mastodon/blob/main/docker-compose.yml target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;docker-compose.yml&lt;/a&gt; が参考になった。docker-compose.yml をもとに Deployment を作り、その後はコンテナ内で &lt;code&gt;RAILS_ENV=production bundle exec rake mastodon:setup&lt;/code&gt; を叩くだけだった (&lt;a href=https://docs.joinmastodon.org/admin/install/#generating-a-configuration target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;セットアップのドキュメント&lt;/a&gt;)。&lt;/p&gt;
&lt;p&gt;SMTP サーバの設定は、localhost にしても特に問題は起きなかった。当然メール送信には失敗するが、失敗したメールのログは Sidekiq で閲覧できるので、個人用途ならあまり問題になることはなさそうだと思っている。&lt;/p&gt;
&lt;p&gt;Admin ユーザーの作成を行うかも問われるが、このバージョンでは Redis への接続に失敗するので、あとから自分で作成する必要があった。ユーザーを作成するには、コンテナ内で &lt;code&gt;bundle exec tootctl accounts create ...&lt;/code&gt; のようにすればよい。&lt;code&gt;role=Owner&lt;/code&gt; を指定する必要があったが、&lt;code&gt;Owner&lt;/code&gt; の値はまだドキュメント化されていない様子。&lt;/p&gt;
&lt;h3&gt;OpenID Connect のセットアップ&lt;/h3&gt;
&lt;p&gt;このバージョンではまだドキュメントが揃っていなかったため、該当する &lt;a href=https://github.com/mastodon/mastodon/issues/7958#issuecomment-1308525637 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;GitHub の Issue&lt;/a&gt; を頼りに設定した。メールアドレスの情報がないと認証が通らないため、 &lt;code&gt;scope&lt;/code&gt; の値に &lt;code&gt;openid,email,profile&lt;/code&gt; を指定しないと動かないようである。メールアドレスが OP から渡されなかったときでもエラーにせず、Mastodon 側で改めてメールアドレスの設定ができると良いのになあと思いながら設定していた。&lt;/p&gt;
&lt;h2&gt;得られたこと&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;例えばブログの草稿を雑に書きなぐることができるようになった&lt;ul&gt;
&lt;li&gt;&lt;a href=https://mastodon.comame.xyz/@comame/109377430498349331 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;スレッド&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;使い方によっては、その時考えていたことを時系列に従って思い返すことができそう&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>Kubernete Dashboard の認証を通す</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-11-03/authenticate-kubernetes-dashboard' />
      <id>https://blog.comame.xyz/entries/2022-11-03/authenticate-kubernetes-dashboard</id>
      <updated>2022-11-03T00:00:00Z</updated>
      <summary>&lt;p&gt;Kubernete Dashboard に OpenID Connect でログインしたかったというメモ。&lt;/p&gt;
&lt;p&gt;まずは、&lt;a href=https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Kubernetes Dashboard のドキュメント&lt;/a&gt; に従って、Service Account を作っておく。&lt;/p&gt;
&lt;p&gt;要するに、Kubernetes の認証を通すため、特定の Service Account に対する ID Token を発行できれば良い。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://kubernetes.io/ja/docs/reference/access-authn-authz/authentication/#openid-connect%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://kubernetes.io/ja/docs/reference/access-authn-authz/authentication/#openid-connect%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3&lt;/a&gt; に従って、kube-apiserver を Relying Party として設定する。&lt;/p&gt;
&lt;p&gt;ID Token に含まれるユーザー ID のクレームに、そのまま Service Account の名前を指定すると、認証できない。そこで、ユーザー名を &lt;code&gt;system:serviceaccount:&amp;lt;namespace&amp;gt;:&amp;lt;name&amp;gt;&lt;/code&gt; のようにしてやると、サービスアカウントとして認証してくれる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kubectl create token &amp;lt;name&amp;gt;&lt;/code&gt; で取得できる JWT を &lt;a href=https://jwt.io target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://jwt.io&lt;/a&gt; などに放り込むと、そうなっていることを読み取れる。&lt;/p&gt;
&lt;h2&gt;ところで&lt;/h2&gt;
&lt;p&gt;ID Token を発行できただけなので、そのトークンをどうやって Kubernetes Dashboard に渡すかというのは別問題。例えばプロキシサーバみたいなものを立てる必要があるかもしれない。&lt;/p&gt;
&lt;p&gt;どうせ自分ひとりしか使わないなら、&lt;code&gt;type: kubernetes.io/service-account-token&lt;/code&gt; の Secret を作っておいて、そのトークンをブラウザに覚えさせておけばよいのでは？&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>PowerShellスクリプトを特権で起動する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-06-26/run-powershell-script-as-admin' />
      <id>https://blog.comame.xyz/entries/2022-06-26/run-powershell-script-as-admin</id>
      <updated>2022-06-26T00:00:00Z</updated>
      <summary>&lt;p&gt;PowerShell スクリプト (.ps1) をコマンドプロンプト (あるいはバッチファイル) から管理者権限で実行したかった。&lt;/p&gt;
&lt;p&gt;次のようなスクリプトを置いておく。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;# sudo.ps1&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;start-process powershell -ArgumentList @(&quot;-ExecutionPolicy&quot;, &quot;Bypass&quot;, &quot;-File&quot;, $Args[0]) -verb runas&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;呼び出すときは次のようにする。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;powershell -ExecutionPolicy Bypass -File path\to\sudo.ps1 path\to\script.ps1&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>SoftEther で自宅ネットワークを公開する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-06-18/publish-local-network-softether' />
      <id>https://blog.comame.xyz/entries/2022-06-18/publish-local-network-softether</id>
      <updated>2022-06-18T00:00:00Z</updated>
      <summary>&lt;h2&gt;やりたかったこと&lt;/h2&gt;
&lt;p&gt;自宅のネットワークに対して外部から直接アクセスするのが難しい (ポート開放できないとか) ときに、VPS 等を経由すればよいのではないかというアイデア。SoftEther のカスケード接続を使ってみる。&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;VPS&lt;/h3&gt;
&lt;p&gt;チャージ残高が余っていたので、ConoHa で Ubuntu 22.04 のサーバを作成した。&lt;/p&gt;
&lt;p&gt;SoftEther VPN Server をインストールする。ローカルブリッジの設定で TAP デバイスを作成する。作成した TAP デバイスに対して、自宅ネットワークと同じセグメントの重複しない IP アドレスを割り当てる。&lt;/p&gt;
&lt;h3&gt;自宅&lt;/h3&gt;
&lt;p&gt;SoftEther VPN Bridge をインストールする。VPS に構築した仮想ハブに対してカスケード接続する。必要なインターフェースにローカルブリッジを設定する。&lt;/p&gt;
&lt;h3&gt;動作確認&lt;/h3&gt;
&lt;p&gt;VPS から、自宅ネットワーク内にあるホストに対して ping を投げてみる。あとは適当に NAT を設定すればよい。&lt;/p&gt;
&lt;h2&gt;考えていること&lt;/h2&gt;
&lt;p&gt;今回は SoftEther を使用したが、これは要するに拠点間接続なので、他の VPN を使っても同様のことができるのではないだろうかと思っている。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Kubernetes のコンテナランタイムを containerd にする</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-06-03/migrate-kubernetes-runtime-to-containerd' />
      <id>https://blog.comame.xyz/entries/2022-06-03/migrate-kubernetes-runtime-to-containerd</id>
      <updated>2022-06-03T00:00:00Z</updated>
      <summary>&lt;p&gt;Kubernetes 1.24 で Dockershim が廃止となったので、コンテナランタイムを containerd に変更した。&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;p&gt;手順としては公式ドキュメントに従えば問題ないが、おそらく自分の環境依存で、いくつか気になるところがあった。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://kubernetes.io/docs/tasks/administer-cluster/migrating-from-dockershim/change-runtime-containerd/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Changing the Container Runtime on a Node from Docker Engine to containerd&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;kubectl drain &amp;lt;node&amp;gt;&lt;/code&gt; が正しく完了しなかった&lt;/h3&gt;
&lt;p&gt;kubernetes-dashboard の Pod を削除できないことが原因で &lt;code&gt;kubectl drain&lt;/code&gt; に失敗しており、手動で kubernetes-dashboard の Deployment を削除したところ問題なく作業を進めることができるようになった。&lt;/p&gt;
&lt;h3&gt;containerd の cgruop ドライバー&lt;/h3&gt;
&lt;p&gt;手元の環境では &lt;code&gt;containerd config default&lt;/code&gt; で出力されるものをそのまま使用すれば問題なかった。&lt;code&gt;[plugins.&amp;quot;io.containerd.grpc.v1.cri&amp;quot;.containerd.runtimes.runc.options]&lt;/code&gt; に &lt;code&gt;SystemdCgroup = true&lt;/code&gt; を追記せずとも動作した。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;kubeadm upgrade apply v1.24.1&lt;/code&gt; で警告が出た&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;kubeadm upgrade apply v1.24.1&lt;/code&gt; を実行したところ、&lt;code&gt;initconfiguration.go:120] Usage of CRI endpoints without URL scheme is deprecated and can cause kubelet errors in the future&lt;/code&gt; のエラーが出力された。&lt;code&gt;/var/lib/kubelet/kubeadm-flags.env&lt;/code&gt; には &lt;code&gt;--container-runtime-endpoint=unix:///run/containerd/containerd.sock&amp;quot;&lt;/code&gt; のように URL スキームを記述しており、心当たりがないので気にせずアップグレードした。次回のパッチアップグレードでこの警告が消えるかどうか確認したい。&lt;/p&gt;
&lt;h2&gt;kube-apiserver に接続できない&lt;/h2&gt;
&lt;p&gt;この問題については原因が不明である。6443 番ポートが Connection refused となり、クラスタが全く機能しなくなってしまう問題が発生していた。iptables のルールを見たところ、すべてのパケットを DROP する設定になってしまっていた (おそらく Kubernetes の初期状態) ことが理由であると考えている。kube-proxy の ConfigMap を書き換え、 mode を ipvs に設定してみたところ、動作するようになった。正しい対処法かどうかは分からず。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ワコムのペンタブレットドライバが応答しないときの対処法</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-05-29/wacom-driver-no-response' />
      <id>https://blog.comame.xyz/entries/2022-05-29/wacom-driver-no-response</id>
      <updated>2022-05-29T00:00:00Z</updated>
      <summary>&lt;p&gt;発生条件が不明だが、たまに Wacom のペンタブが正しく動作しない (ドライバの設定が反映されていない) ことがある。&lt;/p&gt;
&lt;h2&gt;手元の環境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;One by Wacom small&lt;/li&gt;
&lt;li&gt;Windows 11 Pro (21H2)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;対処法&lt;/h2&gt;
&lt;h3&gt;WTabletServicePro サービスの再起動&lt;/h3&gt;
&lt;p&gt;タスクマネージャの「サービス」タブを開き、「WTabletServicePro (Wacom Professional Service)」を右クリック、再起動する。&lt;/p&gt;
&lt;h3&gt;ワコムのドライバを起動する&lt;/h3&gt;
&lt;p&gt;スタートメニューから「ワコム タブレットのプロパティ」を起動すると、ドライバが正しく再起動する。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Kubernetes を 1.24 にしたら壊れた</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-05-16/kubernetes-1-24-docker-remove' />
      <id>https://blog.comame.xyz/entries/2022-05-16/kubernetes-1-24-docker-remove</id>
      <updated>2022-05-16T00:00:00Z</updated>
      <summary>&lt;p&gt;Docker サポートが削除される警告をきちんと読まずに Kubernetes を 1.24 にアップグレードしたらクラスタが壊れたという話。&lt;/p&gt;
&lt;h2&gt;経緯&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;kubeadm upgrade plan&lt;/code&gt; に警告がでる&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;apt update&lt;/code&gt; をしたところ Kubernetes 1.24 へのアップグレードができそうだったので、&lt;code&gt;kubeadm upgrade plan&lt;/code&gt; を実行したところ、次のような警告が出た。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[upgrade/config] FYI: You can look at this config file with &apos;kubectl -n kube-system get cm kubeadm-config -o yaml&apos;&lt;/code&gt;
&lt;code&gt;W0505 14:28:42.708621  126359 initconfiguration.go:120] Usage of CRI endpoints without URL scheme is deprecated and can cause kubelet errors in the future. Automatically prepending scheme &quot;unix&quot; to the &quot;criSocket&quot; with value &quot;/var/run/dockershim.sock&quot;. Please update your configuration!&lt;/code&gt;
&lt;code&gt;[preflight] Running pre-flight checks&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;なにやら Docker 周りの警告のようだが、自動でスキームを付けるといったことが書かれているし問題ないだろうとおもってアップグレードを続行した。&lt;/p&gt;
&lt;h3&gt;Pod がスケジュールされなくなる&lt;/h3&gt;
&lt;p&gt;アップグレードが終わった直後から、HTTP が繋がらないというアラートが大量に飛んでくる。&lt;code&gt;kubectl describe pod&lt;/code&gt; をしてみると、どうやら Node に Pod をスケジュールできないことが原因の模様。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kubectl describe nodes&lt;/code&gt; をすると、なぜか &lt;code&gt;NodeNotReady&lt;/code&gt; の文字が。&lt;/p&gt;
&lt;h3&gt;原因の特定&lt;/h3&gt;
&lt;p&gt;Kubelet がうまいこと動いていないのだろうと当たりをつけ、Kubelet のログを確認する。そうすると、なにやら次のようなエラーを吐いて起動に失敗していることが判明した。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;Error: failed to parse kubelet flag: unknown flag: --network-plugin&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;/var/lib/kubeadm-flags/env&lt;/code&gt; を見ると、(当時のファイルが残っていないので定かではないが) 確かに &lt;code&gt;--network-plugin=cni&lt;/code&gt; のようなオプションが付けられていた。&lt;/p&gt;
&lt;p&gt;ここで、はじめに &lt;code&gt;kubeadm upgrade plan&lt;/code&gt; したときの警告に含まれていた &lt;code&gt;dockershim.sock&lt;/code&gt; やオプションに含まれる &lt;code&gt;CNI&lt;/code&gt; から、そういえば Kubernetes が Docker のサポートを打ち切るというような記事を以前に読んだことを思い出す。&lt;/p&gt;
&lt;p&gt;慌ててリリースノートを読むと、確かに 1.24 から Docker をそのまま使い続けることができないことが書かれていた。&lt;/p&gt;
&lt;h3&gt;解決&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://kubernetes.io/blog/2022/02/17/dockershim-faq/#can-i-still-use-docker-engine-as-my-container-runtime target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;FAQ&lt;/a&gt; を読むと 、Docker のまま今回のアップデートに対応する方法が書かれていることに気がついた。&lt;/p&gt;
&lt;p&gt;思い切って &lt;code&gt;containerd&lt;/code&gt; に差し替えることも考えたが、とりあえずの対策として &lt;code&gt;cri-dockerd&lt;/code&gt; をインストールして解決することにした。&lt;/p&gt;
&lt;h3&gt;その後&lt;/h3&gt;
&lt;pre&gt;
&lt;code&gt;Flag --pod-infra-container-image has been deprecated, will be removed in 1.27. Image garbage collector will get sandbox image information from CRI.&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt; &quot;--pod-infra-container-image will not be pruned by the image garbage collector in kubelet and should also be set in the remote runtime&quot;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;のような警告も出ていたので、該当のオプションも消した。&lt;/p&gt;
&lt;h2&gt;教訓&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;リリースノートを読むこと (特にマイナーアップデートをするときは)&lt;/li&gt;
&lt;li&gt;警告をおろそかにしないこと&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然のことである。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Ubuntu でアプリケーションアイコンを手動で追加する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-05-10/register-app-to-launcher-ubuntu' />
      <id>https://blog.comame.xyz/entries/2022-05-10/register-app-to-launcher-ubuntu</id>
      <updated>2022-05-10T00:00:00Z</updated>
      <summary>&lt;p&gt;.appimage 形式の実行ファイルをインストールするとき、手動でランチャーに登録しないとならないようだったので、その手順をメモ。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.local/share/applications/&amp;lt;app&amp;gt;.desktop&lt;/code&gt; あるいは &lt;code&gt;/usr/share/applications/&amp;lt;app&amp;gt;.desktop&lt;/code&gt; を作成し、次のような内容を書き込む。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[Desktop Entry]&lt;/code&gt;
&lt;code&gt;Name=Application&lt;/code&gt;
&lt;code&gt;Commenct=Comment&lt;/code&gt;
&lt;code&gt;Exec=/path/to/executable&lt;/code&gt;
&lt;code&gt;Terminal=false&lt;/code&gt;
&lt;code&gt;Type=Application&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Icon&lt;/code&gt; や &lt;code&gt;Categories&lt;/code&gt; も指定できるようである。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;毎回お世話になる ArchWiki。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://wiki.archlinux.jp/index.php/%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;デスクトップエントリ-ArchWiki&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Chromebook のセットアップ</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-04-26/setup-chromebook' />
      <id>https://blog.comame.xyz/entries/2022-04-26/setup-chromebook</id>
      <updated>2022-04-26T00:00:00Z</updated>
      <summary>&lt;p&gt;Crostini を入れて使えるようになるまで&lt;/p&gt;
&lt;p&gt;100.0.4896.133 時点&lt;/p&gt;
&lt;h2&gt;Chrome OS の設定&lt;/h2&gt;
&lt;h3&gt;画面ロックの設定&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;PIN を設定する&lt;/li&gt;
&lt;li&gt;Smart Lock を設定する (「デバイスのロック解除のみ」)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;日本語入力の設定&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;辞書とかスペースとか&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ネットワークの設定&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Wi-Fi&lt;/li&gt;
&lt;li&gt;VPN&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Crostini の設定&lt;/h2&gt;
&lt;h3&gt;Crostini を有効にする&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;設定アプリから&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;日本語入力をできるようにする&lt;/h3&gt;
&lt;pre&gt;
&lt;code&gt;$ sudo apt install fcitx fcitx-mozc&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;# 自動起動&lt;/code&gt;
&lt;code&gt;$ echo &quot;/usr/bin/fcitx-autostart&quot; &amp;gt;&amp;gt; ~/.sommelierrc&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;# IME 切り替えキーの設定 / Mozc の選択&lt;/code&gt;
&lt;code&gt;$ fcitx-configtool&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;# 辞書とかスペースとかの設定&lt;/code&gt;
&lt;code&gt;$ /usr/lib/mozc/mozc_tool --mode=config_dialog&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;/etc/systemd/user/cros-garcon.service.d/cros-garcon-override.conf に追記する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;Environment=&quot;GTK_IM_MODULE=fcitx&quot;&lt;/code&gt;
&lt;code&gt;Environment=&quot;QT_IM_MODULE=fcitx&quot;&lt;/code&gt;
&lt;code&gt;Environment=&quot;XMODIFIERS=@im=fcitx&quot;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;fcitx-configtool で Mozc を追加するとき、&lt;code&gt;Only show current language&lt;/code&gt; のチェックに注意&lt;/p&gt;
&lt;h3&gt;konsole のインストール (追記あり)&lt;/h3&gt;
&lt;p&gt;日本語入力とクリップボードがどちらも動いたので、konsole にした。Terminator も問題なく動作しそうなので、好みで選ぶ。&lt;/p&gt;
&lt;p&gt;gnome-terminal は日本語入力が正しく動かず、xterm は起動が速くて好みだったがクリップボードが動作せず。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ sudo apt install konsole&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;(追記) xterm のインストール&lt;/h3&gt;
&lt;p&gt;クリップボードを動作させることができた。他のターミナルよりも圧倒的に起動が速い。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ sudo apt install xterm&lt;/code&gt;
&lt;code&gt;$ echo &quot;XTerm*selectToClipboard: true&quot; &amp;gt;&amp;gt; ~/.Xdefaults&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;# .xsessionrc などは実行されなかったので仕方ない&lt;/code&gt;
&lt;code&gt;$ echo &quot;xrdb ~/.Xdefaults&quot; &amp;gt;&amp;gt; ~/.bashrc&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;MTU の設定 (VPN 接続時)&lt;/h3&gt;
&lt;p&gt;Chrome OS 側で VPN を設定していると、Crostini からネットワークにつながったり繋がらなくなったりする。必要に応じて MTU を小さくする。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ echo &quot;sudo ip link eth0 set mtu &amp;lt;MTU&amp;gt;&quot; &amp;gt;&amp;gt; ~/.sommelierrc&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;普段使ってるアプリケーションを入れる&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;VSCode&lt;/li&gt;
&lt;li&gt;LibreOffice (なんだかんだあると便利)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;主に日本語入力周りで参考にした文献。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://wiki.archlinux.jp/index.php/Fcitx5 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Fcitx5 - ArchWiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://wiki.debian.org/ja/I18n/Fcitx5 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;ja/I18n/Fcitx5 - Debian Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://zenn.dev/igac/articles/84d1f377bcd9d698ee8d target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Chromebook Crostini の有効化と日本語化&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;VPN 設定時の MTU について。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://www.reddit.com/r/Crostini/comments/rajbas/vpn_and_linux_help/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;VPN and Linux help : Crostini&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;gnome-terminal で日本語入力ができない問題について。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://twitter.com/tricken/status/1204380733776596998 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://qiita.com/niwashi/items/dc325c44dcc538245cc4 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Crostiniのfcitxで１文字ずつ勝手に確定される問題の解決方法（暫定）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Xterm について。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://wiki.archlinux.jp/index.php/Xterm target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Xterm - ArchWiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://wiki.archlinux.jp/index.php/Xinit target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;xinit - ArchWiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>Acer Chromebook Spin 311 を買った</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-04-25/chromebook-spin-311' />
      <id>https://blog.comame.xyz/entries/2022-04-25/chromebook-spin-311</id>
      <updated>2022-04-25T00:00:00Z</updated>
      <summary>&lt;p&gt;Amazon.com で Acer Chromebook Spin 311 CP311-2H-C7QD を購入したメモ。&lt;/p&gt;
&lt;h2&gt;選んだ理由&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ノートパソコン型である&lt;/li&gt;
&lt;li&gt;本体サイズが小さい (A4 程度)&lt;/li&gt;
&lt;li&gt;キーボードが US 配列&lt;/li&gt;
&lt;li&gt;自動更新が 2027 年まで保証されている&lt;/li&gt;
&lt;li&gt;技適が通っている (Amazon.com で購入したため)&lt;/li&gt;
&lt;li&gt;USB Type-A とイヤホンジャックがある&lt;/li&gt;
&lt;li&gt;USB-C での充電ができる&lt;/li&gt;
&lt;li&gt;安価&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;予想以上に良かったこと&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;USB Type-A と USB-C が左右両サイドにあること&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;残念だったこと&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;画面の反射が強すぎる (アンチグレアフィルムを貼ることで解決)&lt;/li&gt;
&lt;li&gt;画面がやや暗い&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;需要が少ないから仕方ないのだろうが、US 配列のモデルも取り扱ってほしいと、ノートパソコンを選ぶたびに毎回思う&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>手抜きで自宅サーバを運用する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-02-25/easy-server-op' />
      <id>https://blog.comame.xyz/entries/2022-02-25/easy-server-op</id>
      <updated>2022-02-25T00:00:00Z</updated>
      <summary>&lt;p&gt;個人向け落ちてもどうでもいいようなサーバの超手抜き運用。&lt;/p&gt;
&lt;h2&gt;落ちたときに気づけるようにする&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://developers.google.com/apps-script target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Google Apps Scirpt&lt;/a&gt; で HTTP リクエストを叩くだけのスクリプトを書き、1 分おきのトリガーを設定する。エラー時のメール通知を即時にしておけば、落ちた時にメールが飛んでくる。&lt;/p&gt;
&lt;p&gt;(補足)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GmailApp.sendEmail()&lt;/code&gt; を使わずとも、失敗時に例外を投げておけば、トリガーの失敗が自動的にメール通知される。 &lt;/p&gt;
&lt;h2&gt;やらかしたときに対処できるようにする&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://remotedesktop.google.com target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Chrome Remote Desktop&lt;/a&gt; を入れておく。これは間違えて SSH を落としても、ファイアウォールの設定を間違えても (多分)、端末がインターネットに繋がっていれば繋がる。&lt;/p&gt;
&lt;p&gt;出先で VPN に繋がらなくなって唸っていたら、&lt;a href=https://twitter.com/yasu0796 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;yasu0796&lt;/a&gt; さんにアドバイスを頂いてこの方法に気づいた。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>MetalLB の設定に右往左往した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-02-22/metallb-mode' />
      <id>https://blog.comame.xyz/entries/2022-02-22/metallb-mode</id>
      <updated>2022-02-22T00:00:00Z</updated>
      <summary>&lt;p&gt;2 日間ほど MetalLB と格闘していた。&lt;/p&gt;
&lt;h2&gt;経緯&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;シングルノード Kubernetes クラスターのノードと、SoftEther を 1 つのマシンに同居させていた&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このマシンには 2 つの NIC が生えており、片方は SoftEther のローカルブリッジを設定することで、リモートアクセスに使用していた。そのマシンに MetalLB を Layer-2 モードでデプロイしたところ、SoftEther のローカルブリッジに使用しているインターフェースが MetalLB によって割り当てられた External IP の ARP 応答を行うことで、VPN でリモートアクセスしている環境から Pod にアクセスできなくなる問題が発生した (これは既知の問題であり、SoftEther のドキュメントにも振る舞いが明記されている)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FortiGate を買った&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;せっかく BGP を喋るので、MetalLB を BGP モードにしてみたかった。&lt;/p&gt;
&lt;h2&gt;BGP モードがうまく動かない&lt;/h2&gt;
&lt;p&gt;MetalLB の Configuration を参考に設定したところ、BGP は確立し、ノード内から External IP を使用して Pod と通信できるものの、ノード外からの通信が Pod に到達しない問題が発生した。一晩かけて慣れないパケットキャプチャとにらめっこすると、どうやらマシンの物理インターフェースまではたどり着いているものの、そこから Pod までルーティングされていないことが判明した。&lt;/p&gt;
&lt;p&gt;ノード内からは繋がることから、Kubernetes のネットワークアドオンとして使用している flannel や MetalLB が致命的な原因ではいだろうと推測した。1 日かけて MetalLB の Issue を漁り倒し、物理インターフェースに割り当てられているサブネットとは違う IP アドレスでパケットを受け取っているため、応答先が分からなくなっているのではないかという素人の適当な仮説を立てた。&lt;/p&gt;
&lt;p&gt;試しに &lt;code&gt;ip route add&lt;/code&gt; でルートを設定してみたところ、自宅のネットワーク内からは Pod と通信できるようになり、良かった良かったと思いきや、別の問題が発生した。意気揚々とバーチャル IP を割り当ててインターネットから到達できるようにしたところ、なぜかホストが RST パケットを返し、Connection Refused エラーが出現するようになった。おそらく iptables の設定をいじれば直るだろうと思ったが、Kubernetes によって複雑な設定が書き込まれていたため、解決する気力を失って放り投げた。&lt;/p&gt;
&lt;p&gt;BGP を使うとどのようにパケットが飛んでくるか予測しやすくなりそうで嬉しいので、しばらくしたらまた再挑戦したい。&lt;/p&gt;
&lt;h2&gt;SoftEther のローカルブリッジと ARP 応答問題&lt;/h2&gt;
&lt;p&gt;MetalLB の Issue を漁っていたところ、「L2 モードでインターフェースを指定したい」(&lt;a href=https://github.com/metallb/metallb/issues/277 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/metallb/metallb/issues/277&lt;/a&gt;) というものを発見した。どうやらインターフェースを指定するオプションを導入する方向に議論が向かっているようなので、今後どうなるかが楽しみである。&lt;/p&gt;
&lt;p&gt;とりあえず、&lt;a href=https://github.com/metallb/metallb/issues/277#issuecomment-962454240 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;ワークアラウンドが提案されていた&lt;/a&gt; (感謝) のでそれを導入してみることにする。&lt;/p&gt;
&lt;h2&gt;ところで&lt;/h2&gt;
&lt;p&gt;WireShark とにらめっこしていたら昨晩は 3 時間しか眠れなかった。自宅でサーバを運用するとありとあらゆるところで何かしら問題が起きるので、それが楽しいところではあるなあと思う。&lt;/p&gt;
&lt;p&gt;SoftEther のローカルブリッジのドキュメントを読み直していたところ、ローカルブリッジに使用するインターフェースは TCP/IP を無効にするように書かれていた。この設定を行ったときに MetalLB はどう振る舞うか検証したい。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Rust から C のコードを呼び出す</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-02-07/call-c-from-rust' />
      <id>https://blog.comame.xyz/entries/2022-02-07/call-c-from-rust</id>
      <updated>2022-02-07T00:00:00Z</updated>
      <summary>&lt;p&gt;Rust のコードから C を呼び出して Hello, world! するだけ。&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;C のコードを書いてコンパイルする&lt;/h3&gt;
&lt;p&gt;適当にコードを書く。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;// /project_root/lib/hello_world.c&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;#include &quot;stdio.h&quot;&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;void hello_world() {&lt;/code&gt;
&lt;code&gt;    printf(&quot;Hello, world!&quot;);&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;これをコンパイルして、静的ライブラリにする。ライブラリのファイル名の先頭には &lt;code&gt;lib&lt;/code&gt; を付ける必要がある。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ gcc -c hello_world.c -o hello_world.o&lt;/code&gt;
&lt;code&gt;$ ar crs libhello_world.a hello_world.o&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;Rust から呼び出す&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;link&lt;/code&gt; attribute でライブラリを指定する。&lt;code&gt;libhello_world&lt;/code&gt; を使いたいので、&lt;code&gt;#[link(name=&amp;quot;hello_world&amp;quot;)]&lt;/code&gt; とする。build.rs で &lt;code&gt;cargo:rustc-link-lib=hello_world&lt;/code&gt; を出力してもよい。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;// /project_root/src/main.rs&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;#[link(name=&quot;hello_world&quot;)]&lt;/code&gt;
&lt;code&gt;extern {&lt;/code&gt;
&lt;code&gt;    fn hello_world();&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;fn main() {&lt;/code&gt;
&lt;code&gt;    unsafe {&lt;/code&gt;
&lt;code&gt;        hello_world();&lt;/code&gt;
&lt;code&gt;    }&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;a href=https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;参考 (the book)&lt;/a&gt;, &lt;a href=https://doc.rust-lang.org/nomicon/ffi.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;参考 (the &amp;#39;nomicon)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ライブラリのファイルがある場所を指定する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;// build.rs&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;fn main() {&lt;/code&gt;
&lt;code&gt;    println!(&quot;cargo:rustc-link-search=native=/project_root/lib&quot;);&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;a href=https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;参考 (cargo:rustc-link-search)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;動かす&lt;/h3&gt;
&lt;pre&gt;
&lt;code&gt;$ cargo run&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;ちょっとした疑問点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;the book には &lt;code&gt;extern &amp;quot;C&amp;quot; { }&lt;/code&gt;、the &amp;#39;nomicon には &lt;code&gt;extern { }&lt;/code&gt; と書かれていたが、何が違うのか&lt;ul&gt;
&lt;li&gt;C を Rust から呼び出すときはどちらでもよくて、Rust を C から呼び出すときに影響してくる？&lt;/li&gt;
&lt;li&gt;&lt;a href=https://stackoverflow.com/questions/44056461/difference-externc-vs-extern/44056776 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://stackoverflow.com/questions/44056461/difference-externc-vs-extern/44056776&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;感想&lt;/h2&gt;
&lt;p&gt;C のことはよくわかっていないが、思ったよりも簡単に動かすことができた。実際には &lt;a href=https://crates.io/crates/cc target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;cc&lt;/a&gt; を使うべきだと思われる。&lt;/p&gt;
&lt;p&gt;大抵のことは既存の Crate が用意されていると思うので、自分で使うことは当分の間なさそう。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://blog.ojisan.io/rust-ffi-cpp-wakaran/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://blog.ojisan.io/rust-ffi-cpp-wakaran/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://www.yunabe.jp/docs/static_library.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.yunabe.jp/docs/static_library.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>自宅に k8s を構築した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-01-23/k8s-at-home' />
      <id>https://blog.comame.xyz/entries/2022-01-23/k8s-at-home</id>
      <updated>2022-01-23T00:00:00Z</updated>
      <summary>&lt;p&gt;以前からやりたいと思っていた自宅シングルノード Kubernetes クラスターをついに構築し、とりあえず動くまで至った。&lt;/p&gt;
&lt;h2&gt;やったこととハマったところ&lt;/h2&gt;
&lt;h3&gt;Kubeadm を使ったクラスタのインストール&lt;/h3&gt;
&lt;p&gt;基本的には&lt;a href=https://kubernetes.io/ja/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;公式ドキュメントの指示&lt;/a&gt;に従っただけ。&lt;/p&gt;
&lt;h4&gt;cgroup ドライバが Docker と Kubelet で異なっていた&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;kubeadm init&lt;/code&gt; をしても Kubelet が動いていないというエラーが発生した。Docker は cgroupfs、Kubelet は systemd を使用していたことが原因だった。&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; に &lt;code&gt;&amp;quot;exec-opts&amp;quot;: [&amp;quot;native.cgroupdriver=systemd&amp;quot;]&lt;/code&gt; を追記することで解決。&lt;/p&gt;
&lt;h3&gt;Pod ネットワークアドオンがうまくインストールできなかった&lt;/h3&gt;
&lt;p&gt;まだ挙動をよく理解できているわけではないが、現時点では Kubeadm を使用すると flannel が使用される模様。&lt;code&gt;kubeadm init --pod-network-cidr 10.244.0.0/16&lt;/code&gt; のようにアドレスを指定することで無事に構築できた。&lt;/p&gt;
&lt;p&gt;また、Flannel のマニフェストを自分で読み込まないといけないことに気づかず、 CoreDNS の Pod が Pending のままになってしまった。&lt;/p&gt;
&lt;h3&gt;MetalLB のセットアップ&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;type=LoadBalancer&lt;/code&gt; を使いたかったので、MetalLB のセットアップをした。&lt;a href=https://metallb.universe.tf/configuration/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;IP アドレスの範囲を指定する&lt;/a&gt; 場面で少しつまり、ルータの設定が必要なのかと右往左往していた。ネットワークに対する理解の浅さが露呈した (友人に聞いたら MetalLB は ARP を喋るということを教えてもらった)。最終的に、自宅内ネットワークのサブネット内の IP アドレスを指定してみたところ、なぜかうまく動いた。&lt;/p&gt;
&lt;h3&gt;Ingress のセットアップ&lt;/h3&gt;
&lt;p&gt;ingress-nginx を使用した。&lt;code&gt;svc/ingress-nginx-controller&lt;/code&gt; に &lt;code&gt;.spec.loadBalancerIP&lt;/code&gt; を指定して、IP を固定化することにした。将来的に困ることがあればまた考えることにする。&lt;/p&gt;
&lt;h3&gt;cert-manager のセットアップ&lt;/h3&gt;
&lt;p&gt;CloudFlare API を使用して、dns-01 で検証するようにした。ClusterIssuer を使って Ingress を別の Namespace に配置しようと思ったが、dns-01 用の Cloudflare API トークンを保存している Secret を見に行けずにチャレンジが失敗するので、結局 Ingress 用の Namespace を用意することにした。&lt;/p&gt;
&lt;h3&gt;Docker Registry を構築した&lt;/h3&gt;
&lt;p&gt;HTTPS でないとエラーが出るため、&lt;code&gt;comame.dev&lt;/code&gt; をローカルサブネットに向けて、Let&amp;#39;s Encrypt で証明書を発行した。Ingress に &lt;code&gt;nginx.ingress.kubernetes.io/proxy-body-size&lt;/code&gt; のアノテーションを指定しておかないと、大きいイメージをアップロードするときに怒られる。&lt;/p&gt;
&lt;p&gt;インターネットからアクセスすることは想定していないものの、Ingress はインターネットから見えてしまうため、Host ヘッダフォージェリを恐れて一応認証を設定しておいた。&lt;/p&gt;
&lt;h3&gt;Kubernetes Dashboard を設置してみた&lt;/h3&gt;
&lt;p&gt;今動かしているクラスターのバージョンは 1.23 らしいので、&lt;a href=https://kubernetes.io/ja/docs/tasks/access-application-cluster/web-ui-dashboard/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;ドキュメントに書いてあるコマンド&lt;/a&gt; を愚直に叩くと、絶妙に API リクエストが失敗するものがデプロイされてしまう。&lt;a href=https://github.com/kubernetes/dashboard/releases target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;GitHub のリリースページ&lt;/a&gt; にバージョンごとの対応表があるが、1.23 の情報はなかったのでとりあえず最新の 2.4.0 をいれておくことにした。&lt;/p&gt;
&lt;p&gt;いまのところ、Pod のログが見れて便利だな程度なので、将来的にどうするかは検討したい。&lt;/p&gt;
&lt;h2&gt;考えている運用方針&lt;/h2&gt;
&lt;p&gt;HTTP サーバは Ingress を、それ以外は &lt;code&gt;type=LoadBalancer&lt;/code&gt; の Service に &lt;code&gt;loadBalancerIP&lt;/code&gt; を直接指定して IP を固定化してしまおうと考えている。Node を増やすつもりはないので、当面の間は困らないだろうと妄想している。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;というよりやらないといけないこと&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cert-manager の導入 (やった)&lt;/li&gt;
&lt;li&gt;証明書の自動更新&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;マニフェストのレポジトリ&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://github.com/comame/kubernetes-manifests target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/comame/kubernetes-manifests&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Chromebook は気軽に使えて良い</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2022-01-07/chromebook' />
      <id>https://blog.comame.xyz/entries/2022-01-07/chromebook</id>
      <updated>2022-01-07T00:00:00Z</updated>
      <summary>&lt;p&gt;去年の春に Lenovo Chromebook Duet を購入した。&lt;/p&gt;
&lt;h2&gt;なぜ買ったか&lt;/h2&gt;
&lt;p&gt;以前 ASUS Chromebook Flip C100PA を 2 年ほど使用していたことがあり、Crostini が使える端末であれば気軽に持ち出せる端末として便利だと思い、Chromebook を買うことにした。&lt;/p&gt;
&lt;p&gt;とにかく気軽に使いたかったので、5 万円以内、小型サイズ (11 インチ以下)、タッチパネル対応ありの条件で端末を探したところ、キーボードを取り外せる Chromebook Duet が良さそうということになった。&lt;/p&gt;
&lt;h2&gt;なぜ Chromebook なのか&lt;/h2&gt;
&lt;p&gt;箇条書きでずらっと挙げていくと、次のような点を気に入っている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Crostini があるので自宅のパソコンに接続すれば重たい作業でもできなくはないこと&lt;/li&gt;
&lt;li&gt;Chrome OS はスペックが貧弱なコンピュータでも快適に動作すること&lt;/li&gt;
&lt;li&gt;バッテリー持ちが良い (端末が多い) こと&lt;/li&gt;
&lt;li&gt;ソフトウェアアップデートなどのメンテナンスをほぼ気にする必要がないこと&lt;/li&gt;
&lt;li&gt;あくまでパソコンであること (Windows や Mac ほどの拡張性は無いにせよ)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;実際使ってみてどうか&lt;/h2&gt;
&lt;p&gt;Chrome OS の使い心地は以前 Chromebook Flip を使用したことがあるので、ほぼ予想通りであった。Chromebook Flip では使用できなかった Crostini については、GUI アプリケーションが使用できることを知らなかったため、思っていたよりも幅広く使えることに気がついた。VSCode の Remote SSH で自宅のパソコンに繋いだり、GIMP でちょっとした画像の加工をしたりしている。&lt;/p&gt;
&lt;p&gt;Chromebook Flip 固有の部分について、個人的にはキーボードが取り外せて便利だった場面はあまりなく、鞄の中でキーボードが外れたことで電源が入り、使いたいときに電池が切れている事故が何回かあった。それ以外に不満点はなく、常に持ち歩くようにしている。バッテリーは重たいことを長時間しない限り 1 日は余裕で持ち、10.1 インチと小柄なのでどこにでも持っていける機動性が気に入っている。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Chromebook から Windows のフォルダにアクセスする</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-12-20/chromeos-smb' />
      <id>https://blog.comame.xyz/entries/2021-12-20/chromeos-smb</id>
      <updated>2021-12-20T00:00:00Z</updated>
      <summary>&lt;p&gt;Chrome OS には、Windows のファイル共有 (SMB) にアクセスできる機能が標準で組み込まれている。従来 (2019 年頃まで？) は拡張機能をインストールする必要があったようだが、現在は「ファイル」アプリで初めから使用できるようになっている。&lt;/p&gt;
&lt;h2&gt;ファイル共有を有効にする&lt;/h2&gt;
&lt;p&gt;Windows で、ファイル共有の設定をする。共有したいディレクトリを右クリックし、「アクセスを許可する」&amp;gt;「特定のユーザー」と進む。「ネットワークアクセス」ダイアログが開くので、共有したいユーザーを設定し、「共有」をクリックする。Windows 11 では、右クリックメニューの「その他のオプションを表示」をクリックすると「アクセスを許可する」の項目が表示されるようになる。&lt;/p&gt;
&lt;p&gt;次に、アクセスするためのネットワークパスを調べる。先程共有の設定をしたディレクトリのプロパティを開き、「共有」タブへ移動する。「ネットワーク パス」という項目を控える。表示されるネットワークパスは &lt;code&gt;\\[コンピュータ名]\[フォルダ名]&lt;/code&gt; という形式になっている。&lt;/p&gt;
&lt;p&gt;必要に応じてファイアウォールやルータの設定をする (SMB のポートをインターネットに公開するのはやめたほうが良いと思う)。&lt;/p&gt;
&lt;h2&gt;Chromebook から接続する&lt;/h2&gt;
&lt;p&gt;Chromebook の「ファイル」アプリを開き、右上の三点メニューをクリックし、「サービス」&amp;gt;「SMB ファイル共有」を開く。&lt;/p&gt;
&lt;p&gt;「ファイル共有 URL」には Windows に表示されたネットワークパスを入力する。ただし、「共有フォルダのマウントエラー。指定された共有フォルダがネットワークで見つかりませんでした。」のエラーが表示されるときは、ネットワークパスを &lt;code&gt;\\[Windows マシンの IP アドレス]\[フォルダ名]&lt;/code&gt; と置き換える。例えば、Windows に表示されたネットワークパスが &lt;code&gt;\\Desktop-ABC\Share&lt;/code&gt; で、Windows マシンの IP アドレスが 192.168.0.100 であるならば、&lt;code&gt;\\192.168.0.100\Share&lt;/code&gt; と置き換える。&lt;/p&gt;
&lt;p&gt;「ユーザー名」には Windows のユーザー名を入力する。スタートメニューやログインがメインに表示されるフルネームではなく、「コンピュータの管理」の「ローカル ユーザーとグループ」&amp;gt;「ユーザー」に表示される「名前」を入力する。(Windows の仕様に詳しくないので不正確な可能性があるが、この名前はユーザーディレクトリのディレクトリ名と同じだと思われる)。&lt;/p&gt;
&lt;p&gt;「パスワード」には Windows のパスワードを入力する。Microsoft アカウントでのサインインにしているときは、Microsoft アカウントのパスワードを入力する必要がある (PIN ではない)。&lt;/p&gt;
&lt;h2&gt;感想&lt;/h2&gt;
&lt;p&gt;Chrome OS が SMB に対応していることに気付くまでは WSL 2 に立てた SSH サーバに向けて Crostini から SFTP でファイルの転送をしていたので、作業が大幅に効率化されることになり、Chromebook の機動力をより活かすことができるようになったと思う。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Hyper-V の Windows 11 で WSL 2 を実行する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-09-23/wsl2-on-hyperv' />
      <id>https://blog.comame.xyz/entries/2021-09-23/wsl2-on-hyperv</id>
      <updated>2021-09-23T00:00:00Z</updated>
      <summary>&lt;p&gt;Hyper-V にインストールしている Windows 11 で WSL 2 を動かすことができた。&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;1. Hyper-V の管理機能を有効にする&lt;/h3&gt;
&lt;p&gt;「Windows の機能の有効化または無効化」で Hyper-V &amp;gt; Hyper-V 管理ツール &amp;gt; Windows PowerShell 用 Hyper-V モジュール を有効にする。&lt;/p&gt;
&lt;p&gt;参考: &lt;a href=https://docs.microsoft.com/ja-jp/skypeforbusiness/troubleshoot/server-install-or-uninstall/get-vm-not-recognized-during-install-cloud-connector target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://docs.microsoft.com/ja-jp/skypeforbusiness/troubleshoot/server-install-or-uninstall/get-vm-not-recognized-during-install-cloud-connector&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;2. Hyper-V での入れ子になった仮想化を有効にする&lt;/h3&gt;
&lt;p&gt;PowerShell で以下のコマンドを実行する。&lt;code&gt;&amp;lt;VMName&amp;gt;&lt;/code&gt; は &lt;code&gt;Get-VM&lt;/code&gt; で確認できる。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;Set-VMProcessor -VMName &amp;lt;VMName&amp;gt; -ExposeVirtualizationExtensions $true&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;WSL 2 は Hyper-V を使用しているため、この方法で仮想マシン内で仮想環境を使用できる。&lt;/p&gt;
&lt;p&gt;参考: &lt;a href=https://docs.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/user-guide/nested-virtualization target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://docs.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/user-guide/nested-virtualization&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Windows 11 の Insider Preview を Hyper-V でインストールする</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-07-04/install-windows11-hyperv' />
      <id>https://blog.comame.xyz/entries/2021-07-04/install-windows11-hyperv</id>
      <updated>2021-07-04T00:00:00Z</updated>
      <summary>&lt;h2&gt;前提条件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10 Pro を使用していること (Hyper-V を使用するため)&lt;/li&gt;
&lt;li&gt;ホストが Windows 11 の動作要件を満たしていること (満たしていなくとも動作する可能性はあるが)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;Windows 10 の ISO を用意する&lt;/h3&gt;
&lt;p&gt;現時点では、公式に Windows 11 の ISO を直接入手することができない。なので、Windows 10 をインストールした後、Windows Update で Windows 11 にアップグレードする。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewiso target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Download Windows 10 Insider Preview ISO - microsoft.com&lt;/a&gt; からダウンロードするか、あるいは &lt;a href=https://www.microsoft.com/ja-jp/software-download/windows10 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Media Creation Tool&lt;/a&gt; で ISO イメージを作成する。&lt;/p&gt;
&lt;h3&gt;Hyper-V の仮想マシンを作成する&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://www.microsoft.com/ja-jp/windows/windows-11-specifications#primaryR2 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Windows 11 の動作要件&lt;/a&gt; を満たした仮想マシンを作成するため、設定を少し変更する。以下の項目以外については、自動的に Windows 11 の動作要件を満たすと思われるが、適宜変更が必要かもしれない。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「セキュリティ」の「トラステッド プラットフォーム モジュールを有効にする」にチェックを入れる&lt;/li&gt;
&lt;li&gt;メモリを 4 GB 以上割り当てる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hyper-V のグラフィックは WDDM 1.3 にしか対応していないはずだが、今回はインストールを進めることができた。&lt;/p&gt;
&lt;h3&gt;Windows 11 にアップグレードする&lt;/h3&gt;
&lt;p&gt;Windows Insider Program を Dev チャンネルにすると、Windows 11 のアップデートを受け取ることができる。&lt;/p&gt;
&lt;h2&gt;ところで&lt;/h2&gt;
&lt;p&gt;WSL 2 がきちんと動作するかどうかテストしてみたかったが、Hyper-V 内部で仮想化をすることができないようで、インストールに失敗した、残念。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ピクシブのインターンに参加した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-04-01/pixiv-intern' />
      <id>https://blog.comame.xyz/entries/2021-04-01/pixiv-intern</id>
      <updated>2021-04-01T00:00:00Z</updated>
      <summary>&lt;p&gt;ピクシブの春インターンに参加しました。インターンから少し時間が経ってしまいましたが、せっかくなので記事として残しておこうと思います。技術的な要素は控えめ。&lt;/p&gt;
&lt;h2&gt;どんなインターンだったか&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://www.pixiv.co.jp/springbootcamp target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;これ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;約 2 週間、「pixiv リクエスト」のチームに参加しました。自分の場合は Web フロントエンド (React) メインで必要に応じて PHP も書く、といった内容でした。&lt;/p&gt;
&lt;h2&gt;応募したきっかけ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;そもそも pixiv は日常的に見ているサイトだった&lt;/li&gt;
&lt;li&gt;最近の PWA 化や (招待制だから参加はできなかったけど) PIXIV DEV MEETUP などで技術面でも興味があった&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;やったこと&lt;/h2&gt;
&lt;p&gt;ユーザーによる直接の反応が目に見える部分の実装をタスクとして与えていただきました。&lt;/p&gt;
&lt;h3&gt;「リクエスト」への作品投稿時に行動を促す / 褒めるモーダルの作成&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/img/pixiv-intern/confetti-modal.jpg&quot; alt=&quot;モーダルの画像&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;この機能の良いところは、Twitter でエゴサすると実際にこの機能を使ってもらっているのを見れる点です。&lt;/del&gt; このモーダルがあると、「リクエスト」に作品を投稿したときに SNS にシェアする敷居が下がったり、受けたリクエストをプロフィールページに公開できたりします。&lt;/p&gt;
&lt;h3&gt;「リクエスト」の詳細ページの OGP をリッチに&lt;/h3&gt;
&lt;p&gt;従来は、「リクエスト」の詳細ページを Twitter などにシェアすると、「リクエスト」のロゴがプレビュー画像として表示されていました。そこで、その画像を「リクエスト」に投稿された作品やリクエスト文などをプレビューするように変更しました。&lt;del&gt;これまたエゴサできて嬉しい&lt;/del&gt;&lt;/p&gt;
&lt;h2&gt;インターン期間中にいいなと思ったこと&lt;/h2&gt;
&lt;h3&gt;生活習慣が圧倒的に改善されたこと&lt;/h3&gt;
&lt;p&gt;朝 10 時から始まるので、何としてもそれまでに起きなければいけませんでした。朝 6 時まで Discord でグダグダして 16 時に起きる、素晴らしく規則正しい生活習慣から脱却できました。&lt;/p&gt;
&lt;h3&gt;チームの方に気軽に話せること&lt;/h3&gt;
&lt;p&gt;常に Discord に繋がっているので、疑問点などがあったときに気軽に相談できました。真面目に仕事をしつつ雑談もするといった、仕事も楽しむという雰囲気を味わうことができました。&lt;/p&gt;
&lt;h3&gt;人のコードを読む機会を得られたこと&lt;/h3&gt;
&lt;p&gt;既存のプロジェクトに参加したため、元々あるコードを読みながら自分の実装を進める必要がありました。個人で趣味開発をしていると中々得られない経験だったと思います。&lt;/p&gt;
&lt;h3&gt;インターンでの成果を発表する機会を得られたこと&lt;/h3&gt;
&lt;p&gt;インターン期間の最終日に、インターン中の成果をスライドで発表する時間がありました。発表するときは緊張で震えていたのですが、優しく受け入れてくださいました。最後に成果を発表することで、自分のやったことはどのような意図があったのか、どのような結果となったのか、何を学ぶことができたかを振り返ることができました。&lt;/p&gt;
&lt;h3&gt;普段の業務に飛び込めること&lt;/h3&gt;
&lt;p&gt;チームの皆さんが普段使っているツールを使って、定例ミーティングに参加して...など、通常の業務に飛び込むことができました。本当に動いているコードに手を加えますし、本当に自分の作ったものがリリースされますし、本当にユーザーに使ってもらえますし、本当にアナリティクスの数字も出てきます。すごい。&lt;/p&gt;
&lt;h2&gt;インターンを終えてからしみじみと思ったこと&lt;/h2&gt;
&lt;h3&gt;関わる人のことを考えるということ&lt;/h3&gt;
&lt;p&gt;サービスを使うユーザーのことを考えるのは当然です。しかし、ただユーザーのことだけを考えていれば良いのではなく、サービスを運営する立場としての嬉しさも追求していかなければならないということをふと思いました。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;皆さん忙しい中インターンとして参加させていただいて、本当にありがとうございました！初インターンでこんなに楽しむことができて幸運でした。&lt;/p&gt;
&lt;h2&gt;(おまけ) pixiv の p は小文字&lt;/h2&gt;
&lt;p&gt;Pixiv だと思ってました。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Softether を systemd のサービスとして起動する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-03-16/softether-systemd-service' />
      <id>https://blog.comame.xyz/entries/2021-03-16/softether-systemd-service</id>
      <updated>2021-03-16T00:00:00Z</updated>
      <summary>&lt;p&gt;Ubuntu 20.04 で Softether を systemd のサービスとして登録するには、&lt;code&gt;/etc/systemd/system/vpnserver.service&lt;/code&gt; に次のように記述する。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://ja.softether.org/4-docs/1-manual/7/7.3#7.3.8_.E3.82.B9.E3.82.BF.E3.83.BC.E3.83.88.E3.82.A2.E3.83.83.E3.83.97.E3.82.B9.E3.82.AF.E3.83.AA.E3.83.97.E3.83.88.E3.81.B8.E3.81.AE.E7.99.BB.E9.8C.B2 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Softether のドキュメント&lt;/a&gt; をほぼ移植しただけだが、今回はロックファイルの作成はしていない。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;[Unit]&lt;/code&gt;
&lt;code&gt;Description=Softether VPN&lt;/code&gt;
&lt;code&gt;After=network.target&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;[Service]&lt;/code&gt;
&lt;code&gt;Type=forking&lt;/code&gt;
&lt;code&gt;User=root&lt;/code&gt;
&lt;code&gt;ExecStart=/usr/local/vpnserver/vpnserver start&lt;/code&gt;
&lt;code&gt;ExecStop=/usr/local/vpnserver/vpnserver stop&lt;/code&gt;
&lt;code&gt;Restart=always&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;[Install]&lt;/code&gt;
&lt;code&gt;WantedBy=multi-user.target&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>自分の作業環境のセットアップ</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-02-12/my-workspace-setup' />
      <id>https://blog.comame.xyz/entries/2021-02-12/my-workspace-setup</id>
      <updated>2021-02-12T00:00:00Z</updated>
      <summary>&lt;p&gt;めも&lt;/p&gt;
&lt;h2&gt;Windows&lt;/h2&gt;
&lt;h3&gt;インストールするアプリケーション&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CubePDF Utility&lt;/li&gt;
&lt;li&gt;foobar2000&lt;/li&gt;
&lt;li&gt;GIMP&lt;/li&gt;
&lt;li&gt;Google Chrome&lt;/li&gt;
&lt;li&gt;Google Drive&lt;/li&gt;
&lt;li&gt;iTunes&lt;/li&gt;
&lt;li&gt;Microsoft Visual Studio Code&lt;/li&gt;
&lt;li&gt;Mozilla Firefox&lt;/li&gt;
&lt;li&gt;OBS Studio&lt;/li&gt;
&lt;li&gt;PowerToys&lt;/li&gt;
&lt;li&gt;VLC media player&lt;/li&gt;
&lt;li&gt;Windows Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;やること&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=/entries/2020-12-19/import-wsl&gt;WSL をセットアップする&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=https://comame.xyz/dotfiles/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;各種設定ファイルを入れる&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=/entries/2021-01-18/windows-lockscreen-registry&gt;スリープ設定をいじる&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;高速スタートアップをオフにする&lt;/li&gt;
&lt;li&gt;電源ボタンを押したとき、休止状態にする&lt;/li&gt;
&lt;li&gt;ローカルグループポリシーエディター (gpedit.msc) で Aero Shake を無効にする (ユーザーの構成 &amp;gt; 管理用テンプレート &amp;gt; デスクトップ)&lt;/li&gt;
&lt;li&gt;&lt;a href=/entries/2020-03-26/wsl2-publish-server&gt;WSL 2 のポートを公開できるようにする&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;適当にエクスプローラーの設定をする&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>自作 PC を組んだ</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-02-11/built-pc' />
      <id>https://blog.comame.xyz/entries/2021-02-11/built-pc</id>
      <updated>2021-02-11T00:00:00Z</updated>
      <summary>&lt;p&gt;去年の年末あたりにパソコンを組んだ。&lt;/p&gt;
&lt;h2&gt;モチベーション&lt;/h2&gt;
&lt;p&gt;ノートパソコンには &lt;a href=https://support.apple.com/kb/SP775?locale=ja_JP target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Macbook Pro 2018&lt;/a&gt; をメモリ 16 GB、SSD 512 GB 積んだものを使用しているが、結局 Bootcamp しか使用していないし、グラフィック性能が足りない場面もあったので、自宅用にデスクトップマシンを組むことにした。&lt;/p&gt;
&lt;h2&gt;パーツ&lt;/h2&gt;
&lt;p&gt;ある程度の性能を確保しつつ、低予算を重視した構成で選んだ。&lt;/p&gt;
&lt;h3&gt;マザーボード&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://www.msi.com/Motherboard/B460M-PRO-VDH-WIFI target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;MSI B460M PRO-VDH WIFI&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LGA1200 で MicroATX で Wi-Fi がもともと付いていて安いもの。Amazon で 12000 円程度で購入した。&lt;/p&gt;
&lt;h3&gt;CPU&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://ark.intel.com/content/www/jp/ja/ark/products/199278/intel-core-i5-10400f-processor-12m-cache-up-to-4-30-ghz.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Intel Core i5-10400F&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;今までノートパソコン向けの Core i5 のスペックで特に不満はなかったので、2 万円以下の価格で購入できる Core i5 にした。AMD ではなく Intel にした理由が特にあるわけではなく、今までのパソコンの CPU がすべて Intel だったため。&lt;/p&gt;
&lt;h3&gt;ビデオカード&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://jp.msi.com/Graphics-Card/GeForce-GTX-1660-SUPER-AERO-ITX-OC target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;MSI GEFORCE GTX 1660 SUPER AERO ITX OC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;今までのパソコンではゲーム性能などが基本的に期待できなかったため、とりあえずゲームや VR が動くもののうち、安いものを選んだ。結局ゲームはほとんどやらないので、より高いグレードのものは必要ないと判断した。&lt;/p&gt;
&lt;h3&gt;メモリ&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://www.teamgroupinc.com/jp/product/elite-plus-u-dimm-ddr4 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;TEAM ELITE PLUS U-DIMM DDR4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;はじめは 2 本刺し (16 GB) で、後から 4 本刺し (32 GB) に増設した。Amazon で適当に安かったものを選んだ。赤い。&lt;/p&gt;
&lt;h3&gt;SSD&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://www.crucial.jp/ssd/eol_p1/ct1000p1ssd8 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Crucial P1 500GB&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Macbook Pro の 500 GB では特に不満はなかったので、同じ容量の 500 GB を選んだ (後から HDD を増設した)。&lt;/p&gt;
&lt;h3&gt;電源&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://jp.thermaltake.com/smart-500w.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Thermaltake Smart 500W&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ビデオカードの推奨電源ユニット容量 が 450 W だったので、500 W の電源にした。配線するときはプラグイン式にすればよかったと思ったが、結局ペリフェラル電源以外のケーブルを使うことになったので気にしないことにした。&lt;/p&gt;
&lt;h3&gt;ケース&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://jp.thermaltake.com/versa-h17.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Thermaltake Versa H17&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;MicroATX 対応のケースで、3000 円程度で買えることで有名なケース。この値段で性能に全く不満がないので、大当たりだったと思う。&lt;/p&gt;
&lt;h2&gt;後から付け足したもの&lt;/h2&gt;
&lt;h3&gt;3.5 インチ HDD&lt;/h3&gt;
&lt;p&gt;TOSHIBA の 1 TB のバルク品&lt;/p&gt;
&lt;p&gt;ドスパラで安く売っていたバルク品を買った。&lt;/p&gt;
&lt;h3&gt;CPU クーラー&lt;/h3&gt;
&lt;p&gt;&lt;a href=https://www.scythe.co.jp/product/cpu-cooler/scbyk-1000I/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;サイズ 白虎&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Intel のリテールクーラー (付属の CPU ファン) でも十分に冷やし切れていたが、夏になったときにやや不安があったので、買ってみた。懐中電灯を当てるとヒートシンクに反射して目がやられる。&lt;/p&gt;
&lt;h2&gt;合計金額&lt;/h2&gt;
&lt;p&gt;89,999 円 + Windows のライセンス&lt;/p&gt;
&lt;p&gt;満足感に対してかなり安いという印象がある。自作なので、不足を感じるならばパーツを交換すればよいというのもパーツ選びには影響したと思う。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>特定の Microsoft Office アプリのみインストールする</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-01-26/ms-office-odt' />
      <id>https://blog.comame.xyz/entries/2021-01-26/ms-office-odt</id>
      <updated>2021-01-26T00:00:00Z</updated>
      <summary>&lt;p&gt;Office Deployment Tool (Office 展開ツール) を使用することで、特定の Office アプリのみインストールできる。&lt;/p&gt;
&lt;h2&gt;ドキュメント&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://docs.microsoft.com/ja-jp/deployoffice/overview-office-deployment-tool target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://aka.ms/ODT&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;設定ファイルのサンプル&lt;/h2&gt;
&lt;p&gt;Excel, Word, PowerPoint のみをインストールするときの例。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;&amp;lt;Configuration&amp;gt;&lt;/code&gt;
&lt;code&gt;    &amp;lt;Add&amp;gt;&lt;/code&gt;
&lt;code&gt;        &amp;lt;Product ID=&quot;O365ProPlusRetail&quot;&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;Language ID=&quot;ja-jp&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Access&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Groove&quot;/&amp;gt;    &amp;lt;!-- Onedrive for Business --&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Lync&quot;/&amp;gt;      &amp;lt;!-- Skype for Business --&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;OneDrive&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;OneNote&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Outlook&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Publisher&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;            &amp;lt;ExcludeApp ID=&quot;Teams&quot;/&amp;gt;&lt;/code&gt;
&lt;code&gt;        &amp;lt;/Product&amp;gt;&lt;/code&gt;
&lt;code&gt;    &amp;lt;/Add&amp;gt;&lt;/code&gt;
&lt;code&gt;&amp;lt;/Configuration&amp;gt;&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>Windows 10 のロック画面でスリープさせない設定</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2021-01-18/windows-lockscreen-registry' />
      <id>https://blog.comame.xyz/entries/2021-01-18/windows-lockscreen-registry</id>
      <updated>2021-01-18T00:00:00Z</updated>
      <summary>&lt;h2&gt;ロック中に Windows がスリープする&lt;/h2&gt;
&lt;p&gt;通常、ロック画面で放置しておくと勝手にスリープしてしまうようなのだが、この挙動で困ってしまうので、次の方法で対処した。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;regedit.exe で &lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\238C9FA8-0AAD-41ED-83F4-97BE242C8F20\7bc4a2f9-d8fc-4469-b07b-33eb785aaca0&lt;/code&gt; の Attribute を 2 にする&lt;/li&gt;
&lt;li&gt;電源オプションの詳細設定で「スリープ」-「システム無人スリープタイムアウト」の時間を適当に設定する&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;ロック中にモニターが切れる&lt;/h2&gt;
&lt;p&gt;同様に、スリープ中にモニターが切れる問題。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\8EC4B3A5-6868-48c2-BE75-4F3044BE88A7&lt;/code&gt; の Attribute を 2 にする&lt;/li&gt;
&lt;li&gt;「ディスプレイ」-「コンソールロックディスプレイオフのタイムアウト」を適当に設定する&lt;/li&gt;
&lt;/ol&gt;
</summary>
    </entry>
    
    <entry>
      <title>npm のパッケージをとりあえず全部アップデートする方法</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-12-23/update-npm-packages' />
      <id>https://blog.comame.xyz/entries/2020-12-23/update-npm-packages</id>
      <updated>2020-12-23T00:00:00Z</updated>
      <summary>&lt;p&gt;次のコマンドを叩く。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;node -e &quot;console.log(Object.keys(require(&apos;./package.json&apos;).dependencies).join(&apos; &apos;))&quot; | xargs npm i&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;やっていることは単純で、package.json の &lt;code&gt;dependencies&lt;/code&gt; のパッケージ名を Node.js で列挙して、 &lt;code&gt;npm install&lt;/code&gt; の引数に渡している。package.json のバージョン指定をすべて無視して、とりあえず普通に &lt;code&gt;npm install&lt;/code&gt; したときにインストールされるものにバージョンを揃えることができる。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>WSL を引っ越す</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-12-19/import-wsl' />
      <id>https://blog.comame.xyz/entries/2020-12-19/import-wsl</id>
      <updated>2020-12-19T00:00:00Z</updated>
      <summary>&lt;ol&gt;
&lt;li&gt;&lt;code&gt;wsl --export ubuntu-20.04 &amp;lt;アーカイブのパス&amp;gt;&lt;/code&gt; でエクスポート&lt;/li&gt;
&lt;li&gt;Ubuntu-20.04 のストアアプリをアンインストールしておく&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wsl --import ubuntu-20.04 &amp;lt;インストール場所&amp;gt; &amp;lt;アーカイブのパス&amp;gt;&lt;/code&gt; でインポート&lt;/li&gt;
&lt;li&gt;Microsoft Store から Ubuntu-20.04 をインストール&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ubuntu2004 config --default-user &amp;lt;user&amp;gt;&lt;/code&gt; でデフォルトユーザーを普段のものに変更&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;少なくとも現時点では、ほぼ公式ドキュメントに書いてある手順そのまま。&lt;/p&gt;
&lt;h2&gt;なぜこうなるか&lt;/h2&gt;
&lt;p&gt;既に同じ名前のディストリビューションがインストールされている場合 &lt;code&gt;wsl --import&lt;/code&gt; で弾かれるため、インポート時にはアンインストールしておく必要がある。アンインストールせずとも &lt;code&gt;wsl --unregister &amp;lt;distro&amp;gt;&lt;/code&gt; だけで良いかどうかは未検証。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wsl --import&lt;/code&gt; だけでは &lt;code&gt;ubuntu2004&lt;/code&gt; コマンドが使用できず、デフォルトユーザーを root から変更できない。どうやら Microsoft Store からインストールしたディストリビューションでないとディストリビューション名のコマンドは使用できないようであるため、改めて Microsoft Store からディストリビューションをインストールする。この時、ストアアプリとしてインストールされる Ubuntu のディストリビューション名も &lt;code&gt;ubuntu-20.04&lt;/code&gt; だが、インポートしたデータは上書きされない。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=https://docs.microsoft.com/ja-jp/windows/wsl/wsl-config#change-the-default-user-for-a-distribution target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;配布の既定のユーザーを変更する, Linux ディストリビューションの管理 | Microsoft Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>[Pixel] ゲーム中にうっかりホーム画面に戻ってしまう</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-12-10/pixel-pin-screen-game' />
      <id>https://blog.comame.xyz/entries/2020-12-10/pixel-pin-screen-game</id>
      <updated>2020-12-10T00:00:00Z</updated>
      <summary>&lt;h2&gt;Q. ゲームするときにうっかりホーム画面に戻ってしまうのが面倒&lt;/h2&gt;
&lt;p&gt;音ゲーなど、画面の上端や下端からスワイプするような操作を頻繁にするゲームだと、誤って通知バーを出してしまったりホーム画面に戻ってしまったりする事故が多発する (特にジェスチャーナビゲーションを使っている場合)。&lt;/p&gt;
&lt;h2&gt;A. 画面固定を使おう&lt;/h2&gt;
&lt;p&gt;画面固定モードを使うことで、通知センターとホーム画面に戻るジェスチャーを無効にできる。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://support.google.com/pixelphone/answer/6118421?hl=ja target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://support.google.com/pixelphone/answer/6118421?hl=ja&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;「戻る」ジェスチャーは相変わらず反応するので注意。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ブログを Next.js で作り直した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-08-30/renew-blog-nextjs' />
      <id>https://blog.comame.xyz/entries/2020-08-30/renew-blog-nextjs</id>
      <updated>2020-08-30T00:00:00Z</updated>
      <summary>&lt;p&gt;このブログを &lt;a href=https://nextjs.org/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Next.js&lt;/a&gt; で作り直した。以前に動いていたものをほぼ再現できたと思う。&lt;/p&gt;
&lt;h2&gt;嬉しくなった点&lt;/h2&gt;
&lt;h3&gt;ソースコードが大幅に読みやすくなった&lt;/h3&gt;
&lt;p&gt;以前は自作ルーター + 巨大 &lt;a href=https://github.com/comame/blog.comame.xyz/blob/707d9271f0e17eddd231ee7041408566e424bbdc/assets/js/app.js target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;app.js&lt;/a&gt; で全てを行っていたため、これは何ですかと言いたくなるようなコードになっていた。&lt;/p&gt;
&lt;h3&gt;アクセス時に読み込まれる CSS が少し小さくなった&lt;/h3&gt;
&lt;p&gt;以前はページごとに 3 つの CSS ファイルに分割し、minify したものを HTML に埋め込んでいた。今は Next.js がうまいことコンポーネント分割してくれている、らしい。&lt;/p&gt;
&lt;p&gt;以前はインラインで記述していたためすべてのアクセスでダウンロード量に影響したが、今は &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; タグで埋め込まれているため、初回より後のアクセスでは読み込みが速くなると思われる。ただ、初回アクセスでどちらが速いのかはよく分かっていない。&lt;/p&gt;
&lt;h3&gt;GitHub Actions でのビルドが相当速くなった&lt;/h3&gt;
&lt;p&gt;以前は Puppeteer を使って静的サイトジェネレーションを行っていたが、それを Next.js がやってくれるようになったおかげで、ビルドが相当高速化した。以前は 1 分以上かかっていたものが、今は 30 秒程度で終わるようになった。&lt;/p&gt;
&lt;h3&gt;開発環境でのプレビューが容易になった&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npx next dev&lt;/code&gt; を叩くだけで容易にプレビューできるようになった。&lt;/p&gt;
&lt;h2&gt;私はここで苦しみました&lt;/h2&gt;
&lt;h3&gt;SSG をすると、&lt;code&gt;next/link&lt;/code&gt; のリンクが機能しない&lt;/h3&gt;
&lt;p&gt;これは以前の URL 構造をそのまま保とうとしたことが原因であるため、どちらかといえば私の使い方の問題であろう。Next.js の Dynamic Routing では、各ページの URL に &lt;code&gt;.html&lt;/code&gt; の拡張子を付与することはできないため、SSG したときにサイト内リンクが尽くリンク切れを起こす問題が発生した。例えば、SSG 後の URL は &lt;code&gt;/tags/foo.html&lt;/code&gt; であるにも関わらず、サイト内リンクの &lt;code&gt;href&lt;/code&gt; は &lt;code&gt;/tags/foo&lt;/code&gt; のままである、といった具合である。&lt;/p&gt;
&lt;p&gt;この問題は Next.js の設定を弄ることで容易に解消できる。&lt;code&gt;next.config.js&lt;/code&gt; で &lt;a href=https://nextjs.org/docs/api-reference/next.config.js/trailing-slash target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;&amp;lt;code&amp;gt;trailingSlash: true&amp;lt;/code&amp;gt;&lt;/a&gt; を設定すればよい。しかし、私のブログの URL 構造を保とうとした場合、これでは過去のリンクがすべてリンク切れを起こしてしまう問題があり、この方法は導入できなかった。&lt;/p&gt;
&lt;p&gt;そこで、&lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; をラップし、SSG を行うときは手動で &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; タグを返すようにした。SSG かどうかは環境変数によって判別することとした (&lt;a href=https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;NEXT_PUBLIC_ から始まる環境変数はブラウザからも参照できる&lt;/a&gt;) 。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://github.com/comame/blog.comame.xyz/blob/b7b80220685f6c568bcb9cfedf820db953899123/src/lib/link.tsx target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/comame/blog.comame.xyz/blob/b7b80220685f6c568bcb9cfedf820db953899123/src/lib/link.tsx&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;GitHub Pages でうまいことホストされない&lt;/h3&gt;
&lt;p&gt;以前と同様に GitHub Pages でホストしようと思ったところ、なぜか &lt;code&gt;docs/_next/&lt;/code&gt; 配下のファイルが 404 を返してしまう問題に遭遇した。これは GitHub Pages に組み込まれている Jekyll が原因であり、&lt;code&gt;_&lt;/code&gt; から始まるディレクトリ名を持つディレクトリは配信されないようである。&lt;/p&gt;
&lt;p&gt;これを解消するために、GitHub Pages で配信するディレクトリの最上位ディレクトリ (このブログでは &lt;code&gt;&amp;lt;projectroot&amp;gt;/docs/&lt;/code&gt;) に `.nojekyll ファイルを置いた。&lt;/p&gt;
&lt;p&gt;どうやらこの方法は GitHub のドキュメントには書かれておらず、&lt;a href=https://github.blog/2009-12-29-bypassing-jekyll-on-github-pages/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;GitHub Blog&lt;/a&gt; にしか公式の記事を見つけることができなかった。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>TOTP をフルスクラッチした</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-07-20/implement-totp-in-js' />
      <id>https://blog.comame.xyz/entries/2020-07-20/implement-totp-in-js</id>
      <updated>2020-07-20T00:00:00Z</updated>
      <summary>&lt;p&gt;JavaScript で TOTP を実装してみたので、その実装メモなど。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://github.com/comame/TOTP target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/comame/TOTP&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;TOTP&lt;/h2&gt;
&lt;p&gt;RFC 6238: TOTP: Time-Based One-Time Password Algorithm で規定される。Google Authenticator アプリなどを使って時刻ベースで生成される (大抵) 6 桁のワンタイムパスワードであり、2 要素認証に用いられる。&lt;/p&gt;
&lt;p&gt;疑似コードで次のように表される。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;K: 共有シークレット、QR コードとかで読み込むやつ&lt;/code&gt;
&lt;code&gt;X: ステップ秒。大抵 30 秒。この値の周期でトークンが切り替わる&lt;/code&gt;
&lt;code&gt;T0: Unix Time の開始秒。大抵 0&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;T = (Current Unix Time - T0) / X) as Integer&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;TOTP(K, T) = HOTP(K, T) = Truncate(HMAC-SHA-1(K, T))&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;トークンの生成アルゴリズムには HOTP を使用する。&lt;/p&gt;
&lt;p&gt;のちに記述するように、HOTP では T の値が 32-bit までしかサポートされていないため、4.2 節では 32-bit より大きい整数をサポートするように規定されている。具体的にどう実装するのかは記述されていないので、Google Authenticator の実装などを見るのが良いと思われる。&lt;/p&gt;
&lt;h2&gt;HOTP&lt;/h2&gt;
&lt;p&gt;RFC 4226: HOTP: An HMAC-Based One-Time Password Algorithm で規定される。カウンターベースのワンタイムパスワード。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;K: 共有シークレット&lt;/code&gt;
&lt;code&gt;C: カウンター。8-byte の整数&lt;/code&gt;
&lt;code&gt;HS: 20-byte&lt;/code&gt;
&lt;code&gt;S: 31-bit&lt;/code&gt;
&lt;code&gt;D: 桁数が Digit の HOTP トークン&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;DT(HS: bytes[20])&lt;/code&gt;
&lt;code&gt;    OffsetBits = HS[19] &amp; 0xF // HS[19] の下位 4-bit&lt;/code&gt;
&lt;code&gt;    Offset = OffsetBits as Integer // 0 &amp;lt;= offset&amp;lt;= 15&lt;/code&gt;
&lt;code&gt;    P = HS[Offset]...HS[Offset + 3]&lt;/code&gt;
&lt;code&gt;    return P &amp; 0x7FFFFFFF // 下位 31-bit&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;HS = HMAC-SHA-1(K, C)&lt;/code&gt;
&lt;code&gt;S = DT(HS)&lt;/code&gt;
&lt;code&gt;D = (S as Integer) mod 10^Digit&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;RFC の 5.4 節の例は次の通りである。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;HS = {&lt;/code&gt;
&lt;code&gt;    1F, 86, 98, 69, 0E,&lt;/code&gt;
&lt;code&gt;    02, CA, 16, 61, 85,&lt;/code&gt;
&lt;code&gt;    50, EF, 7F, 19, DA,&lt;/code&gt;
&lt;code&gt;    8E, 94, 5B, 55, 5A&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;OffsetBits = 0xA&lt;/code&gt;
&lt;code&gt;Offset = 10&lt;/code&gt;
&lt;code&gt;P = 0x50EF7F19&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;S = 0x50EF7F19 &amp; 0x7FFFFFFF = 0x50EF7F19&lt;/code&gt;
&lt;code&gt;D = 872921&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;HMAC&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://www.ipa.go.jp/security/rfc/RFC2104JA.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.ipa.go.jp/security/rfc/RFC2104JA.html&lt;/a&gt; に日本語での解説がある。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;H: ハッシュ関数&lt;/code&gt;
&lt;code&gt;K: シークレット&lt;/code&gt;
&lt;code&gt;M: メッセージ&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;ipad = 0x3636...&lt;/code&gt;
&lt;code&gt;opad = 0x5C5C...&lt;/code&gt;
&lt;code&gt;// ipad, opad の長さはハッシュ関数のブロック長 (SHA-1 の場合 512-bit) と同一&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;||: ビットの連結&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;HMAC(K, M) = H((K xor opad) || H((K xor ipad) || M))&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;K と opad, ipad とで排他的論理和をとっていることから分かるように、K の長さもハッシュ関数のブロック長と同一にする必要がある。K は入力値であることから、長さを揃えるために次のような処理を順に行う。&lt;/p&gt;
&lt;p&gt;K の長さがブロック長より大きい場合、K をハッシュ関数に通す。K の長さがブロック長より小さい場合、ブロック長と同一になるまで末尾に 0 を追加する。&lt;/p&gt;
&lt;h2&gt;SHA-1&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://www.ipa.go.jp/security/rfc/RFC3174JA.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.ipa.go.jp/security/rfc/RFC3174JA.html&lt;/a&gt; に日本語での解説がある。&lt;/p&gt;
&lt;p&gt;まずはメッセージのパディングを行い、メッセージ長を 512-bit の倍数にする。オリジナルのメッセージ長が 512-bit の倍数であった時にもパディングは行う。パディングは次の手順で行う。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;末尾に 1-bit の 1 を付加する&lt;/li&gt;
&lt;li&gt;メッセージ長が mod 512-bit = 448 になるまで 0 を末尾に付加する&lt;/li&gt;
&lt;li&gt;オリジナルのメッセージ長を 64-bit で付加する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;次の関数を定義する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;f(t; B, C, D) = (B &amp; C) | ((~B) &amp; D); (0 &amp;lt;= t &amp;lt;= 19)&lt;/code&gt;
&lt;code&gt;f(t; B, C, D) = B ^ C ^ D; (20 &amp;lt;= t &amp;lt;= 39)&lt;/code&gt;
&lt;code&gt;f(t; B, C, D) = (B &amp; C) | (B &amp; D) | (C &amp; D) (40 &amp;lt;= t &amp;lt;= 59)&lt;/code&gt;
&lt;code&gt;f(t; B, C, D) = B ^ C ^ D; (60 &amp;lt;= t &amp;lt;= 79)&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;K(t) = 0x5A827999; (0 &amp;lt;= t &amp;lt;= 19)&lt;/code&gt;
&lt;code&gt;K(t) = 0x6ED9EBA1; (20 &amp;lt;= t &amp;lt;= 39)&lt;/code&gt;
&lt;code&gt;K(t) = 0x8F1BBCDC; (40 &amp;lt;= t &amp;lt;= 59)&lt;/code&gt;
&lt;code&gt;K(t) = 0xCA62C1D6; (60 &amp;lt;= t &amp;lt;= 79)&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;S^N(n) = (M &amp;lt;&amp;lt; n) | (M &amp;gt;&amp;gt; (32 - n)) // 循環左シフト&lt;/code&gt;
&lt;code&gt;A + B = (A + B) mod (2 ** 32)&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;複数の計算方法があるようだが、今回は次の方法で計算した。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;M: 512-bit ごとに区切ったメッセージ&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;H0 = 0x67452301&lt;/code&gt;
&lt;code&gt;H1 = 0xEFCDAB89&lt;/code&gt;
&lt;code&gt;H2 = 0x98BADCFE&lt;/code&gt;
&lt;code&gt;H3 = 0x10325476&lt;/code&gt;
&lt;code&gt;H4 = 0xC3D2E1F0&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;for each M(i)&lt;/code&gt;
&lt;code&gt;    W[16] = M(i) // M(i) を 32-bit ごとに分割する&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;    for t from 16 to 79&lt;/code&gt;
&lt;code&gt;        W[t] = S^1(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16])&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;    A = H0; B = H1; C = H2; D = H3; E = H4&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;    for t from 0 to 79&lt;/code&gt;
&lt;code&gt;        TMP = S^5(A) + f(t; B, C, D) + E + W[t] + K(t)&lt;/code&gt;
&lt;code&gt;        E = D; D = C; C = s^30(B); A = TMP&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;    H0 += A; H1 += B; H2 += C; H3 += D; H4 += E&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;// H0 H1 H2 H3 H4 をこの順で並べる&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;実装で苦しんだところ (大体うっかり)&lt;/h2&gt;
&lt;h3&gt;HOTP&lt;/h3&gt;
&lt;p&gt;C は 8-byte 整数。&lt;/p&gt;
&lt;h3&gt;HMAC&lt;/h3&gt;
&lt;p&gt;ハッシュ関数に SHA-1 を使用する場合、ブロック長は 64-byte、出力長は 20-byte であることから、K にハッシュを通した後に 0 を追加する処理を行う必要がある。&lt;/p&gt;
&lt;h3&gt;SHA-1&lt;/h3&gt;
&lt;p&gt;(追記あり) 左循環シフト。&lt;code&gt;(n: number, x: number) =&amp;gt; (x &amp;lt;&amp;lt; n) | (x &amp;gt;&amp;gt; (32 - n))&lt;/code&gt; と素直に書くと、x は 32-bit 整数、&lt;code&gt;0 &amp;lt;= n &amp;lt; 32&lt;/code&gt; であることからオーバーフローする。&lt;/p&gt;
&lt;h2&gt;参考にしたもの&lt;/h2&gt;
&lt;dl&gt;
    &lt;dt&gt;IPA の RFC 日本語訳&lt;/dt&gt;
    &lt;dd&gt;HMAC と SHA-1 の実装&lt;/dd&gt;
    &lt;dt&gt;Golang のソースコード&lt;/dt&gt;
    &lt;dd&gt;テストケースや各アルゴリズムの実装。読みやすい。&lt;/dd&gt;
    &lt;dt&gt;RFC&lt;/dt&gt;
    &lt;dd&gt;テスト用のサンプルケースが Appendix に書いてあることを初めて知った&lt;/dd&gt;
&lt;/dl&gt;


&lt;h2&gt;左循環シフトの高速化&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://github.com/comame/TOTP/compare/e495732d9156e805c78ff2626e95f09301123b32...361549fc64a207bcc402f48f802ee4d68b61cac6 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://github.com/comame/TOTP/compare/e495732d9156e805c78ff2626e95f09301123b32...361549fc64a207bcc402f48f802ee4d68b61cac6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;オーバーフローをしないためにビットの配列に変換してシフト操作をしていたものを、number のまま計算できるように書き換えた。オーバーフローを起こさないため、32-bit 整数を 8-bit ずつに区切り、場合分けした。また、シフト演算子を使うとオーバーフローを起こすため、掛け算と割り算でシフト演算子と同等の計算を行うようにした。&lt;/p&gt;
&lt;p&gt;次のようなコードで 10 回ずつ計測しそれぞれ平均をとったところ、高速化前は 616 ms、高速化後は 5 ms となった。&lt;/p&gt;
&lt;p&gt;また、SHA-1 の計算自体も、おおむね半分から 5 分の 1 程度の実行時間に短縮された。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;const t = Date.now()&lt;/code&gt;
&lt;code&gt;for (let i = 0; i &amp;lt; 100000; i +=1) circularShift(16, 0x12345678)&lt;/code&gt;
&lt;code&gt;console.log(Date.now() - t)&lt;/code&gt;
&lt;/pre&gt;&lt;pre&gt;
&lt;code&gt;測定結果&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;高速化前 [ms]&lt;/code&gt;
&lt;code&gt;    608 605 611 598 612 599 698 605 612 611&lt;/code&gt;
&lt;code&gt;高速化後 [ms]&lt;/code&gt;
&lt;code&gt;    4   4   6   4   5   5   5   4   5   5&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>Chrome の検索エンジン設定</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-07-08/chrome-search-engine' />
      <id>https://blog.comame.xyz/entries/2020-07-08/chrome-search-engine</id>
      <updated>2020-07-08T00:00:00Z</updated>
      <summary>&lt;p&gt;Chrome には、アドレスバーに特定のキーワードを入力すると、特定のサイト内を直接検索できる機能がある。手動で検索エンジンとキーワードを設定することもできるので、頻繁に検索するサイトは登録しておくと便利。&lt;/p&gt;
&lt;p&gt;最近登録していた検索エンジンを整理したので、備忘録も兼ねて以下に記す。&lt;/p&gt;
&lt;h2&gt;辞書とか翻訳とか&lt;/h2&gt;
&lt;p&gt;英単語の意味を調べたり、言葉遣いに誤りがないか調べたりするときに便利。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;キーワード&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;説明&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;URL (%s = 検索ワード)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;translate&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Google 翻訳&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://translate.google.com/#auto|auto|%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://translate.google.com/#auto|auto|%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;en&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Dictionary.com&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.dictionary.com/browse/%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.dictionary.com/browse/%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;lexico&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Oxford Dictionaries&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.lexico.com/definition/%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.lexico.com/definition/%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;jp&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;weblio&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.weblio.jp/content/%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.weblio.jp/content/%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;開発の資料とか&lt;/h2&gt;
&lt;p&gt;公式の資料に当たりたいときに関係ないサイトが検索結果に出てきたり、頻繁にサイト内検索するのに毎回 URL を直打ちするのが面倒なので設定。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;キーワード&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;説明&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;URL (%s = 検索ワード)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;caniuse&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;ブラウザごとの実装状況&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://caniuse.com/#search=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://caniuse.com/#search=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;mdn&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;MDN Web Docs 内を Google で検索&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.google.com/search?q=site%3Adeveloper.mozilla.org%2Fja+%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.google.com/search?q=site%3Adeveloper.mozilla.org%2Fja+%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;node&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Node.js のドキュメントを Google で検索&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.google.co.jp/search?q=site%3Ahttps%3A%2F%2Fnodejs.org%2Fapi+%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.google.co.jp/search?q=site%3Ahttps%3A%2F%2Fnodejs.org%2Fapi+%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;rfc&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;RFC を Google で検索&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.google.com/search?q=site%3Ahttps%3A%2F%2Ftools.ietf.org%2Fhtml+%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.google.com/search?q=site%3Ahttps%3A%2F%2Ftools.ietf.org%2Fhtml+%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;npm&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;npm パッケージの検索&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.npmjs.com/search?q=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.npmjs.com/search?q=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;docker&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Docker Hub&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://hub.docker.com/search?q=%s&amp;type=image target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://hub.docker.com/search?q=%s&amp;amp;type=image&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;eng&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Google を英語で検索&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://google.com/search?gl=us&amp;hl=en&amp;gws_rd=cr&amp;pws=0&amp;q=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://google.com/search?gl=us&amp;amp;hl=en&amp;amp;gws_rd=cr&amp;amp;pws=0&amp;amp;q=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;その他&lt;/h2&gt;
&lt;p&gt;その他、よく使うもの。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;キーワード&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;説明&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;URL (%s = 検索ワード)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;whois&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;whois&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://www.whois.com/search.php?query=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://www.whois.com/search.php?query=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;chrome&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Google Chrome ヘルプ&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://support.google.com/chrome/search?hl=ja&amp;q=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://support.google.com/chrome/search?hl=ja&amp;amp;q=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;gmail&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Gmail ヘルプ&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://support.google.com/mail/search?hl=ja&amp;q=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://support.google.com/mail/search?hl=ja&amp;amp;q=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;accounts&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Google アカウント ヘルプ&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;a href=https://support.google.com/accounts/search?hl=ja&amp;q=%s target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;https://support.google.com/accounts/search?hl=ja&amp;amp;q=%s&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
</summary>
    </entry>
    
    <entry>
      <title>Docker を使用したGradle のビルドを高速化する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-07-02/improve-perforcmance-docker-build-gradle' />
      <id>https://blog.comame.xyz/entries/2020-07-02/improve-perforcmance-docker-build-gradle</id>
      <updated>2020-07-02T00:00:00Z</updated>
      <summary>&lt;h2&gt;問題&lt;/h2&gt;
&lt;p&gt;Docker で &lt;code&gt;gradle build&lt;/code&gt; を実行すると、&lt;code&gt;Starting a Gradle Daemon&lt;/code&gt; で 1 分近くかかってしまう。&lt;/p&gt;
&lt;h2&gt;解決法&lt;/h2&gt;
&lt;p&gt;適当に &lt;code&gt;gradle clean&lt;/code&gt; などをあらかじめ実行しておく&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;  # Dockerfile&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;  FROM gradle&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;  COPY build.gradle.kt build.gradle.kt&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+ RUN gradle clean&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;  COPY src src&lt;/code&gt;
&lt;code&gt;  RUN gradle build&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;Daemon 起動中の出力を見る限り、依存関係の計算やダウンロード、&lt;code&gt;build.gradle.kt&lt;/code&gt; のコンパイルに時間がかかっているように見える。一度 &lt;code&gt;gradle clean&lt;/code&gt; を実行することによって、&lt;code&gt;gradle build&lt;/code&gt; の実行時にはそれらを回避できる。また、&lt;code&gt;src/&lt;/code&gt; 以下を変更しただけの場合 &lt;code&gt;RUN gradle clean&lt;/code&gt; はキャッシュが利用できるため、初回ビルドより後では高速にビルドできる。&lt;/p&gt;
&lt;h2&gt;測定&lt;/h2&gt;
&lt;p&gt;簡単な Hello, world! コードで検証した。次に記載するファイル以外は IntelliJ IDEA で生成されたものをそのまま使用した。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;# src/main/kotlin/xyz/comame/test/Main.kt&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;package xyz.comame.test&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;fun main() {&lt;/code&gt;
&lt;code&gt;    println(&quot;Hello,world!&quot;)&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;/pre&gt;&lt;pre&gt;
&lt;code&gt;# Dockerfile&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;FROM gradle&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;RUN useradd -m user&lt;/code&gt;
&lt;code&gt;USER user&lt;/code&gt;
&lt;code&gt;WORKDIR /home/user&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;COPY gradle.properties gradle.properties&lt;/code&gt;
&lt;code&gt;COPY settings.gradle.kts settings.gradle.kts&lt;/code&gt;
&lt;code&gt;COPY build.gradle.kts build.gradle.kts&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;RUN gradle clean&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;COPY src src&lt;/code&gt;
&lt;code&gt;RUN gradle build&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;初回ビルド&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RUN gradle clean&lt;/code&gt; を追加した場合、&lt;code&gt;RUN gradle clean&lt;/code&gt; は 49 秒、&lt;code&gt;RUN gradle build&lt;/code&gt; は 18 秒かかった。一方、追加しなかった場合、&lt;code&gt;RUN gradle build&lt;/code&gt; は 56 秒かかった。&lt;/p&gt;
&lt;h3&gt;2 回目のビルド&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/main/kotlin/xyz/comame/test/Main.kt&lt;/code&gt; の Hello, world! の文字列を変更して再度ビルドした。&lt;code&gt;docker build&lt;/code&gt; のキャッシュは有効である。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RUN gradle clean&lt;/code&gt; を追加した場合、&lt;code&gt;RUN gradle clean&lt;/code&gt; はキャッシュが使用され、&lt;code&gt;RUN gradle build&lt;/code&gt; は 18 秒かかった。一方、追加しなかった場合、&lt;code&gt;RUN gradle build&lt;/code&gt; は 1 分 3 秒かかった。&lt;/p&gt;
&lt;p&gt;複数回測定を繰り返した場合でも、上記とおおむね同様の結果を得られた。&lt;/p&gt;
&lt;h2&gt;考慮事項&lt;/h2&gt;
&lt;p&gt;Dockerfile に &lt;code&gt;USER root&lt;/code&gt; 、&lt;code&gt;WORKDIR /root&lt;/code&gt; を指定した場合、&lt;code&gt;RUN gradle build&lt;/code&gt; は高速化されなかった。&lt;code&gt;root&lt;/code&gt; ユーザーでは効果がないのか、&lt;code&gt;WORKDIR /root&lt;/code&gt; の場合に問題があるのか、あるいは他に原因があるのか、今回は未検証である。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Kotlin プロジェクトを作成する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-06-28/create-kotlin-project' />
      <id>https://blog.comame.xyz/entries/2020-06-28/create-kotlin-project</id>
      <updated>2020-06-28T00:00:00Z</updated>
      <summary>&lt;p&gt;Gradle 周りの設定の備忘録。&lt;/p&gt;
&lt;h2&gt;Gradle Wrapper&lt;/h2&gt;
&lt;p&gt;Gradle Wrapper を準備する。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;$ gradle wrapper&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;ビルド設定&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;gradle jar&lt;/code&gt; で実行可能な Jar ファイルを吐けるようにする。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;// build.gradle.kts&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;  plugins {&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+   application&lt;/code&gt;
&lt;code&gt;  }&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+ application {&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+     mainClassName = &quot;xyz.comame.project.MainKt&quot;&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+ }&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+ val jar by tasks.getting(Jar::class) {&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+     manifest {&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+         attributes[&quot;Main-Class&quot;] = &quot;xyz.comame.project.MainKt&quot;&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+     }&lt;/code&gt;
&lt;code class=&apos;addition&apos;&gt;+ }&lt;/code&gt;
&lt;/pre&gt;&lt;h2&gt;ソースコード&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;src/main/kotlin/xyz/comame/project/&lt;/code&gt; に置く。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;正直よく分かっていない。&lt;code&gt;gradle init&lt;/code&gt; を試してみたら、実はこれでもプロジェクト設定をしてくれるらしい。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Windows で動くおうちサーバにアクセスできなかった問題</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-05-17/cannot-connect-house-server' />
      <id>https://blog.comame.xyz/entries/2020-05-17/cannot-connect-house-server</id>
      <updated>2020-05-17T00:00:00Z</updated>
      <summary>&lt;h2&gt;端的に&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;「プライベート」ネットワークとして接続する&lt;/li&gt;
&lt;li&gt;ファイアウォールを確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;h3&gt;ネットワーク&lt;/h3&gt;
&lt;pre&gt;
&lt;code&gt;+--------------------+&lt;/code&gt;
&lt;code&gt;|       Router       |&lt;/code&gt;
&lt;code&gt;|   192.168.0.1/24   |&lt;/code&gt;
&lt;code&gt;+--------------------+&lt;/code&gt;
&lt;code&gt;           |&lt;/code&gt;
&lt;code&gt;           |---------------------------|&lt;/code&gt;
&lt;code&gt;           |                           |&lt;/code&gt;
&lt;code&gt;+--------------------+      +--------------------+&lt;/code&gt;
&lt;code&gt;|       Windows      |      |       Android      |&lt;/code&gt;
&lt;code&gt;|     192.168.0.2    |      |     192.168.0.3    |&lt;/code&gt;
&lt;code&gt;+--------------------+      +--------------------+&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;HTTP サーバ&lt;/h3&gt;
&lt;p&gt;Windows で 8080 番ポートで待ち受け。&lt;/p&gt;
&lt;h2&gt;発生した問題&lt;/h2&gt;
&lt;p&gt;Windows マシンで動かしているサーバに、自宅内の他の端末からアクセスできなかった&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows から &lt;code&gt;curl http://localhost:8080&lt;/code&gt; は問題なし&lt;/li&gt;
&lt;li&gt;Windows から &lt;code&gt;curl http://192.168.0.2:8080&lt;/code&gt; は問題なし&lt;/li&gt;
&lt;li&gt;Android から Chrome で &lt;a href=http://192.168.0.2:8080 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;http://192.168.0.2:8080&lt;/a&gt; を開こうとすると&lt;code&gt;ERR_CONNECTION_REFUSED&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;原因と対処&lt;/h2&gt;
&lt;h3&gt;Windows ファイアウォール&lt;/h3&gt;
&lt;p&gt;8080 番ポートへのアクセスが許可されていなかった。「受信の規則」に「TCP 8080番ポート」のアクセス許可を追加した。自宅のネットワークからのみアクセスできるようにするため、リモートアドレスは 192.168.0.1/24 のみとした。&lt;/p&gt;
&lt;h3&gt;ネットワーク プロフィール&lt;/h3&gt;
&lt;p&gt;ネットワークプロフィールが「パブリック」になっていた。Wi-Fi 設定から「プライベート」に変更した。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>PubSubHubbub について</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-05-04/websub' />
      <id>https://blog.comame.xyz/entries/2020-05-04/websub</id>
      <updated>2020-05-04T00:00:00Z</updated>
      <summary>&lt;p&gt;この記事は、&lt;a href=https://pubsubhubbub.appspot.com target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;pubsubhubbub.appspot.com&lt;/a&gt; で Subscribe した時の挙動について書く。&lt;/p&gt;
&lt;p&gt;pubsubhubbub.appspot.com は &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;PubSubHubbub Core 0.4&lt;/a&gt; と Permanent subscriptions を除く &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;PubSubHubbub Core 0.3&lt;/a&gt; に従っている。&lt;/p&gt;
&lt;p&gt;PubSubHubbub とは、Subscriber 側から見た場合は Webhook である。現在は W3C によって &lt;a href=https://w3c.github.io/websub/ target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;WebSub&lt;/a&gt; として公開されている。WebSub は PubSubHubbub Core 0.4 と概ね同じだと思われる (あまり読み込んでいない)。&lt;/p&gt;
&lt;h2&gt;PubSubHubbub Core 0.4&lt;/h2&gt;
&lt;p&gt;仕様を正確に翻訳したものではない。実装する際には元の仕様を参照することをおすすめする。&lt;/p&gt;
&lt;h3&gt;用語&lt;/h3&gt;
&lt;dl&gt;
    &lt;dt&gt;Topic&lt;/dt&gt;
    &lt;dd&gt;HTTP の URL。変更を購読するリソースを一意に表す。&lt;/dd&gt;
    &lt;dt&gt;Hub&lt;/dt&gt;
    &lt;dd&gt;PubSubHubbub の Subscribing と Publishing をどちらも実装するサーバ。&lt;/dd&gt;
    &lt;dt&gt;Subscriber&lt;/dt&gt;
    &lt;dd&gt;Topic の更新を受け取るエンティティ。&lt;/dd&gt;
    &lt;dt&gt;Subscription&lt;/dt&gt;
    &lt;dd&gt;Topic とその更新通知を受け取る Subscriber の一意な関係。Topic URL と Subscriber Callback URL をキーとする。&lt;/dd&gt;
    &lt;dt&gt;Subscriber Callback URL&lt;/dt&gt;
    &lt;dd&gt;Subscriber が通知を受け取る URL。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.2 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;2. Definitions&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscribing&lt;/h3&gt;
&lt;p&gt;購読を登録するとき、次のような流れとなる。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Subscriber が Hub に Subscription Request を送信する&lt;/li&gt;
&lt;li&gt;Hub が Subscription Request を検証する (OPTIONAL)&lt;/li&gt;
&lt;li&gt;Hub が Subscriber に Verificatin Request を送信する&lt;/li&gt;
&lt;li&gt;Subscriber が Hub に Verification Response を返す&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;5. Subscribing and Unsubscribing&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscription Request&lt;/h3&gt;
&lt;p&gt;以下のパラメータを &lt;code&gt;Content-Type: application/x-www-form-urlencoded&lt;/code&gt; で POST する。&lt;/p&gt;
&lt;dl&gt;
    &lt;dt&gt;hub.callback&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. Subscriber Callback URL。配信通知はこの URL に対して POST される。クエリパラメータは保持される (&lt;a href=&apos;https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.1.1&apos; rel=&apos;noopener&apos; target=&apos;_blank&apos;&gt;5.1.1. Subscription Parameter Details&lt;/a&gt;)。&lt;/dd&gt;
    &lt;dt&gt;hub.mode&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. &lt;code&gt;subscribe&lt;/code&gt; あるいは &lt;code&gt;unsubscribe&lt;/code&gt; のいずれかの値をとる。&lt;/dd&gt;
    &lt;dt&gt;hub.topic&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. 購読する Topic の URL。&lt;/dd&gt;
    &lt;dt&gt;hub.lease_seconds&lt;/dt&gt;
    &lt;dd&gt;OPTIONAL. 購読の有効な時間 (秒)。Hub はこの値に従ってもよいし、従わなくともよい。&lt;code&gt;hub.mode&lt;/code&gt; が &lt;code&gt;unsubscribe&lt;/code&gt; の場合は無視される。&lt;/dd&gt;
    &lt;dt&gt;hub.secret&lt;/dt&gt;
    &lt;dd&gt;OPTIONAL. 後に Subscriber が更新通知を検証できるようにするための値。詳しくは後述。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;既に有効な Subscription がある場合、Hub は Subscription を更新する。Subscription の確認に失敗した場合は、以前の購読状態を継続する。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.1 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;5.1. Subscriber Sends Subscription Request&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscription Response&lt;/h3&gt;
&lt;p&gt;Hub は HTTP &lt;code&gt;202 Accepted&lt;/code&gt; によって、Subscription Request が検証され、確認されることを示す。エラーが発生した場合、Hub は 4xx または 5xx を返す。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.1.2 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;5.1.2. Subscription Response Details&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Verification Request&lt;/h3&gt;
&lt;p&gt;Subscription Request が正しいものかを確認するため、Hub は Subscriber Callback URL に以下のクエリパラメータとともに GET リクエストを送信する。&lt;/p&gt;
&lt;dl&gt;
    &lt;dt&gt;hub.mode&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. Subscription Request に指定した値と同じ値を示す。&lt;/dd&gt;
    &lt;dt&gt;hub.topic&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. Subscription Request に指定した値と同じ値を示す。&lt;/dd&gt;
    &lt;dt&gt;hub.challenge&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. Hub によって生成されたランダムな文字列。Subscriber はこの値をレスポンスとして返すことにより、Subscription を確認する。&lt;/dd&gt;
    &lt;dt&gt;hub.lease_seconds&lt;/dt&gt;
    &lt;dd&gt;REQUIRED/OPTIONAL. &lt;code&gt;hub.mode&lt;/code&gt; が &lt;code&gt;subscribe&lt;/code&gt; の場合、REQUIRED となる。Hub によって決定された購読の有効な時間 (秒) を表す。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.3 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;5.3. Hub Verifies Intent of the Subscriber&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;リクエストされた内容が正しい場合、Subscriber は &lt;code&gt;hub.challenge&lt;/code&gt; の値をレスポンスボディに含め、2xx を返す。リクエストされた内容が正しくない場合、Subscriber は 404 を返す。&lt;/p&gt;
&lt;p&gt;その他のステータスコードを返した場合、またはレスポンスボディが &lt;code&gt;hub.challenge&lt;/code&gt; の値と異なる場合、確認は失敗する。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#rfc.section.5.3.1 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;5.3.1. Verification Details&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;更新の通知&lt;/h3&gt;
&lt;p&gt;コンテンツが更新された時、Hub は Subscriber Callback URL に POST リクエストを送信する。&lt;/p&gt;
&lt;p&gt;リクエストを受け取ったとき、Subscriber は 2xx を返さなければならない。それ以外のステータスコードを返した場合、通知は失敗したとみなされる (リダイレクトも機能しない)。2xx のレスポンスはあくまで正常に通知を受け取ったことを表すのみであり、通知の内容が正しいことを示すものではない。&lt;/p&gt;
&lt;p&gt;リクエストには、RFC5988 に規定される &lt;a href=https://tools.ietf.org/html/rfc5988 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Link Header&lt;/a&gt; が付与される。&lt;code&gt;rel=hub&lt;/code&gt; には Hub の URL を、&lt;code&gt;rel=self&lt;/code&gt; には Topic の URL が指定される。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#contentdistribution target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;7. Content Distribution&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;検証可能な更新の通知&lt;/h3&gt;
&lt;p&gt;Subscription Request に &lt;code&gt;hub.secret&lt;/code&gt; を含めた場合、Hub はペイロードの HMAC 署名をヘッダに付与する。署名は 40 バイトの 16 進数表示の SHA-1 である。ヘッダの形式は次のようになる。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;X-Hub-Signature: sha1=&amp;lt;signature&amp;gt;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;これにより、通知の内容が第三者に偽造されたものではないことを確認できる。&lt;/p&gt;
&lt;p&gt;&lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html#authednotify target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;8. Authenticated Content Distribution&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;PubSubHubbub Core 0.3 による追加事項&lt;/h2&gt;
&lt;h3&gt;メッセージの形式&lt;/h3&gt;
&lt;p&gt;更新の通知は &lt;a href=https://tools.ietf.org/html/rfc4287 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Atom&lt;/a&gt; あるいは &lt;a href=https://www.rssboard.org/rss-specification target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;RSS&lt;/a&gt; の形式である。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html#anchor4 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;4.  Atom Details&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscription Request&lt;/h3&gt;
&lt;p&gt;以下のパラメータが追加される。&lt;/p&gt;
&lt;dl&gt;
    &lt;dt&gt;hub.verify&lt;/dt&gt;
    &lt;dd&gt;REQUIRED. Subscriber が Subscription を確認する方法を指定する。&lt;code&gt;sync&lt;/code&gt; あるいは &lt;code&gt;async&lt;/code&gt; のいずれの値をとる。&lt;/dd&gt;
    &lt;dt&gt;hub.verify_token&lt;/dt&gt;
    &lt;dd&gt;OPTIONAL. 指定した場合、Hub はこの値を Verification Request のクエリパラメータに付与する。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;参考: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html#anchor5 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;6.1.  Subscriber Sends Subscription Request&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Verification Request&lt;/h3&gt;
&lt;p&gt;以下のクエリパラメータが追加される。&lt;/p&gt;
&lt;dl&gt;
    &lt;dt&gt;hub.verify_token&lt;/dt&gt;
    &lt;dd&gt;OPTIONAL. Subscription Request に指定した値をそのまま含む。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;以下のパラメータが変更される。&lt;/p&gt;
&lt;dl&gt;
    &lt;dt&gt;hub.lease_seconds&lt;/dt&gt;
    &lt;dd&gt;OPTIONAL/REQUIRED. 値が含まれていた場合の挙動は PubSubHubbub Core 0.4 と同一である。値が含まれない場合、後述するように Subscription の更新の挙動が変わる。&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html#verifysub target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;6.2.  Hub Verifies Intent of the Subscriber&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;自動的な Subscription の更新&lt;/h3&gt;
&lt;p&gt;注意: pubsubhubbub.appspot.com では非対応である。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hub.lease_seconds&lt;/code&gt; が経過する前に、Hub は自動的に Subscription の確認を行う。&lt;/p&gt;
&lt;p&gt;参照: &lt;a href=https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html#autorefresh target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;6.3 Automatic Subscription Refresh&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;pubsubhubbub.appspot.com の挙動&lt;/h2&gt;
&lt;p&gt;筆者が自分の環境で確認したものである。仕様に定められた挙動ではないため、将来的に変更される可能性もある。&lt;/p&gt;
&lt;h3&gt;Subscription Request の宛先&lt;/h3&gt;
&lt;p&gt;送信先は &lt;code&gt;https://pubsubhubbub.appspot.com/subscribe&lt;/code&gt; である。&lt;/p&gt;
&lt;h3&gt;Subscription Response のステータスコード&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;hub.verify&lt;/code&gt; が &lt;code&gt;sync&lt;/code&gt; の場合、正常に Subscription Request が受理されたときは 204 No Content を返す。&lt;/p&gt;
&lt;h3&gt;Permanent Subscription の非対応&lt;/h3&gt;
&lt;p&gt;PubSubHubbub Core 0.3 の 6.3 Automatic Subscription Refresh には対応しない。Subscriber は hub.lease_seconds が経過する前に再び Subscription Request を行う必要がある。&lt;/p&gt;
&lt;h2&gt;補足・考察&lt;/h2&gt;
&lt;p&gt;pubsubhubbub.appspot.com は PubSubHubbub Core 0.4 に加えて PubSubHubbub Core 0.3 も参照するが、Permanent Subscription には非対応であるため、実用上は &lt;code&gt;hub.verify_token&lt;/code&gt; が追加されることにだけ注意すれば良いと思われる。大抵の場合は &lt;code&gt;hub.verify&lt;/code&gt; を &lt;code&gt;sync&lt;/code&gt; として問題ないであろう。&lt;/p&gt;
&lt;p&gt;PubSubHubbub Core 0.3 では更新通知のメッセージ形式が Atom または RSS と規定されているが、PubSubHubbub Core 0.4 および WebSub では撤廃されている。これにより、より汎用性の高いプロトコルを目指したと思われる。&lt;/p&gt;
&lt;p&gt;また、新しいバージョンでは &lt;code&gt;hub.verify_token&lt;/code&gt; も撤廃されている。このプロパティによって Verification Request を検証できるというメリットがあったが、これは Subscriber Callback URL に独自のクエリパラメータを付与することで代替できる。あるいは、第三者によって意図しない Subscription を登録されたとしても、更新通知を処理するかどうかは変わらず Subscriber の判断によるため、仕様から削除されたのかもしれない。&lt;/p&gt;
&lt;p&gt;Automatic Subscription Refresh の撤廃は、より簡単な Webhook の登録を目指すという観点からは悪手であろう。一方、PubSubHubbub はそもそも RSS や Atom フィードのリアルタイム化を目指したものであること (&lt;a href=https://ja.wikipedia.org/wiki/WebSub target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Wikipedia&lt;/a&gt; を参照のこと) を踏まえれば妥当であるともいえる。もし Subscriber が Subscription の更新を忘れてしまったとしても、最新の更新情報は元のリソースを参照することで簡単に取得できるからである。&lt;/p&gt;
&lt;h2&gt;経緯 (余談)&lt;/h2&gt;
&lt;p&gt;YouTube Data API v3 について調べていたのだが、Search: list (100 Units/Request) をむやみに叩くとすぐに制限 (10,000 Units/Day) に達してしまう。特定のチャンネルのアップデートを素早く受け取るために、&lt;a href=https://developers.google.com/youtube/v3/guides/push_notifications target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;PubSubHubbub をサポート&lt;/a&gt;していた。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>WSL 2 のサーバをネットワークに公開する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-03-26/wsl2-publish-server' />
      <id>https://blog.comame.xyz/entries/2020-03-26/wsl2-publish-server</id>
      <updated>2020-03-26T00:00:00Z</updated>
      <summary>&lt;p&gt;Hyper-V の NAT をこねくり回そうとしたけれども、うまくいかなかった。&lt;code&gt;netsh&lt;/code&gt; コマンドを使うことで、簡単にポート変換ができる。&lt;/p&gt;
&lt;h2&gt;Workaround&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://github.com/microsoft/WSL/issues/4150#issuecomment-504209723 target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Issue #4150 microsoft/WSL | GitHub&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;# PowerShell (Administrator)&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;&amp;gt; netsh interface portproxy add v4tov4 \&lt;/code&gt;
&lt;code&gt;    listenport=&amp;lt;External Port&amp;gt; \&lt;/code&gt;
&lt;code&gt;    listenaddress=&amp;lt;External Address&amp;gt; \&lt;/code&gt;
&lt;code&gt;    connectport=&amp;lt;Internal Port&amp;gt; \&lt;/code&gt;
&lt;code&gt;    connectaddress=&amp;lt;Internal Address&amp;gt;&lt;/code&gt;
&lt;/pre&gt;&lt;h3&gt;connectaddress&lt;/h3&gt;
&lt;p&gt;WSL 2 に割り当てられた内部 IP を指定する必要がある。WSL 2 内で&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;# bash (WSL 2)&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;$ ip a show eth0&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;を叩くと取得できる。&lt;/p&gt;
&lt;h2&gt;netsh interface portproxy&lt;/h2&gt;
&lt;p&gt;&lt;a href=https://docs.microsoft.com/ja-jp/windows-server/networking/technologies/netsh/netsh-interface-portproxy target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;Netsh interface portproxy コマンド | Microsoft Docs&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;サンプルスクリプト&lt;/h2&gt;
&lt;pre&gt;
&lt;code&gt;$port = Read-Host &apos;Port&apos;&lt;/code&gt;
&lt;code&gt;$internal_ip = bash.exe -c &quot;ip a show eth0 | grep -E &apos;^\s*inet &apos; | xargs echo | cut -d &apos; &apos; -f 2 | cut -d &apos;/&apos; -f 1&quot;&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;netsh interface portproxy delete v4tov4 listenport=$port listenaddress=0.0.0.0&lt;/code&gt;
&lt;code&gt;netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=$port connectaddress=$internal_ip connectport=$port&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;/pre&gt;</summary>
    </entry>
    
    <entry>
      <title>WSL 2 で Docker を自動起動する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-03-08/docker-startup-wsl2' />
      <id>https://blog.comame.xyz/entries/2020-03-08/docker-startup-wsl2</id>
      <updated>2020-03-08T00:00:00Z</updated>
      <summary>&lt;p&gt;WSL 2 では systemctl が使えないため、システムの起動時に Docker を自動起動することができない。ただ、Windows 側のタスクスケジューラを使えばできるらしい。&lt;/p&gt;

&lt;h2&gt;方法&lt;/h2&gt;
&lt;h3&gt;起動用スクリプトを書く&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
# このファイルに実行権限も付与しておく
sudo service docker start&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;パスワードなしで実行できるようにする&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#/etc/sudoers
user ALL=(ALL:ALL) NOPASSWD: /bin/sh /path/to/script
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;タスクスケジューラに設定する&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;全般「最上位の特権で実行する」&lt;/li&gt;
    &lt;li&gt;トリガー「ログイン時」&lt;/li&gt;
    &lt;li&gt;操作「C:\Windows\System32\bash.exe」「-c &quot;sudo /bin/sh /path/to/script&quot;」&lt;/li&gt;
    &lt;li&gt;条件「AC電源に接続されているときのみ」を外す&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;参考&lt;/h3&gt;
&lt;p&gt;こちらの Qiita 記事を参考にした。&lt;a href=&quot;https://qiita.com/Ningensei848/items/75adeb29bb143633d60c&quot; target=&quot;_blank&quot;&gt;Automatically Docker Daemon Boot on Windows Subsystem Linux（WSLにおけるdockerデーモンの自動起動）&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ブログシステムを新しくした (Blogger からの移行)</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-03-07/new-blog-system' />
      <id>https://blog.comame.xyz/entries/2020-03-07/new-blog-system</id>
      <updated>2020-03-07T00:00:00Z</updated>
      <summary>&lt;p&gt;新しいブログシステムを作成して、Blogger から移行した。今は静的ファイルとして静的ファイルサーバから配信している。GitHub でソースコードを公開した。&lt;a href=&quot;https://github.com/comame/blog.comame.xyz&quot; target=&quot;_blank&quot;&gt;comame/blog.comame.xyz&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;モチベーション&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;自分が制御可能な形でデータを置きたかった&lt;/li&gt;
    &lt;li&gt;自分の慣れているエディタで HTML を書きたかった&lt;/li&gt;
    &lt;li&gt;Blogger は読み込み速度が少し遅かった&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;仕組み&lt;/h2&gt;

&lt;h3&gt;記事の管理&lt;/h3&gt;
&lt;p&gt;&lt;a target=&apos;_blank&apos; href=&quot;https://github.com/comame/blog.comame.xyz/tree/master/archives&quot;&gt;archives/&lt;/a&gt; 配下にすべての記事データを置いている。タイトルやタグ、日付などのメタデータは archives/entries.json に記載する。&lt;/p&gt;

&lt;h3&gt;ページの生成&lt;/h3&gt;
&lt;p&gt;JavaScript で archives/entries.json を読み、記事データを展開している。これらは &lt;a target=&apos;_blank&apos; href=&quot;https://github.com/comame/blog.comame.xyz/blob/master/assets/js/app.js&quot;&gt;assets/js/app.js&lt;/a&gt; で処理している。History API を使って動的に URL を書き換えているため、Apache や Nginx で URL のリライトが必要。&lt;/p&gt;

&lt;h3&gt;静的ビルド&lt;/h3&gt;
&lt;p&gt;ユーザーが閲覧する環境で毎回スクリプトでページを生成するのは筋が悪いため、手元で静的ファイルとしてビルドを行う。ページは JavaScript を使って生成するため、Puppeteer でクロールしてスクリプト実行後の HTML を静的ファイルとして保存している。画像ファイルなどのアセットは、/assets に置いておけば /build/assets にそのままコピーされる。ビルド用のスクリプトは &lt;a target=&apos;_blank&apos; href=&quot;https://github.com/comame/blog.comame.xyz/blob/master/build.js&quot;&gt;build.js&lt;/a&gt; である。&lt;/p&gt;

&lt;h2&gt;今後の運用&lt;/h2&gt;
&lt;p&gt;VSCode で記事を書き、手元でビルドし、GitHub にアップする。あとはビルド済みの静的ファイルを適当に配信すればよい。今回は GitHub Pages で公開することにした。自分の開発環境で記事をかけるため、かなり書きやすくなった。一方で、ビルドをするためだけにローカルサーバを立てなければいけないのは結構面倒くさい。&lt;/p&gt;
&lt;p&gt;[追記] Git の post-commit フックを使って、自動的にビルドを走らせるようにしてみた。本当は GitHub Actions を使いたかったが、ビルド用のサーバを立てて、Puppeteer を起動して...とするのは厄介そうだったので妥協策ではある。post-commit フックは &lt;a href=&quot;https://github.com/comame/blog.comame.xyz/blob/194b480ee2beec8a75c7638ab7453078faa7aa3f/post-commit&quot; target=&quot;_blank&quot;&gt;post-commit&lt;/a&gt; に置いておく。&lt;/p&gt;
&lt;p&gt;[追記] &lt;a href=&quot;https://github.com/comame/blog.comame.xyz/tree/master/.github/workflows/build.yml&quot; target=&quot;_blank&quot;&gt;GitHub Actions&lt;/a&gt; で push したときにビルドが走るようにした。&lt;/p&gt;

&lt;h2&gt;今後やりたいこと&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Markdown で記事を書けるようにする&lt;/li&gt;
    &lt;li&gt;WebP などの画像フォーマットに自動的に変換できるようにする&lt;/li&gt;
    &lt;li&gt;OGP に対応&lt;/li&gt;
    &lt;li&gt;[DONE] RSS フィードを生成できるようにする&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>Windows 10 の ssh で Permission refusedが出る問題</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-02-19/windows-ssh-error' />
      <id>https://blog.comame.xyz/entries/2020-02-19/windows-ssh-error</id>
      <updated>2020-02-19T00:00:00Z</updated>
      <summary>&lt;h2&gt;対処法&lt;/h2&gt;
&lt;p&gt;ssh-agent を有効にして、ssh-add する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Set-Service -Name ssh-agent -StartupType Manual
Start-Service ssh-agent

ssh-add ~/.ssh/id_rsa&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&apos;https://qiita.com/yuta0801/items/d65f1fc3115773861283&apos;&gt;Win10でssh-addがError connecting to agent: No such file or directory - Qiita&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>CNAME トラッキングについての議論</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2020-01-20/cname-tracking' />
      <id>https://blog.comame.xyz/entries/2020-01-20/cname-tracking</id>
      <updated>2020-01-20T00:00:00Z</updated>
      <summary>&lt;p&gt;先日&lt;a href=&quot;https://twitter.com/comameito/status/1218434263768428544&quot;&gt;割と感情的に呟いたもの&lt;/a&gt;がそこそこ拡散されてしまって、「良くないものだ」という気持ちだけが広まってしまうのは本意ではないので、改めてきちんとした議論をしたいという思いでこの記事を書きます。
&lt;/p&gt;
&lt;h2&gt;前提となる立場&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;トラッキングをすること自体の是非は問わない&lt;/li&gt;
    &lt;li&gt;トラッキングの手法についての議論である&lt;/li&gt;
    &lt;li&gt;ユーザーがトラッキングを望まない場合、積極的な対処をせずとも有効な選択肢が与えられる状態が望ましい&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;CNAME トラッキングの方法・背景&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://support.ebis.ne.jp/all_articles/100_featuretype/200_systemsetting/29550/&quot;&gt;アドエビスによる解説&lt;/a&gt;がわかりやすいです。最近の
    Third-party Cookie に対する制限の回避策となり得ます。&lt;/p&gt;
&lt;h2&gt;セキュリティの懸念事項&lt;/h2&gt;
&lt;h3&gt;不適切な設定をされた Cookie が、トラッキングサービスに送信されてしまう&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Set-Cookie&lt;/code&gt; ヘッダに &lt;code&gt;Domain&lt;/code&gt; ディレクティブを指定した場合、すべてのサブドメインに Cookie
    が送信されてしまいます。もし認証に用いる機密情報などをこの方法で設定した場合、CNAME レコードとして設定したトラッキングサービスにも Cookie が送信されてしまいます。&lt;a
        href=&quot;https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie#Directives&quot;&gt;Set-Cookie - HTTP | MDN&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;プライバシーの懸念事項&lt;/h2&gt;
&lt;h3&gt;ユーザーに情報収集することについての選択肢が与えられない&lt;/h3&gt;
&lt;p&gt;Third-party Cookie を使用して情報を収集する場合、ユーザーが Third-party Cookie
    をブロックすればトラッキングを防ぐことができます。また、これによってトラッキング以外の要素に与える影響はあまり大きくありません (Third-party Cookie
    を認証に使うようなことがない限り...)。一方、First-party Cookie を用いてトラッキングをする場合、ユーザーができることは First-party Cookie を拒否することです。First-party
    Cookie は認証などの様々な用途に使われており、影響は大きくなると想定されます。&lt;/p&gt;
&lt;p&gt;First-party Cookie
    をブロックすることによる影響が大きいことを鑑みると、これはユーザーによる積極的な行動とみるべきです。私の提案としては、ユーザーの不便が行動による効果を上回る場合、積極的な行動と取るべきだと考えています。積極的な行動か否かの判断は極めて難しいものであり、今後議論が進むことを期待します。
&lt;/p&gt;
&lt;p&gt;次に、ブラウザの拡張機能によって事前に DNS をクエリし、トラッキングサービスへの CNAME レコードがあればアクセスをブロックするという手法が考えられます。この手法にはいくつかの問題があります。すべてのリクエストごとに DNS
    クエリを走らせるため、パフォーマンスに影響が出ることが想定されます。ブラウザが拡張機能に対して DNS クエリを行う機能を提供するかどうかに依存します。&lt;/p&gt;
&lt;h2&gt;CNAME トラッキングに対する考察・提案&lt;/h2&gt;
&lt;p&gt;現時点において、有効な選択肢をユーザーに与えることが難しいこと・セキュリティの問題があることを踏まえると、代替手段が存在しない場合にのみ CNAME トラッキングを導入するべきだと考えます。&lt;/p&gt;
&lt;h3&gt;トラッキングサービスの提供者に期待すること&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;Set-Cookie&lt;/code&gt;の不適切な設定による機密情報の漏洩を防ぐため、導入者に対して必要十分な助言をすること&lt;/li&gt;
    &lt;li&gt;ユーザーの同意を得るためのプロンプトを表示させる等、ユーザーに選択肢を提示するよう導入者に指導すること&lt;/li&gt;
    &lt;li&gt;ユーザーの同意が得られるまでトラッキングを行わないようにする機能を提供すること&lt;/li&gt;
    &lt;li&gt;将来的に有効な代替手段が提案されたときは、CNAME トラッキングを停止すること&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;トラッキングの導入者に期待すること&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;Set-Cookie&lt;/code&gt;について正しい知識を得て、Cookie の漏洩が起こらないようにすること&lt;/li&gt;
    &lt;li&gt;トラッキングを有効にする前に、個別のユーザーに対して有効な同意を得ること&lt;/li&gt;
&lt;/ul&gt;
</summary>
    </entry>
    
    <entry>
      <title>Blogger のテーマ作成について</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-12-10/blogger-theme' />
      <id>https://blog.comame.xyz/entries/2019-12-10/blogger-theme</id>
      <updated>2019-12-10T00:00:00Z</updated>
      <summary>&lt;p&gt;&lt;a href=&apos;https://adventar.org/calendars/4197&apos;&gt;Google Blogger Advent Calendar 2019&lt;/a&gt; の 10 日目の記事です。&lt;/p&gt;

&lt;p&gt;Blogger には、標準でおしゃれなテーマがいくつか付属しています。また、検索エンジンで少し調べてみれば、有志が作成した素晴らしいテーマを簡単に見つけることができます。しかし、こうは思ったことはありませんか？「自分だけのテーマを作ってみたい」と。&lt;/p&gt;
&lt;p&gt;この記事では、Blogger のテーマを作成するためのチュートリアルと TIPS をお届けします。&lt;/p&gt;

&lt;h2&gt;前置き&lt;/h2&gt;
&lt;p&gt;HTML や CSS などの基本的な Web 技術が多少分かることを前提としています。また、SEO を目的としたテーマ作成について扱うわけではありません。&lt;/p&gt;

&lt;h2&gt;まずはじめに&lt;/h2&gt;
&lt;h3&gt;Blogger のテーマってどこで作るの？&lt;/h3&gt;
&lt;p&gt;&lt;a href=&apos;https://blogger.com&apos;&gt;Blogger&lt;/a&gt; を開き、「テーマ」を開きます。「HTML を編集」のボタンをクリックすると、エディタが開きます。あとは自分の HTML を書いていくだけです。(このエディタは結構使いにくいので、手元で書いたものを貼り付ける方が良いかもしれません。テキストエディタは VSCode や Atom, Sublime Text などが有名です)&lt;/p&gt;

&lt;h3&gt;Blogger のテンプレートを記述する XML って何？&lt;/h3&gt;
&lt;p&gt;Blogger のテーマは HTML とは少し形式の異なる、XML という言語で記述します。大まかな違いとしては、HTML は多少誤った記述をしても寛大に解釈されます (例えば、閉じタグを忘れても自動的に閉じてくれます)。一方で、XML は文法に則った記述をしないと、エラーとなってしまいます。具体的な違いとしては、まず XML では閉じタグを省略できません。&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;などの閉じタグが不要なタグでも、必ず記述しなければなりません。
&lt;pre&gt;&lt;code&gt;閉じタグを忘れるとエラーになる
ng -- &amp;lt;h1&amp;gt;&amp;lt;h2&amp;gt;Heading&amp;lt;/h1&amp;gt;

閉じタグを省略できない
ok -- &amp;lt;img src=&apos;https://example.com/foo.png&apos;&amp;gt;&amp;lt;/img&amp;gt;
ok -- &amp;lt;img src=&apos;https://example.com/foo.png&apos;/&amp;gt;
ng -- &amp;lt;img src=&apos;https://example.com/foo.png&apos;&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;また、ファイルの先頭に決められた形式の記述をしなければなりません。Blogger の場合は次のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html b:responsive=&apos;true&apos; lang=&apos;ja&apos; xmlns=&apos;http://www.w3.org/1999/xhtml&apos; xmlns:b=&apos;http://www.google.com/2005/gml/b&apos; xmlns:data=&apos;http://www.google.com/2005/gml/data&apos; xmlns:expr=&apos;http://www.google.com/2005/gml/expr&apos;&amp;gt;

&amp;lt;!-- Your cool things here --&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;h2&gt;テーマの構成&lt;/h2&gt;
&lt;p&gt;Blogger のテーマには、決められた構成が存在します。これは皆さんが Blogger 管理画面の「レイアウト」で見慣れているものとほぼ同じです。Blogger のテーマで使用されるレイアウトのパーツには、b:section, b:widget, b:includable (b:include) があります。&lt;/p&gt;

&lt;p&gt;Blogger のレイアウト構成は次のようになっています。&lt;img alt=&apos;Blogger のレイアウト構成図&apos; src=&quot;https://1.bp.blogspot.com/-Xtu3MKf6JhI/Xek_htDkw3I/AAAAAAABIcc/M-Wig09D2c0Pn3QEGAf80aSy6I24fhfWQCLcBGAsYHQ/s1600/%25E7%2584%25A1%25E9%25A1%258C%25E3%2581%25AE%25E5%259B%25B3%25E5%25BD%25A2%25E6%258F%258F%25E7%2594%25BB.png&quot; /&gt;&lt;/p&gt;

&lt;h3&gt;b:section&lt;/h3&gt;
&lt;p&gt;レイアウトの最上位に配置されます。日本語の管理画面では「セクション」と表示されていますね。セクションにはウィジェットだけを含むことができます。
&lt;code&gt;id&lt;/code&gt;属性は必須です。他の ID と重複しない一意な文字列を指定します。
&lt;pre&gt;&lt;code&gt;&amp;lt;b:section id=&apos;foo&apos;&amp;gt;
  &amp;lt;!-- widgets --&amp;gt;
&amp;lt;/b:section&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;h3&gt;b:widget&lt;/h3&gt;
&lt;p&gt;セクションの中に配置されます。管理画面では「ウィジェット」と表示されています。皆さんが Blogger をカスタマイズするときには、最も良く扱う部分ではないでしょうか。ウィジェットの特徴としては、 Blogger によって用意されているものだけを使用できるという点が挙げられます。また、ウィジェット内には後述する &lt;code&gt;b:includable&lt;/code&gt;のみを配置できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;id&lt;/code&gt;と&lt;code&gt;type&lt;/code&gt;属性は必須です。&lt;code&gt;type&lt;/code&gt;属性には BlogArchive, Blog, Header, LinkList, NavBar, BlogProfile などを指定できます。これらは管理画面から追加できるウィジェットと同じです。また、&lt;code&gt;id&lt;/code&gt;属性は Type + 連番の形式にする必要があります。
&lt;pre&gt;&lt;code&gt;&amp;lt;b:section id=&apos;foo&apos;&amp;gt;

  &amp;lt;b:widget id=&apos;Blog1&apos; type=&apos;Blog&apos;&amp;gt;
    &amp;lt;-- includables --&amp;gt;
  &amp;lt;/b:widget&amp;gt;

  &amp;lt;b:widget id=&apos;LinkList1&apos; type=&apos;LinkList&apos;&amp;gt;
    &amp;lt;-- includables --&amp;gt;
  &amp;lt;/b:widget&amp;gt;

&amp;lt;/b:section&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;h3&gt;b:includable&lt;/h3&gt;
&lt;p&gt;ウィジェットの中で使用されるパーツです。includable という名前が表すように、他の場所で再利用可能です。セクションやウィジェットと異なり、この中では自由に要素を追加できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;id&lt;/code&gt;属性は必須です。また、各ウィジェット内には必ず&lt;code&gt;main&lt;/code&gt;を ID とした includable が必要です。&lt;code&gt;main&lt;/code&gt;以外を指定した includable は、&lt;code&gt;b:include&lt;/code&gt;で明示的に指定しない限り、表示されません。
&lt;pre&gt;&lt;code&gt;&amp;lt;b:section id=&apos;foo&apos;&amp;gt;
  &amp;lt;b:widget id=&apos;Blog1&apos; type=&apos;Blog&apos;&amp;gt;

    &amp;lt;b:includable id=&apos;main&apos;&amp;gt;
      &amp;lt;p&amp;gt;やっと他の要素を書き込める！&amp;lt;/p&amp;gt;
    &amp;lt;/b:includable&amp;gt;

    &amp;lt;b:includable id=&apos;another-includable&apos;&amp;gt;
      &amp;lt;p&amp;gt;まだ表示されない&amp;lt;/p&amp;gt;
    &amp;lt;/b:includable&amp;gt;

  &amp;lt;/b:widget&amp;gt;
&amp;lt;/b:section&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;h3&gt;b:include&lt;/h3&gt;
&lt;p&gt;includable を再利用します。&lt;code&gt;name&lt;/code&gt;属性を使用して include するものを指定します。

&lt;pre&gt;&lt;code&gt;&amp;lt;b:section id=&apos;foo&apos;&amp;gt;
  &amp;lt;b:widget id=&apos;Blog1&apos; type=&apos;Blog&apos;&amp;gt;

    &amp;lt;b:includable id=&apos;main&apos;&amp;gt;
      &amp;lt;p&amp;gt;やっと他の要素を書き込める！&amp;lt;/p&amp;gt;
      &amp;lt;b:include name=&apos;another-includable&apos;&amp;gt;&amp;lt;/b:includable&amp;gt;
    &amp;lt;/b:includable&amp;gt;

    &amp;lt;b:includable id=&apos;another-includable&apos;&amp;gt;
      &amp;lt;p&amp;gt;main の中に表示された！&amp;lt;/p&amp;gt;
    &amp;lt;/b:includable&amp;gt;

  &amp;lt;/b:widget&amp;gt;
&amp;lt;/b:section&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;h2&gt;データタグ / 制御タグ&lt;/h2&gt;

&lt;p&gt;ここまでは、Blogger のレイアウトを構成するタグを説明しました。実際にブログとして機能させるには、ブログのデータを埋め込んでいく必要があります。そこで使用するのがウィジェットタグです。ウィジェットタグは大まかに分類してデータタグと制御タグに分けることができます。&lt;/p&gt;

&lt;p&gt;データタグは、ブログのデータを表現します。例えば、&lt;code&gt;&amp;lt;data:blog.title/&amp;gt;&lt;/code&gt;というタグは、ブログのタイトルに置き換えられます。使用可能なデータタグは&lt;a href=&apos;https://support.google.com/blogger/answer/47270&apos;&gt;ヘルプページ&lt;/a&gt;に説明がありますが、いくつか補足があります。「グローバルに利用可能なデータ」は、テーマ内の任意の場所で使用可能です。セクションの外であっても、例えば&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;タグなどに使用できます。一方で、それ以外のデータタグは特定のウィジェット内でしか使用できません。例えば「ブログの投稿」で説明されているデータタグは、&lt;code&gt;&amp;lt;b:widget id=&apos;Blog1&apos; type=&apos;Blog&apos;&amp;gt;&amp;lt;b:widget&amp;gt;&lt;/code&gt;内でのみ使用できます。&lt;/p&gt;

&lt;p&gt;制御タグを用いて、ループ (繰り返し) や条件分岐を記述できます。大抵の場合、ブログのトップページに表示する記事の一覧はループを用いることになるでしょう。また、特定の条件でのみ表示させたいタグがあるとき、条件分岐を使います。これは、例えば Blogger 内での記事検索を実装するのに使えるでしょう。条件分岐には b:if, b:elseif, b:else タグを、ループには b:loop タグを使います。
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- &amp;lt;/b:loop&amp;gt; までを、記事の数だけ繰り返す --&amp;gt;
&amp;lt;b:loop var=&apos;post&apos; values=&apos;data:posts&apos;&amp;gt;

  &amp;lt;b:if cond=&apos;data:blog.pageType == &quot;index&quot;&apos;&amp;gt;
    &amp;lt;!-- トップページや検索結果画面などで表示される --&amp;gt;
    &amp;lt;h2&amp;gt;&amp;lt;data:post.title/&amp;gt;&amp;lt;/h2&amp;gt;

    &amp;lt;!-- それ以外で表示される --&amp;gt;
    &amp;lt;b:else/&amp;gt;
    &amp;lt;h1&amp;gt;&amp;lt;data:post.title/&amp;gt;&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;&amp;lt;data:post.body/&amp;gt;&amp;lt;/p&amp;gt;
  &amp;lt;/b:if&amp;gt;

&amp;lt;/b:loop&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;b:eval&lt;/code&gt;タグと&lt;code&gt;expr:&lt;/code&gt;属性 (&lt;a href=&apos;https://developer.mozilla.org/ja/docs/Learn/Getting_started_with_the_web/HTML_basics#Anatomy_of_an_HTML_element&apos;&gt;Attribute&lt;/a&gt;) は特殊で難しい構文です。&lt;code&gt;eval:&lt;/code&gt;属性は、HTML の属性にブログのデータを埋め込みたいときに使用します。例えば、ブログのホームページへのリンクは次のようなものになるでしょう。
&lt;pre&gt;&lt;code&gt;&amp;lt;a expr:href=&quot;data:homepageUrl&quot;&amp;gt;ホームページ&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;b:eval&lt;/code&gt;タグも同様の役割を持ちますが、これは属性ではなくタグです。&lt;/p&gt;

&lt;p&gt;制御タグと&lt;code&gt;b:eval&lt;/code&gt;、&lt;code&gt;expr:&lt;/code&gt;についての&lt;a href=&apos;https://support.google.com/blogger/answer/46995&apos;&gt;ヘルプページはこちら&lt;/a&gt;です。&lt;/p&gt;

&lt;h2&gt;TIPS&lt;/h2&gt;
&lt;h3&gt;SNS などへの共有時にプレビューを表示する&lt;/h3&gt;
&lt;p&gt;SNS に記事を共有したときにプレビューを表示させるには、OGP (Open Graph Protocol, &lt;a href=&apos;https://ogp.me&apos;&gt;ogp.me&lt;/a&gt;) を使用します。
&lt;pre&gt;&lt;code&gt;&amp;lt;html prefix=&apos;og: http://ogp.me/ns#&apos;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta property=&apos;og:title&apos; content=&apos;記事のタイトル&apos;&amp;gt;
    &amp;lt;meta property=&apos;og:type&apos; content=&apos;article (トップページなら website)&apos;&amp;gt;
    &amp;lt;meta property=&apos;og:site_name&apos; content=&apos;サイト名&apos;&amp;gt;
    &amp;lt;meta property=&apos;og:url&apos; content=&apos;記事の URL&apos;&amp;gt;
    &amp;lt;meta property=&apos;og:image&apos; content=&apos;記事のサムネイル画像の URL&amp;gt;
    &amp;lt;meta property=&apos;og:description&apos; content=&apos;記事の概要&apos;&amp;gt;
    &amp;lt;meta property=&apos;og:locale&apos; content=&apos;ja_JP&apos;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;h3&gt;読み込みを高速化する&lt;/h3&gt;

&lt;p&gt;Blogger のテーマは、そのまま書くと勝手に CSS や JavaScript を挿入されてしまいます。これらは&lt;a href=&apos;https://blog.comame.xyz/2019/04/web-performance.html&apos;&gt;レンダリングをブロックする要因&lt;/a&gt;になり、読み込みの低速化に繋がります。もしパフォーマンスにこだわりのある方は、次のような一手間を加えてみてください。

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html b:responsive=&amp;#039;true&amp;#039; lang=&amp;#039;ja&amp;#039; xmlns=&amp;#039;http://www.w3.org/1999/xhtml&amp;#039; xmlns:b=&amp;#039;http://www.google.com/2005/gml/b&amp;#039; xmlns:data=&amp;#039;http://www.google.com/2005/gml/data&amp;#039; xmlns:expr=&amp;#039;http://www.google.com/2005/gml/expr&amp;#039;&amp;gt;

  &amp;lt;!-- head 開始タグ--&amp;gt;
  &amp;amp;lt;!--&amp;lt;head&amp;gt;--&amp;amp;gt;&amp;amp;lt;head&amp;amp;gt;
    &amp;lt;!-- ... --&amp;gt;
  &amp;lt;!-- head 終了タグ --&amp;gt;
  &amp;amp;lt;!--&amp;lt;/head&amp;gt;--&amp;amp;gt;&amp;amp;lt;/head&amp;amp;gt;

  &amp;lt;!-- body 開始タグ (変更する必要なし) --&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;!-- ... --&amp;gt;
  &amp;lt;!-- body 終了タグ --&amp;gt;
  &amp;amp;lt;!--&amp;lt;/body&amp;gt;--&amp;amp;gt;&amp;amp;lt;/body&amp;amp;gt;

&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;ただし、これによって Blogger が用意している CSS が読み込まれなくなるため、公式のウィジェットは正常に機能しなくなる可能性があります。&lt;/p&gt;

&lt;h3&gt;凝りすぎない&lt;/h3&gt;

&lt;p&gt;自分のブログでしかテーマを使わないのであれば、汎用性にこだわりすぎる必要はありません。例えば、Blogger 公式テーマのように、ヘッダーやフッター、サイドバーなどのパーツに分けて、そこにウィジェットが挿入される可能性を考えて...などやっていると、軽く数日どころではなく日が暮れることになります。例えば Web に公開して誰でも使えるようなものにする、などの目的がないのであれば、「見た目が整えば OK!」という気持ちで挑むのがまずは良いと思います。これが辛いのは、後述するように公式情報が乏しいのも理由だったりするわけですけども...&lt;/p&gt;
&lt;p&gt;例えば、このブログのテーマも1つの変更不可能な巨大ウィジェットだけで構成されています。&lt;img alt=&apos;このブログの「レイアウト」画面&apos; src=&quot;https://2.bp.blogspot.com/-WPVLWOc-Ua0/Xe2iUHPfxHI/AAAAAAABIfI/DdiyKmiU_b4Wx6WEmnZcpCQBeLeiIibEACLcBGAsYHQ/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2019-12-09%2B10.24.11.png&quot;/&gt;&lt;/p&gt;

&lt;h3&gt;公式情報はあまり当てにならない...&lt;/h3&gt;

&lt;p&gt;Advent Calendar の&lt;a href=&apos;https://outing-diary.blogspot.com/2019/12/blogger.html&apos;&gt;前日の記事&lt;/a&gt;にも書かれていたように、テーマ作成について公式に出されている情報は非常に乏しいものになっています。英語のページですが、とてもわかり易く書かれたドキュメントが存在するため、困ったときはそちらも見てみると良いかもしれません (&lt;a href=&apos;https://sites.google.com/site/templateofdoom/Home&apos;&gt;https://sites.google.com/site/templateofdoom/&lt;/a&gt;)。Google Developers にきちんとリファレンスを出してほしいな...&lt;/p&gt;


&lt;h2&gt;終わりに&lt;/h2&gt;
&lt;p&gt;参考に、これまでのことをまとめた最小のテンプレートを公開します。&lt;a href=&apos;https://gist.github.com/comame/4556f8b06a8b34de688cb7cd454452d7&apos;&gt;GitHub Gist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Blogger のテーマを作成するには、ループや条件分岐など、いくらか高度な概念を用いることになります。確かにやや難易度は高いですが、自作のテーマが完成した時は嬉しいですし、何より深い愛着が湧くものです。自分のブログを開くたびに少し嬉しくなるような、そんな素敵なテーマが完成することを祈っています。&lt;/p&gt;
&lt;p&gt;次の記事は minato さんです。楽しい Blogger ライフを！&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Google のエキスパートとして回答すること</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-12-01/google-expert' />
      <id>https://blog.comame.xyz/entries/2019-12-01/google-expert</id>
      <updated>2019-12-01T00:00:00Z</updated>
      <summary>&lt;p&gt; &lt;a href=&apos;https://adventar.org/calendars/4226&apos;&gt;Google Products Advent Calendar 2019&lt;/a&gt; の 1 日目の記事です。&lt;/p&gt;


&lt;p&gt;こんな記事が初日を飾るのも申し訳ない気がしますが、私が数年間ヘルプコミュニティで回答してきた中で、なぜ自分は回答を続けているんだろう、ということを考えてみたポエムです。&lt;/p&gt;

&lt;h2&gt;Google Product Experts とは&lt;/h2&gt;
&lt;p&gt; Google Product Experts (PE) は、Google の&lt;a href=&apos;https://support.google.com/&apos;&gt;ヘルプコミュニティ&lt;/a&gt;に日々投稿される質問に回答するユーザーのことです。コミュニティには、日々たくさんの質問が寄せられます。中にはよくある質問から、誰にも解決策が見つからないような問題まで投稿されます。&lt;/p&gt;
&lt;p&gt;エキスパートは、全員がパソコンの前に座り続けている技術オタクというわけではありません (私はそうかもしれませんが)。様々な職業・立場・趣味の人が回答しています。&lt;/p&gt;


&lt;h2&gt;なぜ回答し続けるのか&lt;/h2&gt;

&lt;p&gt; これはエキスパートによって答えが変わるでしょう。なぜ回答し続けるのか、というのは非常に難しい問いになり得ます。&lt;/p&gt;
&lt;p&gt;Help on Social という取り組みがありました (14 日目の yasu0796 さんの記事をお楽しみに！)。これは、Twitter 上で困っていそうな人を見つけ、リプライで回答をするものです。#gHelp のハッシュタグでご存じの方もいるかもしれません。Twitter でいきなりリプライを飛ばすという性質上、極稀に罵倒されるようなこともありました。これは Twitter に限ったことではありません。Google のコミュニティでも、直接的な表現で投稿するユーザーは存在します。回答者も人間ですから、当然嫌な気持ちになります。それでも、エキスパートは回答を続けます。なぜでしょうか。&lt;/p&gt;
&lt;p&gt;エキスパートになることによって得られる特典でしょうか。ベータテストに参加できたり、イベントに参加できたりと、特典は嬉しいものがあります。では、もし仮に明日からこれらの特典を一切受けられなくなるとしたら、エキスパートは回答をやめるでしょうか。おそらく、多くのエキスパートは回答をやめることはないと思います。少なくとも僕は回答をやめません (Googler と連絡が取れなくなるのは、エスカレーションできなくてとても困るけど)。特典があるから回答を続ける、というのは不適切でしょう。&lt;/p&gt;
&lt;p&gt;正直なところ、少なくとも私にとって習慣になってしまったから、というのは1つの正解なのかもしれません。朝起きたらコミュニティをチェックする、仕事が終わったら返信が来ていないか確認する、といったように、日々のルーティンに組み込まれているエキスパートもいるかもしれません。では、習慣になったから惰性で回答を続けているのでしょうか。エキスパートの皆さんの回答を見ていると、これも不適切です。&lt;/p&gt;
&lt;p&gt;おそらく、最終的には「好きだから」「楽しいから」「色んな人に使ってほしいから」「知識を広めたいから」という理由に行き着くでしょう。Google の製品について日々真剣に議論し、アップデート情報に一喜一憂し、Google にフィードバックを送りまくるエキスパートの姿を見ていると、そう結論づけるのが妥当に思えます。&lt;/p&gt;


&lt;h2&gt;質問に答えるということ&lt;/h2&gt;

&lt;p&gt;質問に答えることによって質問者を助けるだけではなく、自分が得られるものも非常に大きいということが言えます。自身が回答することでより深い知識を得られる、他の回答者と交流できる、問題を解決したときの達成感を得られる、など様々なことが挙げられます。回答することでなにか得ることを目的にしているわけではないのに、「与えよ、さらば与えられん」とはよく言ったものです (旧 Top Contributor Program の日本語ページに書いてあった気がします、確か)。&lt;/p&gt;


&lt;h2&gt;&lt;/h2&gt;

&lt;p&gt;Google の製品が好きな方、質問に答えるのが好きな方、ぜひヘルプコミュニティへの投稿をお待ちしています。次は仁田坂 淳史さんによる Inbox なき後のメール管理についての記事です。お楽しみに！&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Catalina で Chrome の画面共有が使用できない問題</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-11-04/catalina-chrome-screen-share' />
      <id>https://blog.comame.xyz/entries/2019-11-04/catalina-chrome-screen-share</id>
      <updated>2019-11-04T00:00:00Z</updated>
      <summary>&lt;p&gt;macOS 10.15 Catalina において、Chrome の画面共有が使用できない問題が発生しています。これにより、例えば Hangout で手元の画面を映すことできなくなってしまいます。&lt;/p&gt;

&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;Catalina でアプリケーションが画面を取得するには、ユーザーによる許可が必要です（システム環境設定 &gt; セキュリティとプライバシー &gt; プライバシー &gt; 画面収録 で設定)。Chrome 78 では、ユーザーが画面収録を許可していても、権限がないものとして扱ってしまうようです。&lt;/p&gt;

&lt;h2&gt;今後どうなるか&lt;/h2&gt;
&lt;p&gt;Chrome Canary 80.0.3955.4 ではこの問題が解決されていることを確認できました。今後のアップデートで修正されると思われます。&lt;/p&gt;

&lt;p&gt;&lt;a href=&apos;https://bugs.chromium.org/p/chromium/issues/detail?id=993692&apos;&gt;Issue Tracker&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Unlimited Mirai に参加しました</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-10-30/unlimited-mirai' />
      <id>https://blog.comame.xyz/entries/2019-10-30/unlimited-mirai</id>
      <updated>2019-10-30T00:00:00Z</updated>
      <summary>&lt;p&gt;2019年10月29日に開催された&lt;a href=&apos;https://www.waseda.jp/inst/icc/news/2019/09/04/10146/&apos;&gt;Unlimited Mirai&lt;/a&gt;に参加しました。&lt;/p&gt;

&lt;h2&gt;本編&lt;/h2&gt;
&lt;p&gt;クリプトン創設者の伊藤博之さんによる1時間の講演でした。クリプトンにとって初音ミクとは何であるか、どのような存在にしたいのかといったような内容でした。&lt;/p&gt;

&lt;h3&gt;初音ミク&lt;/h3&gt;
&lt;p&gt;初音ミクは仮想楽器であり、キャラクターでもある。仮想楽器 (Virtual Instruments) は、本物の楽器と異なり、個人で扱うことが容易である。&lt;/p&gt;

&lt;h3&gt;ビジネスにおける新規性&lt;/h3&gt;
&lt;p&gt;音声合成技術 (Text to Speech) とコンピュータミュージックは、それぞれ単独では特に新規性のあるものではない。一方で、2つを組み合わせた歌唱合成技術は今までになかったジャンルである。そこにキャラクターを組み合わせたのが初音ミク。&lt;/p&gt;

&lt;h3&gt;創作の連鎖・デジタルコンテンツは使えば使うほど価値が増える (いかに見てもらうか)&lt;/h3&gt;
&lt;p&gt;創作物を囲い込んで無断使用とみなすのではなく、創作の連鎖を共感 (使ってくれてありがとう) の連鎖にしたい。&lt;br&gt;
著作権に絡んだ問題によって創作の連鎖を萎縮させたくないという思いから、&lt;a href=&apos;https://piapro.jp/license/pcl/summary&apos;&gt;ピアプロ・キャラクター・ライセンス&lt;/a&gt;と&lt;a href=&apos;https://piapro.jp&apos;&gt;ピアプロ&lt;/a&gt;を用意。クリエイターには様々な人がいるため、簡単に内容をつかめるよう PCL は要約したものを正文に加えて表示している。また、創作の連鎖を生み出すために、ピアプロの利用規約には投稿物の2次創作を許可する項目を設けてある。&lt;br&gt;
また、初音ミクをサブカルチャーの分野に限定しないために、様々なジャンルのものとコラボしている (ファッションや伝統芸能など)。マジカルミライでは、創作体験を得られるワークショップを開催している。雪ミクは、公募したミクのデザインから、ラッピング電車やグッズ販売など広げている。&lt;/p&gt;

&lt;h3&gt;コンテンツと技術の両面に明るいことを活かす&lt;/h3&gt;
&lt;p&gt;クリプトンでは、スタッフによる研究開発を行っている。素早い準備と撤収が求められるレディー・ガガの前座や、冨田勲のイートハーヴ交響曲やマジカルミライ2019などで使った現実に合わせて初音ミクを動かす技術を作った。また、不自然だった初音ミクの喋りのシステムも作った。&lt;/p&gt;

&lt;h2&gt;質疑応答&lt;/h2&gt;
&lt;p&gt;会場では30分ほど質疑応答が設けられました。&lt;/p&gt;

&lt;h3&gt;初音ミクを盛り上げるために、挑戦したいことは？&lt;/h3&gt;
&lt;p&gt;ミク自体は意思を持たないので、クリエイターがやりたいことを代弁できるようにしていきたい。&lt;/p&gt;

&lt;h3&gt;PCL では人を傷つけるコンテンツを禁止しているが、実際には死を連想させるような作品もある&lt;/h3&gt;
&lt;p&gt;PCL の目的は創作を萎縮させないことである。どのコンテンツは良くて何が悪いかの線引きは難しいので、ファンに一任している。&lt;/p&gt;

&lt;h3&gt;音を扱うクリプトンを立ち上げたきっかけは？&lt;/h3&gt;
&lt;p&gt;楽器をやっていたから。皆さんと違わない。&lt;/p&gt;

&lt;h3&gt;今後クリプトンがやっていきたいことは？&lt;/h3&gt;
&lt;p&gt;ソフトウェアとしての初音ミクができること (機能) の向上によって、クリエイターができることを増やしたい。創作の方向性についてはクリプトンとしてできることはない。&lt;/p&gt;

&lt;h3&gt;クリプトンの新社員に求めることは？&lt;/h3&gt;
&lt;p&gt;社員のやりたいことの積み重ねが会社なので、何がしたいかを大事にしている。&lt;/p&gt;

&lt;h3&gt;コラボしていくなかでの困難をどう乗り越えた？&lt;/h3&gt;
&lt;p&gt;初音ミクは通常のアーティストと異なり楽器なので、業界的な違和感もあった。伝統芸能やクラシックに参加するのは大変だったが、サブカルチャーに限らない幅広い分野に初音ミクを広げていく。&lt;/p&gt;

&lt;h3&gt;初音ミクより前に発売されたメイコ・カイトではなく、なぜ初音ミクがヒットしたのか？&lt;/h3&gt;
&lt;p&gt;ニコニコ動画や YouTube などの動画共有サイトの存在が大きい。メイコやカイトが出た頃は、創作物を音楽のコミュニティに投稿するしかなかった。一方初音ミクは音楽だけではなく動画をつけることができた。&lt;/p&gt;

&lt;h3&gt;ビジネスとアートをどうやって統合した？&lt;/h3&gt;
&lt;p&gt;初音ミクのモチーフを特定の分野だけにせず、様々な分野に存在を拡散したかった。陳腐化した伝統に初音ミクを入り込ませる。&lt;/p&gt;

&lt;h3&gt;技術が進歩すればもっと人の声に寄せられるはず。初音ミクをもっと人に近づけたいか、あえて機械的な面を残したいか？&lt;/h3&gt;
&lt;p&gt;リアルを突き詰めることは技術的には可能である。初音ミクを声優のクローンにせず、ミクっぽさを残すべきなのではないか？音色やイントネーションは改善すべきだが、ある種のたどたどしさ・機械らしさを残している部分はある。&lt;/p&gt;

&lt;h2&gt;まとめ・その他&lt;/h2&gt;
&lt;p&gt;創作の連鎖を大切にすること、特定の分野に限定しないことが強調されていました。動画から始まりイラストや 3DCG など幅広い分野に広まっていった背景を踏まえたからこその発想なのかなというように思います。&lt;/p&gt;
&lt;p&gt;当日は Twitter で内容を実況していた方もいました。&lt;a href=&apos;https://twitter.com/search?q=%23UnlimitedMirai&apos;&gt;#UnlimitedMirai&lt;/a&gt;&lt;br&gt;
講演の中で紹介された &lt;a href=&apos;https://youtu.be/MGt25mv4-2Q&apos;&gt;Google Chrome の広告&lt;/a&gt;。創作の連鎖を表現する動画で、Everyone, Creator のキャッチフレーズが印象的です。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Let's Encrypt (Certbot) でワイルドカード証明書を取得できないときの解決法</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-10-03/letsencrypt-wildcard-error' />
      <id>https://blog.comame.xyz/entries/2019-10-03/letsencrypt-wildcard-error</id>
      <updated>2019-10-03T00:00:00Z</updated>
      <summary>&lt;h2&gt;発生する問題&lt;/h2&gt;
&lt;p&gt;Certbot でワイルドカード証明書を発行しようとしたときに&lt;code&gt;The currently selected ACME CA endpoint does not support issuing wildcard certificates&lt;/code&gt;というお叱りを受ける。&lt;/p&gt;

&lt;h2&gt;解決方法&lt;/h2&gt;
&lt;p&gt;ACME v2 のエンドポイントを直接指定してやれば良い。
&lt;pre&gt;&lt;code&gt;$ certbot certonly --server https://acme-v02.api.letsencrypt.org/directory&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s Encrypt のエンドポイントは&lt;a href=&apos;https://letsencrypt.org/ja/docs/acme-protocol-updates/&apos;&gt;このドキュメント&lt;/a&gt;に記載されている。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>AdSense の申請が落ちた理由？</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-09-27/why-fail-adsense' />
      <id>https://blog.comame.xyz/entries/2019-09-27/why-fail-adsense</id>
      <updated>2019-09-27T00:00:00Z</updated>
      <summary>&lt;p&gt;結論を先に述べると、AdSense の審査は無事に通過しました。1度審査に落ちたため、その理由と思われるものを残しておこうと思います。&lt;p&gt;

    &lt;h2&gt;落ちた原因の仮説&lt;/h2&gt;
    &lt;p&gt;はじめに審査に落ちた理由は「コンテンツの不足」でした。しかしこのブログにはすでに20ほど記事があり、またユーザーにとって無意味なコンテンツではないと思われます (少なくともそう思いたい)。そのため、ドメインを変更する前のデータを Google が何らかの原因で持っていて、参照しているのではないかという仮説を立てました。&lt;br&gt;
    ドメインを変更する経緯は&lt;a href=/entries/2019-09-24/apply-adsense.html&gt;こちら&lt;/a&gt;に書いてあります。&lt;/p&gt;

    &lt;h2&gt;仮説の検証・対処&lt;/h2&gt;
    &lt;p&gt;Google が持っているキャッシュの中でも、心当たりがあったのは Google 検索のインデックスでした。現在のインデックス状況を調べるために、Search Console の URL 検査を https://comame.xyz/ に行いました。そうすると、確かに変更前のものがインデックスされていることを確認できました。&lt;br&gt;
    そこで、検索のインデックスを更新させるために、インデックスの登録リクエストを送信しました。&lt;/p&gt;

    &lt;h2&gt;結果&lt;/h2&gt;
    &lt;p&gt;1日ほどインデックスに登録されるのを待ち、再度 URL 検査でブログがインデックスされていることを確認しました。その後再び AdSense の申請を行いました。その結果、無事に審査を通過しました。&lt;/p&gt;

    &lt;h2&gt;疑問点&lt;/h2&gt;
    &lt;p&gt;この仮説に対して、1点疑問が残ります。AdSense の審査をするときに、Google が指定する JavaScript のコードを Web ページに埋め込む必要があります。しかし、Google が元々インデックスしていたページには当然スクリプトを埋め込んでいませんでした。審査に落ちた理由はあくまでコンテンツの不足であり、指定するコードが見つからなかったことではありませんでした。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>OpenSSL で自己証明書を作成する方法</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-09-26/self-signed-certificate' />
      <id>https://blog.comame.xyz/entries/2019-09-26/self-signed-certificate</id>
      <updated>2019-09-26T00:00:00Z</updated>
      <summary>&lt;p&gt;開発鯖用に毎回 Let&apos;s Encrypt で発行するのめんどいので、自分で発行しようと思った&lt;/p&gt;

&lt;h2&gt;通常の証明書&lt;/h2&gt;

&lt;p&gt;
&lt;pre&gt;&lt;code&gt;$ openssl genrsa -aes256 2048 &gt; server.key
$ openssl req -new -key server.key &gt; server.csr  # Certificate Signing Request
$ openssl x509 -req -days 90 signkey server.key &lt; server.csr &gt; server.crt&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;h2&gt;複数の Common Name に対応する&lt;/h2&gt;
&lt;p&gt;
&lt;pre&gt;&lt;code&gt;$ cat &gt; altNames.txt
subjectAltName = DNS:foo.example.com, DNS:bar.example.com

$ openssl x509 -req -days 90 -signkey server.key -extfile altNames.txt &lt; server.csr &gt; server.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/p&gt;

&lt;h2&gt;CSR を確認する&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ openssl req -in server.csr -noout -text&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;証明書の内容 (あるいは期限のみ) を確認する&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ openssl x509 -in server.crt -noout -text [-dates]&lt;/code&gt;&lt;/pre&gt;
</summary>
    </entry>
    
    <entry>
      <title>AdSense の申請をした</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-09-24/apply-adsense' />
      <id>https://blog.comame.xyz/entries/2019-09-24/apply-adsense</id>
      <updated>2019-09-24T00:00:00Z</updated>
      <summary>&lt;p&gt;今日このブログで AdSense の申請をしましたが、いくつか技術的な問題があったため、それの対処法を紹介します。&lt;/p&gt;

&lt;h2&gt;発生した問題&lt;/h2&gt;
&lt;p&gt;AdSense の申請をするには当然対象の URL が必要ですが、サブドメインでは申請できないという問題があります。このブログは blog.comame.xyz で配信していましたが、comame.xyz に付け替える必要がありました。&lt;br&gt;
一方、comame.xyz はすでに自前のサーバに割り当てていました。このブログで使用している一部の画像やスクリプトなども comame.xyz から取得していました。そのため、DNS を書き換えるだけではブログのレイアウトが崩れてしまいます。&lt;/p&gt;

&lt;h2&gt;状況&lt;/h2&gt;
&lt;p&gt;comame.xyz 上では、すでにいくつかのサービスが動いています。一方で、過去に Docker を使用することで複数のサーバに分割させており、Nginx のリバースプロキシを使ってリクエストを振り分けていました。実際の Nginx のコンフィグの一部は以下のようになっていました。&lt;p&gt;
&lt;pre&gt;&lt;code&gt;
server {
  location / {
    location / {
       # Docker Compose のサービスの1つとしてホームページを配信していた
       proxy_pass http://home/;
    }
    location /assets {
      # 画像や CSS、JavaScript はここから配信していた
      proxy_pass http://assets/;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この設定ファイルからわかるように、アセット配信サーバとホームページ配信サーバは別のコンテナで実行していました。&lt;/p&gt;


&lt;h2&gt;解決策&lt;/h2&gt;
&lt;p&gt;ルートディレクトリだけを blog.comame.xyz へのリバースプロキシとすることで解決しました。アセット配信サーバは変わらず /assets で配信しています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
server {
    location / {
        location / {
            # 今まではコンテナに向けていたものを、blog.comame.xyz に差し替え
            proxy_pass: https://blog.comame.xyz/;

            # TLS 周りのエラーが発生したので
            proxy_ssl_server_name on;
        }
        location /assets {
            # 変更なし
            proxy_pass: http://assets/;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;現状&lt;/h2&gt;
&lt;p&gt;DNS をいじったわけではないので、blog.comame.xyz にアクセスすることでもブログにアクセスできます。comame.xyz にアクセスすると、今まで表示されていたホームページではなく、ブログが表示されるようになっています。&lt;br&gt;
次のような流れになっています。&lt;br&gt;
&lt;code&gt;User Agent -&gt; comame.xyz -(Nginx が代理アクセス)-&gt; blog.comame.xyz&lt;/code&gt;
&lt;/p&gt;

&lt;h2&gt;教訓&lt;/h2&gt;
&lt;p&gt;止めると影響が大きいサーバとそうでもないサーバは、分けておいたほうが後々役に立つ (適当)&lt;br&gt;
AdSense の申請通るといいな&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Ubuntu ユーザー作成</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-09-18/ubuntu-create-user' />
      <id>https://blog.comame.xyz/entries/2019-09-18/ubuntu-create-user</id>
      <updated>2019-09-18T00:00:00Z</updated>
      <summary>&lt;ol&gt;
    &lt;li&gt;&lt;code&gt;adduser [user]&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;gpasswd -a [user] [group]&lt;/code&gt;&lt;/li&gt;
    &lt;/ol&gt;
</summary>
    </entry>
    
    <entry>
      <title>Rootless Docker のインストール方法</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-09-13/install-rootless-docker' />
      <id>https://blog.comame.xyz/entries/2019-09-13/install-rootless-docker</id>
      <updated>2019-09-13T00:00:00Z</updated>
      <summary>&lt;p&gt;今更感は否めないが、備忘録も兼ねて&lt;/p&gt;

&lt;h2&gt;手順&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;https://get.docker.com/rootless&lt;/code&gt;からシェルスクリプトをダウンロードする&lt;/li&gt;
&lt;li&gt;実行する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$HOME/bin/dockerd-rootless.sh --experimental --storage-driver vfs&lt;/code&gt;を実行する
&lt;/ol&gt;

&lt;h2&gt;補足&lt;/h2&gt;

&lt;p&gt;インストールスクリプトを覗いてみたところ、どうやら自動的に Service を作成してくれるらしい。&lt;/p&gt;

&lt;h2&gt;ドキュメント&lt;/h2&gt;
&lt;p&gt;&lt;a href=&apos;https://github.com/moby/moby/blob/master/docs/rootless.md&apos;&gt;https://github.com/moby/moby/blob/master/docs/rootless.md&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>comame.xyz を ConoHa に移した</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-07-11/migrate-to-conoha' />
      <id>https://blog.comame.xyz/entries/2019-07-11/migrate-to-conoha</id>
      <updated>2019-07-11T00:00:00Z</updated>
      <summary>&lt;p&gt;このはちゃんかわいい&lt;/p&gt;


&lt;h2&gt;Docker&lt;/h2&gt;

&lt;p&gt;今回は、Web サーバを Docker でコンテナに包んでみました。デプロイが圧倒的に簡単になりました。今まではマネージドサービスと自分でサーバを管理するのとの間で振り子のように動いていましたが、これからは移動も楽になる気がします。&lt;/p&gt;


&lt;h2&gt;速度&lt;/h2&gt;

&lt;p&gt;今までは Firebase Hosting の US リージョンに置いていたため、RTT が 300 ms くらいありました。流石に堪えられないなーと思っていたのが ConoHa に移した大きな理由の一つです。ConoHa は東京にリージョンがあるので、とてもはやいです (小並感)。&lt;/p&gt;


&lt;h2&gt;開発のしやすさ&lt;/h2&gt;

&lt;p&gt;今回は Nginx もコンテナに閉じ込めたので、リモートマシンでも完全に同じ環境でサーバを起動できるようになりました。これにより、今まではサーバにアップロードしないと確認できなかったようなこと (Secure Context で動く API とか) もローカル環境で手軽にできるようになりました。&lt;/p&gt;

&lt;h2&gt;改善したい点&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;ログ設定をきちんとしていないので、コンテナを止めるとログも消えてしまう&lt;/li&gt;
  &lt;li&gt;Nginx の proxy_pass でサービス名を直に指定しているので、すべてのコンテナを立ち上げないとエラーを吐いてしまう&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;このはちゃんかわいい&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Web ページの読み込みパフォーマンスを改善する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-04-23/improve-web-performance' />
      <id>https://blog.comame.xyz/entries/2019-04-23/improve-web-performance</id>
      <updated>2019-04-23T00:00:00Z</updated>
      <summary>&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;レンダリングブロッキング要素を減らす&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Web フォントのロードを制御する&lt;/li&gt;
&lt;li&gt;画像を最適化する&lt;/li&gt;
&lt;li&gt;地理的に近い場所にサーバを置く&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;レンダリングブロッキング要素を減らす&lt;/h2&gt;
&lt;p&gt;ブラウザのレンダラは、HTML を上から順に (ファイルに記述してある順に) 解釈して DOM を構築します。途中で JavaScript や CSS が埋め込まれている場合は、スクリプトの実行やスタイルの計算が終わるまで DOM の構築は中断されます。これをレンダリングブロッキングと呼びます。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;最も効果的な対処法は、&lt;code&gt;async&lt;/code&gt; 属性をつけることです。&lt;code&gt;async&lt;/code&gt; 属性が付与されたスクリプトは、DOM の構築を妨げません。特に、外部リソースを取得するような場合に効果が発揮されます。一方で、複数の &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; タグにそれぞれ &lt;code&gt;async&lt;/code&gt; 属性が付与されていた場合、実行順序が保証されないことに注意が必要です。&lt;code&gt;HTMLScriptElement.onload&lt;/code&gt; イベントを活用するのが良いでしょう。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot;&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; タグの場合は &lt;code&gt;async&lt;/code&gt; 属性をつけるだけで済みましたね。しかし、&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; タグには非同期に読み込ませるような属性は存在しません。そのため、JavaScript を使用して非同期に読み込ませるような処理を書くことになります。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;
&lt;code&gt;  &amp;lt;noscript id=&quot;async-styles&quot;&amp;gt;&lt;/code&gt;
&lt;code&gt;  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;&lt;/code&gt;
&lt;code&gt;  &amp;lt;/noscript&amp;gt;&lt;/code&gt;
&lt;code&gt;  &amp;lt;script&amp;gt;&lt;/code&gt;
&lt;code&gt;  window.requestAnimationFrame(() =&amp;gt; {&lt;/code&gt;
&lt;code&gt;    document.head.innerHTML +=&lt;/code&gt;
&lt;code&gt;    document.getElementById(&apos;async-styles&apos;).textContent;&lt;/code&gt;
&lt;code&gt;  });&lt;/code&gt;
&lt;code&gt;  &amp;lt;/script&amp;gt;&lt;/code&gt;
&lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;あるいは、&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; タグを使用して HTML に埋め込んでしまうのも有効です。ただし、再利用性が低いこと、Contents Security Policy との相性が悪いことに注意が必要です。&lt;/p&gt;
&lt;p&gt;実際のところ、レンダリングブロックする要素を減らすだけでも相当な効果が見込めます。適切にレンダリングブロックを制御した場合、リソースのダウンロードがすべて完了していなくとも必要最低限の要素だけ表示させることができます。&lt;/p&gt;
&lt;p&gt;一方で、JavaScript の実行が必須な Web アプリケーションの場合、表示されているのに操作できない時間が発生することになります。このような場合は、ローディングアニメーションを表示するか、あるいは意図的にレンダリングブロックを発生させるという手段も選択肢となり得るでしょう。&lt;/p&gt;
&lt;h2&gt;Web フォントのロードを制御する&lt;/h2&gt;
&lt;p&gt;Web フォントというのはとても便利なものです。導入が簡単にもかかわらず、Web ページの印象を大きく変えてくれます。&lt;/p&gt;
&lt;p&gt;しかし、無頓着に Web フォントを使うのは考えものです。ネットワークの速度が遅い環境では、Web フォントのロードが終わるまで文字が表示されないかもしれません。(みなさんも経験ありませんか？) 酷い場合にはユーザーを5秒以上待たせてしまうこともあります。&lt;/p&gt;
&lt;p&gt;これは CSS ファイルにほんの数行付け足すだけで解決できます。&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;@font-face {&lt;/code&gt;
&lt;code&gt;  /* ... */&lt;/code&gt;
&lt;code&gt;  font-display: swap;&lt;/code&gt;
&lt;code&gt;}&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;この 1 行を付け足すだけで、ブラウザは Web フォントがロードされるまでは、オフラインで使用可能な他のフォントで代わりに表示してくれます！&lt;/p&gt;
&lt;h2&gt;画像を最適化する&lt;/h2&gt;
&lt;p&gt;Web ページの平均サイズはどんどん巨大化しています。そして、そのうちの大部分を画像が占めています。画像のファイルサイズを少しでも減らすことができれば、きっともう少しページの読み込みが早くなることでしょう。&lt;/p&gt;
&lt;p&gt;WebP など最新のフォーマットを有効活用しましょう。フォーマットに気を配るだけで、ファイルサイズを大幅に削減できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; タグを使って複数のフォーマットの画像を選択できるようにすれば、最新のフォーマットに対応していないようなブラウザでも正常に画像を表示させることができます。&lt;/p&gt;
&lt;h2&gt;最後に&amp;gt;&lt;/h2&gt;
&lt;p&gt;これらの対策を施したら、最適化の効果の程を確かめてみましょう。Chrome ブラウザを使用しているのであれば、DevTools の Audits タブでパフォーマンスを調査できます。&lt;/p&gt;
&lt;p&gt;また、&lt;a href=https://developers.google.com/speed/pagespeed/insights/?hl=ja target=&apos;_blank&apos; rel=&apos;noopener&apos;&gt;PageSpeed Insights&lt;/a&gt; を使用するのもおすすめです。&lt;/p&gt;
&lt;p&gt;ここに紹介した以外にもたくさんの高速化の手法があります。調べてみると良いでしょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resource Hints&lt;/li&gt;
&lt;li&gt;Service Worker + Cache API&lt;/li&gt;
&lt;li&gt;Lazy Load (Intersection Observer v2 とかいい感じに使えそう)&lt;/li&gt;
&lt;li&gt;レイアウトを静的に指定する&lt;/li&gt;
&lt;li&gt;AMP (Accelerated Mobile Pages)&lt;/li&gt;
&lt;li&gt;SSR (Server Side Rendering)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2019/07/04 追記&lt;/h2&gt;
&lt;p&gt;上にメモした Lazy Loading ですが、Chrome に &lt;code&gt;loading&lt;/code&gt; 属性が追加されました。まだ実用段階ではなさそうですが、注目していきたいです。&lt;/p&gt;
&lt;h2&gt;2019/07/10 また追記&lt;/h2&gt;
&lt;p&gt;今まで Google Compute Engine の US リージョンに置いていましたが、ConoHa の東京リージョンに置くようにしたらめちゃくちゃ速度が改善しました。地理的な要因も見過ごせませんね。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Safari のブックマークを Chrome にインポートできない問題</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-04-22/safari-bookmark-import' />
      <id>https://blog.comame.xyz/entries/2019-04-22/safari-bookmark-import</id>
      <updated>2019-04-22T00:00:00Z</updated>
      <summary>&lt;p&gt;現在、macOS 10.14 Mojave において、Safari のブックマークを Chrome にインポートできない問題が発生しているようです。&lt;/p&gt;
&lt;h2&gt;
問題が発生しうる環境&lt;/h2&gt;
&lt;p&gt;macOS 10.14 Mojave がインストールされたすべてのコンピュータ&lt;/p&gt;
&lt;h2&gt;
現象の回避方法&lt;/h2&gt;
&lt;p&gt;HTML でブックマークを書き出し、Chrome にインポートする。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Safari を開き、「ファイル」&amp;gt;「ブックマークの書き出し」を実行してください。&lt;/li&gt;
&lt;li&gt;Chrome の設定画面を開いてください。&lt;/li&gt;
&lt;li&gt;「ブックマークと設定のインポート」を開き、インポート先を「HTML ファイルをブックマークに登録」に指定してください。&lt;/li&gt;
&lt;li&gt;「ファイルを選択」ボタンを押し、手順  1 でエクスポートしたファイルを選択してください。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;h2&gt;
原因&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
macOS のセキュリティ機能が原因のようです。&lt;/div&gt;
&lt;div&gt;
現在 Chrome は Safari のブックマークが保存してあるデータベースを直に読み出すようになっています。しかし、Mojave では Safari の閲覧履歴や Cookie を含む機密データを他のアプリケーションが読み取れないような変更が加えられたため、正常にブックマークをインポートできない問題が発生しています。&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
現在の対応状況&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Apple からの情報待ちのようです。&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
参照&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
Issue 850225: Mojave: Review importers from other browsers&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=850225&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;https://bugs.chromium.org/p/chromium/issues/detail?id=850225&lt;/a&gt;&lt;/div&gt;
</summary>
    </entry>
    
    <entry>
      <title>Macbook Pro を買いました</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-03-12/macbook-pro' />
      <id>https://blog.comame.xyz/entries/2019-03-12/macbook-pro</id>
      <updated>2019-03-12T00:00:00Z</updated>
      <summary>&lt;p&gt;
&lt;b&gt;やったぜ！！！！！！！！！！！&lt;/b&gt;


これで Windows, Mac, Chrome OS が揃いました。
もうちょっと使ってみてからそのうちきちんとしたレビューを書くかもしれません。おそらく。きっと。
&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ただいま Firebase Hosting</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-02-04/firebase-hosting' />
      <id>https://blog.comame.xyz/entries/2019-02-04/firebase-hosting</id>
      <updated>2019-02-04T00:00:00Z</updated>
      <summary>&lt;p&gt;
    comame.xyz を Google Compute Engine から Firebase Hosting に戻しました。やはり、自動ですべてを管理してくれるのは便利なものですね。&lt;br&gt;
    Cloud Functions と接続できるので、今まで Node.js で処理していたものも簡単に移植できました。Cloud Functions も Express ベースなので、コードはほとんどそのままでした。&lt;br&gt;
    &lt;br&gt;
    ただし、HTTP ヘッダを自由に書き換えられないのは盲点でした。firebase.json で書き換えることはできるようなのですが、HSTS Preload だけは諦めるしかなさそうです。標準で HSTS は設定されているので、それで妥協することにします。
    &lt;br&gt;
    自分で Linux サーバを動かすのはとても勉強になりますが、静的なファイルを配信するだけであれば、既存のサービスを使ってしまうのがどうしても楽ですね。
&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Windows 10 の Media Creation Tool で USB インストールメディアの作成に失敗する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-01-25/windows10-install-media' />
      <id>https://blog.comame.xyz/entries/2019-01-25/windows10-install-media</id>
      <updated>2019-01-25T00:00:00Z</updated>
      <summary>&lt;p&gt;Windows 10 の Media Creation Tool で USB インストールメディアの作成に失敗するなあ、と思っていたら、予想外のところに理由があったようです。すでに同じようなことを書いてある記事がたくさんありますが、備忘録程度に。&lt;/p&gt;

&lt;h2&gt;問題&lt;/h2&gt;
&lt;p&gt;USB メモリに Windows のインストールメディアを作成しようとすると、「失敗しました」のようなエラーが表示されてしまう。&lt;/p&gt;

&lt;h2&gt;解決方法&lt;/h2&gt;
&lt;p&gt;あらかじめ USB メモリを NTFS でフォーマットしておく。explorer.exe のクイックフォーマットで問題なし。&lt;p&gt;

&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;どうせ NTFS でフォーマットしないと書き込めないのならば、Media Creation Tool 側で自動的にフォーマットしてくれればいいのになあと思いました。&lt;p&gt;
</summary>
    </entry>
    
    <entry>
      <title>USB セキュリティキーを買った</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-01-15/usb-security-key' />
      <id>https://blog.comame.xyz/entries/2019-01-15/usb-security-key</id>
      <updated>2019-01-15T00:00:00Z</updated>
      <summary>&lt;img src=&quot;https://4.bp.blogspot.com/-K7VZSb9bU4I/XD2o-Aew2TI/AAAAAAABDRo/aOmdBYpSjPcWL7bdHBt-ON64j1rsMxN_QCKgBGAs/s1600/IMG_20190115_181149.jpg&quot;&gt;

&lt;p&gt;2 段階認証を楽にしたいと思って飛天の e-Pass FIDO-NFC を購入しました。&lt;br&gt;
Google Authenticator などのコード表示アプリからコードをコピペしなくても、キーを USB ポートに入れるだけでログインできるようになります。フィッシング攻撃に対しても非常に有効です。&lt;/p&gt;

&lt;h2&gt;サービスごとの対応状況&lt;/h2&gt;
&lt;p&gt;早速、各 Web サービスごとに対応状況を確認してみました。&lt;br&gt;
PC: ASUS Chromebook C100PA (Chrome OS 72.0.3626.49 Beta)&lt;br&gt;
Android: Google Pixel 3 (Android 9, Chrome for Android 71.0.3578.99)&lt;/p&gt;

&lt;h3&gt;Google&lt;/h3&gt;
&lt;p&gt;USB, NFC ともに対応済みでした。Android 端末からでも NFC を利用して認証できます。&lt;/p&gt;

&lt;h3&gt;Twitter&lt;/h3&gt;
&lt;p&gt;USB のみ対応済みでした。PC からであれば USB を使って認証できますが、Android からは次のように表示されてしまいます。&lt;/p&gt;
&lt;img src=&quot;https://2.bp.blogspot.com/-WYMjEddpHBg/XD2mzITN3II/AAAAAAABDRU/ZIuQFcbXDj4jCvT_0NkE7zdaW7sfFPcBgCKgBGAs/s1600/Screenshot_20190115-181555%257E2.png&quot;&gt;

&lt;h3&gt;Microsoft&lt;/h3&gt;
&lt;p&gt;設定項目を見つけられませんでした。FIDO 2 でのログインに対応したというニュースを見かけたので、できると思ったのですが。&lt;/P&gt;

&lt;h3&gt;Amazon&lt;/h3&gt;
&lt;p&gt;これまた設定項目が見つかりませんでした。「Amazon セキュリティキー」で調べると商品ページしか出てこないんですね...&lt;/p&gt;

&lt;h3&gt;GitHub&lt;/h3&gt;
&lt;p&gt;01/16 追記&lt;br&gt;
GitHub を調べ忘れていました。GitHub でも USB, NFC ともに使用可能でした。&lt;/p&gt;

&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;(少なくとも僕の身近なところでは) 思ったよりきちんと使えるサービスが少ないということが分かりました。WebAuthn 流行ってほしいです。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>8.8.8.8 が DNS-over-TLS に対応したようです</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2019-01-11/8888-dns-over-tls' />
      <id>https://blog.comame.xyz/entries/2019-01-11/8888-dns-over-tls</id>
      <updated>2019-01-11T00:00:00Z</updated>
      <summary>&lt;p&gt;8.8.8.8 で知られる Google Public DNS が &lt;a href=&quot;https://developers.google.com/speed/public-dns/docs/dns-over-tls&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;DNS-over-TLS&lt;/a&gt; に対応したようです。既に Cloudflare の提供する 1.1.1.1 は対応済みなので、耳にしたことはあるかもしれません。&lt;br&gt;
    DNS への問い合わせを TLS で暗号化することで、応答の改ざんを防ぐことができます。&lt;/p&gt;

    &lt;p&gt;Android 9 Pie 以降を搭載している Android スマートフォンであれば、簡単に設定ができます。Windows や Mac OS 等では、OS 標準では DNS-over-TLS がサポートされないようです。&lt;/p&gt;

    &lt;p&gt;参照: &lt;a href=&quot;https://developers.google.com/speed/public-dns/docs/using&quot; target=_blank&quot; rel=&quot;noopener&quot;&gt;Get Started | Google Public DNS&lt;/a&gt;&lt;/p&gt;


    &lt;h2&gt;設定方法&lt;/h2&gt;

    &lt;p&gt;「設定」アプリを開き、「ネットワークとインターネット」&gt;「詳細設定」&gt;「プライベート DNS」と進みます。&lt;br&gt;
    「プライベート DNS プロバイダのホスト名」に &lt;code&gt;dns.google&lt;/code&gt; と入力します。
    ブラウザを開き、Web サイトに正常にアクセスできたら完了です。&lt;/p&gt;

    &lt;p&gt;大事なことを書き忘れた: これは Pixel 3 での操作方法です。端末によって操作が異なるかもしれません。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Let's Encrypt でワイルドカード証明書を発行してみたお話</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-12-18/letsencrypt-wildcard' />
      <id>https://blog.comame.xyz/entries/2018-12-18/letsencrypt-wildcard</id>
      <updated>2018-12-18T00:00:00Z</updated>
      <summary>&lt;p&gt;Let&apos;s Encrypt でワイルドカード証明書を取得してみたら驚くほど簡単だったので、記事として残しておきます。&lt;/p&gt;

&lt;h2&gt;ワイルドカード証明書とは&lt;/h2&gt;

&lt;p&gt;ワイルドカード証明書とは、サブドメイン全てに一致する証明書です。例えば &lt;code&gt;*.example.com&lt;/code&gt; で取得した証明書は、&lt;code&gt;sub1.example.com&lt;/code&gt; や &lt;code&gt;sub2.example.com&lt;/code&gt; に一致します 。&lt;/p&gt;

&lt;p&gt;ワイルドカード証明書を使用することで、サブドメイン全てに個別の証明書を発行する手間が省けるため、管理がぐっと楽になります。&lt;/p&gt;

&lt;h2&gt;手順&lt;/h2&gt;

&lt;p&gt;今回は Dry Run を使用して、証明書の発行ができることを確認するだけにします。&lt;/p&gt;

&lt;p&gt;ワイルドカード証明書を発行するには、DNS を使用してドメインを検証する必要があります。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo certbot certonly --dry-run &lt;b&gt;--manual --preferred-challenges dns-01&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;次に、発行先のドメインを聞かれます。&lt;br&gt;
&lt;code&gt;*.example.com&lt;/code&gt; のように入力するだけで、自動的にワイルドカード証明書として発行してくれます。便利ですね。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Please enter in your domain name(s) (comma and/or space separated)  (Enter &apos;c&apos; to cancel):
*.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最後にドメインの検証です。表示される案内に従って、DNS に TXT レコードを設定してください。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:

ランダムな文字列

Before continuing, verify the record is deployed.
Press Enter to Continue
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;DNS を設定したら、エンターキーを押すと証明書が発行されます。&lt;/p&gt;

&lt;h2&gt;注意事項・ハマったところ&lt;/h2&gt;

&lt;h3&gt;サブドメインにしか適用されない&lt;/h3&gt;

&lt;p&gt;先程は &lt;code&gt;*.example.com&lt;/code&gt; に対して証明書を発行しましたが、この証明書は &lt;code&gt;example.com&lt;/code&gt; には使用できません。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;example.com&lt;/code&gt; にも対応する証明書を作成するには、次のようにします。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Please enter in your domain name(s) (comma and/or space separated)  (Enter &apos;c&apos; to cancel):
&lt;b&gt;*.example.com example.com&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;証明書の自動更新がめんどい&lt;/h3&gt;

&lt;p&gt;ワイルドカード証明書を発行するときに &lt;code&gt;--manual&lt;/code&gt; を指定する必要がありましたが、この影響でいつもどおり &lt;code&gt;sudo certbot renew&lt;/code&gt; しても怒られてしまいます。シェルスクリプトを作成して、引数に渡す必要があるみたいです。&lt;/p&gt;

&lt;p&gt;デフォルトの certbot の設定では自動更新がなされないので、注意が必要です。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>LINE Pay を Google Pay で使ってみた</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-11-30/line-pay-google-pay' />
      <id>https://blog.comame.xyz/entries/2018-11-30/line-pay-google-pay</id>
      <updated>2018-11-30T00:00:00Z</updated>
      <summary>&lt;p&gt;&lt;a href=&quot;http://pay-blog.line.me/archives/13246446.html&quot;&gt;LINE Pay を Google Pay で使用できるようになった&lt;/a&gt;ので、早速使い始めてみました。今までスマホを使って支払いをしたことがなかったので、おサイフケータイを使うこと自体が初めてです。&lt;/p&gt;

&lt;h2&gt;便利なところ&lt;/h2&gt;

&lt;p&gt;まずはやはり、スマホだけを持って行けば支払いができるという点が最も便利です。近くのコンビニで買い物をしたいときなど、ちょっとした買い物のたびに財布を持ち出す必要がなくなりました。また、レジ前で焦って小銭を探す手間も無くなりました。&lt;br&gt;
また、物理的なカードも併用できるため、非接触型決済に対応していない店舗でも LINE Pay を使用して支払うことは可能です。&lt;/p&gt;


&lt;h2&gt;不便・改善してほしいところ&lt;/h2&gt;

&lt;p&gt;端末のロック時は決済をできないようにする機能が欲しいと思います。LINE Pay はプリペイドなので悪用された際の被害はあまり大きくありませんが、やはり気にすると気になるものです。最近は指紋認証が搭載されている Android 端末も増えてきているので、ぜひロックを追加してほしいですね。&lt;/p&gt;

&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;もしおサイフケータイに対応している端末をお持ちの方は、この機会にぜひ試してみてください。今なら LINE Pay を Google Pay に登録することで 1000 円分のクレジットを入手できます。&lt;br&gt;
今まで「Suica でいいじゃん」などと思っていましたが、常に持ち歩くスマホだけで済むというのはやはり便利だと思いました。&lt;/br&gt;
</summary>
    </entry>
    
    <entry>
      <title>「Google Pixel まだ見ぬ世界展」に行ってきた</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-10-21/pixel-event' />
      <id>https://blog.comame.xyz/entries/2018-10-21/pixel-event</id>
      <updated>2018-10-21T00:00:00Z</updated>
      <summary>&lt;p&gt;表参道駅前で28日まで行われている、&lt;a href=&quot;http://designart.jp/designarttokyo2018/exhibitor/google/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google Pixel まだ見ぬ世界展&lt;/a&gt; に遊びに行ってきました。このイベントでは、11月1日に日本で発売される Google Pixel 3 の実機を試すことができます。&lt;br&gt;
    会場は Pixel のカメラ性能を実感できるような造りになっていましたが、カメラ以外にもかなり自由に端末に触れることができました。&lt;/p&gt;

    &lt;img src=&quot;https://1.bp.blogspot.com/-LlOgKux_tls/W8xIwBcmlGI/AAAAAAABAoo/2JKtsomi8Zo4a_HijCBkxNqtL754UycaQCKgBGAs/s1600/52_1540099167.jpg&quot; alt=&quot;会場のロゴ写真&quot;&gt;

    &lt;h2&gt;Pixel 3&lt;/h2&gt;

    &lt;h3&gt;サイズ感&lt;/h3&gt;
    &lt;p&gt;僕は今 ASUS Zenfone Go を使用しているのですが、手があまり大きくないため片手での操作があまり快適とはいえません。しかし、Pixel は横幅が少し小さめにとってあるため、片手での操作も楽々といった印象でした。&lt;br&gt;
    逆に、動画やゲームを楽しみたいという方は XL でないと少し物足りないと感じるかもしれません。最も、僕があまりゲームや動画の閲覧をスマホですることが余りないので、あまり自信をもって言うことはできないのですが。&lt;/p&gt;

    &lt;h3&gt;触り心地&lt;/h3&gt;
    &lt;p&gt;裏面もガラスでコーティングされているのですが、手に持った時に落としそうという感じはあまりしませんでした。ツルツルではあるのですが、手に吸い付くような感じとでも言いましょうか。&lt;/p&gt;

    &lt;h3&gt;物理ボタン&lt;/h3&gt;
    &lt;p&gt;端末を握った右側面に、電源ボタンと音量ボタンが付いています。かなりしっかりとしていて、強く押し込む必要がありました。&lt;br&gt;
    上から順に電源ボタン、音量ボタンの順についているのですが、個人的には逆向きのが好きです。&lt;/p&gt;

    &lt;h3&gt;カメラ&lt;/h3&gt;
    &lt;p&gt;売りにしているだけあり、やはりすごいなあという印象でした。適当に撮影しても、それなりに絵になる写真は撮れるんじゃないかと思います。カメラについてはあんまりわからない...&lt;br&gt;
    この記事の上のほうにある写真も、Pixel で撮影したものです。結構いい感じじゃないですか？&lt;/p&gt;

    &lt;h3&gt;Google Lens&lt;/h3&gt;
    &lt;p&gt;ついにこの機能が日本にもやってまいりました！ 今まで Google Lens を使ったことがないのですが、結構きちんと認識してくれました。この機能はいろいろと遊べそうなので、今後ほかの端末でも使えるようになるのかが楽しみです。&lt;/p&gt;

    &lt;h3&gt;Playground (AR)&lt;/h3&gt;
    &lt;p&gt;Pixel のカメラアプリに搭載されている、&lt;a href=&quot;https://developers.google.com/ar/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ARCore&lt;/a&gt; を使用してマスコットや文字を空間上に配置することができる機能です。実際にこの機能を便利に使うかといわれると正直微妙ですが、床や壁を認識する精度は結構きちんとしている印象でした。少なくとも普通の部屋であれば問題なく使用できるかなという感じです。&lt;br&gt;
    空間に配置できるマスコットは Google Play Store からインストールして、自由に追加できるようです。&lt;/p&gt;

    &lt;h2&gt;まだ見ぬ世界展&lt;/h2&gt;

    &lt;p&gt;イベント会場は4階建てのビルになっていて、各階ごとにテーマが設定されています。それぞれの階でテーマに沿った体験をし、すべての階を回ると最後に飲み物がもらえます。&lt;br&gt;
    所要時間はおよそ30分程度ですが、同時に入れる人数が決まっている影響で、入場待ちに時間がかかります。&lt;/p&gt;

    &lt;h3&gt;1F Cave&lt;/h3&gt;
    &lt;p&gt;受付です。ここで端末を受け取り、上の階に上がっていきます。&lt;/p&gt;

    &lt;h3&gt;2F Forest&lt;/h3&gt;
    &lt;p&gt;この階では Google Lens を試すことができます。Pixel 3XL も展示してあります。&lt;/p&gt;

    &lt;h3&gt;3F Sky&lt;/h3&gt;
    &lt;p&gt;この階ではインカメラやポートレート撮影を試すことができます。&lt;/p&gt;

    &lt;h3&gt;4F Beyond&lt;/h3&gt;
    &lt;p&gt;この階では ARCore を使用したアプリケーションで遊ぶことができます。&lt;/p&gt;

    &lt;h2&gt;&lt;/h2&gt;
    &lt;p&gt;発売前に自由に Pixel を触ることができる良い機会でした。時間があればぜひ足を運んでみてください。&lt;br&gt;
    &lt;a href=&quot;http://designart.jp/designarttokyo2018/exhibitor/google/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;「Google Pixel まだ見ぬ世界展」の詳細を確認する&lt;/a&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>ブログの新テンプレートが完成！</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-10-20/new-template' />
      <id>https://blog.comame.xyz/entries/2018-10-20/new-template</id>
      <updated>2018-10-20T00:00:00Z</updated>
      <summary>&lt;p&gt;やっと Blogger 向けのテンプレートが完成しました！&lt;br&gt;
    &lt;a href=&quot;/p/test.html&quot;&gt;サンプル&lt;/a&gt;&lt;/p&gt;
    &lt;p&gt;HTML も CSS も全て手書きです。Blogger のテンプレートは XML の単一ファイルに書かなくてはならない上に、公式ドキュメントが非常に乏しいので、本当に苦労しました。&lt;br&gt;できる限り記事自体を読みやすいようなデザインを心がけてみたのですが、デザインが全く分からない人間なのでご意見あったらどんどんお寄せください。&lt;/p&gt;
    &lt;p&gt;せっかく作ったので、このテンプレートを GitHub かどこかで公開したいなーと思ったりしてますが、広告の配置を一切考えていなかったり、一部の URL をハードコードしていたりで、誰かに使ってもらうのはちょっと難しいかな？&lt;/p&gt;
    &lt;p&gt;GitHub で見つけたこちらのサンプルコードには非常にお世話になったので、リンクをしておきます。これがなかったらテンプレート造りを諦めてた！&lt;br&gt;
    &lt;a href=&quot;https://github.com/lmatteis/blogger-skeleton&quot;&gt;lmatteis/blogger-skeleton | GitHub&lt;/a&gt;&lt;/p&gt;
    &lt;p&gt;今後検索結果画面とかはもうちょっと何とかしたいと思ってます。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>App Engine から Compute Engine に移行</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-08-19/ae-to-ce' />
      <id>https://blog.comame.xyz/entries/2018-08-19/ae-to-ce</id>
      <updated>2018-08-19T00:00:00Z</updated>
      <summary>&lt;p&gt;今まで Google App Engine Node.js Standard Environment (GAE/Node.js SE) で動かしていた &lt;a href=&quot;https://comame.xyz/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;comame.xyz&lt;/a&gt; を、Compute Engine に移しました。&lt;/p&gt;&lt;h2&gt;理由&lt;/h2&gt;&lt;p&gt;Nginx をいじってたら楽しくなってきちゃったから。&lt;/p&gt;&lt;h2&gt;感想&lt;/h2&gt;&lt;ul&gt;    &lt;li&gt;予想を遥かに上回るレベルで楽だった&lt;/li&gt;
    &lt;li&gt;環境依存するコードをほぼ書かなくて済むので、GAE/Node.js SE はいいぞ&lt;/li&gt;
    &lt;li&gt;Nginx 楽しい&lt;/li&gt;
    &lt;li&gt;Certbot (Let&apos;s Encrypt) すごい楽&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;環境&lt;/h2&gt;&lt;ul&gt;    &lt;li&gt;Google Compute Engine / f1-micro&lt;/li&gt;
    &lt;li&gt;Ubuntu 18.04&lt;/li&gt;
    &lt;li&gt;Nginx, Node.js&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;手順&lt;/h2&gt;&lt;ol&gt;    &lt;li&gt;GAE で動いていたコードを、Compute Engine に移す。&lt;/li&gt;
    &lt;li&gt;Nginx の設定ファイルにバーチャルホストの設定を追加する。&lt;/li&gt;
    &lt;li&gt;Node.js を systemd のサービスとして設定する。&lt;/li&gt;
    &lt;li&gt;Nginx の設定ファイルに Node.js へリクエスト回す処理を追記する。&lt;/li&gt;
    &lt;li&gt;Certbot で電子証明書を取得する。&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;コードを移す&lt;/h3&gt;&lt;p&gt;GAE/Node.js SE は GAE に依存する部分が少ないため、ほぼそのままコードを移すだけで完了。静的なファイルを配信する部分だけ、Nginx 側に処理を回すためにコードを削除。&lt;/p&gt;&lt;h3&gt;Nginx の設定をする&lt;/h3&gt;&lt;p&gt;こんな感じ。80 版ポートの方は省略。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# /etc/nginx/sites-enabled/comame.xyz

server {
    server_name comame.xyz;
    listen 443 http2 ssl;

    # ....
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;systemd に Node.js をサービスとして登録する&lt;/h3&gt;&lt;p&gt;多分落ちたときに自動再起動してくれると思う。まだ落ちてないからわからん。&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# /etc/systemd/system/nodejs.service

[Unit]
Description=Node.js server

[Service]
WorkingDirectory=/home/hoge
Type=simple
ExecStart=/usr/bin/node /path/to/script.js
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=node
User=hoge
Group=hoge

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$ systemctl start nodejs.service&lt;/code&gt; でサービスを起動。&lt;code&gt;$ systemctl enable nodejs.service&lt;/code&gt; で OS の起動時にサービスを起動するよう設定。&lt;/p&gt;
&lt;h3&gt;Nginx から Node.js にリクエストを回す&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;# /etc/nginx/sites-enabled/comame.xyz

upstream node {
    # Node.js は 3000 番ポートで動かすことにした
    server 127.0.0.1:3000;
}

server {
    # ...
    location / {
        proxy_pass http://node;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Certbot で電子証明書を取得&lt;/h3&gt;&lt;p&gt;コマンド叩くだけで電子証明書が取得できるの、本当にすごいよね。あとは Nginx 側で証明書のパスを指定するだけ。&lt;/p&gt;&lt;p&gt;&lt;code&gt;$ certbot certonly --nginx&lt;code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Gmail アドレスにおけるピリオドの扱い</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-07-28/gmail-period' />
      <id>https://blog.comame.xyz/entries/2018-07-28/gmail-period</id>
      <updated>2018-07-28T00:00:00Z</updated>
      <summary>&lt;p&gt;&lt;a href=&quot;https://productforums.google.com/forum/#!forum/gmail-ja&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gmail ヘルプフォーラム&lt;/a&gt; で回答していると、それなりの頻度で次のような質問が届きます。&lt;/p&gt;&lt;blockquote&gt;ピリオドの有無が違うだけのメールアドレス宛のメールが、私にも届いています。これは他人宛のものではないのですか？&lt;/blockquote&gt;&lt;p&gt;Gmail の仕様では、@ より左側にあるピリオドの有無は無視されるようになっています。この仕様について、実験を交えて少し詳しく解説します。&lt;/p&gt;&lt;h2&gt;どのような仕様になっているの？&lt;/h2&gt;&lt;p&gt;以下のメールアドレスは、全て同じアカウントのメールアドレスとして扱われます。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;example@gmail.com&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;exam.ple@gmail.com&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;&lt;i&gt;e.x.a.m.p.l.e@gmail.com&lt;/i&gt;&lt;/li&gt;
    &lt;/ul&gt;&lt;h2&gt;他の人に届くことはないのか？&lt;/h2&gt;&lt;p&gt;A. ありません。&lt;/p&gt;&lt;h3&gt;実験&lt;/h3&gt;&lt;p&gt;では、実験をしてみましょう。まずは、&lt;a href=&quot;https://gmail.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gmail&lt;/a&gt; にアクセスし、新規メール作成ダイアログを開いてください。&lt;/p&gt;&lt;ol&gt;&lt;li&gt;ご自身の Gmail アドレスに適当にピリオドを挟んで送信してみる
    例: &lt;i&gt;example@gmail.com&lt;/i&gt; → &lt;i&gt;exam.ple@gmail.com&lt;/i&gt;&lt;/li&gt;
    &lt;li&gt;ご自身の Gmail アドレスから適当にピリオドを外して送信してみる
    例: &lt;i&gt;your.address@gmail.com&lt;/i&gt; → &lt;i&gt;youraddress@gmail.com&lt;/i&gt;&lt;/li&gt;
    &lt;/ol&gt;&lt;h3&gt;解説&lt;/h3&gt;&lt;p&gt;手順 1 と 2 どちらの場合も、自分の受信トレイにメールが届いたはずです。
    Gmail では、@ より左側にあるピリオドは無視され、全て同じアカウントのメールアドレスとして扱われます。つまり、&lt;b&gt;ピリオドがあろうがなかろうが、全て同じ人に届く&lt;/b&gt;ということがわかります。&lt;/p&gt;&lt;h2&gt;他の人が使っている可能性は？&lt;/h2&gt;&lt;p&gt;A. ありません。&lt;/p&gt;&lt;h3&gt;実験&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;Google アカウントからログアウトする&lt;/li&gt;
    &lt;li&gt;再び&lt;a href=&quot;https://accounts.google.com/signin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ログイン画面&lt;/a&gt;を開く&lt;/li&gt;
    &lt;li&gt;普段ログインしているメールアドレスに、適当にピリオドを挟んでログインしてみる&lt;/li&gt;
    &lt;li&gt;普段ログインしているメールアドレスから、適当にピリオドを外してログインしてみる&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://accounts.google.com/signup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;アカウントの作成画面&lt;/a&gt;を開く&lt;/li&gt;
    &lt;li&gt;Gmail アドレスの入力欄に、普段ログイン時に使用している Gmail アドレスを入力し、アカウントを作成しようとしてみる&lt;/li&gt;
    &lt;li&gt;普段ログイン時に使用している Gmail アドレスに適当にピリオドを挟んで入力してみる&lt;/li&gt;
    &lt;li&gt;普段ログイン時に使用している Gmail アドレスから適当にピリオドを外して入力してみる&lt;/li&gt;
    &lt;/ol&gt;&lt;h3&gt;解説&lt;/h3&gt;&lt;p&gt;手順 3 と 4 どちらでも、ご自身のアカウントにログインできることが確認できたはずです。
    また、手順 6, 7, 8 では全て「すでに使用されています」と警告が表示されたはずです。
    つまり、&lt;b&gt;ピリオドの有無が異なるだけの Gmail アドレスは、同一のアカウントとして扱われる&lt;/b&gt;ということがわかります。同一のアカウントなわけですから、他人が使用している可能性はありません。&lt;/p&gt;&lt;h2&gt;まとめ&lt;/h2&gt;
    &lt;p&gt;以上より、Gmail では @ より左側にあるピリオドの有無は無視され、同一のアカウント・宛先として扱われていることがわかります。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>Gmail のフォローアップ機能を停止する</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-07-27/stop-gmail-followup' />
      <id>https://blog.comame.xyz/entries/2018-07-27/stop-gmail-followup</id>
      <updated>2018-07-27T00:00:00Z</updated>
      <summary>&lt;p&gt;新デザインの Gmail には、返信し忘れたメールや対応が必要なメールを判別し、受信トレイの先頭に表示してくれる機能が追加されました。
    このフォローアップ機能は便利ですが、一方で不要な方もいるでしょう。この機能を停止する方法を紹介します。&lt;/p&gt;&lt;h2&gt;モバイル&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Gmail アプリを開く&lt;/li&gt;
    &lt;li&gt;左上のメニューアイコンをタップして、サイドバーを開く&lt;/li&gt;
    &lt;li&gt;最下部の「設定」を開き、フォローアップ機能を停止したいアカウントを選択する&lt;/li&gt;
    &lt;li&gt;「アクションの提案」セクション内の「返信とフォローアップ」を開く&lt;/li&gt;
    &lt;li&gt;停止したいもののチェックボックスを外す&lt;/li&gt;
    &lt;/ol&gt;&lt;h2&gt;デスクトップ&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://gmail.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gmail&lt;/a&gt; を開く&lt;/li&gt;
    &lt;li&gt;右上の歯車アイコンを押し、設定を開く&lt;/li&gt;
    &lt;li&gt;「概要」タブの「アクションの提案」セクションに移動する&lt;/li&gt;
    &lt;li&gt;チェックボックスを外す&lt;/li&gt;
    &lt;/ol&gt;&lt;p&gt;有効にする手順もこれと同様です。&lt;/p&gt;
</summary>
    </entry>
    
    <entry>
      <title>#31DaysOfKotlin Week 1 日本語訳</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-04-30/31daysofkotlin' />
      <id>https://blog.comame.xyz/entries/2018-04-30/31daysofkotlin</id>
      <updated>2018-04-30T00:00:00Z</updated>
      <summary>&lt;p&gt;この記事は、&lt;a href=&quot;https://twitter.com/androiddev&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;@AndroidDev&lt;/a&gt; による &lt;a href=&quot;https://medium.com/google-developers/31daysofkotlin-week-1-recap-fbd5a622ef86&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;#31DaysOfKotlin — Week 1 Recap&lt;/a&gt; を&lt;strong&gt;勝手に&lt;/strong&gt;日本語訳したものです。
    基本的な文法からちょっとした TIPS まで様々なことが紹介されているので、知らないものがあったらぜひ使ってみてください。&lt;/p&gt;&lt;h2&gt;Day 1: Elvis Operator (エルビス演算子)&lt;/h2&gt;&lt;p&gt;Null チェックをよりスマートにするには、エルビス演算子を使ってみましょう。
    Null を別の値に置き換えたり、あるいは&lt;code&gt;return&lt;/code&gt;したりもできます！
    Docs: &lt;a href=&quot;https://kotlinlang.org/docs/reference/null-safety.html#elvis-operator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Elvis Operator&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;val name: String = person.name ?: “unknown”
    val age = person.age ?: return&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;a class=&quot;name&quot; name=&quot;day2&quot;&gt;&lt;/a&gt;Day 2: String Templates (文字列テンプレート)&lt;/h2&gt;&lt;p&gt;文字列に変数を埋め込むには、変数名の前に&lt;code&gt;$&lt;/code&gt;を入れましょう。
    式を入れるには&lt;code&gt;${expression}&lt;/code&gt;のようにしてください。
    Docs: &lt;a href=&quot;https://kotlinlang.org/docs/reference/basic-types.html#string-templates&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;String Templates&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;val language = “Kotlin”

    // “Kotlin has 6 characters”
    val text = “$language has ${language.length} characters”&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Day 3: Destructing Declarations (分解宣言)&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/android/kotlin-extensions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Android KTX&lt;/a&gt; では、色の成分値を割り当てるのに分解宣言を使用します。
    自作のクラス内で分割代入を使用できますし、既存のクラスを拡張して追加することも可能です。(&lt;a href=&quot;http://sys1yagi.hatenablog.com/entry/2016/06/05/171132&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;日本語名称について参考にした記事&lt;/a&gt;)
    Docs: &lt;a href=&quot;https://kotlinlang.org/docs/reference/multi-declarations.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Destructing Declarations&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// 色要素
    val (red, green, blue) = color

    // 四角形
    val (left, top, right, bottom) = rect

    // 座標
    val (x, y) = point&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;a class=&quot;name&quot; name=&quot;day4&quot;&gt;&lt;/a&gt;Day 4: When Expression (When 式)&lt;/h2&gt;&lt;p&gt;条件分岐をよりよく書くには&lt;code&gt;when&lt;/code&gt;式を使ってみましょう。
    Kotlin の&lt;code&gt;when&lt;/code&gt;式は、文字列リテラルや Enum、Range など何にでも使用できます！任意の関数を呼ぶことも可能です。
    Docs: &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/control-flow.html#when-expression&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/control-flow.html#when-expression&quot;&gt;When&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;class Train(val cargo: Number?) {
        override fun toString(): String {
            return when (cargo) {
                null, 0 -&amp;gt; &quot;empty&quot;
                1 -&amp;gt; &quot;tiny&quot;
                in 2..10 -&amp;gt; &quot;small&quot;
                is Int -&amp;gt; &quot;big inty&quot;
                else -&amp;gt; &quot;$cargo&quot;
            }
        }
    }&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Day 5: For loops, range expressions and destructing (For ループと Range、分解宣言)&lt;/h2&gt;&lt;p&gt;&lt;code&gt;for&lt;/code&gt;ループは Kotlin の Range や分解宣言を使うと便利です。
    Docs: &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/ranges.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/ranges.html&quot;&gt;Ranges&lt;/a&gt;, &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/multi-declarations.html#destructuring-declarations&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/multi-declarations.html#destructuring-declarations&quot;&gt;Destructuring&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// 1 から 100　まで
    for(i in 1..100) {…}

    // 1 から 100 まで降順
    for(i in 100 downTo 1){…}

    // 配列の要素を1つ飛ばしで
    val array = arrayOf(“a”, “b”, “x”)
    for(i in 1 until array.size step 2 ){…}

    // 配列の要素を分解宣言で取り出す
    for((index, element) in array.withIndex()) {…}

    // Map の要素を分解宣言で取り出す
    val map = mapOf(1 to “one”, 2 to “two”)
    for( (key, value) in map){…}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;a class=&quot;name&quot; name=&quot;day6&quot;&gt;&lt;/a&gt;Day 6: Properties (プロパティ)&lt;/h2&gt;&lt;p&gt;Kotlin では、クラスのプロパティのゲッターとセッターが自動生成されます。必要に応じてカスタマイズすることも可能です。
    Docs: &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/properties.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/properties.html&quot;&gt;Properties&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;class User {
        // properties
        val id: String = “” // 変更禁止。ゲッターのみ

        var name: String = “” // デフォルトのゲッターとセッター

        var surname: String = “” // カスタムゲッターとデフォルトのセッター
          get() = surname.toUpperCase() // custom getter declaration

        var email: String = “” // デフォルトのゲッターとカスタムセッター
          set(value) { // カスタムセッターの宣言
              // “value” = セッターのパラメータ
              // “field” = 自動生成されるバッキングフィールド
              if(isEmailValid(value)) field = value
          }
    }&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Day 7: Data Classes and equality (データクラスと等価性)&lt;/h2&gt;&lt;p&gt;データの保持だけが目的であれば、データクラスを使ってみましょう。構造的等価性 (Structual Equality) をチェックする&lt;code&gt;equals()&lt;/code&gt;や&lt;code&gt;hashCode()&lt;/code&gt;,&lt;code&gt;toString()&lt;/code&gt;,&lt;code&gt;copy()&lt;/code&gt;などの関数が自動生成されます。
    Docs: &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/data-classes.html#data-classes&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/data-classes.html#data-classes&quot;&gt;Data Classes&lt;/a&gt;, &lt;a class=&quot;markup--anchor markup--p-anchor&quot; href=&quot;https://kotlinlang.org/docs/reference/equality.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot; data-href=&quot;https://kotlinlang.org/docs/reference/equality.html&quot;&gt;Equality&lt;/a&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;data class User(
        val name: String,
        val email: String,
        val address: Address,
        …
    )
    public class UserListDiffCallback: DiffUtil.Callback() {
        override fun areContentsTheSame(
             oldItemPosition: Int,
             newItemPosition: Int
        ): Boolean {
        // 自動生成された比較メソッドを使用
        return newUserList[newItemPosition] ==
               oldUserList[oldItemPosition])
    }&lt;/code&gt;&lt;/pre&gt;
</summary>
    </entry>
    
    <entry>
      <title>新しいデザインの Gmail がリリースされました</title>
      <link rel='alternate' href='https://blog.comame.xyz/entries/2018-04-26/new-design-gmail-released' />
      <id>https://blog.comame.xyz/entries/2018-04-26/new-design-gmail-released</id>
      <updated>2018-04-26T00:00:00Z</updated>
      <summary>&lt;p&gt;新しいデザインの Gmail が、昨日リリースされました！マテリアルデザインが適用されたほか、カレンダーや ToDo リストが使いやすくなっているようです。
    &lt;a href=&quot;https://japan.googleblog.com/2018/04/gmail.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;新しい Gmail をお試しください。- Google Japan Blog&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;変更点&lt;/h2&gt;&lt;h3&gt;スヌーズ機能が追加された&lt;/h3&gt;&lt;p&gt;この機能は &lt;a href=&quot;https://inbox.google.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Inbox by Gmail&lt;/a&gt; からの逆輸入ですね。メールを一旦受信トレイから非表示 (アーカイブ) にし、指定した日付に再び受信トレイに戻す機能です。&lt;/p&gt;&lt;h3&gt;メールに対しての操作が簡単になった&lt;/h3&gt;&lt;p&gt;これまた Inbox からの逆輸入です。メールの一覧画面から、今までより簡単にアーカイブ、削除、移動などの操作が行えるようになりました。マウスカーソルをメールに合わせると、下の画像のようなアイコンが表示されます。&lt;/p&gt;&lt;img src=&quot;https://2.bp.blogspot.com/-IMmNaD33EcI/W8oUwVKCbcI/AAAAAAABAYQ/QaRXXsRJrnMRb8beuxaGIDIvLjY2fDXxACLcBGAs/s1600/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588.png&quot;/&gt;
    &lt;h3&gt;添付ファイルが見やすくなった&lt;/h3&gt;&lt;p&gt;メールに添付ファイルがある場合、メールの一覧画面に今までよりわかりやすく表示されるようになりました。1つ面白いのが、Google ドキュメントのファイルでも添付ファイルとして表示される点です。これらのファイルは、Gmail 以外から見るとただの URL としてしか表示されません。Google だからこその連携といえますね。&lt;/p&gt;
    &lt;h3&gt;Keep やカレンダー、ToDo リストがサイドバーに表示されるようになった&lt;/h3&gt;&lt;p&gt;Google Keep や Google カレンダー、ToDo リストが画面の右側に常駐するようになりました。これらの機能をよく使う人にとっては、アクセスがかなり楽になると思います。&lt;/p&gt;&lt;h3&gt;ローディングアニメーションがちょっとおしゃれになった&lt;/h3&gt;&lt;p&gt;小さな変更点ですが、個人的にはかなり気に入っています。ロード時に表示される Gmail アイコンの封筒が スルッ とアニメーションするようになりました。&lt;/p&gt;&lt;img src=&quot;https://4.bp.blogspot.com/-48suJpjRAe4/W8c80H2uGQI/AAAAAAABAXA/BcDabZoczOYidXo8f0ACvP3g1CyOKiAQgCLcBGAs/s1600/ezgif-1-51157c01d8.gif&quot;&gt;
    &lt;h2&gt;移行方法&lt;/h2&gt;&lt;p&gt;受信トレイの右上に表示されている歯車アイコンをクリックし、「新しい Gmail を試す」を選択してください。新デザインに切り替えたあとでも、同様の手順でいつでも従来のデザインに戻すことができます。&lt;/p&gt;&lt;h2&gt;感想&lt;/h2&gt;&lt;p&gt;今回のアップデートは、Inbox からの逆輸入が多かったように思います。どれも使いやすい機能ですので、ぜひアップデートしてみてください。&lt;/p&gt;
</summary>
    </entry>
    
    </feed>