GitHub Actions の Composite run steps action を使った Action を作る

この夏に登場してたのを見逃してました。

github.blog

以前は GitHub Actions のワークフローで再利用できる Action は Docker Container Action と JavaScript Action でした。

blog.kondoumh.com

Docker Container Action は手軽に作れますが、コンテナで実行するため揮発性で step 毎に docker build が実行されるとか、コンテナイメージをビルドする Action を作るにはベースイメージを docker にする必要があるなどやや不便なところもあります。JavaScript Action は JavaScript のスキルとGitHub 提供ライブラリの習得が必要でやや敷居が高いです。

GitHub Actions のワークフローで使われるシンタックスで Action が記述できればいいのにと思った人は多いのではないでしょうか。それが、Composite run steps action です。

これは、ワークフローで使う run step を Action 定義に埋め込むもので、Dockerfile や JavaScript の記述なしに action.yaml だけで Action が作れてしまうというものです。

docs.github.com

公式ドキュメントのサンプルはこんな感じです。

name: 'Hello World'
description: 'Greet someone'
inputs:
  who-to-greet:  # id of input
    description: 'Who to greet'
    required: true
    default: 'World'
outputs:
  random-number: 
    description: "Random number"
    value: ${{ steps.random-number-generator.outputs.random-id }}
runs:
  using: "composite"
  steps: 
    - run: echo Hello ${{ inputs.who-to-greet }}.
      shell: bash
    - id: random-number-generator
      run: echo "::set-output name=random-id::$(echo $RANDOM)"
      shell: bash
    - run: ${{ github.action_path }}/goodbye.sh
      shell: bash

runs: セクションに using: "composite" を書いて steps セクション配下に、run step を書き連ねて行けば実行してくれます。まさに欲しかったものです。このサンプルでは、別のシェルを作って起動していますが、複数コマンドを直接 yaml に書いても OK。ソフトウェアをダウンロードしてインストールするような処理も簡単に書けます。

    - run: |
        curl -LO https://github.com/roboll/helmfile/releases/download/v0.135.0/helmfile_linux_amd64
        mv helmfile_linux_amd64 helmfile
        chmod +x helmfile
        mkdir -p $HOME/bin
        mv helmfile $HOME/bin
        echo "$HOME/bin" >> $GITHUB_PATH
      shell: bash

shell 属性は必須のようです。

Composite run steps action は GitHub Actions でワークフローを書いた経験があれば作成できるため、Action を作る障壁はかなり下がったと言えると思います。

利用できるのは run のみで、uses で他の Action を呼び出したりすることはできません。複雑なワークフローをまるっと Action にして再利用することはできませんがシェルスクリプトと併用すれば相当複雑なこともできそうです。ただし Action 自体はシンプルな機能にとどめた方が開発も利用も楽ですので、高機能すぎる Action を作るのはやめた方がよいです。

ただ、ワークフロー自体を organization などの単位で共有して色々なリポジトリから利用したいという要求はあって、そのために Organization and enterprise workflows というフィーチャーが予定されています。

github.com

残念ながら現時点では提供時期未定ですが。

Netlify Functions を PWA のバックエンドで使う

Netlify は静的サイトをホスティングするのに便利です。

blog.kondoumh.com

JSON データを置いてちょっとしたデータソース的にも使えます。

blog.kondoumh.com

しかし、JSON を静的に配置できるだけだと上の記事のように更新のためのパイプラインを実装する必要があるし、リアルタイム性も犠牲になります。PWA のバックエンドでちょっと動的な処理をしたい場合に Netlify Functions を利用する選択肢が提供されています。

docs.netlify.com

Functions は AWS Lambda を利用したサービスであり、簡単にバックエンドの API を追加できるようになっています。Node.js が 使えるようになったのが1年ぐらい前で今は Go も使えるようです。

www.netlify.com

JAMstack の A は API の A ですが、その API Stack が強化されたことになります。Functions がなくても外部の API を叩くことはできましたが、サーバー不要で自作の API と一緒にデプロイできるのが進化です。

既存の PWA サイトに Netlify Functions を追加するには、プロジェクトのルートに netlify.toml ファイルを追加します。

[build]
  command = "npm run build"
  functions = "dist/api"

npm -i netlify-lambda で Netlify Functions を使うためのライブラリを追加します。その他、API の実装に必要なライブラリも追加しておきます。

  "dependencies": {
    :
    "netlify-lambda": "^2.0.1",
  },

PWA の src ディレクトリとは別の専用のディレクトリを用意して API のソースコードを追加します。

exports.handler = async (e, c) => {

  const data = await hoge();
     :
  return {
    statusCode: 200,
    body: JSON.stringify(data),
  };
};

Functions の API を resources/api/hoge.js に書いたとして Vue.js のプロジェクトへの追加後の構成はこんな感じ。

.
├── resources
│   └── api
│        └── hoge.js     
├── src
│   ├── assets
│   ├── components
│   ├── plugins
│   ├── router
│   └── views
├── netlify.toml
├── package.json
├── vue.config.js
└── test

package.json の build script で PWA のビルドと netlify-lambda の CLI を順次実行するように変更します。下記は Vue CLI と Netlify Functions CLI を実行する例です。

{
  "scripts": {
     :
    "build": "vue-cli-service build && netlify-lambda build resources/api"
  }
}

これで PWA と Functions のエンドポイントが一気にデプロイできます。

Functions の利用側では同一サイトの .netlify/functions 配下のパスを指定して API を呼び出します。

  const res = await fetch(".netlify/functions/hoge", {
    headers: {
      "foo": bar
    }
  })
  const json = await res.json()

Functions がデプロイできてしまえば、ローカルの開発時もデプロイ済みの Functions を呼び出せるように vue.config.js で devServer の proxy を設定しておけば楽です。

module.exports = {
  lintOnSave: false,
  devServer: {
    proxy: "https://hogehoge.netlify.app"
  }
}

Netlify のコンソール画面で呼び出しログも確認できます。もちろん、npm run dev でローカルに Functions を起動して、そのポートに proxy することも可能です。

以上のように AWS Lambda の機能をアドオンしたサイトがサクッと構築できるので、応用の幅が広がりました。

Functions の機能強化として、現在はベータ版ですが、時間のかかる処理(最大15分)をバックグラウンドで行わせることができる Background Functions も使えるようになるみたいです。

docs.netlify.com

macOS Big Sur にアップデート

Big Sur がリリースされてから10日。2013 - 2014 の 13インチ MacBook Pro では起動しなくなる問題がある模様です。

support.apple.com

一応2015年モデルだし他に大きな不具合もなさそうということで、アップデートすることにしました。

f:id:kondoumh:20201122072428p:plain

ダウンロードは20分ぐらいでした。

f:id:kondoumh:20201122075126p:plain

真っ黒のインストール画面になって40分ぐらいで起動してきました。ダイアログの色味がちょっと茶色がかった気がします。

f:id:kondoumh:20201122090113p:plain

左が Catalina 右が Big Sur の Dock ですが角丸のラジアンが少し大きくなり画面端に少し隙間ができました。Safari、ターミナル、システム環境設定、スティッキーズなど組み込みアプリのアイコンも角丸になりました。

f:id:kondoumh:20201122093431p:plain

BetterTouchTool や Karabiner-Elements はそのまま動作しました。

Docker for Desktop も Big Sur 対応のアップデートを適用して Kubernetes と共に問題なく動作。

f:id:kondoumh:20201122093845p:plain

Yosemite に始まり Sierra ⇨ High Sierra ⇨ Mojave ⇨ Catalina ⇨ Big Sur とアップデートしてきました。来年は Apple Silicon の Mac に移行してるかな?

Octokit で GitHub repo からファイルの中身を取得する

GitHub では Octokit という API クライアントが提供されています。

github.com

GitHub の REST API は fetch でも叩けますが、octokit/rest が使いやすくラップしてくれてます。

リポジトリ内のファイルの中身は getContent で取得して Base64 デコードするだけで取れます。

const { Octokit } = require("@octokit/rest");

(async () => {
  const octokit = new Octokit();
  const content = await octokit.repos.getContent({
    owner: "kondoumh",
    repo: "mtwe",
    path: "README.md"
  });
  const data = new Buffer.from(content.data.content, content.data.encoding).toString();
  console.log(data);
})();

1MB を超えるファイルは Contents API では取れなくて Blob API を使う必要があります。まず対象ファイルの SHA を取得し、取得した SHA を指定して git.getBlob メソッドで blob を取得するという2段回になります。SHA はファイル名では取れないので、親ディレクトリ (下記の例では src ディレクトリ) を指定して getContent して得られたファイルリストから目的のファイル名で絞り込みます。

const { Octokit } = require("@octokit/rest");
const jq = require("node-jq");

(async () => {
  const octokit = new Octokit();
  const files = await octokit.repos.getContent({
    owner: "kondoumh",
    repo: "sbe",
    path: "src"
  });
  const data = await jq.run('.data | map(select(.name == "renderer.js")) | .[0].sha', files, {input: 'json'});
  const sha = data.replace(/['"]+/g, '');
  const blob = await octokit.git.getBlob({
    owner: "kondoumh",
    repo: "sbe",
    file_sha: sha
  });
  const content = new Buffer.from(blob.data.content, blob.data.encoding).toString();
  console.log(content);
})();

ファイルリストからの抽出に jq のラッパー node-jq を使いました。2回のクエリが必要だし、SHA 1個取るのに全ファイルのデータを取得してるので非効率ですね。

こういう用途ではやはり GraphQL を使った方がよさそうです。GraphQL 用の octokit/graphql も提供されてます。上記のファイルの中身を GraphQL で取得してみます。

const { graphql } = require("@octokit/graphql");

(async() => {
  const graphqla = graphql.defaults({
    headers: {
      authorization: `token ${process.env.GH_TOKEN}`,
    },
  });
  const { repository } = await graphqla(`
    {
      repository(owner: "kondoumh", name: "sbe") {
        content:object(expression: "master:src/renderer.js") {
          ... on Blob {
            text
          }
        }
      }
    }
  `);
  console.log(repository.content.text);
})();

GraphQL API は public repo でも認証情報が必須です。対象のファイルパスを expression で指定して一発で取れました。Base64 変換も不要。便利ですね。

GitHub リポジトリをバックエンドのデータストアとして Web アプリなどを作ることも可能ですね。

Windows Terminal で SSH 接続先毎に Profile を作る

仕事用マシンがようやく Windows 10 1909 (November 2019 Update) に更新されました。

これまで SSH するとき Git Bash や Cygwin を mintty で使ってました。タブ機能がないので接続先毎にウィンドウが散らばりますし、 コマンド実行時に横幅が足りなくて表示が崩れた時にウィンドウをリサイズしても改行位置はそのままです*1

ということで待望の Windows Terminal を導入しました。

Git Bash の設定。Campbell という標準的なカラースキーマを指定。フォントは Windows Terminal 標準の固定ピッチフォント Cascadia code を指定。

{
  "profiles": [
    {
      "guid": "{c0a23c69-addf-41c8-a214-dc4af501d7ec}",
      "name": "Git bash",
      "acrylicOpacity": 0.70,
      "closeOnExit": true,
      "colorScheme": "Campbell",
      "commandline": "C:\\Program Files\\Git\\bin\\bash.exe --login -i",
      "cursorColor": "#FFFFFF",
      "cursorShape": "bar",
      "fontFace": "cascadia code",
      "fontSize": 12,
      "historySize": 9001,
      "icon" : "C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico",
      "padding": "0, 0, 0, 0",
      "snapOnInput": true,
      "startingDirectory": "C:\\Users\\kondoh",
      "useAcrylic": true
    },
}

起動したところ。

f:id:kondoumh:20201022224648p:plain

仕事では複数のマシンに SSH 接続する必要があります。Windows Terminal に 接続先ごとに Profile を作ればメニューから一発で接続できます。

SSH 用の Profile。commandline に接続先を含めた SSH コマンドを指定。カラースキーマは Solarized Dark でフォントは oh-my-zsh に適した Source Code Pro for Powerline を指定してみました。

{
  "profiles": [
    {
      "guid": "{73DB597C-2D03-4BCE-9D9C-9DB0294BEC6C}",
      "name": "SSH to Mac",
      "acrylicOpacity": 0.70,
      "closeOnExit": true,
      "colorScheme": "Solarized Dark",
      "commandline": "ssh masa@10.0.1.4",
      "cursorColor": "#FFFFFF",
      "cursorShape":"filledBox",
      "fontFace": "Source Code Pro for Powerline",
      "fontSize": 12,
      "historySize": 9001,
      "icon": "C:\\Users\\kondoh\\Pictures\\Windows_Terminal_Logo_256x256.png",
      "padding": "0, 0, 0, 0",
      "snapOnInput": true,
      "useAcrylic": true
    },
}

起動したところ。

f:id:kondoumh:20201022224711p:plain

接続先によってカラースキーマを変えればオペミス防止にもなります。

ウィンドウ幅が足りずに kubectl の結果が崩れても

f:id:kondoumh:20201024095118p:plain:w300

リサイズすれば整形されます。

f:id:kondoumh:20201024095135p:plain

これで、快適なターミナル生活を送れます。

*1:コマンドプロンプトですら整形されるのに・・

Kubernetes ネイティブなカオスエンジニアリングツール Chaos Mesh を使ってみる

Netflix が提唱した耐障害テスト技法 Chaos Engineering。実行環境に実際に障害を発生させてアプリケーションの復旧をテストする方法です。

Chaos Mesh は Kubernetes クラスターで稼働するアプリケーションをターゲットに Chaos Engineering テストを実行できるツールです。CNCF でホストされており、最近 v1.0 に到達しました。

chaos-mesh.org

以下のような障害発生の Experiment を実施できます。

  • PodChaos : Pod に定期的にエラーを発生させたり、Kill したりする
  • NetworkChaos : Pod のネットワークを遮断したり、遅延やロスを発生させる
  • StressChaos : Pod に負荷をかける
  • TimeChaos : 時刻取得のシステムコールを置き換えて時刻同期を狂わせる
  • IOChaos : IO エラーを発生させる
  • KernelChaos : Linux Kernel レベルのエラーを発生させる (デフォルトでは無効化されておりプロダクション環境での使用は禁止)

v1.0.1 現在、素の Pod への Chaos 注入はサポートされておらず、deployment, statefulset, daemonset が対象とのことです。

Docker Desktop for Mac の Kubernetes 環境で動かしてみました。

ドキュメントにしたがって、インストール*1

% curl -sSL https://mirrors.chaos-mesh.org/v1.0.1/web-show/deploy.sh | sh

Install Chaos Mesh chaos-mesh
   :
Waiting for pod running
Chaos Mesh chaos-mesh is installed successfully

無事デプロイされ、namespace chaos-testing に次のような Pod や Service が起動しました。

% kubectl -n chaos-testing get po,svc,deploy
NAME                                            READY   STATUS    RESTARTS   AGE
pod/chaos-controller-manager-86c96f985f-pvjcv   1/1     Running   0          11m
pod/chaos-daemon-9n9mb                          1/1     Running   0          11m
pod/chaos-dashboard-5d8dff7df9-5hp6h            1/1     Running   0          11m

NAME                                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                       AGE
service/chaos-dashboard                 NodePort    10.97.228.5      <none>        2333:31287/TCP                11m
service/chaos-mesh-controller-manager   ClusterIP   10.106.150.179   <none>        10081/TCP,10080/TCP,443/TCP   11m

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/chaos-controller-manager   1/1     1            1           11m
deployment.apps/chaos-dashboard            1/1     1            1           11m

次のような CRD (Custom Resource Definition) が適用されています。IO, Kernel, Network, Pod, Pod Network, Stress, Time などの Chaos type があるのがわかります。

% kubectl get crd
NAME                             CREATED AT
iochaos.chaos-mesh.org           2020-10-22T15:03:17Z
kernelchaos.chaos-mesh.org       2020-10-22T15:03:18Z
networkchaos.chaos-mesh.org      2020-10-22T15:03:18Z
podchaos.chaos-mesh.org          2020-10-22T15:03:18Z
podiochaos.chaos-mesh.org        2020-10-22T15:03:18Z
podnetworkchaos.chaos-mesh.org   2020-10-22T15:03:18Z
stresschaos.chaos-mesh.org       2020-10-22T15:03:18Z
timechaos.chaos-mesh.org         2020-10-22T15:03:18Z

Network chaos を引き起こすサンプルマニフェスト。NetworkChaos という CRD として適用され、10ms のレイテンシを web-show というラベルの Pod に 60秒毎に30秒の間発生させます。

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: web-show-network-delay
spec:
  action: delay # the specific chaos action to inject
  mode: one # the mode to run chaos action; supported modes are one/all/fixed/fixed-percent/random-max-percent
  selector: # pods where to inject chaos actions
    namespaces:
      - default
    labelSelectors:
      "app": "web-show"  # the label of the pod for chaos injection
  delay:
    latency: "10ms"
  duration: "30s" # duration for the injected chaos experiment
  scheduler: # scheduler rules for the running time of the chaos experiments about pods.
    cron: "@every 60s"

ターゲットとなる web-show アプリをデプロイ。これは、kube-system Pod に ping してレイテンシーを表示する React アプリです。

% curl -sSL https://mirrors.chaos-mesh.org/v1.0.1/web-show/deploy.sh | sh
service/web-show created
deployment.apps/web-show created
 :
Waiting for pod running

localhost:8081 に接続するとこんな感じで、レイテンシーの推移が表示されます。

f:id:kondoumh:20201023080812p:plain

この状態で先ほどの Network Chaos のサンプルを apply してみます。

% kubectl apply -f network-delay.yaml

Network Chaos の引き起こしたレイテンシが一定間隔で発生しているのが見て取れます。

f:id:kondoumh:20201023083219p:plain

Chaos Mesh のダッシュボードは Node Port で公開されているため port-forward して画面を表示します。

% kubectl port-forward -n chaos-testing svc/chaos-dashboard 2333:2333
Forwarding from 127.0.0.1:2333 -> 2333
Forwarding from [::1]:2333 -> 2333

localhost:2333 に接続するとダッシュボードが表示され、適用中の Experiment (この場合は Network Chaos) が確認できます。

f:id:kondoumh:20201023083729p:plain

タイムラインや詳細情報を閲覧できます。

f:id:kondoumh:20201023084221p:plain

f:id:kondoumh:20201023084243p:plain

このように Chaos Mesh はターゲットの Kubernetes アプリケーションに対して様々な障害を注入することで耐障害性をテストすることができます。ダッシュボードも見やすくて完成度が高い印象です。リリース前のシステムテストなどに組み込めば、アプリケーションのウィークポイントを発見したり、運用時の障害復旧のリハーサルにも活用できそうです。

*1:Helm でも導入可能です。

リモートワーク時代のスマホ活用 - Pixel + ThinkPad TrackPoint Keyboard

リモートワークになって、Pixel 3 XL の稼働率が如実に下がりました。外出しない日はほぼ電話を受けるだけなので、バッテリーもあまり減りません。

ところで、数ヶ月前に買った ThinkPad TrackPoint Keyboard 2

blog.kondoumh.com

最初は ThinkPad ファンとしてのノスタルジーもあって喜んで使ってたのですが、Ctrl キーが押しづらく長時間使ってると小指がつるようになり*1、結局 REALFORCE に回帰してしまいました。

サブマシンに iPad Pro + Trackpad + Magic Keyboard 使ってたのですが、奥さんが iPad Pro を再び使い始めたので TrackPoint Keyboard と Pixel 3 XL をペアリングしてみました。TrackPoint Keyboard 2 には Android 用のスイッチが用意されていて問題なく設定できました。

Nexus 6P を使ってた時に PC 用のキーボードを Bluetooth でペアリングできるデバイスを使ってたりしてたので、なんとなく使用感はわかっていました。

blog.kondoumh.com

Pixel 3 XL 買った直後は Termux + 物理キーボードでコード書いたりしてたし。

blog.kondoumh.com

信頼できるデバイスに登録することで、指紋センサーに触らなくてもロック解除できます。TrackPoint がポインティングデバイスになるので画面にタッチせずに操作も可能。

Zoom 会議も ペアリングしてる完全ワイヤレスのヘッドセットでこなせてシンプルになりました*2。画面小さいけど YouTube もランドスケープ&スレテオで普通に見れますし YouTube Music も流しっぱなしでいけます。ということでしばらく Pixel 3 XL を仕事中のサブマシンとして使っていこうと思います。

f:id:kondoumh:20201012225541j:plain

*1:CapsLock ももちろん Ctrl キーに割り当ててました・・が、Ctrl キーは小指の肉球で押す派なので・・。

*2:会議中に電話がなるとプチパニックになってしまいますが