Hateburo: kazeburo hatenablog

Operations Engineer / 運用系小姑 / Perl Monger

さくらのクラウド DNSサーバのレコード編集時の反映時間を可視化する

dehydratedとさくらのクラウドDNSを使った証明書取得の記事を書いておりますが、

kazeburo.hatenablog.com

DNS-01 チャレンジを使って証明書を作成しようとする際に気になるのはDNSレコードの登録から、実際に反映するまでの待ち時間だったりします。dehydratedのhookスクリプトの例をみていると、30秒ほどsleepしているものもあったりします。

github.com

そこで、さくらのクラウドDNS機能のクライアントである sacloudns コマンドとmackerelをつかって、ゾーンへのレコード追加削除など編集から実際にDNSへの反映にかかる時間を可視化してみました。

github.com

計測方法

sacloudnsコマンドを使って適当なレコードを更新するshell scriptを書きます。

$ cat time-taken.sh 
#!/bin/sh
set -e
export SAKURACLOUD_ACCESS_TOKEN="xxx"
export SAKURACLOUD_ACCESS_TOKEN_SECRET="xxx"
TOKEN_VALUE=$(openssl rand -hex 18)

sacloudns rset --wait --wait-timeout=80s --zone example.com --name time-taken --ttl 30 --type TXT --data ${TOKEN_VALUE}

sacloudnsコマンドは、TXTレコードを追加・編集した際にのみ --wait というオプションが使えます。--wait オプションを指定して実行すると、ゾーン編集リクエストをさくらのクラウドAPIに送信したのち、指定されたゾーンのNSレコードからDNSサーバを調べ、そのDNSサーバに対して、2秒おきに実際に更新されているか名前解決を行うことで、実際に更新されるまで待つことができる機能です。ACMEdns-01チャレンジをスムーズに行うためにつくったものですが、今回これを利用します。

このshell scriptをcronで実行し、mackerelに送信するのが次のcrontabの設定です。

*/5 * * * * mkr wrap -n sacloud-dns-rset -d -a -- bash -c 'set -e -o pipefail; \
  /opt/mackerel-agent/plugins/bin/mackerel-plugin-command-status \
  --timeout 90s -n sacloud-dns-rset -- sh /path/to/time-taken.sh  | mkr throw -s my-service'

mkr wrap はコマンドを実行しエラーがあった場合、mackerelのアラートを発生させるコマンドです。sacloudnsコマンドが失敗した際にわかるようにしています。

mackerel-plugin-command-status は与えられたコマンドを実行し、その終了コードとかかった時間をメトリクスにするmackerelのプラグインです。

kazeburo.hatenablog.com

こちらで紹介してますが、バージョン 0.0.2 でかかった時間もメトリクスとして取得できるようになりました。

ホストのメトリクスとして使うことを想定したプラグインではありますが、crontabを実行するサーバを変更してもメトリクスが残るよう、mackerel-plugin-command-statusの結果をサービスメトリクスとして mkr throw を使って送信しています。

これで5分ごとにDNSを更新し、かかった時間をmackerelに投稿できました。

可視化結果

結果はこんな感じ。

f:id:kazeburo:20210317003256p:plain

グラフをみると、大体は20秒強、あるいは45秒前後で終わっているようです。また、APIのレスポンスに時間がかかったのか、反映の待ち時間が長かったのかはここではわかりませんが、たまに60秒以上かかることもあるようです。

これらを指標としつつ、改善していきたい所存です。

Go net/httpで任意のタイミングでchunked レスポンスを返す

調査のため、chunked レスポンスを行っている合間に遅延をいれる必要があったので、Go net/httpでの実現方法を調べました。

こちらにあった Flusher.Flush() を使うことになります。

stackoverflow.com

golang.org

FizzBuzzっぽいものを遅延をいれつつ書き出すにはこんな感じ

func fizzBuzz(w http.ResponseWriter, r *http.Request) {
    flusher, ok := w.(http.Flusher)
    if !ok {
        w.WriteHeader(500)
        w.Write([]byte("expected http.ResponseWriter to be an http.Flusher"))
        return
    }
    for i := 1; i <= 15; i++ {
        p := fmt.Sprintf("#%03d ", i)
        if i%3 == 0 {
            p += "Fizz"
        }
        if i%5 == 0 {
            p += "Buzz"
        }
        p = strings.TrimSpace(p)
        p += "\n"
        w.Write([]byte(p))
        flusher.Flush()
        time.Sleep(300 * time.Millisecond)
    }
}

telnetで試すと意図した通り chunk に分かれてレスポンスされてくるのがわかる

]% telnet localhost 3000
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /demo/fizzbuzz_stream HTTP/1.1
Host: example.com

HTTP/1.1 200 OK
Vary: Accept-Encoding
Date: Wed, 10 Mar 2021 01:46:27 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked

5
#001

5
#002

a
#003 Fizz

5
#004

a
#005 Buzz

a
#006 Fizz

5
#007

5
#008

このFlushは github.com/NYTimes/gziphandler にて圧縮をかけていても有効でした。

github.com

gziphandlerの簡単な使い方

// fizzbuzzのためMinSizeを小さくしておく。デフォルト 1400
gz, _ := gziphandler.NewGzipLevelAndMinSize(gzip.DefaultCompression, 5)
http.Handle("/", gz(http.HandlerFunc(fizzBuzz)))

テスト・検証用で作っている http-dump-request というサーバにこのFizzBuzzを返すエンドポイントを追加しています。このサーバを今後は監視にも利用していくつもりです。

github.com

GitHub Actionsからさくらのクラウド オブジェクトストレージへアクセスする

2月1日よりオープンβとなった新しいさくらのクラウド オブジェクトストレージサービスにGitHub Actionsからアクセスしてみるサンプルです。

オブジェクトストレージは2021年3月31日までオープンベータ期間となります。お試しいただいてフィードバックいただけると嬉しいです! オブジェクトストレージ(β)の利用についてはこちらのニュースを確認してください。

cloud-news.sakura.ad.jp

この新しいオブジェクトストレージの特徴の一つとして、Amazon S3と高い互換性を持つAPIの提供があげられ、AWS CLIを通してオブジェクトの操作が可能です。マニュアルはこちらから

manual.sakura.ad.jp

ということで、GitHub ActionsからAWS CLIを使ってオブジェクトストレージにアクセスを試してみました。

アクセストークンについて

オブジェクトストレージ利用開始時に、 アクセスキーID と シークレットアクセスキー が表示されます。こちらのアクセスキーは全てのバケットを操作できてしまうので、パーミッションメニューからバケットごとにアクセス件が設定なアクセスキーを生成するのがお勧めです。

f:id:kazeburo:20210215094509p:plain (画像はマニュアルより)

アクセスキーを作成したら、GitHub Actionsを設定するrepositoryのSecretsに登録します。

f:id:kazeburo:20210215095118p:plain

Workflow の作成

GitHub ActionsでAWSへのアクセスを行う際には、

github.com

configure-aws-credentials を使うことで設定をシンプルにすることができますが、このactionの中で aws sts get-caller-identity 相当のAPI呼び出しを行うため、オブジェクトストレージのアクセスキーではエラーになってしまい、今回は使うことができません。

なので、環境変数でアクセスキーを渡すようにしてワークフローを作ったところ動きました。

name: release
on:
  push:
    branches:
      - main

jobs:
  renew-cert:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: sync to object storage
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: eu-west-1

        run: |
          aws --endpoint-url=https://s3.isk01.sakurastorage.jp s3 --only-show-errors --delete sync public_html/ s3://my-website-data/public_html/

aws コマンドに --endpoint-url=https://s3.isk01.sakurastorage.jp を渡せばオブジェクトストレージにアクセス可能です。

どうぞお試しあれ~

2021/03/03 追記

GitHub Actionsで

<botocore.awsrequest.AWSRequest object at 0x7f22f2d7d630>

のようなエラーが出る際は環境変数 AWS_REGION を指定するか、awsコマンドに --region オプションの追加をするとなおります。

参考

github.com

dehydrated とさくらのクラウドのDNS機能をつかってワイルドカード・マルチドメイン証明書取得

dehydrated はshell scriptでできた letsencrypt/acme のクライアントです。certbotlegoなど様々なツールがある中で、わりと長いこと使ってますが、安定してワイルドカード・マルチドメイン対応の証明書の取得・更新ができています。

github.com

dehydratedとさくらのクラウドDNS機能と先週末つくったクライアント sacloudns を使うことで、ほぼ自動的に証明書の取得と更新が行えるので、その紹介です。

github.com

こちらのツールは、リリースページからのダウンロードもしくはMacなら homebrew でインストールが可能です。

% brew install kazeburo/tap/sacloudns

dehydrated の準備

dehydrated はGitHubからcloneしてきます

$ git clone https://github.com/dehydrated-io/dehydrated.git
$ cd dehydrated

そして設定ファイルを用意します。

まず、 config ファイルを作成

CERTDIR="${BASEDIR}/certs"
ACCOUNTDIR="${BASEDIR}/accounts"
HOOK="${BASEDIR}/hook.sh"
HOOK_CHAIN="no"
CHALLENGETYPE="dns-01"
KEY_ALGO="prime256v1"

configのサンプルは docs/example 以下にあります。KEY_ALGOにprime256v1を指定することで楕円曲線暗号であるECDSAを使った証明書を作成できます。デフォルトはRSAになります

つぎに証明書を作成するドメイン一覧を記した domains.txt を作ります

# スペース区切りで複数のドメインを記せます。ワイルドカードも使えますが行頭にはかけません
example.com *.example.com example.jp *.example.jp > certalias
chocon.me *.chocon.me

この記事で紹介する方法で証明書を作成する場合、こちらのドメインはすべてさくらのクラウドDNS機能で管理されている必要があります。試してはないですが、Let's Encryptではマルチドメイン証明書のSAN(Subject Alternative Name)に100個までFQDNを追加することができるようです。

そして最後に、dns-01 で利用するDNSレコードの作成などを行う hook.sh を用意します。

こちらはAmazon Route 53のクライアント、 cli53 を使う以下コードを参考にして作りました

https://github.com/whereisaaron/dehydrated-route53-hook-script/blob/master/hook.sh

#!/bin/bash
set -e
# This hook script is written based on dehydrated-route53-hook-script
# https://github.com/whereisaaron/dehydrated-route53-hook-script

deploy_challenge() {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"

    local ZONE=$(find_zone "${DOMAIN}")
    
    if [[ -n "$ZONE" ]]; then
        echo "Creating challenge record for ${DOMAIN} in zone ${ZONE}"
        sacloudns radd --wait --zone ${ZONE} --name _acme-challenge.${DOMAIN}. --ttl 60 --type TXT --data ${TOKEN_VALUE}
    else
        echo "Could not find zone for ${DOMAIN}"
        exit 1
    fi
}

clean_challenge() {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"

    local ZONE=$(find_zone "${DOMAIN}")
    
    if [[ -n "$ZONE" ]]; then
        echo "Deleting challenge record for ${DOMAIN} from zone ${ZONE}"
        sacloudns rdelete --zone ${ZONE} --name _acme-challenge.${DOMAIN}. --type TXT --data ${TOKEN_VALUE}
    else
        echo "Could not find zone for ${DOMAIN}"
        exit 1
    fi
}

deploy_cert() {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
    # NOP
}

unchanged_cert() {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
    # NOP
}

function invalid_challenge {
    local DOMAIN="${1}" RESPONSE="${2}"

    local HOSTNAME="$(hostname)"

    (>&2 echo "Failed to issue SSL cert for ${DOMAIN}: ${RESPONSE}")
}

function get_base_name() {
    local HOSTNAME="${1}"

    if [[ "$HOSTNAME" == *"."* ]]; then
      HOSTNAME="${HOSTNAME#*.}"
      echo "$HOSTNAME"
      return 0
    else
      echo ""
      return 1
    fi
}

function find_zone() {
  local DOMAIN="${1}"

  local ZONELIST=$(sacloudns list | jq -r '.DNS[].DNSZone'|xargs echo -n)

  local TESTDOMAIN="${DOMAIN}"

  while [[ -n "$TESTDOMAIN" ]]; do
    for zone in $ZONELIST; do
      if [[ "$zone" == "$TESTDOMAIN" ]]; then
        echo "$zone"
        return 0
      fi
    done
    TESTDOMAIN=$(get_base_name "$TESTDOMAIN")
  done

  return 1
}

#
# This hook is called at the end of a dehydrated command and can be used
# to do some final (cleanup or other) tasks.
#
exit_hook() {
  :
}

HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|exit_hook)$ ]]; then
  "$HANDLER" "$@"
fi

さくらのクラウドAPIキーを環境変数を設定

さくらのクラウドのコントロールパネルからAPIキーを作成し、SAKURACLOUD_ACCESS_TOKENSAKURACLOUD_ACCESS_TOKEN_SECRET環境変数に設定します。

export SAKURACLOUD_ACCESS_TOKEN=xxx
export SAKURACLOUD_ACCESS_TOKEN_SECRET=yyy

sacloudns は作業ディレクトリに、.env ファイルを作成し、

SAKURACLOUD_ACCESS_TOKEN=xxx
SAKURACLOUD_ACCESS_TOKEN_SECRET=yyy

のように記すと、コマンド実行時に自動で読み込むこともできます。

dehydrated の実行

準備ができたのでdehydratedを実行

# 初回はアカウントの作成が必要
$ ./dehydrated --register --accept-terms -f config 
$ ./dehydrated -c -f config 

これで、DNSレコードの作成と検証が行われたのち、証明書が certs ディレクトリ以下に作成されます。

% ls -l certs/chocon.me 
cert-1612763291.csr
cert-1612763291.pem
cert.csr -> cert-1612763291.csr
cert.pem -> cert-1612763291.pem
chain-1612763291.pem
chain.pem -> chain-1612763291.pem
fullchain-1612763291.pem
fullchain.pem -> fullchain-1612763291.
privkey-1612763291.pem
privkey.pem -> privkey-1612763291.pem

証明書は nginx や Apache などのWebサーバや各クラウドにアップロードして使うこともできます。

おまけ: さくらのクラウド エンハンスドロードバランサーへの証明書アップロード

取得した証明書をさくらのクラウドのエンハンスドロードバランサーに設定してみます。

エンハンスドロードバランサーとは大規模なHTTP/HTTPSサービスに最適な高性能・高機能なロードバランサアプライアンスです。詳しくはこちら

manual.sakura.ad.jp

こちらも参考になります。

qiita.com

エンハンスドロードバランサーの構築が済んでいるといるとして、以下のようにすると証明書が登録できます。

$ cat template.jq
{
  "ProxyLB": {
    "PrimaryCert": {
      "ServerCertificate": $ServerCertificate,
      "IntermediateCertificate": $IntermediateCertificate,
      "PrivateKey": $PrivateKey
    },
    "AdditionalCerts": []
  }
}
$ jq -n \
-f template.jq 
--rawfile ServerCertificate certs/chocon.me/cert.pem \
--rawfile IntermediateCertificate certs/chocon.me/chain.pem \
--rawfile PrivateKey certs/chocon.me/privkey.pem \
| \
curl -d @- -X PUT \
-H "Content-Type: application/json" \
--user $SAKURACLOUD_ACCESS_TOKEN:$SAKURACLOUD_ACCESS_TOKEN_SECRET \
https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/commonserviceitem/${リソース番号}/proxylb/sslcertificate

jqのテンプレート機能と、rawfileオプションとても便利ですね!

なお、この方法で証明書を登録した場合は、let's encryptでの証明書取得・更新機能は停止しておく必要があります。

エンハンスドロードバランサーのlet's encryptを利用した証明書取得では、1つのロードバランサーあたり、1個のドメインの証明書しか作れませんが、この方法だとそれよりも多くの証明書を登録できるので、たくさんのドメインを利用される場合は便利かもしれません。

最後に、ここまでの方法を一つのシェルスクリプトにまとめるなど自動化しておくのがいいでしょう。

何かのお役に立てれば幸いです。

さくらインターネット株式会社に入社しました

あっという間に1週間経ってしまいましたが、2021年1月18日より、さくらインターネットにてお仕事をしております。

前職中は本当にたくさんの皆様にお世話になりました。ありがとうございます。挨拶もほとんど出ておらず申し訳ありません。

さくらインターネットは20年ほどユーザとして使っており、またmixiやメルカリでもサーバを利用しており、公私共に馴染みのある会社です。

2018年の北海道胆振東部地震の際には対応はもちろん、情報がまとまってない中で直接お話を聞く機会をいただいたり、逐次DMなどで情報をいただくなど大変お世話になったのは強く印象に残っています。そのほか、ISUCON9でも問題の出題を一緒にやらせていただいたり、サイボウズ様、jig.jp様との共催で行われた小学生向けのIoTプログラム教室などにも息子とともに参加させていただきました。このためかさくらインターネットに転職をすることを息子に伝えると、ニンマリと喜んでおりました。

これまでのキャリアではWebサービスのパフォーマンス改善、運用などに携わっていたわけですが、さくらインターネットは少し異なるレイヤーへの挑戦になります。今までの経験を生かしつつ、新しいチャレンジも行い、さくらインターネットのお客様とそのお客様にとってよりよいサービスを提供していけるようやっていきます。

もし、さくらインターネットの利用に関わらずWebサービスのパフォーマンスや運用での悩みなどがありましたら、力になれることがあるかと思いますのでtwitterなどで相談くださいませ。

COVID-19の緊急事態宣言が出ているなか、初日からリモートとなかなか難しい船出になっておりますが、素晴らしい同僚の力を借りつつ、一つ一つやりはじめております。今後ともよろしくお願いします。

GitHub Actionsからさくらクラウドのコンテナレジストリ(Lab)にイメージをpushする

こちらのリポジトリが完成形です。

github.com

ここで紹介しているコンテナレジストリはLabプロダクトになります。「Labプロダクト」では、新機能の動作検証およびフィードバック収集を目的として、さくらのクラウドにおける開発中サービスを実験的に提供しておりますので、ご注意ください。

マニュアルはこちら

manual.sakura.ad.jp

「Labプロダクトとは」も合わせてご確認ください。

manual.sakura.ad.jp

コンテナレジトストリの作成

さくらのクラウドのメニューからコンテナレジトストリを選び、「追加」ボタンを押します。

f:id:kazeburo:20210122172222p:plain

f:id:kazeburo:20210122172400p:plain

公開・非公開は目的に応じて選択してください。

コンテナレジストリができたら、push/pullするためのユーザを作成します。

f:id:kazeburo:20210122172747p:plain

権限も適したものを選びましょう。今回はGitHub Actionsからpushを行うので、「Push & Pull」がよさそうです。

f:id:kazeburo:20210122172836p:plain

このアカウントはコンテナレジストリにのみ使われるアカウントになります。

GitHubリポジトリにシークレット登録

コンテナイメージを作成するリポジトリの Settings -> Repository secrets にて、先ほど作成した id とパスワードを SAKURACR_USER、SAKURACR_PASSWORDとして登録しました。

workflow YAMLの作成

リポジトリ.github/workflow/ 以下にコンテナイメージの作成とコンテナレジストリ(Lab)にpushするためのYAMLファイルを作成します。

さくらのコンテナレジストリ(Lab)はDocker開発元が提供するDocker Hub互換のレジストリサービスであり、プロトコルにも互換性があります。GitHub Container RegistryやAWSのコンテナレジストリにpushする際に利用できる docker/login-actiondocker/build-push-action の Actionを特にカスタマイズすることなく使えます。

github.com

http-dump-requestでは次のようにファイルを作成しました。

name: release
on:
# gitのtagが作られた時
  push:
    tags:
    - "v[0-9]+.[0-9]+.[0-9]+"

jobs:
  build_and_push:
    runs-on: ubuntu-latest
    env:
      IMAGE_NAME: http-dump-request
      REGISTRY: nomadscafe.sakuracr.jp
    steps:
      - name: checkout
        uses: actions/checkout@v2

# tagを入手
      - name: Set tag to environment variable
        id: set-tag
        run: echo ::set-output name=version::${GITHUB_REF#refs/*/}

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Sakura Container Registry
        uses: docker/login-action@v1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.SAKURACR_USER }}
          password: ${{ secrets.SAKURACR_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.set-tag.outputs.version }}

このファイルをコミットしgit tagを作成すると、狙い通りイメージの作成とさくらのコンテナレジトストリ(Lab)へのpushができました。

2021年1月現在、コンテナレジストリ(Lab)にはイメージの一覧機能がないので、今回つくったコンテナは以下のコマンドでレジストリからpullして実行ができることで動作を確認しました。

$ docker run -p 3000:3000 nomadscafe.sakuracr.jp/http-dump-request

おまけ

http-dump-request について

似たようなモノはたくさんありそうですが、検証の際に使える http request をそのままレスポンスとして返す Go のサーバです。

f:id:kazeburo:20210125153518p:plain

こんな感じで表示できます。上記で紹介しておりますが、 nomadscafe.sakuracr.jp/http-dump-request から docker pullして利用できます。

2020年に買って良かったもの

ほぼ1年間家で仕事をすることになり、自宅の机まわりの環境をこれまで以上に整えてきた1年だったので「#買って良かった2020」というお題で書いてみる。

ヘッドホンとマグカップ

f:id:kazeburo:20201225133336p:plain

奥さんから誕生日にもらったソニーの WH-H910N。色はアッシュグリーンです。ありがとう。

www.sony.jp

ワイヤレスで軽くてつけ心地がよく、ノイズキャンセルもあってとても気に入っています。

夏は冷たい飲み物、冬は暖かい飲み物を飲むために買ったKEYUKAのマグカップ。偶然にもヘッドホンと同じ色味でいい感じです。

www.keyuca.com

また、Apple Musicのサブスクリプションも加入しました。

www.apple.com

仕事しながら聞いてますが、「おしりたんてい」の「ププっとふむっと解決ダンス」だったり鬼滅の刃の曲が最もよくかかっているような気がします。

椅子

家で長く座って作業ができる椅子がない、ということで在宅勤務になってまず購入したのが椅子。

f:id:kazeburo:20201225134524p:plain

オフィスチェアの多くは黒い色ですが、割と大きい黒い物体が家にあると、家の中が暗くなりそうということで、オカムラのシルフィーのホワイトフレーム、座面はベージュを選びました。

届くまでに1ヶ月以上かかり、その間は折り畳み椅子でなんとかなんとか過ごしてましたが、背面フレームのデザインもよく満足。

www.okamura.co.jp

椅子と言えば、ISUCON10個人スポンサーでいただいたアウトドアチェアもよい。

f:id:kazeburo:20201225134738p:plain

子供たちがよく座っています。941さんありがとう。

ちなみに机は場所的な問題で、ニトリの小さめの机

www.nitori-net.jp

キーボード

f:id:kazeburo:20201225141211p:plain

今はBAROCCO MD770 RGB BTを使ってます。真ん中にMagic Trackpadをおくスタイル。左右分離型だいぶ慣れてきましたが、電話をしながら片手で打てないという弱点があります。

archisite.co.jp

www.apple.com

Magic Trackpadをそのままおくと低すぎて使いにくいので、百均で買った耐震マットを二枚重にして下にくっつけてます。

普段はFILCOの左右分離型のウレタンリストレストをキーボードの前において使っています。

www.diatec.co.jp

冬対策

窓の近くに机を置いているので、寒いということで、こちらのパネルヒーターを買ってみた。

足の裏はもちろん、ブランケットを上にかければ簡易こたつになりとても暖かい。奥さんも小さいホットカーペットを買って机の下で使っています。

Apple Watch

Series 6を初購入。

f:id:kazeburo:20201225140105p:plain

Apple Watch Hermèsの赤色のレザーストラップにしました。1時間に1度立ち上がったり、20秒手を洗っているだけで褒めてくれるので自己肯定感がある。運動も気にし。。。

www.apple.com

スタンドはEINBANDさんのAppleWatch木製スタンド。充電器のところはぴったりとハマったりではないので剥がせるタイプの両面テープで固定している。

einbandwatch.com

最近はRaspberry Piを買ったので、homebridgeいれてApple WatchのホームアプリやSiriからLチカして喜んでいます。

homebridge.io

来年は何買うかな~