ラズパイにgolangで書かれているdocker composeをinstallする

多分令和最新版。

linuxでdocker composeを使いたい場合、macOSwindowsDocker for xxx系とは違い、docker composeはバンドルされてないので、自分でインストールする必要があった。

特にラズパイではdocker-compose時代(Pythonスクリプト)の場合はpipでインストールする必要があった。 我々はdockerでローカルのことを考えずに動かしたいのにPython3関係の構築をしないといけないのは面倒....。

現代ではdocker composeはgolangで書き直されており、これを使う場合はpip installが不必要。 さらにARMバイナリ用のdocker composeはGitHub Releaseで配布されているので、落としてくれば使えるという極めて親切な設計になっている。

今回はすでにdockerはインストールされているものとして、docker composeのインストールを見てみる。

インストール方法

基本はcomposeのREADMEに書いてあるとおり。

github.com

自分だけで使う場合は、$HOME/.docker/cli-pluginsに、全ユーザーで使いたい場合は/usr/local/lib/docker/cli-pluginsなどにdocker-composeの名前でバイナリをポン置きすれば使えるようになる。

バイナリはリリースから持ってくれば良い。

github.com

気をつける点として、uname -mすると微妙にarmv7lとcomposeで打たれているアーキテクチャ名(armv7)と異なる。ワンライナーの中でuname -mしていい感じに取得しようとする場合は注意。

$ uname -m
armv7l

自宅のラズパイ4で、全ユーザーにインストールしたい場合は次のような感じでインストールする。

とりあえずrootになり

$ sudo -i 

後は素朴に

# mkdir -p  /usr/local/libexec/docker/cli-plugins
# cd /usr/local/libexec/docker/cli-plugins
# wget https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-armv7 -O docker-compose
# chmod +x docker-compose

こうするとdocker composeが使えるようになる。

$ docker compose

Usage:  docker compose [OPTIONS] COMMAND

Docker Compose

Options:
      --ansi string                Control when to print ANSI control characters ("never"|"always"|"auto") (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --env-file string            Specify an alternate environment file.
  -f, --file stringArray           Compose configuration files
      --profile stringArray        Specify a profile to enable
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the Compose file)
  -p, --project-name string        Project name

Commands:
  build       Build or rebuild services
  convert     Converts the compose file to platform's canonical format
  cp          Copy files/folders between a service container and the local filesystem

バイナリポン置きなのでローカル環境に色々いれなくていいので便利ですね。現場からは以上です。

入社してから書いていた分報の行数を眺めてみる

この記事ははてなエンジニア Advent Calendar 2021 の4日目の記事です。

昨日は同期のid:gurriumくんでした。MOD Pythonで色々できるんだ...。僕も趣味で好きなゲームに色々コード書いて楽しんでますが、それは社のアドベントカレンダーには書けないゲームなのでまたの機会に書こうと思います。

社ではScrapboxを使っていて、僕は分報の置き場として色々書いています。個人的にはTwitter感覚で使っています。 分報は以下のフォーマットで基本書いています。

anatofuznote 2021/11/29 - 12/03
[anatofuznote 2021/11/22 - 11/26]  <=> [anatofuznote 2021/12/06 - 12/10]

#anatofuz #anatofuznote
[** 2021/12/3]

実際には↓の用な感じでScrapbox上は見れる f:id:anatofuz:20211204183001p:plain

忙しいときはツイート数が減っている気がしていて、逆になんか盛り上がっているときはツイート数が多いかなとなんとなく感じています。 つまりツイート数で自分の状態が測れるのではと思っています。 せっかくなので、本当にそうなのか調べてみました!!! 分報なのでツイート数==scrapboxの行数として考えてみます。

準備

APIを使ってかっこよくやるとかは頭になく、素朴にざっくりと外観が知られればいいので、雑スクリプトで集計します。 分報は先程例示したフォーマットで書いているので、[** 2021/%m/%d]に囲まれている間の行数を雑にカウントする方法でやってみます。

データはどうやって持ってくるかというと、コピペですね。はい。素朴にやるのが早いねんな。 ということで次のようなPerlスクリプトを書きました。

use strict;
use warnings;
use Time::Piece;

my $current_date = undef;
my $result = {};

while (my $line = <DATA>) {
  if ($line =~ /^\A\s*\[\*.+\s*(2021.*)\]/) {
      $current_date = $1;
  }
  $result->{$current_date}++;
}


my @sorted_date = sort {  Time::Piece->strptime($a, '%Y/%m/%d') <=>  Time::Piece->strptime($b, '%Y/%m/%d') } keys %$result;

for my $date (@sorted_date) {
    print "$date $result->{$date}\n";
}


__DATA__
[** 2021/12/3]
  今日も
  1日
[** 2021/12/2]
   オハヨッ
....

Perl__DATA__以下に書いたテキストを<DATA>で読めるので、Scrapboxからかき集めた分報をDATA以下にコピっています。 それを素朴な集計方法で数え上げて、最後に日付順にソートして表示します。

実行するとこんな感じ

❯ perl note.pl | head -4
2021/4/8 503
2021/4/9 11
2021/4/12 260
2021/4/14 256

これをグラフ化したいので、学生時代から慣れ親しんでいるgnuplotでシュッとグラフを作ります。matplotlib? 俺はgnuplotのほうが慣れてるんだ...。

❯ gnuplot

        G N U P L O T
        Version 5.4 patchlevel 2    last modified 2021-06-01

        Copyright (C) 1986-1993, 1998, 2004, 2007-2021
        Thomas Williams, Colin Kelley and many others

        gnuplot home:     http://www.gnuplot.info
        faq, bugs, etc:   type "help FAQ"
        immediate help:   type "help"  (plot window: hit 'h')

Terminal type is now 'qt'
gnuplot> set term svg

Terminal type is now 'svg'
Options are 'size 600,480 fixed enhanced font 'Arial,12' butt dashlength 1.0 '
gnuplot> set output 'out.svg'
gnuplot> set xdata time
gnuplot> set timefmt '%Y/%m/%d'
gnuplot> plot '<perl note.pl' using 1:2 with line
gnuplot> exit

ということでできたのがこれです。

GnuplotProduced by GNUPLOT 5.4 patchlevel 2 0 100 200 300 400 500 600 04/01/21 05/01/21 06/01/21 07/01/21 08/01/21 09/01/21 10/01/21 11/01/21 12/01/21 01/01/22 '<perl note.pl' using 1:2 '<perl note.pl' using 1:2

見ていると入社当時は盛り上がっていたものの、9月から口数が少なくなり、11月は一般人みたいなツイート数になってましたね..。 最近ツイート数が上がってきているので盛り上がってきているという感じなきがする。 初期が長めなのは環境構築のログがはられていたりするからっぽかった。

可視化してみると色々見れて便利ですね。ここまで書いてmackerelのサービスメトリックに日毎に送信すると面白いのでは? みたいな気持ちになったので、調べたところ、過去のデータは送信できなさそう? なので次回にご期待ください。

明日はid:mizdraさんです。

調子が悪いときの傾向と対策

最近なんか調子悪かったんですがようやく回復してきたので自覚している調子の悪さについて書いておこう。。。

症状

  • 口数が減る
    • ツイート数が減る
      • 分報を書かなくなる
    • 頭の中は色々考えているがアウトプットする気力が減っている
      • アウトプットに対してネガティブな反応をもらうと立ち直れなくなるなる状態になってるので本能的にしなくなるのだろう
    • でかいタスクをどーんとやって死ぬみたいなのがでる
  • 相談の内容がうまくまとまらない感じで相談する
    • まとめるほどの気力がない
    • まとめるエネルギーと時間がないので頭の中のごちゃごちゃした解像度で会話をする
  • 言語化が全般的にできなくなる
  • ちょっとしたことで傷つき出す
    • ミーティング時の状態などで「あぁ....」みたいになり悲しい気持ちになる
    • 寝る前や土日にそのことばかり考えてしまう
  • 常に業務のことを考えている
    • 土日と退勤後と出勤前もギョームのコードや情報を見ている
    • 楽しくて見ているのではなく不安に駆られてみている
  • よく考えればできること、普段できることができなくなる
    • 質問の仕方とPRの説明分の情報量をごちゃまぜにして処理してしまう
    • 普段なら気にせずできる作文などができなくなってくる
    • おちついてデバッグすればわかる問題がわからなくなる
  • ものをこぼしたり落としたりするようになる
  • 不安感にさいなまれる
    • なんか出勤したら大声で紛糾されるんじゃないかみたいな恐怖を考えてしまう(完全に調子悪いとき)
    • なんか裏で自分のことについて話されているのではないか、責められているのではないかとなる
  • 作業がバッティングする
  • 同じ行を書いたり消したりする
  • 金の出費が激しくなる
    • DLsiteとDMMで色々買う
    • 月末のカードの請求でさらに調子が悪くなる
  • 新しいコンテンツが全般的に受け付けなくなる
    • 音楽はだいたい同じプレイセットをリピートしたり、動画もよく見る人の投稿ばかりみる
    • 映画館に行って新しいコンテンツを見る元気がなくなる
  • 朝起きれなくなる
  • 退勤後何もできなくなる
  • 突然よくわからないLINEを知人に送る

対策

  • 有給をとっても気持ちが休まらないので仕事はする
  • 読書をする
    • 好きなホラーを読む
  • 紅茶(アールグレイ)を濃いめに入れて飲む
    • 他の味のティーパックだと気持ち悪くなる
  • 深く考えずできる単純作業をする
  • たまに好きな言語でプログラミングする
  • ポモドーロテクニックで切り替える
  • どうせ仕事は頑張るので頑張りすぎないことを意識する
    • とはいえ「もう少し頑張れるのでは...」となりがちなので難しい
  • 1日の情報量/思考量を減らす
    • あわあわしているときに毎日仕事の着手順を考えてたり他の進捗を考えているとリソースがだいぶ持っていかれる
    • 今日しないといけないことだけ見ておく
    • タスク順を他の人に考えてもらう
  • 焼き肉に行く
    • 思えば去年は安々行きまくってたな...
  • 水辺に行く

少し余力が出てきたタイミングでまとめるとまぁこういう感じだったんだなーとなりますね。自覚症状以外にもいろいろありそう

TypeScriptで関数合成をする関数を書いてみた

関数型プログラミングといえば関数合成みたいなところありますよね。Haskellだと.でagdaだとoだった気がする。

最近やっているTypeScriptで関数合成をやってみました。

以下はググって出てきたこのサイトを参考にしています。 minaluke.medium.com

まず例題のために簡単なInterfaceを定義してみます。 nameというフィールドがあってstringが入っています。

interface IPerson {
  name: string;
}
const person: IPerson = {
  name: "Mina",
};

ここから次の関数を順に実行することを考えます。 処理としてはgetNameでIPerson型からnameのstringだけ取り出して、その結果をgetLengthに流して文字列長を取得。 その結果が偶数かどうかを判定する感じですね。

const getName = (p: IPerson) => p.name;
const getLength = (str: string) => str.length;
const isEven = (num: number) => num % 2 === 0;

合成関数使わずに純粋に書くとこんな感じだと思います。

console.log(isEven(getLength(getName(person))))

これでも良いんだけど関数が多いと書くのがしんどい!!!! ので合成して一つの関数にまとめることを考えます。

そこで関数合成をする関数composeを考えます。 Javascript的な型を考えないと次のようなコードになりそうです。

const compose = (...fns) => arg =>
      fns.reduce((composed, f) => f(composed), arg);

引数として関数の配列fnsを受け取って、reduceで部分適用して進めていく感じですね。 これをTypeScript化することを考えると、fnsargに型を指定する必要があります。 それで実際に書いてみると次のようになりました。

const compose =
  (...fns: ((arg: any) => any)[]) =>
  (arg: any) =>
    fns.reduce((composed, f) => f(composed), arg);

...fnsは「なにかしらの引数を受け取って何かしらの返り値を返す関数 ( (arg:any) => any)の配列」なので((arg: any) => any)[]となり、arg自体は何かしらの型なのでanyになります。 書いてみるとまぁそうだよねという感じですね。多分いい感じのライブラリがありそうなので情報お待ちしております。

ApolloServer/Clientのフルスタックチュートリアルやった

最近はGraphQLの時代!!!!! ということでApolloGraphQLのフルスタックチュートリアルをやりました。

www.apollographql.com

構造自体は1-4まではApolloServer側の話で、6-9がApolloClient側の話。 ServerからやっているとGraphQLサーバーの実装から出来てお得という感じ。

例題としてはSpaceXの無料APIをApolloServer側でラップしてGraphQLにして返すのがファーストステップとなっている。

github.com

その後はミューテーションとしてログインや宇宙旅行の予約をServerで実装する。 テストはApollo StudioっぽいGUIの画面で実際にQueryやMutationが返ってきているかを確認する感じ。 リポジトリの雛形実装はPureなJavaScriptだったので、定期的にフィールドの大文字小文字をミスって時間が溶けそうになるなどがあった。 ここだけ別言語で実装してみてもいいかもしれない。

6移行はApolloClientのチュートリアルで、TypeScriptとReactを使っていい感じに宇宙旅行のwebサイトを作っていく。 とはいえあくまでApolloClientのチュートリアルなので、各種componentはすでに実装されているものを使っていく感じ。 componentを作る感じは別のチュートリアル/書籍をやれという感じだけど、ApolloClientで共通した処理の書き方や、Query、Fragmentの書き方などがわかった。

特にPagination関連の実装は自分で手を動かしてみると処理の雰囲気がなんとなく理解できてきて良かった。 ClientだとfetchMore受け取ってcursorいじって再度リクエストするのはなるほどねーとなっていた。

とはいえかなりコンポーネント関係お膳立てされているので素朴にアプリケーション組んでみたほうが良さそうな気がする。 アプリケーション自分で書く前の助走としてはいい感じに出来たかな。また見返したい。

dein vimでプラグインをupgradeする

何かしらのbrewの更新でしくったのか。vimを起動しようとするとfugitive.vimがエラーするようになった。

/Users/anatofuz/.cache/dein/.cache/.vimrc/.dein/plugin/fugitive.vim の処理中にエラーが検出されました:
行  471: E1208: -complete used without -nargs行  472: E1208: -complete used without -nargs行  476: E1208: -complete used without -nargs行  479: E1208: -complete used without -nargs続けるにはENTERを押すかコマンドを入力してください

fugtive.vimはgitをvimから操作するプラグイン。実はあまり使っていない。

github.com

プラグインでなくてvim本体の問題かと思って、vim自体をupgradeしたけれど状況変わらず。 う〜んと思っていたところ、vimプラグイン管理で使っているdeinでupgradeすればいけるのではと思いやってみた。 upgrade自体は死ぬほど簡単で、vimを開いてコマンドモードでcall dein#update()を実行するといい。

upgradeしたところ無事に起動出来た。fugitiveの最新版で治った説もあるけれど、upgrade時に依存ライブラリが解決されたとかな気がする。

PerlのXSをlldbでデバッグする

興味が湧いたのでPerlのC拡張であるXS言語で書かれているList::Utilを読んでいるのだけど、C言語チックなものを読むときはやはりデバッガが使いたい。 XSも結局C言語なのでgdb/lldbでデバッグできないかなと思ってググったところ、できそうなエントリを見つけたので試していく。

stackoverflow.com

XSのデバッグビルド

C言語デバッグにはCで書かれたコードにデバッグオプションを付けてビルドする必要がある。 XSも同様だと思われるので、何かしらの方法でデバッグビルドする必要がある。

XSモジュールに限らずPerlモジュールのビルドは、Makefile.PLかBuild.PLのどちらかのビルドツールがよく使われている。 Makefile.PLは名前の通りMakefileを生成してくれる君で、Build.PLはModule::Buildを前提としたシステムになっている。 詳細はモダンPerlの世界へようこそ 第23回 Module::Build:MakeMakerの後継者を目指してが詳しい。

今回は読みたいコードがList::Utilで、List::UtilはMakefile.PLを使っていたのでこちらの雰囲気を見る。

最終的にMakefileを生成しにいくのはExtUtils::MakeMakerが提供しているWriteMakefileを実行するタイミングである。 List::UtilのMakefile.PLの場合は一番最後の行でこのように実行している。

WriteMakefile(%params);

この引数%paramsにはビルドに関する様々な情報を入れる事ができる。 ExUtils::MakeMakerのドキュメントを見ると、XSの最適化の度合いを調整出来るOPTIMIZEがオプションとして存在している。 ドキュメントを見る限りCの最適化オプションをそのまま渡せるので、デバッグしたかったら-O0 -gあたりを渡せばよいだろう。

ということでList::Utilのparamsを作っている箇所の一番下にしれっとOPTIMIZEを指定する。

my %params = (
  NAME         => q[List::Util],
  ABSTRACT     => q[Common Scalar and List utility subroutines],
  AUTHOR       => q[Graham Barr <gbarr@cpan.org>],
  DEFINE       => $defines,
  DISTNAME     => q[Scalar-List-Utils],
  VERSION_FROM => 'lib/List/Util.pm',
  OPTIMIZE    => '-g -O0',

準備は整ったのでビルドしていく。まずはMakefileを生成する必要があるので、Makefile.PLを実行する。

❯ perl Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for List::Util
Writing MYMETA.yml and MYMETA.json

無事Makefileができたので素朴にmakeする

❯ make
cp lib/Scalar/Util.pm blib/lib/Scalar/Util.pm
cp lib/List/Util.pm blib/lib/List/Util.pm
cp lib/Sub/Util.pm blib/lib/Sub/Util.pm
cp neko.pl blib/lib/List/neko.pl
cp lib/List/Util/XS.pm blib/lib/List/Util/XS.pm
Running Mkbootstrap for Util ()
chmod 644 "Util.bs"
"/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0" -MExtUtils::Command::MM -e 'cp_nonempty' -- Util.bs blib/arch/auto/List/Util/Util.bs 644
"/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0" "/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/ExtUtils/xsubpp"  -typemap '/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/ExtUtils/typemap'  ListUtil.xs > ListUtil.xsc
mv ListUtil.xsc ListUtil.c
cc -c   -fno-common -DPERL_DARWIN -mmacosx-version-min=11.2 -fno-strict-aliasing -pipe -fstack-protector-strong -DPERL_USE_SAFE_PUTENV -g -O0   -DVERSION=\"1.56\" -DXS_VERSION=\"1.56\"  "-I/Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/darwin-2level/CORE"  -DPERL_EXT -DUSE_PPPORT_H ListUtil.c
rm -f blib/arch/auto/List/Util/Util.bundle
cc  -mmacosx-version-min=11.2 -bundle -undefined dynamic_lookup -fstack-protector-strong  ListUtil.o  -o blib/arch/auto/List/Util/Util.bundle  \
              \

chmod 755 blib/arch/auto/List/Util/Util.bundle
Manifying 4 pod documents

無事makeが実行できた。 ちなみにmake cleanとするとMakefileごとビルドしたものを消してくれるので便利。

デバッグビルドしたXSをPerlから呼び出す

ビルドするともとのxsのコードを純粋なC言語*1に変換されたものなどが生成される。 実際にPerlインタプリタが読みこめるファイル形式のものはblib以下に書き出される。

blibディレクトリはMakeMakerがビルドしたファイルが含まれている。 List::Utilの場合は次のようなファイルが含まれている。

❯ tree blib
blib
├── arch
│   └── auto
│       └── List
│           └── Util
│               └── Util.bundle
├── bin
├── lib
│   ├── List
│   │   ├── Util
│   │   │   └── XS.pm
│   │   └── Util.pm
│   ├── Scalar
│   │   └── Util.pm
│   ├── Sub
│   │   └── Util.pm
│   └── auto
│       └── List
│           └── Util
├── man1
├── man3
│   ├── List::Util.3
│   ├── List::Util::XS.3
│   ├── Scalar::Util.3
│   └── Sub::Util.3
└── script

16 directories, 9 files

ちなみにUtil.bundleの場合はバイナリファイルだったりする。

普通のPerlのライブラリの場合はlib以下のものを@INCに入れれば実行できた。 似た感じでblib以下のものをPerlに教えてあげれば自分でビルドしたXSをPerlインタプリタから呼び出す事ができる。

blibをPerlスクリプトから呼び出すにはblibモジュールを使う方法もあるけれど、勝手にカレントのblibディレクトリ以下を読み込んでくれるExtUtils::testlibを使うのが便利。

というわけで自分でビルドしたList::Utilを呼び出すサンプルコードは次のようになる。今回はめんどくさいのでビルドしたディレクトリ上でやっている。

use strict;
use warnings;
use ExtUtils::testlib;
use List::Util qw/all/;

my $hoge = [1,2,3];

if (all { $_ } @$hoge) {
    print "hello!\n";
}

xsのデバッグ実行

さて例題も書けたのでデバッグしていこう。 一点ポイントとなるのは、このblibを実行できるのは、XSをビルドする際に利用したPerlじゃなくと実行ができない。

今回では上のログにある通り/Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0が実行したPerlのバイナリである。 これ以外のPerlのバイナリ、例えば/usr/bin/perlで実行すると以下のような感じでモジュールロードに失敗する。

❯ /usr/bin/perl neko.pl
Can't load '/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle' for module List::Util: dlopen(/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle, 0x0001): symbol '_PL_DBsub' not found, expected in flat namespace by '/Users/anatofuz/src/github.com/Dual-Life/Scalar-List-Utils/blib/arch/auto/List/Util/Util.bundle' at /System/Library/Perl/5.30/darwin-thread-multi-2level/DynaLoader.pm line 197.
 at neko.pl line 4.
Compilation failed in require at neko.pl line 4.
BEGIN failed--compilation aborted at neko.pl line 4.

ビルド時に使ったPerlだと問題なく実行できる。

❯ /Users/anatofuz/.plenv/versions/5.32.0/bin/perl5.32.0 neko.pl
hello!

ではデバッガをかけて実行してみる。今回はlldbでやる。

❯ lldb -- /Users/anatofuz/.plenv/versions/5.32.0/bin/perl neko.pl
(lldb) target create "/Users/anatofuz/.plenv/versions/5.32.0/bin/perl"
Current executable set to '/Users/anatofuz/.plenv/versions/5.32.0/bin/perl' (arm64).
(lldb) settings set -- target.run-args  "neko.pl"
(lldb)

今回デバッグしたいxsのファイルはListUtil.xsで、使っているallのコードを見てみたい。 allは実はanyのエイリアスで、any関数は702行目から本体がある

というわけでListUtil.xsの702行目にbreak pointを貼ってみる。

(lldb) b ListUtil.xs:702
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.

そして実行すると見事に止まる。

(lldb) process launch
Process 69006 launched: '/Users/anatofuz/.plenv/versions/5.32.0/bin/perl' (arm64)
1 location added to breakpoint 1
Process 69006 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00000001004ae5a0 Util.bundle`XS_List__Util_any(cv=0x00000001010fd810) at ListUtil.xs:702:22
   699  PROTOTYPE: &@
   700  PPCODE:
   701  {
-> 702      int ret_true = !(ix & 2); /* return true at end of loop for none/all; false for any/notall */
   703      int invert   =  (ix & 1); /* invert block test for all/notall */
   704      GV *gv;
   705      HV *stash;
Target 0: (perl) stopped.

これで止まるとこっちのものなので、あとはnextしたり値をprintしたりして確かめていくと普通に読んでいける。 読み方としてはPerlインタプリタ読んでるのと同じなので、SVの中身に思いを馳せる感じになっていくと思う。

というわけでXSも実はlldbで読める!!! 読んでいくぞ!!!

*1:といってもPerlのマクロがバチバチにあたっている