Upgrade to Pro — share decks privately, control downloads, hide ads and more …

nuget-server - あなたが必要だったNuGetサーバー

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

nuget-server - あなたが必要だったNuGetサーバー

第11回 Center CLR 勉強会 登壇発表: https://centerclr.connpass.com/event/380921/

.NETのパッケージ"NuGet"を配布するサーバーを一から実装したので、その成果について共有します。

NuGetサーバーは公式のnuget.orgの他に、OSSやプロプライエタリ製品によるサーバー実装があります。これらのソフトウェアを使わずに自分で実装した背景や、nuget-serverの構造やdocker imageの構築などを解説します。

Avatar for Kouji Matsui

Kouji Matsui PRO

February 27, 2026
Tweet

More Decks by Kouji Matsui

Other Decks in Programming

Transcript

  1. Kouji Matsui (https://www.kekyo.net/) Kouji Matsui • kekyo, けきょ , kozy_kekyo

    というクレジットを使うことがあります。 • 愛知県の付近のモグリで引き籠モラー。 • 自転車乗りです(最近忙しすぎて乗れてない) • Center CLR 主催しています。 • Ubuntu/Debian 使い。バックエンドシステム・言語処理系とそのツール チェイン・ライブラリインターフェイス設計、などが主戦場 / 興味の中心。 • 赤い色は、某 WP 1520 由来。 今ではマイカラー。燃える赤 🔥
  2. Kouji Matsui (https://www.kekyo.net/) NuGet って ? • .NET のライブラリパッケージの名称 •

    .NET 以外のファイルの配布も一応可能(極めてマイナー。忘れていい) • 拡張子 *.nupkg 及び *.snupkg (え?) • ローカルディレクトリからインポートも不可能ではないけど、基本的には 「 NuGet サーバー」から参照して取得する • .NET CLI, Rider, Visual Studio などから参照できる
  3. Kouji Matsui (https://www.kekyo.net/) nuget.org • NuGet 公式の公開サーバー。 • .NET の標準パッケージリポジトリ。誰でも使用できる。

    • 基本的に OSS パッケージの公開を行うところ(実は他人に見えて良いなら OSS でなくても良い、はず) • 取り扱いに神経使う(失敗したらとか)
  4. Kouji Matsui (https://www.kekyo.net/) プライベート NuGet サーバー • BaGet (https://loic-sharma.github.io/BaGet/) •

    ぐぐるの中の人。 • 割と歴史は古い。昔試したことがあるけどその時は運用までやらなかっ た。 • 最近はあまりメンテナンスされてなくて、 亜種が出たりしている。
  5. Kouji Matsui (https://www.kekyo.net/) プライベート NuGet サーバー • NuGet.Server (https://www.nuget.org/packages/NuGet.Server) •

    公式の実装らしいけど、全くメンテナンスされていない。 • NuGet.Server を nuget.org から取得しろって、意味がわからなかったけ ど、どうやらこれを使って、自分でサーバー を実装しろ(略 • .NET Framework なのでお察し • これを改良した実装も、もしかしたら あるのかもしれないけど、探索する気が。
  6. Kouji Matsui (https://www.kekyo.net/) プライベート NuGet サーバー • Nexus3 (https://www.sonatype.com/products/sonatype-nexus-repository) •

    売り物だけど、フリー利用モードもあり。 • 大変高機能で、 NuGet 以外のパッケージリポジトリも同時にサポートでき る。特に maven (Java) は数少ない認められたサーバーらしく、あまり選択 肢がないことから、非常に良く使用されている(らしい) • これをまあまあ長い期間運用していた (現在も NuGet 以外で使用中)
  7. Kouji Matsui (https://www.kekyo.net/) クラウドベース NuGet サーバー • MyGet (https://www.myget.org/) •

    これも歴史が古く、 GitHub と Azure がやり始めるまでは、クラウドベー スでほぼ独占していた。 • OSS プロジェクトで無償利用できていたが、 Azure が提供し始めた頃か ら ... • それから殆ど見てなかったので 現在はどうなのかは不明だけど、 トップページは垢抜けた感じになってた。
  8. Kouji Matsui (https://www.kekyo.net/) クラウドベース NuGet サーバー • GitHub package registry

    (https://docs.github.com/en/packages/working-with-a-github-packages-registry ) • よく知られたパッケージレジストリ。無償枠もある(容量制限あり)。 • 管理画面は一切存在しない。大事なことなのでもう一度 : 管理画面は一切存在しない。 CLI でどうぞ。 • ちな GitLab はパッケージ一覧は 見れます。
  9. Kouji Matsui (https://www.kekyo.net/) クラウドベース NuGet サーバー • ほか、 ProGet, TeamCity,

    Gitea などあるようです。 • どれもこれもめんどくさそう ... (失礼)
  10. Kouji Matsui (https://www.kekyo.net/) 困ったこと • OSS 開発とお仕事で非公開のパッケージの取り扱いを行う必要が あって、どうやってホストするかが問題だった。 – OSS

    開発 : 実験的プロジェクトのパッケージを nuget.org に登録 するのは流石に気が引ける。 – お仕事のパッケージは当然公開できないので、見えないところで パッケージを配布できなければならない。
  11. Kouji Matsui (https://www.kekyo.net/) 困ったこと • クラウドベースだとカネがかかる。 – 当初は MyGet を使いたかったが、予算の折り合いがつかなかっ

    た(この時点で結構な量あった) • クラウドベースではない場合、 NuGet サーバーを立ち上げること自 体に割と負担がある。 – ローカルサーバー費用(まあこれはなんとかできる) – 運用保守作業(普段からやってないと、いざという時にすっかり 忘れてる問題)
  12. Kouji Matsui (https://www.kekyo.net/) 困ったこと • 再び BaGet を試したがうまく動かなかったので、 Nexus3 を使用し

    た。問題なく起動したが、管理コストがキツイ ... – オーバースペック(細かいユーザー管理・権限設定・パッケージ プロキシ・煩雑な UI ) – データベース管理(バックアップ・更新マイグレーション作業)
  13. Kouji Matsui (https://www.kekyo.net/) 作るチャンスでは? • つまり、こういうやつだ : – ローカルでサクッと起動できる –

    データベースを一切使わない。ファイル管理だけで実現する – パッケージの一覧ぐらいは見れる – ユーザーの管理が(出来れば)ベター • 下手に高機能にしないで、細かいこと言わず、ゴールを絞り込めば、 十分射程にある気がする。 UI がめんどくさそう?
  14. Kouji Matsui (https://www.kekyo.net/) 作るチャンスでは? • 特に、お仕事方面で、マジでライブラリ管理がヤヴァイ方向に流れそ うだったので、今のうちに何とかしないと、後で火の粉が降り掛かっ てきそうだった、と言う闇があって ?「 DLL

    (アセンブリ)ファイルを手でコピーしてだな ... 」 ぼく「(今 2020 年台っすよ ... )」 • Nexus3 のあるバージョンでデータベースマイグレーションに失敗し て、壊れなかったけどそこからバージョンを上げられなくなってし まって詰んだ ...
  15. Kouji Matsui (https://www.kekyo.net/) 特徴 • ベタな名前で “ nuget-server” (まだパッケージが無いぞ。しめしめ ...

    ) • TypeScript (Node.js) で作る • NuGet V3 API にのみ準拠( V2 API はサポートしない) • 管理画面は React+React MUI で実装。イケてる感じにしたい。 • パッケージは単純なディレクトリ構造で管理。データベースは絶対に 使わない( SQLite も不可)。だからスケールアウトとか不要。 • ユーザー管理によるアクセス制限は実装。 Basic 認証・ API キー • Docker イメージを作ってコンテナ運用も可能にする
  16. Kouji Matsui (https://www.kekyo.net/) 特徴 • TypeScript (Node.js) で作ること – 技術面

    :NuGet サーバーは、 .NET アセンブリの読み取りを行う 必要はない。 nuspec ファイル (XML) を読めれば良いの で、 .NET で作る必要性はない。 – しかし、最大の理由は 2013 年ごろの経験まで遡る ...
  17. Kouji Matsui (https://www.kekyo.net/) 特徴 • その頃、 ASP.NET (net4.0) の有償セミナー講師をやってた。 •

    すでに ASP.NET では、 WebForms と MVC と Razor が JavaScript エコシステムと整合出来なくて酷いことが静かに進 行していた。どうやってこれをスムーズに学習して貰うかに腐 心していた ... • 覚えているだろうか。この頃ウェブサーバーを起動すると言え ば、 IIS か IIS Express を構成するという、非常にしんどい作業 が待っていた。
  18. Kouji Matsui (https://www.kekyo.net/) 特徴 • この時、初めて NPM を触ったわけだが ... •

    スクショが現在のターミナルで 申し訳ないが、 おわかりいただけたであろうか $ npm i -g http-server $ http-server -p 1234
  19. Kouji Matsui (https://www.kekyo.net/) 特徴 • この時の私の気持ちを 40 字以内で述べよ。 • 正確なことを言えば、

    Windows+.NET Framework の話なの で、今は多少改善されているけど、現代でもこの「ポータビリ ティ」は覆せていない。 • 目的を達成できるなら、手段はシンプルな方が良い。 この時、逆らい難いこの現実が、私のマインドの奥深くに刻み 込まれた。 何なら、 npx 使えば 1 ライナーですよ ...
  20. Kouji Matsui (https://www.kekyo.net/) 特徴 • 有言実行 $ npm i -g

    nuget-server $ nuget-server -p 1234 本当にこれだけです。 Node.js がインストールされていれば、 ゼロコンフィグで起動します! あなたの端末でも動くはず!
  21. Kouji Matsui (https://www.kekyo.net/) 特徴 • `nuget-server` だけで起動可能。 – 最小構成で良ければゼロコンフィグ –

    匿名アクセス – デフォルトポート番号 (5963) – もちろんパッケージファイルは そのままディレクトリに格納 – データベースファイルなし – 事前要求は Node.js のみ ( バージョン 20.18.0 以上)
  22. Kouji Matsui (https://www.kekyo.net/) デモ • 認証機能(これで十分) モード パッケージ読み取り パッケージ登録 備考

    `none` 匿名可能 匿名可能 完全匿名モード。個人運用向け。 殆ど管理の手間いらず。 `publish` 匿名可能 認証必要 標準的なプライベート運用、ある いは限定された組織運用(公開あ り)。 `full` 認証必要 認証必要 必ずログインが必要。
  23. Kouji Matsui (https://www.kekyo.net/) デモ • 認証モードを変更するため、 config.json に `authMode` を定義する。

    • nuget-server を再起動する。 全体的な配置。 何度も言うけどデータ ベースは無い。 ファイルとしてこれだけ
  24. Kouji Matsui (https://www.kekyo.net/) その他の機能 • API パスワード発行・管理機能 • ユーザー登録・管理機能 •

    別の NuGet サーバーからのパッケージインポート機能 ( 既存のサーバーの移行 ) • リバースプロキシサポート ( エンドポイントの公開 ) • Docker イメージで実行 詳しくはリポジトリのドキュメントを参照
  25. Kouji Matsui (https://www.kekyo.net/) 内部構造 • TypeScript – prettier-max, screw-up, dayjs,

    adm-zip, xml2js • ブラウザサイド – React (UI フレームワーク ) – React MUI, react-infinite-scroll, notistack, typed-message • サーバーランタイム : Node.js 20.18.0+ – Fastify (Node.js ウェブサーバーライブラリ ) – Passport (Fastify 向け認証処理ライブラリ ) – commander, zxcvbn
  26. Kouji Matsui (https://www.kekyo.net/) 内部構造 パッケージ 内容 prettier-max Prettier を自動適用可能にする Vite

    プラグイン screw-up ビルドやパッケージに Git タグのバージョンを自動適用する Vite プラグイン dayjs 有名な日時操作ライブラリ adm-zip zip ファイル操作ライブラリ( NuGet が zip 形式なので) xml2js XML 読み取り (nuspec)
  27. Kouji Matsui (https://www.kekyo.net/) 内部構造 パッケージ 内容 React React React MUI

    React で Modern UI を簡単に実装できる ( ナビバー・メニュー・アコーディオン・サイドパネル・プログレ スバー・ボタンなど ) react-infinite-scroll React で無限スクロールを実装できる notistack React で通知表示 ( 操作完了結果など ) typed-message メッセージ多国語化 ( 型付きシンボル )
  28. Kouji Matsui (https://www.kekyo.net/) 内部構造 パッケージ 内容 Fastify Node.js ウェブサーバー (

    軽量・高速・拡張性 ) 内部構造は割とすっぴんの ASP.NET Core に似てる Passport Fastify で認証ハンドリングを行うことが出来る (Basic 認証・ OAuth2 ・ OpenID など。 Node.js 20.18.0+ が必要 ) zxcvbn パスワード強度チェックライブラリ commander Node.js コマンドラインパーサー
  29. Kouji Matsui (https://www.kekyo.net/) 内部構造 • TypeScript(JavaScript) でウェブサーバーと言えば、 http-server でも使われている express

    が有名。 • 当初はこれを使おうとしていたけど、 TS 全般の事情にまだ疎かった 事もあり(今でも)、認証周りの問題を解決出来なかったの で、 Fastify に移行した。 • Fastify で Passport を使う場合に、 Node.js のバージョンが 20.18.0 以上を要求されるので、これが下限となった。
  30. Kouji Matsui (https://www.kekyo.net/) 内部構造 • Fastify は、ルーティング処理とハンドラでリクエストをさばける点 で ASP.NET Core

    の生の操作と似ている。 • ミドルウェアとしてパイプラインを挟むのと同じような、プラグイン アーキテクチャ構成を取ることが出来る(構造は異なる)。但し今回 は使っていない(そこまで規模が大きくない)。 • JSON Schema でのバリデーションが売りらしいけど、今回は使って いない(対象が NuGet V3 プロトコルの一部だけなので)。
  31. Kouji Matsui (https://www.kekyo.net/) 内部構造 • UI セッションと NuGet V3 API

    で、認証周りのハンドリングは異な る : – UI セッションは、セッション維持による認証検査( @fastify/ secure-session, @fastify/passport )。ログイン画面を必要とす る。 – NuGet V3 API は純粋な API アクセスなので、 API パスワードによ る「 Basic 認証」(えっ?と思った人は沼に浸かった人)
  32. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (1):

    – パッケージアップロード時に、 Content-Type: multipart/form-data を 必要とする。 – 仕様自体は問題ないが、 MFD を正しく通せないゲートウェイがある ( Cloudflare tunnel で発生。非常に悩んだ ... ) – どうやら、 *.nupkg と *.snupkg (シンボルパッケージ)を同時にアッ プロードできることを趣旨にしようとしていたようだが、仕様自体未完 だし、もはや ... – V2 API は application/octet-stream だったらしい。
  33. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (1):

    – 雰囲気的に MFD は鬼門と思えたので、 nuget-server でパッケージ をアップロードする場合は素直に application/octet-stream で送信 させることにした( V3 未準拠・ V2 を踏襲したわけではない) – 表面的には、 dotnet nuget push コマンドが使えない。 代わりに curl 使ってね(簡単に送信出来て、むしろ便利)
  34. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (1):

    – NuGet V3 API で dotnet nuget push を実現する場合、 API キーは独自 の HTTP リクエストヘッダ `X-NuGet-ApiKey` を使用する必要がある (何でだよ) – しかし、もはや application/octet-stream で送信すると決めたので、普 通に Basic 認証でやればいい :
  35. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (1):

    – 個人開発者や小規模チームは nuget-server を UI で使用するだろ う : パッケージのアップロードも基本的に UI から行うだろうか ら、ここは手厚くモダン UI で便利にする。 – 一方で、 CI からパッケージを自動登録する場合も、 donet nuget push が使用できないことの制約は無い。 CI スクリプトで curl 使うだけだし、パスワードは CI 変数として注入されるのだか ら、条件はイーブンのはずだ。 熟考したこと
  36. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (1):

    – .NET 使用者にとっては、小さいが学習コストとなる。が、私でも dotnet nuget コマンド使用するときはいちいち調べてる(殆ど使 わないし面倒に感じてるし覚える気がない)ので、やはりイーブ ンだ。 熟考したこと
  37. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (2):

    – パッケージ ID のリストを取得する API で、仕様上は、存在しな いパッケージ ID を要求された場合に 404 を返すことになってい るが、これをやるとクライアントが正しくパッケージを取得でき ない事がある。 – クライアントがマルチパッケージソースを構成している場合(普 通 nuget.org+ プライベート n 箇所と思われる)に、どれかの サーバーが 404 を返すと、そこで取得を諦める(何でだよ)
  38. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (2):

    – ものすごい重箱の角を突くような条件の場合を考慮してこうなっ ているらしい(めちゃくちゃ悩んだ 2 回目なのでもうスルー) – nuget-server では、 404 の代わりに「空のリスト」を返すこと で、クライアントが取得を諦めないようにした。従って、厳密に は V3 API 準拠違反。嫌なら config.json で回避できるようにして おいた。 – なおこのハックは Nexus3 でも使われているらしい ... (仕様とは
  39. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (3):

    – これは V3 API というより Visual Studio が悪いのだが ... – NuGet V3 API は最初にサービスインデックスという、サポートして いる V3 API と URL エンドポイントのリストを返すのだが、 VS はこ の URL を見てない(えぇ)。もしかするとこのインデックスすら見 てない ... – それで、 VS は固定の URL パスを使用してブラインドアクセスして いる(らしい) – dotnet CLI や Rider は問題ない ...
  40. Kouji Matsui (https://www.kekyo.net/) 内部構造 • NuGet V3 API の闇 (3):

    – もう VS 使ってないからよくわからんけど、 nuget.org が返すイ ンデックスの構造にわかる範囲で合わせておいた。 – 本来は、もっと柔軟にパスを決定できるはずだが、サービスイン デックスで全ての仕様を決定出来るわけではないので、これで本 当にあってるのか不明 ...
  41. Kouji Matsui (https://www.kekyo.net/) 内部構造 • Docker イメージ (https://hub.docker.com/r/kekyo/nuget-server/tags) – amd64/arm64

    それぞれでイメージを提供 (RPi とか ARM 系で動かせる ところを狙った ) – ビルドは podman で行う。ホストは amd64 で、 arm64 はクロスビル ドする。 Radxa ROCK 5B(arm64/aarch64):
  42. Kouji Matsui (https://www.kekyo.net/) 内部構造 • クロスビルドの事前準備は、 binfmt_misc ドライバ , qemu

    コンテナ – sudo modprobe binfmt_misc – sudo podman run --rm --privileged docker.io/multiarch/qemu-user- static --reset -p yes • コンテナのクロス環境は、上記の準備で内部で自動的に qemu を使用して ターゲット環境を用意するので、まるでそのアーキテクチャで実行している かのようにコンテナを実行できる(便利だ ... もちろん遅い)。
  43. Kouji Matsui (https://www.kekyo.net/) 内部構造 • 準備出来れば、 --platform で指定するだけ。 • ホスト実行

    podman run --rm -it --platform linux/amd64 docker.io/library/alpine:latest • クロス実行 podman run --rm -it --platform linux/arm64 docker.io/library/alpine:latest • もちろん、ターゲットイメージがそのプラットフォームイメージを含ん でいる必要がある。
  44. Kouji Matsui (https://www.kekyo.net/) 内部構造 • Docker alpine イメージはサイズが小さくて良いが、 C ランタイムライブラ

    リが glibc ではなく musl 。 – すると、 glibc 前提でコンパイルされているネイティブライブラリが ロードに失敗する場合がある。 – ネイティブライブラリなら Node.js と関係ないと思うかもしれないが ... @fastify/secure-session --> sodium-native(libsodium) --> glibc • A modern, portable, easy to use crypto library: https://github.com/jedisct1/libsodium – .NET で言うところのネイティブ DLL 同梱パッケージみたいな。
  45. Kouji Matsui (https://www.kekyo.net/) 内部構造 • なので、 Docker イメージビルド時に、 sodium-native を

    alpine 環 境でソースコードからビルドして生成している。 更に、 sodium-native 5.x 系は別の問題があるようなので、 4.3.3 (最終)に固定。 • nuget-server ではこの経路以外の問題のある glibc 依存はなさそう。 他のプロジェクトで alpine 使う場合は注意が必要(保守が面倒くさ くなったら alpine やめて debian 化すると思う)
  46. Kouji Matsui (https://www.kekyo.net/) 内部構造 • マルチ対応イメージをデプロイするには、「マニフェスト」を作る必要が ある podman build --platform

    linux/amd64,linux/arm64 --manifest nuget- server:1.0.0 ... • 確認 podman manifest inspect nuget-server:1.0.0 • push podman manifest push --all nuget-server:1.0.0 docker.io/kekyo/nuget- server:1.0.0
  47. Kouji Matsui (https://www.kekyo.net/) 反響 • 某所に垂れ流したら、案の定「何で .NET で実装しないんだ」とかあれやこれ や ...

    – .NET で書いたら(つまり ASP.NET Core と Blazor って事ですか?)、 1 ヶ 月で実装出来ない自信があります。特に UI 周り。そもそも、想定していた ポータビリティが実現できない。 – このプロジェクト自体、最初に説明したように、きっかけは後向きだったの で、あまり手間を掛けたくなかったという背景があります。結果的には、細 かいところを拘ったので、かなりのボリュームにはなりましたが ... (このことをフォローしてくれている人も居たので、心的ダメージは少な かったけど、正直もういいやって思った)
  48. Kouji Matsui (https://www.kekyo.net/) 反響 – 指摘の一つに、 .NET で実装しないとメタデータを拾えない、みたいなのが あったけど、それは .NET

    アセンブリのメタデータを取得できないという話。だ けど、 nuget-server にそれ必要? NuGet Package Explorer のようなものを実装したいなら、 .NET アセンブリを 読み取る能力が必要だけど、 nupkg は zip だし、 nuspec に XML で必要な情報 が書かれているので、パッケージを配信するだけなら .NET でやる必要性は全く 無いです(専門外だけど Go でも Py でも Ruby でも出来ると思う)。 – 全般的に、 .NET で書かなかったのは前向きな選択の結果で、そのことによる問 題は全く発生しませんでした。割とまとまった開発事例になったので、ご自身の プロジェクトでの選択の参考にしてもらえれば嬉しいです。
  49. Kouji Matsui (https://www.kekyo.net/) 質疑応答 • nuget-server 、真のポータビリティを実現できたと思ってます。便利 なので使ってくれたら嬉しいです。 • 自分で

    NuGet サーバーを実装しよう、とは思わないほうがいいです (もしやるなら、もっとクールなやつを)