🛠️ 技術框架與設計模式
🐹 Go 1.24+
主要開發語言,提供優秀的併發處理和交叉編譯能力
📦 containerd v2
容器執行時與映像檔儲存後端,直接整合避免中介層
🔌 Distribution v3
Docker Registry 實作基礎,提供完整的 OCI 合規性
📊 OpenTelemetry
可觀測性與追蹤,提供詳細的效能監控資訊
⚙️ Cobra CLI
命令列框架,提供豐富的參數處理和說明功能
🔐 SSH 隧道
安全傳輸通道,支援金鑰認證和 Port Forwarding
核心設計模式
🔌 Adapter Pattern - containerd 整合適配器
Unregistry 需要將 containerd 的 Content Store 和 Image Store API 適配成符合 Docker Distribution 規範的介面。Adapter Pattern 讓兩個不相容的介面能夠協同工作,實現無縫整合。
主要優勢:
- 無需修改 containerd 或 distribution 的現有程式碼
- 提供清晰的介面轉換層
- 支援未來版本的升級相容性
🔧 containerd 儲存適配器實作
type registry struct {
client *client.Client
}
var _ distribution.Namespace = ®istry{}
func (r *registry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
return newRepository(r.client, name), nil
}
func (r *registry) BlobStatter() distribution.BlobStatter {
return &blobStore{client: r.client}
}
這段程式碼展示了 Adapter Pattern 的核心實作,registry 結構體作為適配器,將 containerd client 的功能適配為符合 distribution.Namespace 介面的行為。
Adapter Pattern 類別圖
🏢 Facade Pattern - Registry 統一介面
Unregistry 需要整合多個複雜的子系統:containerd 客戶端、分發處理器、中介軟體配置等。Facade Pattern 提供一個簡化的統一介面,隱藏內部複雜性。
主要優勢:
- 簡化客戶端與複雜子系統的互動
- 降低系統間的依賴關係
- 提供清晰的系統邊界
🔧 Registry Facade 實作
type Registry struct {
app *handlers.App
server *http.Server
}
func NewRegistry(cfg Config) (*Registry, error) {
distConfig := &configuration.Configuration{
Storage: configuration.Storage{...},
Middleware: map[string][]configuration.Middleware{
"registry": {{
Name: containerd.MiddlewareName,
Options: configuration.Parameters{...},
}},
},
}
app := handlers.NewApp(context.Background(), distConfig)
server := &http.Server{Addr: cfg.Addr, Handler: app}
return &Registry{app: app, server: server}, nil
}
Registry 結構體作為 Facade,統一管理 HTTP 伺服器、應用程式處理器和 containerd 中介軟體的複雜初始化過程。
Facade Pattern 類別圖
🏭 Factory Pattern - Middleware 註冊工廠
Unregistry 使用 Factory Pattern 來動態建立和註冊 containerd 中介軟體。透過工廠函數,系統可以根據配置參數建立適當的 Registry 實例。
🔧 Middleware Factory 實作
const MiddlewareName = "containerd"
func init() {
err := middleware.Register(MiddlewareName, registryMiddleware)
if err != nil {
panic(fmt.Sprintf("failed to register containerd middleware: %v", err))
}
}
func registryMiddleware(
_ context.Context, _ distribution.Namespace, _ storagedriver.StorageDriver,
options map[string]interface{},
) (distribution.Namespace, error) {
cli, err := client.New(sock, client.WithDefaultNamespace(namespace))
return ®istry{client: cli}, err
}
這個 Factory Pattern 實作允許 Distribution 框架根據配置動態建立 containerd backend,實現了鬆耦合的插件架構。
Factory Pattern 類別圖
🎯 Strategy Pattern - Manifest 處理策略
Unregistry 需要支援多種 Manifest 格式:OCI Manifest、Docker Schema2 和 Manifest List。Strategy Pattern 讓系統能夠根據內容動態選擇適當的解析策略。
🔧 Manifest 解析策略實作
func unmarshalManifest(blob []byte) (distribution.Manifest, error) {
var ociManifest ocischema.DeserializedManifest
if err := ociManifest.UnmarshalJSON(blob); err == nil {
return &ociManifest, nil
}
var schema2Manifest schema2.DeserializedManifest
if err := schema2Manifest.UnmarshalJSON(blob); err == nil {
return &schema2Manifest, nil
}
var manifestList manifestlist.DeserializedManifestList
if err := manifestList.UnmarshalJSON(blob); err == nil {
return &manifestList, nil
}
return nil, distribution.ErrManifestVerification{errors.New("unknown manifest format")}
}
透過順序嘗試不同的解析策略,系統能夠自動適應各種 Manifest 格式,提供良好的相容性。
Strategy Pattern 類別圖
⚡ Command Pattern - CLI 命令處理
docker-pussh 腳本使用 Command Pattern 來封裝不同的操作步驟:SSH 連線、容器啟動、Port Forwarding 等。每個命令都是可獨立執行和撤銷的操作。
🔧 命令封裝實作
ssh_remote() {
local ssh_addr="$1"
ssh "${ssh_opts[@]}" -f -N "${target}"
SSH_ARGS=("${ssh_opts[@]}" "${target}")
}
run_unregistry() {
ssh "${SSH_ARGS[@]}" "${REMOTE_SUDO} docker run -d ..."
UNREGISTRY_CONTAINER="unregistry-pussh-$$-${UNREGISTRY_PORT}"
}
cleanup() {
docker rm -f "${DOCKER_DESKTOP_TUNNEL_CONTAINER}" &>/dev/null || true
ssh "${SSH_ARGS[@]}" "${REMOTE_SUDO} docker rm -f ${UNREGISTRY_CONTAINER}" &>/dev/null || true
ssh "${SSH_ARGS[@]}" -O exit 2>/dev/null || true
}
每個函數代表一個命令,具有明確的執行和清理邏輯,確保系統狀態的一致性。
Command Pattern 類別圖