GitHub Actions で EKS を触る
GitHub Actions で EKS を触ることのできる kubectl をセットアップします。
kubectl だけほしい
「github actions kubectl」などのワードで検索すると、「Azure がどうのこうの」「個人が作った Docker な GitHub Actions」といったページがヒットします。もちろん後者が悪いわけではないのですが、kubectl がほしいだけなのに、Docker イメージになっているのはちょっとオーバースペックです。かといって、自分でアクションを作るのは手間ですし、「見つけ切れていないだけでなんかあるんじゃないか」と不安になります。
というわけで、azure/setup-kubectl アクションを使います。「あれ?Azure じゃん」と思われがちですが、このアクションは正真正銘の「kubectl をセットアップするだけのアクション」です。
- 手軽
- 余計なものが入らない
- 認証済みプロバイダによる公開 (Microsoft)
といったメリットが挙げられます。
使い方
https://github.com/Azure/setup-kubectl
README に記載されているままですが、アクションに
- uses: azure/setup-kubectl@v1
を追加するだけです。これでこのステップ以降にて kubectl が使えるようになりました。
EKS との連携
ただし、もちろんこのままでは EKS に接続ができません。EKS に接続できる kubeconfig (.kube/config) を食わせる必要があります。
最近の aws-cli で EKS の設定を行った (aws eks update-kubeconfig) 場合、kubeconfig の users はだいたい下記のようになっているはずです。
- name: arn:aws:eks:ap-northeast-1:XXXXX:cluster/XXX user: exec: apiVersion: client.authentication.k8s.io/v1alpha1 args: - --region - ap-northeast-1 - eks - get-token - --cluster-name - XXX command: aws
一昔前のやり方だと aws-iam-authenticator を使う (command: aws-iam-authenticator になっている) ようになっているので、その場合は aws-cli をアップデートして再度 update-kubeconfig するか、GitHub Actions 上で aws-iam-authenticator をセットアップする必要があります。
この kubeconfig を GitHub リポジトリの Secrets に登録します。名前は KUBE_CONFIG_DATA としておきます (なんでもいいです)。kubeconfig はテキストファイルですが、ファイルをそのまま Secrets に置くという意味では base64 エンコードするべきですので、
cat .kube/config | base64 --encode
この結果を Secrets の値として入れておきます。
さて、この kubeconfig は aws コマンド (aws-cli) を必要とします。これを使うためのアクションは AWS 公式に用意されています。
- uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1
これで aws configure が完了した状態で aws コマンドが使用できます。
そして、リポジトリ Secrets の AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY に、EKS にアクセスできる IAM ユーザーを入れておきます。
kubectl が Secrets 内の kubeconfig を使えるようにファイルに書き出し、ファイルパスを環境変数 KUBECONFIG にて設定します。そうすると EKS に接続できる kubectl が使えるようになります。
- env: KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} run: | echo "$KUBE_CONFIG_DATA" | base64 --decode > /tmp/kube_config export KUBECONFIG=/tmp/kube_config kubectl get pods
うまくいっていればポッドの一覧が出力されます。
もちろん、実際に運用するときは、CI 専用の IAM ユーザー/ロールを作り、対応する Kubernetes RBAC を割り当てる必要があります。
完成形
最小限のワークフローはこんな感じになります。
name: CI on: - push jobs: deploy: runs-on: ubuntu-latest steps: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Install kubectl uses: azure/setup-kubectl@v1 - name: Run kubectl env: KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} run: | echo "$KUBE_CONFIG_DATA" | base64 --decode > /tmp/kube_config export KUBECONFIG=/tmp/kube_config kubectl get pods
VSCode で Samba 上の JavaScript/TypeScript プロジェクトの初期化がめっちゃ遅かった (Windows)
さらに追記
数日様子を見ておそらく治ったのでコレというやつを。
Windows の NFS クライアントを有効にしていると、たとえ何もマウントしていなくても現象が起こりやすくなるようで、「Windows の機能の有効化または無効化」から「NFS 用サービス」をオフにしたら、いまのところ快適になっています。オンにしたままでもたまに調子が良いときがあるのが原因究明に手間取った。
追記
下記の記述内容はまったくの大嘘な可能性があります。
少なくとも TSC_WATCHFILE はなんの影響も及ぼしていないようです。
いろいろ調査中ですが、「これかも」っていうのは見つけたのでしばらく様子を見てまた記事にします。
TL;DR
環境変数 TSC_WATCHFILE を UseFsEventsPriorityPollingInterval にする。
以下本文
Windows の VSCode で、Samba 上の JavaScript/TypeScript プロジェクトを開くと、「JS/TS 言語機能を初期化しています」が数分間出続け、その間 IntelliSense が効かない。
昔(半年前とか)は大丈夫だったので、TypeScript のバージョンをいろいろすげ替えながら試してみたところ、2.9 から怪しくなったようだ。 (2.9 の時点で、初期化時にメッセージは表示されないが、IntelliSense が数分間働かなかった)
tsserver のログを見てみると、Watch がどうのこうの、というところで1分くらい待たされている。
どうも TypeScript 2.9 で Watch の仕様が変わったらしく、各ファイルの親ディレクトリを監視し、CPU 使用率を下げるという修正が入ったようだ。 だが、これが Windows のネットワークディレクトリ上だとうまく動かないように見えた。
そんなわけで、TypeScript の Watch の挙動を変更するには、環境変数で指定するようなので、Windows のシステムのプロパティの環境変数から値を指定したところ、早くなった。めでたしめでたし。
Mastodon インスタンス管理者が忘れてはならないたったひとつのこと
追記
v1.3.3でこのタスクは自動化されました。ですので購読更新という意味ではデイリータスクを回す必要はなくなりました。 (ただし、不要なファイルの削除などが含まれるのでデイリータスクはちゃんと回すことをおすすめします)
以下、以前の本文:
rake mastodon:daily を毎日まわせ。 (できれば1日2回が良い)
忘れるとなにが起きるか
あなたのインスタンスにリモートアカウントから 徐々に トゥートが届かなくなる。 (PubSubHubbub用の購読情報が更新されないため)
ちゃんと読もう、Production guide
(でもこのサンプル crontab も、結果を /dev/null に捨ててるからあんまり筋良くないね)
Mastodon でリモートインスタンスから自分のトゥートがみえなくなることがある
Mastodon v1.4 以降について
Mastodon v1.4 以降では、この問題が解消されています (配信時に 3xx/4xx 系が返っても配信解除を行わなくなった)。
下記は v1.4 未満の情報です。
★前提★ (2017年4月22日追記)
この文書は、すべての インスタンス管理者が、デイリータスク (mastodon:daily) を回していることが前提です
こんな現象がおきている
- トゥートする
- 特定のインスタンスにトゥートが出ない (プッシュされてない)
- なので、フォロワーにもトゥートが届かない
- ReplyやBoost/Favはちゃんと届く
PubSubHubbub
PubSubを司っているPubSubHubbubの配信先リストを眺めてみると、そのインスタンスが居なくなっている。
この状態で相手のインスタンスから自分のアカウントをフォローしなおしたりしても変わらない。
データベース上の subscriptions テーブル (自⇒リモートへの配信先URL一覧) からも消え失せており、callback_url/secret が分からないのでもはやお手上げである。
どうしてこうなった?
Mastodonは、トゥートをpushするときに、相手のサーバが3xx/4xxのステータスコードを返却すると、即座に配信を解除するようになっている。 (おそらく相手インスタンスの購読期限が切れたときなどのための処理だろう)
相手インスタンスが、サーバメンテナンスなどで一時的に404などが出ていて、そのときにトゥートを行うとこの現象が発生する。
改めてフォローしなおしても状況が変化しないのは、相手インスタンスはこちらの購読情報をまだ持っている (accounts テーブルにある subscription_expires_atに到達していない) からである。
(購読情報を持っている限り、新たに購読情報を送ることはしないようだ)
解決策
- 相手のインスタンスが
cronでデイリータスクを回している限り、購読期限が7日なのでそのうち新しい配信先情報が送られてくる (ので、最大7日間待つ) - 定期的に
subscriptionsテーブルの dump をとって、問題が起きたら自分で復元してみる (自分でインスタンスを運用している場合) - 相手のインスタンスの管理者に
SubscribeService.new.call(<自分のAccount>)を実行してくださいと言う (最終手段)
余談
Mastodon のコード中の subscription が、「購読している (accounts.subscription_expires_at 等)」「購読されている (subscriptions テーブル等)」と、自インスタンスから見るとまったく逆の意味で用いられている箇所があるので、文脈をよく読まないと混同してしまうので注意。
参考
Mastodon を Docker で立てるときに最低限しておくこと
2017年4月16日現在の情報です
- 4月15日のコミットで
docker-compose.ymlに Docker Hub のイメージの記述が入った。 このイメージは GitHub の master に追従してビルドされるので(多分)、docker-compose buildよりもdocker-compose pullのほうが早い。 - これは各地で言われていることだが、デフォルトの
docker-compose.ymlでは PostgreSQL と Redis の永続化がされていない。volumesのコメントアウトを外すこと。 webとstreamingのポート (3000と4000) が外部におっぴろげになってるので、Docker network 内でリバースプロキシしたほうが良い。(リバースプロキシ用のnginxのコンテナを同一Docket networkに立てる)docker-compose.ymlの PostgreSQL のイメージが最新版に追従するようになっている。PostgreSQLはメジャーバージョン (9.x の x の部分。開発中の 10 以降は 10, 11, … がメジャーバージョン) でデータベースファイルの互換性がないので、メジャーバージョンアップ後に pull すると起動できなくなって慌てることになる。image: postgres:9.6.2-alpineのようにバージョンを指定しておこう。 PostgreSQL が更新されたあとは、余裕が出来たらpg_upgradeやpg_dump/pg_restoreでアップグレードしていくべし (でも Mastodon は PostgreSQL の最新の機能を使ってないので、セキュリティリスクとかが出ない限りは旧バージョンで固定してもよさそう)。 (18:38追記)
2017年4月17日 1:28追記
Mastodon を Docker で立てるときに最低限しておくこと - chaotic valkyrieb.hatena.ne.jp
- [docker]
- [Mastodon]
細かいタグではなくて postgres:9-alpine で大丈夫そうな気もする
2017/04/17 00:34
半分仰るとおりで、 image: postgres:9.6-alpine で充分です。これでセキュリティアップデートはある程度追従できます。
9.7はリリースされず次バージョンは10のはずなので 9-alpine でも大丈夫だとは思いますが、仮に 9.7 がリリースされるとアップグレード作業なしには起動できなくなるので 9.6-alpine が無難かと思います。
mintty でピクセル単位のフォントサイズを指定する
フォントサイズ10.5pt(96dpi環境上での14px)が使いたいのに指定できないとお困りのあなたへ。
.minttyrc に
FontHeight=-14
のようにマイナスで値を指定するとピクセル指定になります。
mintty のソースコード (wintext.c) にも
font_height = size > 0 ? -MulDiv(size, GetDeviceCaps(dc, LOGPIXELSY), 72) : size;
と書かれており、ちゃんとした挙動なのでごあんしんください。
ただし、ピクセル直接指定なので DPI Aware ではなくなります。
PostgreSQL の NOTIFY/LISTEN を使用してテーブルの変化を PHP から検知してみた
PostgreSQL 9.0 から NOTIFY/LISTEN という Pub/Sub を行う仕組みが入っていたので、試してみた。
NOTIFY/LISTEN の挙動を確認する
psql を3枚立ち上げる。

LISTEN <channel>; で <channel> の購読を開始する。
NOTIFY <channel>; で <channel> を購読しているセッションに配信を行う。
購読を psql 上で待つときは、; [Enter] など、何かしら実行させることで通知が表示される。
トランザクションで囲んだ場合は

のように、コミット時に配信される。
トランザクション内で同じチャンネルに対して複数回配信を行った場合は

のように、複数回配信されることはなく、一度だけ配信される。
詳しくはマニュアルに記載されている。
リアルタイム配信システムを作ってみる
チャットのように、発言テーブルに INSERT されたら画面上にリアルタイムに発言が表示されるシステムを作ってみる。
テーブル定義
CREATE TABLE timeline ( id serial NOT NULL PRIMARY KEY, body text NOT NULL, created timestamp NOT NULL DEFAULT now() );
シーケンス、内容、時刻を持つだけの単純なテーブルを作った。
このテーブルに INSERT されたときに自動的に NOTIFY してほしいので、トリガーを作成する。
CREATE OR REPLACE FUNCTION notify_trigger() RETURNS trigger AS $$ BEGIN PERFORM pg_notify(TG_ARGV[0], NULL); RETURN NULL; END; $$ LANGUAGE 'plpgsql'; CREATE TRIGGER timeline_update AFTER INSERT ON timeline EXECUTE PROCEDURE notify_trigger('timeline_update');
pg_notify 関数は、NOTIFY 文が関数になったもので、チャンネル名やペイロード(通知の際に使用できる任意の文字列)が不定である場合に便利とされている。
トリガー関数の戻り値が NULL であるが、これはトリガーが AFTER INSERT であり、戻り値が利用されないため、何でも良い。
なお、トリガーに UPDATE や DELETE も指定し、通知のペイロードとして変更のあった id を渡すことで、リアルタイムに変更を監視することも可能なはずである。
(追記) tcnモジュールを利用することでわざわざトリガー関数を作らなくても良いようだ。ただしこのモジュールは行指向のため、今回の用途からすると少しオーバースペックではある。
API
PHP 5.6 で作った。
なお、ここで使用している PDO::pgsqlGetNotify は PHP 5.6 以降で使用可能な関数であるため、
それ以前のバージョンを使うときは PostgreSQL モジュールに用意されている pg_get_notify を使用することになる。
(ただし、pg_get_notify はタイムアウトが指定できず、通知が無い場合は即座に FALSE が返るため、PHP 内でループを回すことになる… ダサい)
WebSocket などのモダンな技術で作っても良いのだが、単純化するために古典的な Long polling スタイルで作った。
全ソースコードは少し長いので Gist にアップロードした。
function retrieve($since_id = 0, $limit = 10) {
global $db;
$stmt = $db->prepare("SELECT * FROM timeline WHERE id > :since_id ORDER BY id DESC LIMIT :limit");
$stmt->bindValue(':since_id', $since_id);
$stmt->bindValue(':limit', $limit);
$stmt->execute();
return $stmt->fetchAll();
}
$timeline = retrieve($since_id);
if (empty($timeline)) {
$db->exec('LISTEN timeline_update');
$result = $db->pgsqlGetNotify(PDO::FETCH_ASSOC, 30000);
if ($result === false) {
output();
}
$timeline = retrieve($since_id);
}
Twitter 風に、since_id を指定できるようにした。
API 呼び出し時点で id が since_id より上の発言があればそれを返し、無ければ発言されるまで(INSERT されるまで)待機し、新規発言を返す。
30秒たっても新規発言がなければ、空配列が返る。
retrieve.php?since_id=<max_id> をブラウザで開くと読み込み中のままになり、psql などから timeline テーブルに追加すると即座にブラウザ上にレスポンス (JSON) が表示される。
since_id を用いた取得なので、INSERT INTO timeline (body) VALUES ('foo'), ('bar') のように2行が一度に INSERT されても、ちゃんと2行とも表示される。
フロントエンド
$(function() { var retrieve_and_display = (function() { var current_id = 0; return function() { $.post('retrieve.php', { since_id: current_id }) .done(function(result) { if (result.length > 0) { current_id = result[0].id; result.reverse(); $.each(result, function() { $('<li />').text(this.body).prependTo($('#timeline')); }); } retrieve_and_display(); }); }; })(); retrieve_and_display(); });
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <script src="//code.jquery.com/jquery-2.1.3.min.js" type="text/javascript"></script> <script src="pushtest.js" type="text/javascript"></script> <title></title> </head> <body> <p>timeline:</p> <ul id="timeline"> </ul> </body> </html>
retrieve_and_display 関数を呼び出すたびに未取得の発言を最大10件取得し、#timeline の先頭に追加する。
サンプルなのでいろいろツッコミどころがあるが(ajax request が fail したときの処理がない、jQuery で DOM を生成するな、そもそも jQuery を使うな etc.)、そこはサンプルということでひとつ。
ブラウザで開き、最新の10件が表示され、psql 等で timeline テーブルに追加すれば即座にブラウザ側にも反映されることが確認できた。
まとめ
PostgreSQL の NOTIFY/LISTEN を使用することで、Pub/Sub を実現できた。 Publish 側は PostgreSQL に任せることができるため、Subscribe 側のみの実装を行うだけで良いのは楽であった。