その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

GNU Goldのリンカがdeprecatedになる予定だそうなので暫定的にcgoでGNU ldの利用を明示する

www.phoronix.com

ということで、GNU Goldのリンカがdeprecatedになる予定だそうです。

arm64環境のgo *1 でcgoを利用しようとするとデフォルトでgoldのリンカを利用しようとするので、これをGNU ldを使うようにしたいという話です。かつてのGNU ldにはshow stopperとなるバグが存在していましたが、少なくともバージョン2.35以降のGNU ldであれば利用しても良い状況となっているという理解です。
この裏にあるテーマとしてはAmazon Linux 2023だとgoldのbinutilsがシュッと入らず、GNU ldはあるけどgold ldが無いとcgoを使うツールのビルドでfailするという問題があり……このためにいちいちfedoraのrepoを追加するのも面倒なので。

[FYI] goldのldが無いとこのようにコケる:

$ go install github.com/golangci/golangci-lint/cmd/[email protected]
# github.com/golangci/golangci-lint/cmd/golangci-lint
/usr/local/go/pkg/tool/linux_arm64/link: running gcc failed: exit status 1
/usr/bin/gcc -Wl,-z,now -Wl,-z,nocopyreloc -fuse-ld=gold -Wl,--build-id=0x7c06cf55d9e31c7aa2dab2e997ce40f506533556 -o $WORK/b001/exe/a.out -rdynamic /tmp/go-link-748591831/go.o /tmp/go-link-748591831/000000.o /tmp/go-link-748591831/000001.o /tmp/go-link-748591831/000002.o /tmp/go-link-748591831/000003.o /tmp/go-link-748591831/000004.o /tmp/go-link-748591831/000005.o /tmp/go-link-748591831/000006.o /tmp/go-link-748591831/000007.o /tmp/go-link-748591831/000008.o /tmp/go-link-748591831/000009.o /tmp/go-link-748591831/000010.o /tmp/go-link-748591831/000011.o /tmp/go-link-748591831/000012.o /tmp/go-link-748591831/000013.o /tmp/go-link-748591831/000014.o /tmp/go-link-748591831/000015.o /tmp/go-link-748591831/000016.o /tmp/go-link-748591831/000017.o /tmp/go-link-748591831/000018.o /tmp/go-link-748591831/000019.o /tmp/go-link-748591831/000020.o /tmp/go-link-748591831/000021.o /tmp/go-link-748591831/000022.o /tmp/go-link-748591831/000023.o /tmp/go-link-748591831/000024.o -O2 -g -lresolv -O2 -g -ldl -O2 -g -lpthread -O2 -g
collect2: fatal error: cannot find 'ld'

というわけでGNU ldを使うようにするには、

  • -ldflags="-extldflags=-fuse-ld=bfd" をオプションとして指定する (e.g. go install -ldflags="-extldflags=-fuse-ld=bfd" github.com/golangci/golangci-lint/cmd/[email protected])
  • CGO_LDFLAGS="-fuse-ld=bfd" を環境変数として指定する

という2つの方法があり、用途に応じて使い分けると良さそうに思いました。我々のユースケースではdocker containerとして環境を作っているのでcontainerの構築時に環境変数を指定する感じで運用することとしています。

なおこのあたりは処理系のほうでも議論が成されているようで、

github.com

https://go-review.googlesource.com/c/go/+/391115

このパッチが入るとこのようなワークアラウンドは不要になるのではないかと思います。

*1:もしかしたらarm64以外もそうかも?

所属変更のお知らせ

2024年6月1日より下記の通り所属が変更されます。

旧: SB Intuitions株式会社(ソフトバンク株式会社からの100%出向)
新: 株式会社スマートバンク

前回の所属変更からわずか2ヶ月しか経っておらず非常に気まずい状況ですが解雇ではありません。色々ありました。前職在職期間中、コードらしいコードは1行も書いていません。お察しください。

新しい環境であるところのスマートバンクでは今度こそプロダクト(B/43など)に根ざしたソフトウェアエンジニアとして活動する予定です。
奇しくも何の因果か、新しい会社も略称がSBです。面白いですね。

よろしくお願いします!

所属変更のお知らせ

2024年4月1日より id:moznion の所属が以下の通り変更となります。

旧: 株式会社ソラコム
新: ソフトバンク株式会社 (SB Intuitions株式会社出向)

以上となります。
引き続きよろしくお願い申し上げます。



ソラコムには大体6年半くらいいて、実際数えきれないほどたくさんのものを作り、たくさんのものを直し、たくさんのとりくみをしました。なおかつ最後の2年はUSのシアトル駐在で働いていたということもあり非常に貴重な体験となりました。あと在米中にCTO Technical Advisorという迫力のあるタイトルがついたりもしました。
僕がソラコムに入った日はちょうどKDDIがソラコムを買収した2017年9月1日で、そして先日2024年3月26日にソラコムがIPOを成し、ちょうどそのタイミングで退職するということとなり、つまり上場と共に去る男と相成りました。これはソラコムの第二章を一通り見たと言っても過言ではない。良かったですね。
ソラコムに入った時はテレコム技術はもちろん、そもそもコンピュータネットワークも正味よくわかってない状態だったので、入社当時は「普通のWebアプリケーション」みたいな仕事をしていたのに、まさかそこからテレコムコアネットワークに関わる仕事をガッツリすることになるとは思いませんでしたね。しかしいまだにテレコム、というよりも3GPPはいまいちよくわからん。じつに大変な世界ですね。
ソラコムの皆様、長らくの間ありがとうございました。これからの発展を祈っております。

SB Intuitionsではいわゆる「国産LLM」の基礎モデルを作ったり、それを応用したアプリケーションを作成したり、その他にも必要なことは全部やるソフトウェアエンジニアとしてやっていく予定です。つまりテレコムとは無関係なので直接競合ではござません。ご安心ください。
しかしどんどんWebアプリのキャリアから離れていっている感じがあり趣がありますね。頑張ります。応援よろしくお願いします。

GitHub Actionsで複数のworkflowを連携する時にハマったあれこれ

GitHub Actionsで或るworkflowが終わったら別のworkflowを動かす、みたいなことをやりたくなった時にちまちまハマったのでメモとして残します。

workflow_run で呼び出されたworkflowで前workflowのステータスを取る

これはハマったことというか「こうやれば良い」という話なんですが、

on:
  workflow_run:
    workflows:
      - check
    types:
      - completed

jobs:
  ...

みたいに書いておくと check workflowが完了した時にこのworkflowが呼び出されるのですが、この時 check の成否は関係無く呼び出されます。
従って、check が完了した時にのみ実行したい時には以下のようにステータスチェックを入れる必要があります。

on:
  workflow_run:
    workflows:
      - check
    types:
      - completed

jobs:
  do_something:
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      ...

ref: Events that trigger workflows - GitHub Docs

それはそうとして completed typeだけじゃなくて succeeded や failed みたいなtypeを workflow_run で指定できると嬉しいような気はするのですが......

workflow_run はdefault branchに存在していないと動かない

Events that trigger workflows - GitHub Docs

Note: This event will only trigger a workflow run if the workflow file is on the default branch.

公式ドキュメントにめっちゃ目立つように書いてある!!
workflowを整備するブランチで作業してて「おかしいな〜動かないな〜」と思っていたらこういう話だったというオチ。default branchに workflow_run を使うworkflowをpushしておかなければ駄目。

workflow_run で呼び出されるworkflow中では github.ref_type は常に branch になる

1つ前の話題にも通ずるところですが、

Events that trigger workflows - GitHub Docs

このドキュメントにもあるように `GITHUB_REF` はdefault branchとなっているため github.ref_type は常に branch となります。

on:
  workflow_run:
    workflows:
      - check
    types:
      - completed

jobs:
  do_something:
    if: ${{ github.ref_type == 'tag' }}
    steps:
      ...

上のように書いていると、仮に元のworkflow (この場合は check) がtagのpushによってトリガーされていたとしても github.ref_type は branch となるため絶対に do_something は実行されません。これはちょっとハマった......

workflow_call で呼び出されたworkflowからはrepositoryに登録されているsecretsを直接参照できない
on:
  workflow_call:

jobs:
  do_something:
    runs-on: "ubuntu-latest"
    steps:
    - env:
        PASSWORD: ${{ secrets.PASSWORD }}
      run: |
        echo ${{ env.PASSWORD }}
  ...

今度は workflow_run ではなく workflow_call の話題。なぜなら workflow_run では「tagがpushされた時だけなんかやる」が実現できなかったため......
workflow_call により、他のworkflowから明示的に呼び出されたworkflowはrepositoryに登録されているsecretsを直接参照することができないため、上記のechoは何も表示しません。

on:
  workflow_call:
    secrets:
      PASSWORD:
        required: true

jobs:
  do_something:
    runs-on: "ubuntu-latest"
    steps:
    - env:
        PASSWORD: ${{ secrets.PASSWORD }}
      run: |
        echo ${{ env.PASSWORD }}
  ...

上記のように workflow_call.secrets で受け取る変数を明示しておいて、

  call-workflow:
    uses: ./.github/workflows/next.yml
    secrets:
      PASSWORD: ${{ secrets.PASSWORD }}

のような感じで呼び元のworkflowから適切に渡してあげる必要があります。

ref: Reusing workflows - GitHub Enterprise Cloud Docs




というわけで、これらを総合した「tagがpushされ、なおかつ元のworkflow (この場合は check job) が成功した時にrepoに登録されたsecretsを渡しながら別のworkflowを呼び出す」という定義を書きたい時は元のworkflowでは

on:
  push:
jobs:
  check:
    ...
  call-workflow:
    needs: [check]
    if: ${{ github.ref_type == 'tag' }}
    uses: ./.github/workflows/next.yml
    secrets:
      PASSWORD: ${{ secrets.PASSWORD }}

と書いておきつつ、呼ばれるworkflowでは

on:
  workflow_call:
    secrets:
      PASSWORD:
        required: true

jobs:
  do_something:
    ...

というふうに書いておけば目的を満足できることがわかりました。これがやりたかった......

Linuxでdocker loginした際、passを使ってcredentialsを管理することで情報をファイルに残さないようにする

docker login するとデフォルトの状態では $HOME/.docker/config.json の .auths にcredentialsが保存されます。credentialsが平文で保存されるというのもナンなのでこれをパスワードマネージャを介すように変更しましょう。

パスワードマネージャには手っ取り早くインストールできるpassを利用します。インストール方法はオフィシャルページにある通りですが、Ubuntuだと

$ sudo apt install pass

で完了します。

そして以下のようにpassの初期化を行ないます。

$ gpg --generate-key
# ^ 出力された40桁のpublic keyのIDを控えておく
$ pass init $PUBLIC_KEY_ID

これでpassの準備は完了です。


このpassをdockerのcredentials storeとして利用することで、credentialsを平文で保存する代わりにpassで管理できるようになります。そのためにdocker-credential-passを利用する必要があるのでReleasesページから動くバイナリを取ってきましょう。このへん、package managerでインストールできるようになってると嬉しいですね。

そしてその取ってきた docker-credential-pass のバイナリをパスが通ってる場所に置き、

{
  "auths": {
    ...
  },
  "credsStore": "pass"
}

上記のように、$HOME/.docker/config.json 中に "credsStore": "pass" というように credsStore を指定すると、dockerのcredentialsがpassで管理されるようになります。


注意としては、このdockerの config.json はユーザーに紐付く設定なので、rootless dockerを利用していない限りはrootの設定 (つまり /root/.docker/config.json) に書いておく必要があります (逆にrootless dockerを使っているのであれば利用する個ユーザーで設定する必要がある)。そして docker-credential-pass についてもrootから呼べる場所に置いておく必要がありますから気を付けましょう。

NetworkManager 1.36においてL2TP/IPSecのトンネルが上手く張れない問題

NetworkManagerのversion 1.36においてL2TP/IPSecのトンネルを張ろうとすると上手く張れないという問題があります。
自分が使用しているUbuntu 22.04ではL2TP接続は標準では提供されていないのでnetwork-manager-l2tpパッケージをapt等でインストールする必要がありますが、この時に一緒に使用するNetworkManagerのバージョンによって当該問題が発生します。ちなみにNetworkManagerのバージョンは nmcli --version 等で参照可能です。
Ubuntu 22.04のpackage managerで利用可能なNetworkManagerのバージョンは1.36系であるため *1 だいたいこの問題を踏むことになると思われます。


症状としては network-manager-l2tp をインストールした上でL2TP/IPSec接続を試行すると以下のようなエラーメッセージが表示された上でどことも通信できなくなり、最終的にトンネル接続の確立がキャンセルされるというものが見られます。

NetworkManager[1134025]: xl2tpd[1134025]: handle_control: bad control packet!
NetworkManager[1134025]: xl2tpd[1134025]: network_thread: bad packet

L2TPやIPSecの設定に問題があるのかと思って色々と試してみたのですが改善せず、色々調べたところNetworkManagerの開発repositoryのissueに行き着きました:

これは既知の問題であるようで、

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1406609

このコメントで示されているように、トンネルのために作成されるpppインターフェイスが「よくわからないIP」を持っていることに依るようです。
例示されているppp0の状態を借りて説明すると、

43: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 172.16.100.10 peer 63.2.5.44/32 scope global ppp0
       valid_lft forever preferred_lft forever
    inet 172.16.100.10/32 scope global noprefixroute ppp0
       valid_lft forever preferred_lft forever

トンネルの宛先 63.2.5.44/32 に対して 172.16.100.10 がpeerされていることが問題のようです。
というわけでトンネル確立試行中に ip a del 172.16.100.10 peer 63.2.5.44 dev ppp0 などとしてその不正なpeerを削除してあげると、正常にトンネルが張られて通信できるようになります。


めでたしめでたし......と行きたいところですが、このままではトンネルを張るたびに手動でpeerの削除コマンドを打たなければならないのでそれは面倒です。そもそもこの削除コマンドはトンネル確立処理中に打たなければならず、適切なタイミングでの実行が求められて大層面倒です。自動化しましょう。

NetworkManagerには発生するイベントに応じてスクリプトが呼ばれるという仕組みがあるのでそれを利用します。
/etc/network/if-up.d に実行可能なスクリプトを置いておくとインターフェイスがupした時に呼ばれるので、

#!/bin/bash

set -euo pipefail

if [[ "$IFACE" =~ ^ppp[0-9]+ ]]; then
  invalid_addr_json=$(ip -j addr show dev "$IFACE" | jq -r '.[0].addr_info[] | select(.scope == "global" and .address != null)')
  sudo ip a del "$(echo "$invalid_addr_json" | jq -r .local)" \
    peer "$(echo "$invalid_addr_json" | jq -r .address)/$(echo "$invalid_addr_json" | jq -r .prefixlen)" \
    dev "$IFACE"
fi

というようなスクリプトを 999-remove-invalid-peer-on-ppp-if みたいな名前で /etc/network/if-up.d 配下に置いておくと、L2TPトンネル確立の際に自動的に不正なpeerが除去されるので正常にトンネルが張れるようになります。jqはインストールしておきましょう。
ただし、他の用途でpppインターフェイスを利用する際にこのスクリプトが有効になっていると、意図せず動作をおかしくしてしまう可能性があるのでその点についてはご留意ください。
なおこの時、このスクリプトに実行可能なpermissionを与えていないと上手く動かないので注意が必要です。chmod 755 などとして与えましょう。

めでたしめでたし。それはそうとして ip コマンドの結果をJSONで出力する -j オプションは便利でよいですね。


ちなみに

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1725575

このコメントにあるように、NetworkManager 1.40では直っている問題のようです。アップグレードできるならアップグレードしたほうが良いでしょう。Ubuntu 22.04のpackage managerにも入ってほしいですが、ちょっと難しそうなのでUbuntu 24.04を待つしかなさそうな予感がしています。Ubuntu 23.04ではNwtworkManager 1.42を利用しているので *2 24.04では修正版が使えるようになることを期待しています。

Linux DesktopでIntelliJ IDEA 2023.1を4Kディスプレイ上で表示すると色々デカくなって困る

IntelliJ IDEAを2023.1にアップグレードして4Kディスプレイ上で起動したところ、スケールの設定がおかしいのかすべてがデカくなり、相対的に作業領域が狭くなって困っています。

たぶんLinux Desktopでだけ発生している問題のようで、同じような症状が既にレポートされていました。

https://youtrack.jetbrains.com/issue/JBR-5316/Wrong-large-IDE-scaling-on-4k-Screen

ひとまずのWorkaroundとしては起動オプションに-Dsun.java2d.uiScale=1.0を付けると良いとのこと。

直った (ように見える)!! よかったよかった。