> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 選擇別人建好的映像

> 怎麼判斷映像可不可信，以及 Python 官方映像的完整版、slim、alpine 變體怎麼選。

export const OptionPicker = ({options = [], lang = "zh", label}) => {
  const t = lang === "en" ? {
    pick: label || "Pick an option to compare",
    rec: "Recommended"
  } : {
    pick: label || "點選項目比較",
    rec: "推薦"
  };
  const opts = Array.isArray(options) ? options : [];
  const [sel, setSel] = useState(0);
  if (opts.length === 0) return null;
  const idx = Math.min(sel, opts.length - 1);
  const cur = opts[idx];
  const css = `
  .op-root{--op-bg:#FAF8F3;--op-surface:rgba(0,0,0,0.025);--op-border:rgba(0,0,0,0.09);--op-text:#2b2722;--op-dim:#6f6a62;--op-faint:#8a8378;--op-accent:#bf7551;border:1px solid var(--op-border);border-radius:14px;background:var(--op-bg);color:var(--op-text);overflow:hidden;}
  .dark .op-root{--op-bg:#1b1a18;--op-surface:rgba(255,255,255,0.03);--op-border:rgba(255,255,255,0.08);--op-text:#e7e3da;--op-dim:#a8a299;--op-faint:#8a8378;--op-accent:#cf8a68;}
  .op-head{padding:12px 16px 11px;border-bottom:1px solid var(--op-border);font-size:12.5px;color:var(--op-dim);display:flex;align-items:center;gap:8px;}
  .op-head-ic{color:var(--op-accent);flex-shrink:0;}
  .op-tabs{display:flex;gap:8px;padding:14px 16px 4px;flex-wrap:wrap;}
  .op-tab{position:relative;flex:1 1 130px;min-width:120px;text-align:left;background:transparent;border:1px solid var(--op-border);border-radius:11px;padding:11px 13px;cursor:pointer;color:inherit;font:inherit;transition:border-color .15s,background .15s,box-shadow .15s;}
  .op-tab:hover{background:var(--op-surface);}
  .op-tab-on{border-color:rgba(191,117,81,.5);background:rgba(191,117,81,.07);box-shadow:0 0 0 3px rgba(191,117,81,.1);}
  .op-tab-name{font-size:14.5px;font-weight:600;line-height:1.3;}
  .op-tab-on .op-tab-name{color:var(--op-accent);}
  .op-badge{display:inline-block;margin-top:6px;font-size:10.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;padding:2px 7px;border-radius:20px;background:rgba(191,117,81,.14);color:var(--op-accent);}
  .op-rec{position:absolute;top:10px;right:11px;font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--op-accent);}
  .op-body{padding:6px 16px 16px;}
  .op-specs{border:1px solid var(--op-border);border-radius:10px;overflow:hidden;margin-bottom:11px;}
  .op-row{display:flex;border-top:1px solid var(--op-border);font-size:13px;}
  .op-row:first-child{border-top:none;}
  .op-k{width:42%;flex-shrink:0;padding:8px 12px;color:var(--op-dim);background:var(--op-surface);}
  .op-v{flex:1 1 0;min-width:0;padding:8px 12px;font-weight:500;}
  .op-note{font-size:13px;line-height:1.6;color:var(--op-dim);padding:0 2px;}
  `;
  return <div className="op-root">
      <style>{css}</style>
      <div className="op-head"><svg className="op-head-ic" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z" /><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65" /><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65" /></svg>{t.pick}</div>
      <div className="op-tabs">
        {opts.map((o, i) => <button key={i} type="button" className={"op-tab" + (i === idx ? " op-tab-on" : "")} onClick={() => setSel(i)}>
            {o.recommend && <span className="op-rec">★ {t.rec}</span>}
            <div className="op-tab-name">{o.name}</div>
            {o.badge && <span className="op-badge">{o.badge}</span>}
          </button>)}
      </div>
      <div className="op-body">
        <div className="op-specs">
          {(cur.specs || []).map((row, i) => <div className="op-row" key={i}>
              <div className="op-k">{row[0]}</div>
              <div className="op-v">{row[1]}</div>
            </div>)}
        </div>
        {cur.note && <div className="op-note">{cur.note}</div>}
      </div>
    </div>;
};

`Image` `Python`

不是每次都自己從零寫，多數時候是站在別人建好的映像上（`FROM`）。挑映像看兩件事：**可信度**與**版本變體**。

## 可信度：三個層級

| 類別                     | 識別                                | 維護者             |
| ---------------------- | --------------------------------- | --------------- |
| Docker Official Images | 路徑無使用者前綴（`python`、`ubuntu`）、有官方徽章 | Docker 親自策展     |
| Verified Publisher     | 有「Verified Publisher」徽章，路徑帶廠商名    | 經審核的商業廠商        |
| 社群映像                   | 格式 `username/image`、無徽章           | 個別使用者，靠下載數與聲譽判斷 |

判斷可信的可信來源見 [Docker Hub](/notes/docker/guide/hub/)。

## Python 映像變體

以 Python 官方映像為例，撥下面幾個看差異：

<OptionPicker
  lang="zh"
  label="點變體比較 Python 官方映像"
  options={[
{
  name: "python:3.12",
  badge: "完整",
  specs: [
    ["底層", "Debian bookworm（含編譯工具）"],
    ["壓縮大小", "約 350-400 MB"],
    ["適用", "需編譯 C extension、科學運算"],
  ],
  note: "包含完整 Debian 與編譯工具，最大但最不容易缺東西。要在映像裡編譯原生套件時最省事。",
},
{
  name: "python:3.12-slim",
  badge: "精簡",
  recommend: true,
  specs: [
    ["底層", "精簡 Debian bookworm"],
    ["壓縮大小", "約 50-70 MB"],
    ["適用", "生產環境預設、多數 web/API"],
  ],
  note: "去掉文件與編譯工具的精簡版，生產環境的預設首選。需要編譯時自己 apt 裝 build-essential 即可。",
},
{
  name: "python:3.12-alpine",
  badge: "最小",
  specs: [
    ["底層", "Alpine（musl libc）"],
    ["壓縮大小", "約 20-25 MB"],
    ["適用", "純 Python、可接受編譯成本"],
  ],
  note: "最小，但 musl libc 讓很多 binary wheel（numpy / pandas / scipy 等）退回原始碼編譯，build 變慢甚至最終更大，科學運算與 ML 套件不建議用 alpine。",
},
]}
/>

## Alpine 的雷

Alpine 用 **musl libc** 而非 glibc，造成：

* PyPI 上預編譯的 binary wheel（manylinux，以 glibc 為目標）在 Alpine 上會**退回原始碼編譯**，要在映像內裝 `gcc` / `musl-dev` 與各套件的 C headers，build 時間大增、最終 image 未必更小。
* numpy、scipy、pandas、PyTorch、TensorFlow 這類科學運算 / ML 套件，在 Alpine 上需自行編譯或用非官方包，**不建議**。
* Alpine 適合純 Python（無 C extension 相依）、對映像大小有強制要求、且願意接受 multi-stage 裡的編譯成本。

## 選版本的通則

```
需要科學運算套件（numpy / pandas / scipy / torch）？
  → 是 → python:3.X（完整）或 python:3.X-slim + 手動裝 build-essential
  → 否 → python:3.X-slim（預設優先）
          └→ 對映像大小有嚴格限制 + 確認無 C extension？
                → 是 → python:3.X-alpine（接受自行編譯）
```

* **固定 `major.minor` tag**（`python:3.12-slim`），別用 `latest` 或 `python:3`。
* **預設用 slim**，要編譯原生套件時自己補 build 工具。
* `bookworm` = Debian 12、`bullseye` = Debian 11，需要鎖定 Debian 版本時用帶代號的 tag（`python:3.12-slim-bookworm`）。

## 接下來

* [配合 Python 程式](/notes/docker/dockerfile/python/)：用 slim 寫一個實際 Dockerfile。
* [Layer cache 與最佳實踐](/notes/docker/dockerfile/caching/)：固定 tag、非 root 等最佳實踐。

官方參考：[hub.docker.com/\_/python](https://hub.docker.com/_/python)、[Docker Official Images](https://docs.docker.com/trusted-content/official-images/)
