blog.garicchi.me



[Azure] Managed IdentityとWorkload Identity Feferationで別ディレクトリにあるリソースにアクセスする

azure

Azureで、ディレクトリAにあるリソース(blob storageなど)をディレクトリBにあるアプリ(app serviceなど)から参照したいということがあります

この時、認証をどうするかが課題となりますが、 オーソドックスなやり方だと、下記のようになるかなと思います

  1. アクセスしたいリソースのあるテナントAにアプリ登録でアプリケーションを作る
  2. 作ったアプリケーションをアクセスしたいリソースにロール割り当てをする
  3. アプリケーションでクライアントシークレットを発行する
  4. そのクライアントシークレットを使って、ディレクトリBにあるサーバーから、リソースのAPIを呼び出す

この場合、2点の課題が存在します

  1. 長期間有効なシークレットを発行することになり、シークレットが漏洩する危険がある
  2. シークレットの有効期限の上限が2年間であり、2年ごとにシークレットを更新する作業が発生する

managed identityとworkload identity federation

近年のAzureは長期間有効なシークレットは使用せず、 短時間の間のみ有効な一時トークンを認証サーバーに発行してもらい、 それを使ってリソースにアクセスするというのがメジャーかと思います。

これをAzureディレクトリ内のリソース同士で容易に使用できるようにするために、 managed identityという仕組みが存在します。

さらに、クラウド間(別の認証サーバー間)で同じようなことを実現するために workload identity federationというものが存在します。

このように、managed identityとworkload identity federationを使用すれば、 長期間有効なシークレットを発行せずとも、サーバー間の認証が可能になります。

本稿では、managed identityとworkload identity federationを利用して 異なるAzureディレクトリ間で、2年ごとにローテーションする必要のない、 Entra IDベースの認証の検証結果を記載します

構築例

今回、例として下図のような構成を作ります

1

まず、説明をわかりやすくするために、APIを呼び出す側のサーバーが所属するディレクトリをDirectory Appと呼ぶことにします。 今回は、APIを呼び出す側のサーバーとして、Azure App Serviceを使用しますが、managed identityに対応していれば他の物でも大丈夫だと思います。

次に、APIを呼び出されるリソースが所属する側のディレクトリをDirectory Resourceと呼ぶことにします。 今回はStorage Accountとしましたが、大抵のAPIを呼び出せるAzureリソースなら大丈夫だと思います。

Directory Appにはユーザー割り当てのManaged IDを作り、App Serviceにアサインします。

システム割り当てのManaged IDではダメのか?

公式ドキュメントに下記の記載があるため、ユーザー割り当てのManaged IDでないと認証が通りません。 システム割り当てのManaged IDも試しましたが、エラーになりました。

Only user-assigned managed identities can be used as a federated credential for apps. system-assigned identities aren’t supported. https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity?tabs=microsoft-entra-admin-center#important-considerations-and-restrictions

Read more...

ChatGPTを使った英単語暗記法を模索している

最近ChatGPTと一緒に何かを学ぶことを試しています。

その中でも、英単語の暗記に関して、 結構面白い使い方ができるなと思ったので、紹介します。

英単語の覚え方

経験則にはなりますが、英単語を覚えるとき、 下記2つの視点をもつことが有用と自分は思っています。

  • 1 反復記憶
    • 英単語を繰り返し暗記する
    • 忘れている英単語ほど何度も繰り返せるとよい
  • 2 エピソード記憶
    • 英単語にイメージを植え付けて覚える
    • 今の生活で使っているイメージから覚えたり、ほかの単語から関連させて覚えるなど

1の反復記憶に関しては、単語カードと同じ要領で、 知らない単語にマークを付けて、それを集中的に覚える ということをひたすら繰り返せばよさそうです。

それに関して、ChatGPTをうまく使う方法が思い浮かばなかったので、Quizletなどの既存サービスを使うのがよいかと思います。

非英語圏でエピソード記憶をどうするか

2のエピソード記憶をどうするかを考えます。

英語圏で生活できていれば、普通に生活しているだけで英単語を使う機会が多いので イメージやエピソードが頭に残ると思いますが、 残念ながら私の身の回りでは英語を使う機会はあまり多くありません。

英語圏ではない場所で、エピソード記憶をするにはどうすればよいかを考えます。

今までの経験上、下記3パターンの時、エピソード記憶で英単語を覚えやすかった気がします。

  • 2-1 同じ語源を持つ単語との関連性を見つけた時
    • 例えばpresumeはassume(仮定する)と似ていますが、preがついているので という意味があると推察でき、assumeよりも前の単語というイメージを作れば、 推定 という意味を覚えることができます
    • presumeの意味を忘れたとしても、assumeと似ているというところから暗記することができます
  • 2-2 生活していたら、偶然その単語を使用した文章や経験があった時
    • 街中で偶然見かけた英文に、覚えようと思っていた単語が入っていた時などです
    • 例えばenzyme(酵素)は、薬局にあるコエンザイムQ10という商品から暗記することができます
  • 2-3 自分で英文を作らないといけないとき、その単語を使用する機会があった時
    • 英語圏で外国人と話していると、頭の中で英文を生成する過程で、イメージが植え付けられます

このうち、2-3 の英文生成に関しては、自分の経験上、継続が難しいと思っています。 なぜなら、英文を必要としない非英語圏で、英文を生成する習慣をつけるのは(例えば英文日記など)経験上続かなかったからです。

ほんとは、英単語を覚えるとき、2-3の方法で英文を10個ぐらい自分で考えれば、暗記に有用だとは思います。(しかし継続が難しい)

そこで、今回は2-1と2-2に関して、ChatGPTによる解決を試みます。

語源をChatGPTに聞く

自分は英単語が覚えられないとき、語源を調べて語源から暗記を試みます。 これがChatGPTで効率化できそうな気がします。

{覚えたい英単語}の語源と同じ語源を持つ単語の解説 という風に聞いてみます。

1

これで、presumeはconsumeと同じ語源を持つことがわかり、consumeはかなりなじみの深い単語なので 関係性を覚えることができると思います。

英文記事を生成してもらう

2-2の覚えたい英単語が含まれた文章を読む に関しては、なかなかその時覚えたい単語を含む文章にピンポイントに巡り合える機会は少ないと思います。

そこでChatGPTに英語記事を生成してもらって、それを読むことにします。

{覚えたい英単語}を含む英文記事を生成 その他の単語は簡単なものを使用 という風に聞いてみます。

この時重要なのは、覚えたい英単語以外を簡単な単語にすることです。でなければ覚えたい英単語以外にさらにわからない単語が増えて本来の目的を見失います。

2

これを読めば、英文を読む練習になるし、覚えたい英単語へのイメージがつきやすい可能性があります。

おわりに

自分もまだChatGPTを使って英単語を覚える方法を始めたばかりなので模索状態です。 別のいい方法を見つけたらまた共有します。

複数のcsprojがある環境でdocker buildを高速化する

docker dotnet

Dockerfileを書く時、先にパッケージ定義ファイルだけコピーし、 パッケージのインストール、その後、ソースコードをコピーするというテクニックがあります。

pythonだとベストプラクティスにもあるとおり、以下のようになります。

COPY requirements.txt /tmp/
RUN pip install /tmp/requirements.txt
COPY . /tmp/

https://docs.docker.jp/develop/develop-images/dockerfile_best-practices.html

こうすることで、ソースコードを変更したあとの2回目以降のdocker buildは パッケージインストールの部分がレイヤキャッシュされ、スキップされます 結果、高速にビルドすることができます。

これをしない場合、毎回ソースコードを変更してビルドするたびにパッケージのインストールが走るので開発効率が落ちます。

pythonやnode.jsなどはパッケージ定義ファイルが基本的に1つなので、 これが楽にできるのですが、 dotnetの場合はcsprojごとに使用するパッケージが記載されるので 先にすべてのcsprojをコピーしてdotnet restoreをしなければいけません

しかしプロジェクトにかかわるすべてのcsprojをコピーする命令をDockerfileに記載するのはなかなか難しいです。

Central Package Managementを使う

MSBuildには、各csprojが依存するパッケージバージョンを1つのファイルで管理する機能があります。 https://devblogs.microsoft.com/nuget/introducing-central-package-management/

Directory.Packages.propsというファイルに依存パッケージを定義し、 以下のようなディレクトリ構成にします

TestProj1にDockerfileがあり、このプロジェクトをdockerでbuildすることとします。

.
├── Directory.Build.props
├── Directory.Packages.props
├── DotnetDocker.sln
├── TestProj1
│   ├── Dockerfile
│   ├── Program.cs
│   └── TestProj1.csproj
└── TestProj2
    ├── Program.cs
    └── TestProj2.csproj

ダミーcsprojをDockerfile内で作る

その後、TestProj1のDockerfileには以下のように記載をします。

FROM mcr.microsoft.com/dotnet/sdk:8.0

COPY ./Directory.Packages.props ./Directory.Build.props /local/

WORKDIR /local/

# csprojを自動生成してdotnet restore
RUN echo '<Project Sdk="Microsoft.NET.Sdk"><ItemGroup>' > restore.csproj
RUN grep "<PackageVersion Include=" Directory.Packages.props | sed -r "s/<PackageVersion Include=/<PackageReference Include=/g" | sed -r "s/Version=[^ ]+//g" >> restore.csproj
RUN echo '</ItemGroup></Project>' >> restore.csproj

RUN dotnet restore

# ソースコードを変更してもここまではキャッシュされる

COPY . /local/

WORKDIR /local/TestProj1

RUN dotnet build

ポイントは、Directory.*.propsをコピーした後、ダミーのcsprojを作って、 全てのパッケージを自動で記載しているところです。

Read more...

dotnet coreでRazorテンプレートからテキストを生成する

csharp

goでいう text/templateのように、 テンプレートから、オブジェクトを動的に当てはめてテキストを生成したいということがあります。

dotnetでいうと、ASP.NETがRazorテンプレートを使っているので、 似たようなことができそうですが、ASP.NET以外でRazorを使うのは少し難易度が高いです。

dotnet framework時代はRazorEngineというOSSがあったようで、これを使えばテンプレートから文字列生成をできたようです。

しかしRazorEngineはGithubの更新が7年前で、メンテナーを探していると公式サイトに書いてあり、nugetのパッケージはdotnet framework向けで、さらにセキュリティ警告が出ているようです。 自分の環境で試したところでは、dotnet coreのプロジェクトで動きませんでした。

おそらく、Roslynの登場で代替できるようになったからかと思うのですが、 Roslynで似たようなことをする例があまりなかったのでサンプルコードを作ってみました。

namespace Razor
{
    public abstract class TemplateBase
    {
        protected dynamic Model { get; set; } = default!;
        private StringBuilder StringBuilder = new();

        public void SetModel(dynamic model)
        {
            this.Model = model;
        }

        public void WriteLiteral(string literal)
        {
            StringBuilder.Append(literal);
        }

        public void Write(object obj)
        {
            StringBuilder.Append(obj.ToString());
        }

        public string GetGeneratedText()
        {
            return StringBuilder.ToString();
        }

        public virtual async Task ExecuteAsync()
        {
            await Task.Yield();
        }
    }
}

public record class RazorCompileResult
    {
        public required IEnumerable<Diagnostic> Diagnostics { get; init; }
        public required string? GeneratedText { get; init; }

        public bool IsSuccess => !Diagnostics.Any(x => x.Severity is DiagnosticSeverity.Error);
    }
    public class RazorTemplateCompileService
    {
        public static readonly HashSet<string> ReferencedAssemblies = new()
        {
            "System.Private.CoreLib",
            "System.Runtime",
            "Microsoft.CSharp"
        };

        private List<PortableExecutableReference> MetadataReferences { get; }

        private RazorProjectEngine Engine { get; }

        public RazorTemplateCompileService()
        {
            var metadatas = ReferencedAssemblies.Select(x => MetadataReference.CreateFromFile(Assembly.Load(x).Location)).ToList();
            metadatas.Add(MetadataReference.CreateFromFile(typeof(Razor.TemplateBase).GetTypeInfo().Assembly.Location));
            metadatas.Add(MetadataReference.CreateFromFile(typeof(DynamicObject).Assembly.Location));
            MetadataReferences = metadatas;
            var defaultConfig = RazorConfiguration.Default;
            var razorConfig = RazorConfiguration.Create(
                RazorLanguageVersion.Version_6_0,
                defaultConfig.ConfigurationName,
                defaultConfig.Extensions,
                defaultConfig.UseConsolidatedMvcViews);
            this.Engine = RazorProjectEngine.Create(razorConfig, RazorProjectFileSystem.Create("."), builder =>
            {
                builder.SetCSharpLanguageVersion(LanguageVersion.CSharp10);
            });
        }
        public async Task<RazorCompileResult> CompileAsync(string template, dynamic model)
        {
            var codeDoc = Engine.Process(RazorSourceDocument.Create(template, "myfile", Encoding.UTF8), null,
                new List<RazorSourceDocument>(),
                new List<TagHelperDescriptor>());
            var generatedCode = codeDoc.GetCSharpDocument().GeneratedCode;
            generatedCode = generatedCode.Replace("public class Template", "public class Template : TemplateBase");
            var tree = CSharpSyntaxTree.ParseText(generatedCode);

            var compilation = CSharpCompilation.Create("myassembly", new[] { tree }, this.MetadataReferences,
            options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            using var memStream = new MemoryStream();
            var compileResult = compilation.Emit(memStream);
            if (!compileResult.Success)
            {
                return new RazorCompileResult
                {
                    Diagnostics = compileResult.Diagnostics,
                    GeneratedText = null
                };
            }
            memStream.Seek(0, SeekOrigin.Begin);
            var assembly = AssemblyLoadContext.Default.LoadFromStream(memStream);
            var instance = assembly.CreateInstance("Razor.Template");
            var templateClass = assembly.GetType("Razor.Template");
            ArgumentNullException.ThrowIfNull(templateClass);
            var methodSetModel = templateClass.GetMember(nameof(TemplateBase.SetModel)).First() as MethodInfo;
            ArgumentNullException.ThrowIfNull(methodSetModel);
            methodSetModel.Invoke(instance, [model]);
            var methodExecute = templateClass.GetMember(nameof(TemplateBase.ExecuteAsync)).First() as MethodInfo;
            ArgumentNullException.ThrowIfNull(methodExecute);
            var task = methodExecute.Invoke(instance, null) as Task;
            ArgumentNullException.ThrowIfNull(task);
            await task;

            var methodGetGeneratedText = templateClass.GetMember(nameof(TemplateBase.GetGeneratedText)).First() as MethodInfo;
            ArgumentNullException.ThrowIfNull(methodGetGeneratedText);
            var resultStr = methodGetGeneratedText.Invoke(instance, null) as string;
            ArgumentNullException.ThrowIfNull(resultStr);
            return new RazorCompileResult
            {
                Diagnostics = compileResult.Diagnostics,
                GeneratedText = resultStr
            };
        }
    }

Rsolynとdynamic型を使用するので、下記パッケージが必要です

Read more...

Powershellでgit branch表示高速版

powershell

前回、Powershellでgitブランチとazure subsciptionを表示したわけですが、コマンドを利用しているせいで、Powershellのプロファイルロードに1秒ぐらいかかっていたので高速化しました。

gitブランチ情報もazure subscriptionも、ファイル上に記載されているのでそれを読み込みます。

function prompt {
    $ESC = [char]27
    if (test-path .git\HEAD -pathtype leaf) { 
        $BRANCH= "[$ESC[43mgit:$($(get-content .git/HEAD).split("/") | select-object -last 1)$ESC[0m]"
    }

    if (test-path ~/.azure/azureProfile.json -pathtype leaf) { 
        $AZ = "[$ESC[46maz: $($(get-content -raw ~\.azure\azureProfile.json | convertfrom-json | select-object -expandproperty subscriptions | where-object isDefault | select-object -expandproperty name).Substring(0, 9)) $ESC[0m]"
    }

    Write-Output "${PWD} ${AZ} ${BRANCH}> "
}

powershellはデフォルトでjsonを扱えて素敵ですね

Powershellを使いやすくしたい

powershell

最近はWSLでずっと生活しているのですが、 結局書いているコードはdotnet(C#)で、 ssh接続もあまりしなくなり、 Linux特有のこともしなくなってきました。

ので、自分の開発範囲では、IDEがあれば実はどのOSもそんなに開発体験はかわらないのでは と思い始めました。

ただ、ターミナルはEmacsキーバインド + 履歴補完 + git branch表示は譲れないので これをpowershellで実現できたらそれらしく生活できる気がします。

長らくPowershellに苦手意識もあったので、これを機にPowershellの環境整備をしてみます。

Windows Powershellを捨てる

Powershellの環境整備、それはWindows Powershellを捨てるところから始めます。 なぜかというと、Windows11に標準で搭載されているPowershellは 正式にはWindows Powershellで、メジャーバージョンは5系です。

最新のPowershell (Windowsに依存しなくなったやつ?)のメジャーバージョンは7系なので アップグレード、というよりPowershellのインストールをしましょう。

ここら辺を見て、wingetとかでPowershellをインストールします

https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.4

インストールして再ログインとかしてると、Windows Terminalに Windows PowershellではないPowershellが出てくるので、 今後はこれを使うこととします。

Profileをいじる

bashでいう、 ~/.bash_profile と同じようにpowershellにもprofileが存在するのでそれを作ります。

ただし、ホームディレクトリではなく、 $profile 変数に格納されているところにprofileがあります。

Write-Host $profile

ここに以下のprofileを書き込みましょう。

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
Set-PSReadlineOption -EditMode Emacs
Set-PSReadLineOption -BellStyle None
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView

profileでexecution policyをRemoteSignedにしてあげることでbashと同じように、 ローカルで書かれたスクリプトはデジタル署名なしで実行できるようになります。 ただし、怪しいスクリプトを実行するのはよくないので自己責任でお願いします。

-EditModeをEmacsにすると念願のEmacs Keybindが手に入ります。

あとは -PredictionSourdce Historyとかをすると、履歴補完ができるようになります。

Promptをいじる

これで履歴補完とemacs keybindは手に入りました。 次にプロンプトをいじって、git branchを表示しましょう。

Read more...

Denoをシェルスクリプトとして使う

deno,typescript

プロジェクトの開発環境を整えていると、どうしても複雑なコマンドを利用する場面が存在します。

例えばdocker composeを利用したローカルサーバーの起動、CLIコマンドによる自動生成、開発環境へのデプロイコマンドなどがあります。

しかし、これらのコマンドをオプションまですべて暗記して普段使いをするのは難しく、 シェルスクリプトなどを書いて使いやすい形にすることがあると思います。

自分はbashのスクリプトを普段書くことが多いのですが、 開発メンバー全員がLinux環境であることは少なく、WindowsやMacのユーザーが多いと思います。

特にWindowsではbashスクリプトを実行するためには、git bashやmsys2などをインストールしなければいけません。 WSL2もありますが、これはWindows側のファイルシステムでスクリプトを実行すると、耐えられないぐらい遅いので WindowsのユーザーにWSL2側のファイルシステムで開発をすることを強いることになります。

Macではbashのスクリプトがある程度動くと思いますが、sedなど、BSD系とlinux系の細かなオプションの違いなどに困る時があります。

Denoという選択肢

そこで、goやpythonなど、OSの違いをランタイムで吸収してくれるような言語でコマンドランナーを記載したいところです。 自分の周りではWebフロントエンド開発しているメンバーも多くいるので、node.jsであればメンテもしていけそうです。

しかしシェルスクリプトといえど型は欲しくなるので、TypeScriptを使用するとよさそうです。

そこで、denoがよいのではないかと思い始めました。

denoは、TypeScriptをJavaScriptにトランスパイルしなくてもTypeScriptのまま動きます。(ts-nodeなどが不要) また、npm installをしなくても、モジュールがキャッシュされていなければ、実行時に自動でダウンロードしてくれます。

denoをインストールしなければいけないという負担はありますが、 windowsでもwingetで提供されているし、気楽にインストールできそうな雰囲気があります。

Denoでコマンドライン引数を処理する

CLIツールを作るので、コマンドライン引数を処理したいところです。

denoではstdにコマンドライン引数を処理するモジュールがあります。 https://docs.deno.com/examples/command-line-arguments

しかし、ヘルプの生成ができなかったり、機能が足りていないところがあります。

そこで、サードパーティですが cliffy を導入してみることにします。 denolandでも、Extremely Popularとなっているのである程度信頼できそうです。

詳しい使い方はドキュメントを読んでもらうとして、簡単にサブコマンドを実装する例だと以下になります。

import { Command, HelpCommand } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts";

await new Command()
  .name("main")
  .default("help")
  .command(
    "test",
    new Command()
      .option("-t, --test [test:string]", "test option", {
        required: true
      })
      .action(options => {
        console.log(options.test);
      }))
  .command("help", new HelpCommand().global())
  .parse(Deno.args);

Denoでサブプロセスを呼び出す

シェルスクリプトして実行したいので、サブプロセスを飛び出して連携をすることをしたいです。

サブプロセスを呼び出す簡単な例は以下です。

const c = new Deno.Command("cat", {
  args: ["README.md"],
  stdout: 'piped',
  stderr: 'piped'
});
const p = c.spawn();

const stdout = p.stdout.pipeTo(Deno.stdout.writable, { preventClose: true });
const stderr = p.stderr.pipeTo(Deno.stderr.writable, { preventClose: true });
const status = p.status;
const result = await Promise.all([status, stdout, stderr]);
if (!result[0].success) {
  throw new Error(JSON.stringify(result[0]));
}

これで、サブプロセスとして cat README.md を呼び出し、 stdout, stderrをターミナルに表示し、 プロセスが終わるまで待機、終わった後エラーステータスなら例外をthrowしてくれます。

Read more...

SQL ServerとPostgreSQLでロストアップデートの挙動を調査する

azure mssql postgresql

DBMSを利用したシステムを作っていると、 同じレコードを複数のシステムが同時に更新しようとしたとき、 うまく排他制御や競合解決をしなければ更新データが紛失する可能性があります。

例えば2つのアプリから同時に1つのレコードにある値をインクリメントしたい場合、 Amountの初期値が0であれば、2つのアプリから1回ずつインクリメントしたので 結果はAmount = 2になるはずですが、 トランザクション分離レベルが Read Committedであれば、下図のようにAmount = 1になってしまいます。

alt

このように複数のシステムから同時に更新を行ったときに、更新データが消失してしまうことをロストアップデートと呼びます。

これがもし注文システムであり、Amount = 在庫数であったならば、 在庫数が最後の1個で、複数のユーザーが同時に同じ商品を購入したとき、 在庫数が足りないにもかかわらず、両方のユーザーが注文できてしまうことになります。

こういうことを防止するために、DBMSには排他制御や競合解決という概念がありますが、 DBMSによって挙動が異なったり、結構理解していないところがあったので、挙動を調査してみます。

ロック挙動の調べ方

ロックの挙動を調べるために、select、update、commitの操作、各状態でのロック情報の表示 などを実現する必要があります。

まず、テーブルを作ります。 下記ではSQL Serverを例として示します。

create table dbo.Items
(
    Id     int identity constraint PK_Items primary key,
    Amount int default 0 not null
)

そして、下記のことができるdotnetのconsoleアプリを作ってターミナルを2つ同時に立ち上げて 確認することとします。 DBMSとの接続はEntity Framework Coreを使用することとします。

  • トランザクションの開始 (DatabaseFacade.BeginTransactionAsyncを使用)
  • selectクエリで1行取得
  • Amount++
  • updateクエリで1行更新
  • commit

↑を2つのターミナル(tx1、tx2とする)から同時に実行

  • Amountの更新が消失していないか調査
    • 正しく排他制御ができているならAmount = 2になるはず

各ランタイムやパッケージのバージョンは以下です。

  • dotnet core: 8.0.4
  • EntityFrameworkCore: 8.0.4
  • SQL Server: 2019
  • PostgreSQL: 16

なお、トランザクション分離レベルについては、Read CommittedとSerialziedについて調査することとします。

SQL Server、Read Committed、tx2がtx1のupdateよりも前にselectする場合

DBMSはSQL Server、Isolation LevelはRead Committedで、 tx1がselectした後、updateをする前にtx2がselectした場合の挙動を確認してみます。

Read more...

WSLgでグラフィックが壊れる場合がある

wsl

WSLgでGUIアプリを起動していると、 画像のグラフィックが崩壊しているときがあります。

これはPCによって再現するものとしないものがありました。

調べているとどうもGPUサポートに問題がありそうでした

https://github.com/microsoft/wslg/issues/1148

そこで、windows側の ~/.wslconfig に下記を書いて再起動すれば解決しました。

[wsl2]
gpuSupport=false

WSLでfcitx5をsystemdで起動する

wsl

WSLgでGUIアプリを起動するとき、imとしてfcitx5を起動するようにしています。

今まではログインシェルのprofileで起動していましたが、systemdで起動したくなったので unitファイルの例を示します。

まず、環境変数を設定しないといけません。 unitファイルでも環境変数を設定できますが、 自分の環境だとうまく動かなかったので、environment.dに配置します。

mkdir -p ~/.config/environment.d/

cat << _EOS_ > ~/.config/environment.d/im.conf
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"
_EOS_

次にこのようなunitファイルを作ります。 WantsとAfterはもしかしたらいらないかもしれません 自分の環境はwaylandをdisableしているので --disable=wayland をつけています

[Unit]
Description=fcitx5
Wants=default.target
After=default.target

[Service]
ExecStart=/usr/bin/fcitx5 --disable=wayland
Restart=on-failure

[Install]
WantedBy=default.target

あとはユーザー権限でインストールします。 root権限だと、HOME環境変数が設定されていないので起動に失敗します

systemctl --user enable fcitx.service
systemctl --user start fcitx.service
1 of 2 Next Page