<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>VLLM · 仁才徳</title>
    <link>https://jared.lynskey.co.nz/ja/tags/vllm/</link>
    <description>ソフトウェアエンジニアリングと開発組織のリーダーシップについて。評価、採用とチームの拡大、CI/CD、DevOps モニタリング、エージェンティック AI。ソウルより。</description>
    <generator>Hugo</generator>
    <language>ja</language>
    <copyright>© 2026 仁才徳</copyright>
    <lastBuildDate>Fri, 30 Oct 2026 18:20:44 +0900</lastBuildDate>
    <atom:link href="https://jared.lynskey.co.nz/ja/tags/vllm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>vLLM と Ollama の比較：ローカルLLMを用途でどう使い分けるか</title>
      <link>https://jared.lynskey.co.nz/ja/posts/2026/2026-10-30-vllm-vs-ollama/</link>
      <guid isPermaLink="true">https://jared.lynskey.co.nz/ja/posts/2026/2026-10-30-vllm-vs-ollama/</guid>
      <pubDate>Fri, 30 Oct 2026 00:00:00 +1300</pubDate>
      <dc:creator>Jared Lynskey</dc:creator>
      <category>vLLM</category>
      <category>Ollama</category>
      <category>LLM</category>
      <category>Self-Hosted</category>
      <category>MLOps</category>
      <category>DevOps</category>
      <category>Docker</category>
      <category>Kubernetes</category>
      <description>vLLM と Ollama を両方使う立場からの比較です。一人で使うなら Ollama、GPU サーバーで大量のリクエストをさばくなら vLLM。ローカルLLMの導入、Docker・Kubernetes 構成、ハマりどころまで。</description>
      <content:encoded><![CDATA[<p>一人で、あるいは一台のマシン上のひとつのアプリで、数分のうちにモデルを動かしたいなら Ollama を使ってください。GPU サーバーが大量のリクエストを同時にさばく必要があり、スループットが大事なら vLLM です。Ollama はモデルランナーです。量子化されたモデルを取ってきて、頼まれたときにロードし、使い終われば手放します。vLLM は推論サーバーです。起動時に GPU の大半を確保し、処理中のリクエストをすべてバッチにまとめて GPU を遊ばせません。どちらも OpenAI API を話すので、呼び出す側のコードはまったく同じにできます。</p>
<p>私はどちらも毎日使っています。Ollama はラップトップとオフィスの GPU ワークステーションで動いていて、開発と自分用のツールに使っています。vLLM はフリーランスの案件で、96 GB の GPU 一枚からチャットモデルを配信しています。最初は Docker Compose で、今は小さな Kubernetes クラスタの上です。この記事では、それぞれが何に向いているか、どうインストールするか、そして vLLM 側で時間を取られたことを書きます。</p>

<h2 class="relative group">vLLM と Ollama の比較表
    <div id="vllm-と-ollama-の比較表" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vllm-%e3%81%a8-ollama-%e3%81%ae%e6%af%94%e8%bc%83%e8%a1%a8" aria-label="アンカー">#</a>
    </span>
    
</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Ollama</th>
					<th>vLLM</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>向いている用途</td>
					<td>一人で使う: ラップトップ、開発マシン、個人用のツール</td>
					<td>多数のユーザーや忙しいアプリ、GPU サーバー上で</td>
			</tr>
			<tr>
					<td>動く環境</td>
					<td>macOS、Linux、Windows。Apple silicon、NVIDIA、AMD、または CPU</td>
					<td>NVIDIA GPU を積んだ Linux がいちばん楽。ほかのハードウェアでも手間をかければ動く</td>
			</tr>
			<tr>
					<td>モデル</td>
					<td>独自ライブラリの量子化済み GGUF(<code>qwen3:8b</code>)</td>
					<td>Hugging Face の重み。BF16、FP8、AWQ、GPTQ(<code>Qwen/Qwen3-8B</code>)</td>
			</tr>
			<tr>
					<td>インストール</td>
					<td>インストーラーを入れて <code>ollama run</code></td>
					<td><code>pip install vllm</code>、または Docker イメージ <code>vllm/vllm-openai</code></td>
			</tr>
			<tr>
					<td>GPU メモリ</td>
					<td>初回利用時にモデルをロードし、5 分間使われなければアンロード</td>
					<td>起動時にカードの一定割合(デフォルトで 90% 超)を確保し、そのまま保持</td>
			</tr>
			<tr>
					<td>同時リクエスト</td>
					<td>並列スロットがいくつか</td>
					<td>連続バッチ処理(continuous batching)。これが本領</td>
			</tr>
			<tr>
					<td>起動</td>
					<td>小さなモデルなら数秒</td>
					<td>大きなモデルなら数分</td>
			</tr>
			<tr>
					<td>LoRA アダプター</td>
					<td>Modelfile で新しいモデルに焼き込む</td>
					<td>サーバーひとつに複数載せ、リクエストごとに選ぶ</td>
			</tr>
			<tr>
					<td>API</td>
					<td>独自 API に加えて、<code>:11434/v1</code> で OpenAI 互換</td>
					<td><code>:8000/v1</code> で OpenAI 互換</td>
			</tr>
	</tbody>
</table>

<h2 class="relative group">Ollama とは
    <div id="ollama-とは" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ollama-%e3%81%a8%e3%81%af" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>自分のマシンでモデルに答えさせる方法として、Ollama は私の知る限りいちばん簡単です。llama.cpp から生まれたもので、量子化された GGUF モデルを動かします。8B のモデルが約 5 GB に収まり、MacBook でも快適に動くのはそのためです。モデルを名前で pull すると、最初に呼び出したときにロードされ、5 分間何もすることがなければメモリから外れます。なので、ほかに動かしているものと行儀よくマシンを分け合ってくれます。</p>
<p>その行儀の良さがトレードオフです。Ollama は一度に一人を相手にする前提で作られています。いくつかのリクエストを並列に処理することはできますが、並列スロットひとつごとにメモリを食いますし、何十人ものユーザーを一枚の GPU にバッチでまとめるためのものでは、もともとありません。</p>

<h2 class="relative group">vLLM とは
    <div id="vllm-とは" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vllm-%e3%81%a8%e3%81%af" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>vLLM はサービングエンジンで、まさにそのバッチ処理のために作られています。それを支えているのは主に二つのアイデアです。PagedAttention は、リクエストごとの KV キャッシュ(モデルがトークンを読み書きしながら積み上げていく attention の状態)を小さな固定サイズのブロックに格納するので、パディングや断片化で GPU メモリが無駄になりません。連続バッチ処理では、新しいリクエストが今のバッチの終わりを待たずに、生成ステップの合間にバッチへ加わります。この二つのおかげで、vLLM を動かした GPU 一枚は、同じ GPU でリクエストを一件ずつ処理するよりはるかに多くの同時ユーザーをさばけます。</p>
<p>代償として、vLLM はカードを自分のものだと思っているかのように振る舞います。起動時に GPU メモリの取り分を確保し、重みに要らない分を KV キャッシュで埋め、誰も呼び出していなくても全部を握ったままです。それに、必要なのは Hugging Face のフルの重みか、FP8 のような GPU 向けの量子化で、Ollama が使う GGUF ファイルではありません。</p>

<h2 class="relative group">API はひとつ、サーバーは二つ
    <div id="api-はひとつサーバーは二つ" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#api-%e3%81%af%e3%81%b2%e3%81%a8%e3%81%a4%e3%82%b5%e3%83%bc%e3%83%90%e3%83%bc%e3%81%af%e4%ba%8c%e3%81%a4" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>どちらも OpenAI の chat completions API を公開しているので、私のコードは相手がどちらなのかを知りません。URL とモデル名は環境変数から取ります。</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ラップトップ: LLM_URL=http://localhost:11434/v1  LLM_MODEL=qwen3:8b</span>
</span></span><span class="line"><span class="cl"><span class="c1"># GPU サーバー: LLM_URL=http://localhost:8000/v1   LLM_MODEL=qwen3-8b</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">(</span><span class="n">base_url</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&#34;LLM_URL&#34;</span><span class="p">],</span> <span class="n">api_key</span><span class="o">=</span><span class="s2">&#34;not-needed&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">reply</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">chat</span><span class="o">.</span><span class="n">completions</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">model</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">&#34;LLM_MODEL&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span><span class="o">=</span><span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Say hello in Korean.&#34;</span><span class="p">}],</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">reply</span><span class="o">.</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">content</span><span class="p">)</span></span></span></code></pre></div></div>
<p>大きな GPU のないラップトップでは、アプリは Ollama を向きます。サーバーでは vLLM を向きます。ほかは何も変わらず、GPU マシンの電源が入っていなくてもアプリを開発できます。vLLM 側では <code>--served-model-name</code> でモデルに短い名前を付けておけば、クライアントが Hugging Face のフルパスを送らずに済みます。</p>
<p>忘れやすいので、注意をひとつ。同じモデルでも、両者で同じ答えが返るわけではありません。Ollama の <code>qwen3:8b</code> は 4 ビット量子化ですが、vLLM では普通 16 ビットか FP8 の重みを動かします。片方に合わせて調整したプロンプトが、もう片方では違う振る舞いをすることがあります。なので測っているものは、回答の品質であれ、拒否であれ、JSON がパースできるかどうかであれ、本番で実際に使うサーバーで確かめる必要があります。</p>

<h2 class="relative group">Ollama のインストール方法
    <div id="ollama-のインストール方法" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ollama-%e3%81%ae%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e6%96%b9%e6%b3%95" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>Mac か Windows なら、ollama.com からアプリをダウンロードします。Linux ならこうです。</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://ollama.com/install.sh <span class="p">|</span> sh
</span></span><span class="line"><span class="cl">ollama run qwen3:8b</span></span></code></pre></div></div>
<p>これで終わりです。API は <code>http://localhost:11434</code> で使えるようになっていて、<code>OLLAMA_HOST</code> を設定しない限り localhost でしか待ち受けません。それが望ましい挙動です。私の Ollama はオフィスのワークステーションで <code>127.0.0.1</code> にバインドして動いていて、どこからでもトンネル経由で届くようにしています(<a href="https://jared.lynskey.co.nz/ja/posts/2026/2026-10-01-cloudflare-warp-dev-network/" >Cloudflare WARP でそれを組んだ方法</a>)。</p>

<h2 class="relative group">vLLM のインストール方法
    <div id="vllm-のインストール方法" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vllm-%e3%81%ae%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e6%96%b9%e6%b3%95" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>NVIDIA の GPU と新しめのドライバーがある Linux なら、こうです。</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python -m venv .venv <span class="o">&amp;&amp;</span> <span class="nb">source</span> .venv/bin/activate
</span></span><span class="line"><span class="cl">pip install vllm
</span></span><span class="line"><span class="cl">vllm serve Qwen/Qwen3-8B --served-model-name qwen3-8b --max-model-len <span class="m">16384</span></span></span></code></pre></div></div>
<p>初回の実行では Hugging Face から重みをダウンロードします。ログに <code>Application startup complete</code> と出たら、<code>curl localhost:8000/v1/models</code> でモデルが一覧に出て、上の Python がそのまま動きます。</p>

<h2 class="relative group">Docker で vLLM を動かす
    <div id="docker-で-vllm-を動かす" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#docker-%e3%81%a7-vllm-%e3%82%92%e5%8b%95%e3%81%8b%e3%81%99" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>ずっと動いていてほしいものには、pip install ではなく公式イメージを使っています。主な理由は、CUDA、PyTorch、vLLM がセットで固定された状態で手に入るからです。汎用的な Compose のサービスはこうなります。</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vllm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">vllm/vllm-openai:v0.30.0  </span><span class="w"> </span><span class="c"># タグを固定する。イメージは約 30 GB</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ipc</span><span class="p">:</span><span class="w"> </span><span class="l">host                         </span><span class="w"> </span><span class="c"># PyTorch が共有メモリを必要とする</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">&gt;</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">      --model Qwen/Qwen3-8B
</span></span></span><span class="line"><span class="cl"><span class="sd">      --served-model-name qwen3-8b
</span></span></span><span class="line"><span class="cl"><span class="sd">      --gpu-memory-utilization 0.85
</span></span></span><span class="line"><span class="cl"><span class="sd">      --max-model-len 16384</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">${HOME}/.cache/huggingface:/root/.cache/huggingface</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;127.0.0.1:8000:8000&#34;</span><span class="w">          </span><span class="c"># LAN ではなく ssh -L 経由でアクセスする</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">reservations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">devices</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l">nvidia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">count</span><span class="p">:</span><span class="w"> </span><span class="l">all</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">capabilities</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">gpu]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">healthcheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;CMD&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;python3&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;-c&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;import urllib.request; urllib.request.urlopen(&#39;http://localhost:8000/health&#39;)&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l">10s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l">5s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">start_period</span><span class="p">:</span><span class="w"> </span><span class="l">120s</span></span></span></code></pre></div></div>
<p>タグは固定してください。vLLM は動きが速く、リリースのたびにフラグやデフォルトが変わります。それがいつ起きるかは、自分で決められるようにしておきたいところです。<code>ipc: host</code>(あるいは十分な <code>shm_size</code>)は vLLM 自身の Docker の手順に書かれています。長い <code>start_period</code> が必要な理由は、次の節のひとつ目です。</p>

<h2 class="relative group">vLLM の運用でハマったこと
    <div id="vllm-の運用でハマったこと" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#vllm-%e3%81%ae%e9%81%8b%e7%94%a8%e3%81%a7%e3%83%8f%e3%83%9e%e3%81%a3%e3%81%9f%e3%81%93%e3%81%a8" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p><strong>起動に数分かかる。</strong> 重みのロード、コンパイル、CUDA グラフのキャプチャで、私のサーバーでは 190 秒かかりました。大きなモデルなら 3〜5 分は普通です。Web アプリ向けに書いたヘルスチェックだと、準備ができる前に殺してしまいます。Compose なら長い <code>start_period</code>、Kubernetes なら startup probe が必要です。それに、再起動のたびにモデルのいない時間が数分できるので、気軽に再起動するものではありません。</p>
<p><strong>GPU メモリは、与えた分を起動時に持っていく。</strong> <code>--gpu-memory-utilization</code> は vLLM が使ってよいカードの割合で、最近のリリースのデフォルトは 0.92、重みに要らない分はすべて KV キャッシュになります。vLLM がカードを独り占めしているなら、それで問題ありません。私の場合はそうではなく、横で小さなモデルが動いているので、96 GB のカードで 0.70 にしています。モデルは FP8 の 35B の mixture-of-experts で、ロードすると 47.7 GiB、KV キャッシュには 15 GiB が残りました。16k のコンテキストなら十分な量です。最大シーケンス長が KV キャッシュに収まらないと言って vLLM が起動を拒むなら、大きな GPU を探しに行く前に <code>--max-model-len</code> を下げてみてください。</p>
<p><strong>LoRA のスロットは先にメモリを取る。</strong> vLLM はひとつのベースモデルに複数の LoRA アダプターを載せて配信でき、リクエストごとにモデル名で切り替えられます。これは Ollama には実質できないことです。ただし vLLM は起動時に、ランク <code>--max-lora-rank</code> のアダプター <code>--max-loras</code> 個分の領域を確保します。mixture-of-experts のモデルではこの確保がエキスパートにも及ぶので、すぐに膨らみます。0.70 では、ランク 64 のアダプター 4 つは収まりませんでした。2 つなら収まりました。</p>
<p><strong>デフォルトのシーケンス上限がハイブリッドモデルに合わなかった。</strong> 私が配信しているモデルは attention の層に Mamba 系の層が混ざっていて、シーケンスごとにその層用のキャッシュブロックがひとつずつ必要です。そのカードでは vLLM のデフォルトの <code>--max-num-seqs</code> だと起動に失敗し、<code>--max-num-seqs 256</code> で直りました。新しめのアーキテクチャが起動しないときは、もっと込み入った原因を疑う前にこのフラグを確認してください。</p>
<p><strong><code>vllm</code> という名前の Kubernetes Service で vLLM がクラッシュする。</strong> これが個人的にいちばん気に入っています。Kubernetes は namespace 内のすべての Service について、その名前を元にした環境変数を注入します。なので <code>vllm</code> という Service があると、すべての Pod に <code>VLLM_PORT=tcp://&lt;cluster-ip&gt;:8000</code> が渡ります。vLLM は <code>VLLM_PORT</code> を自分の設定のひとつとして読み、そこにある URL を解釈できずに落ちました。もちろん、モデルを丸ごとロードしたあとでです。なので試すたびに 3 分かかりました。vLLM のドキュメントにはこの注意がちゃんと書いてあって、私はそれを後から見つけました。直し方は、Pod に <code>enableServiceLinks: false</code> を付けるか、Service を別の名前にすることです。</p>
<p>動く Deployment の骨格を、大事な部分だけに絞って載せます。</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vllm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Recreate              </span><span class="w"> </span><span class="c"># 先に古い Pod が GPU を解放する必要がある</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">vllm}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">vllm}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">enableServiceLinks</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">  </span><span class="c"># Service 由来の VLLM_PORT=tcp://... を入れない</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vllm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">vllm/vllm-openai:v0.30.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;--model=Qwen/Qwen3-8B&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--served-model-name=qwen3-8b&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">limits</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">nvidia.com/gpu</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">32Gi      </span><span class="w"> </span><span class="c"># ロード時は配信時よりはるかに多くのホスト RAM が要る</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">shm, mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/dev/shm}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">startupProbe</span><span class="p">:</span><span class="w">          </span><span class="c"># モデルがロードされるまでは判定しない</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">httpGet</span><span class="p">:</span><span class="w"> </span>{<span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="nt">/health, port</span><span class="p">:</span><span class="w"> </span><span class="m">8000</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">initialDelaySeconds</span><span class="p">:</span><span class="w"> </span><span class="m">120</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">failureThreshold</span><span class="p">:</span><span class="w"> </span><span class="m">90</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">livenessProbe</span><span class="p">:</span><span class="w">         </span><span class="c"># 再起動はフルリロードになるので気長に待つ</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">httpGet</span><span class="p">:</span><span class="w"> </span>{<span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="nt">/health, port</span><span class="p">:</span><span class="w"> </span><span class="m">8000</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">timeoutSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">failureThreshold</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">shm</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">emptyDir</span><span class="p">:</span><span class="w"> </span>{<span class="nt">medium</span><span class="p">:</span><span class="w"> </span><span class="nt">Memory, sizeLimit</span><span class="p">:</span><span class="w"> </span><span class="l">16Gi}</span></span></span></code></pre></div></div>
<p>真似する価値があるのはプローブの部分です。startup probe は 2 分待ってから、さらに 15 分、モデルが上がってくるのを待ち、それでもだめなら Kubernetes が死んだと判断します。liveness probe が再起動するのは、失敗が 5 分ほど続いてからです。負荷の高いエンジンでは <code>/health</code> が遅くなることがあり、どのみち再起動はフルリロードになるからです。GPU 一枚の環境では <code>Recreate</code> が効いてきます。ローリングアップデートは新しい Pod を先に起動するので、新しい Pod は古い Pod が握ったままの GPU を待って止まってしまいます。メモリの上限が大きく見えるのは、実際に大きいからです。重みが GPU に載ってしまえば、私の環境ではホスト RAM は約 7 GiB に落ち着きますが、ロードそのものにはもっとずっと多く必要です。</p>

<h2 class="relative group">Ollama について知っておきたいこと
    <div id="ollama-について知っておきたいこと" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ollama-%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6%e7%9f%a5%e3%81%a3%e3%81%a6%e3%81%8a%e3%81%8d%e3%81%9f%e3%81%84%e3%81%93%e3%81%a8" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>驚かされることは少なめです。主に、やっていることが少ないからです。</p>
<p>しばらく静かだったあとの最初のリクエストは遅くなります。モデルがアンロードされていて、もう一度ロードしなければならないからです。<code>OLLAMA_KEEP_ALIVE</code> は、最後のリクエストのあとモデルがどれだけメモリに残るかを決めます。その待ち時間が気になるなら値を上げるか、ほかに何もしないマシンなら <code>-1</code> にしてください。</p>
<p>コンテキストウィンドウは GPU メモリの量で変わります。24 GiB 未満だと Ollama のデフォルトは 4k トークンで、ほとんどのモデルが対応している長さよりずっと短く、収まらないプロンプトはエラーにならずに黙って切り詰められます。長いプロンプトの冒頭が無視されているように見えたら、<code>OLLAMA_CONTEXT_LENGTH</code>(またはリクエストごとの <code>num_ctx</code>)を実際に必要な長さに設定してください。</p>
<p>リモートの Ollama をラップトップにフォワードするなら、11434 は避けてください。Ollama のデフォルトのポートなので、ローカルの Ollama がいつか起動すると二つがポートを奪い合い、気づかないまま違うほうと話していた、ということになりかねません。私は 11435 にフォワードしています。</p>
<p>それから、Ollama はマルチユーザーのサーバーではありません。数人が使う社内ツールなら問題ありませんが、本当に同時アクセスが出てきたら、そこが vLLM に移るタイミングです。</p>

<h2 class="relative group">必要な GPU メモリの目安
    <div id="必要な-gpu-メモリの目安" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%bf%85%e8%a6%81%e3%81%aa-gpu-%e3%83%a1%e3%83%a2%e3%83%aa%e3%81%ae%e7%9b%ae%e5%ae%89" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>重みだけのおおよそのサイズです。</p>
<table>
	<thead>
			<tr>
					<th>モデルサイズ</th>
					<th>16 ビット(BF16)</th>
					<th>8 ビット(FP8)</th>
					<th>4 ビット GGUF(Q4_K_M)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>8B</td>
					<td>~16 GB</td>
					<td>~8 GB</td>
					<td>~5 GB</td>
			</tr>
			<tr>
					<td>32B</td>
					<td>~64 GB</td>
					<td>~32 GB</td>
					<td>~20 GB</td>
			</tr>
			<tr>
					<td>70B</td>
					<td>~140 GB</td>
					<td>~70 GB</td>
					<td>~43 GB</td>
			</tr>
	</tbody>
</table>
<p>16 ビットならパラメータあたり約 2 バイト、8 ビットなら 1 バイト、Q4_K_M なら 0.5 バイトを少し超えるくらいです。重みに加えて KV キャッシュが乗り、これはコンテキスト長と処理中のリクエスト数に応じて増えます。同じモデルでも、50 人に配信する vLLM のほうが、あなた一人に応える Ollama よりずっと多くのメモリを必要とするのは、この二つ目の部分のせいです。</p>

<h2 class="relative group">どちらを使うか
    <div id="どちらを使うか" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e3%81%a9%e3%81%a1%e3%82%89%e3%82%92%e4%bd%bf%e3%81%86%e3%81%8b" aria-label="アンカー">#</a>
    </span>
    
</h2>
<p>自分のマシンでモデルを動かしたいなら、Ollama をインストールしてそこで終わりにしてください。10 分もあれば何かが答えてくれるようになります。GPU サーバーがあって、それを一日中呼び出すユーザーやアプリがいるなら vLLM を使い、遅い起動と持っていかれるメモリを受け入れて、プローブをきちんと設定してください。結局両方を使う構成も多く、私のところもそうです。コードを書く場所には Ollama、リクエストが届く場所には vLLM です。</p>
<p>ほかの選択肢もあります。vLLM の代わりによく挙がるのは SGLang ですが、本番で動かしたことはないので、推測で書くよりは外しておきました。モデルをスマホで動かす必要があるなら、それはまた llama.cpp の出番で、これについては <a href="https://jared.lynskey.co.nz/ja/posts/2026/2026-01-30-on-device-ai/" >React Native でオンデバイスの LLM を動かす記事</a>に書きました。</p>
]]></content:encoded>
      <media:content url="https://jared.lynskey.co.nz/en/posts/2026/2026-10-30-vllm-vs-ollama/featured.jpg" medium="image" type="image/jpeg" />
    </item>
  </channel>
</rss>
