<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Private Registry on Wayne's blog | 偉恩的部落格 | 技術博客</title><link>https://blog.wayne-project.com/tags/private-registry/</link><description>Recent content in Private Registry on Wayne's blog | 偉恩的部落格 | 技術博客</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><lastBuildDate>Tue, 31 Jan 2023 00:00:05 +0000</lastBuildDate><atom:link href="https://blog.wayne-project.com/tags/private-registry/index.xml" rel="self" type="application/rss+xml"/><item><title>【Docker】建立私有 Docker Registry</title><link>https://blog.wayne-project.com/2023-01-31/docker-private-registry/</link><pubDate>Tue, 31 Jan 2023 00:00:05 +0000</pubDate><guid>https://blog.wayne-project.com/2023-01-31/docker-private-registry/</guid><description>&lt;img src="https://live.staticflickr.com/65535/52660201816_22c917dc9a_o.png" alt="Featured image of post 【Docker】建立私有 Docker Registry" /&gt;&lt;style&gt;
.article-content p code {
background-color: #f5f5f5;
color: #ff3860;
}
.focus {
background: #f1e2e2;
color: #d62c2c;
padding: 0 5px;
}
&lt;/style&gt;
&lt;p&gt;&lt;a class="link" href="https://www.nuget.org/packages/Lib.AspNetCore.ServerSentEvents" target="_blank" rel="noopener"
&gt;參考網站&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;深度應用 Docker 容器後常會走到這一步 - 自建 Docker Registry (容器 Image 倉庫)。&lt;/p&gt;
&lt;p&gt;不管用 docker 指令或跑 Docker Compose，建立容器都需先載入 Docker Image，若是公開 Image，透過 docker pull 指令、在 docker-compose.yml 指定 image 來源、或 Dockerfile 寫 FROM imageName:tag，都可從 &lt;a class="link" href="https://www.docker.com/products/docker-hub/" target="_blank" rel="noopener"
&gt;Docker Hub&lt;/a&gt; 下載，私有程式或內部應用系統不適合上傳第三方系統，企業內部主機則可能位於無法連 Internet 的環境，從 Docker Hub 下載這條路便不可行。簡單做法是用 &lt;code&gt;docker save container-name &amp;gt; container-name.tar&lt;/code&gt; 再 scp 上傳到 Docker 主機用 &lt;code&gt;docker load -i container-name.tar&lt;/code&gt; 載入，但儲存、上傳、載入(有時還需要壓縮解壓縮)程序複雜，若要部署多台主機格外沒效率。因此，建立私有 Docker Registry 取代 Docker Hub 的角色，是較省事有效率的實務做法。&lt;/p&gt;
&lt;p&gt;(補充：關於 Container Image 部署，可參考保哥這篇&lt;a class="link" href="https://blog.miniasp.com/post/2023/01/02/How-to-Move-Container-Image-to-another-Docker-Engine" target="_blank" rel="noopener"
&gt;遷移容器映像(Container Image)到另一台主機的各種作法&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Docker 支援自建私有 Registry 伺服器，不意外地有安裝成 Docker 容器的便捷做法。經簡單設定與安裝，架一台私有 Docker Registry 儲存非公開容器 Image 並不困難。說是一回事，做是一回事，這篇來實地演練一次。&lt;/p&gt;
&lt;h2 id="開始"&gt;開始&lt;/h2&gt;
&lt;p&gt;首先準備一台 Docker 主機，裝好 Nginx + Certbot 服務，我是在 Azure 開一個 B1s 小 VM 來跑。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;註：Azure 有 &lt;a class="link" href="https://azure.microsoft.com/zh-tw/products/container-registry" target="_blank" rel="noopener"
&gt;Azure Container Registry&lt;/a&gt; 的現成雲端服務，做法更簡便。學習在 Linux 跑 Docker 做法是為將來在離線環境建立 Docker Registry 做準備。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;以我的標準，即便內部伺服器也要控管存取身分，不能讓閒雜人等隨便用。Docker Registry 支援帳號密碼登入，但前題要啟甪 TLS HTTPS 連線，故除了安裝 htpasswd 工具設定密碼雜湊檔，並需要安裝 mkcert 建立 TLS 憑證：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install apache2-utils mkcert
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我在主機端建了一個 /var/registry 資料夾放 Docker Registry 相關檔案，接著用 mkcert 建立自簽憑證給 Registry 用：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52660476204_fdaaf38881_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;設定帳號密碼：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52660195346_c7fb46f15c_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;準備好 Docker Compose 設定檔，REGISTRY_HTTP_TLS_CERTIFICATE 及 REGISTRY_HTTP_TLS_KEY 環境變數指向 mkcert 剛才製作的兩個 pem 檔名、REGISTRY_AUTH_HTPASSWD_PATH 指向密碼檔名；volumes 部分將 Docker 的 Image 資料、憑證、認證資料對應到 /var/registry 實體路徑保存，以免容器關閉後資料消失：&lt;/p&gt;
&lt;h5 id="docker-composeyml"&gt;docker-compose.yml&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;registry:2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY_HTTP_TLS_CERTIFICATE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/certs/localhost.pem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY_HTTP_TLS_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/certs/localhost-key.pem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY_AUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;htpasswd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY_AUTH_HTPASSWD_PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/auth/htpasswd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY_AUTH_HTPASSWD_REALM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Registry Realm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/var/registry/data:/var/lib/registry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/var/registry/certs:/certs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;/var/registry/auth:/auth&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;用 &lt;code&gt;docker-compose up -d&lt;/code&gt; 啟動容器，若一切正常，用 &lt;code&gt;curl https://localhost:5000/v2/&lt;/code&gt; 測試，若傳回 &lt;code&gt;{&amp;quot;errors&amp;quot;:[{&amp;quot;code&amp;quot;:&amp;quot;UNAUTHORIZED&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;authentication required&amp;quot;,&amp;quot;detail&amp;quot;:null}]}&lt;/code&gt; 就代表成功了。&lt;/p&gt;
&lt;p&gt;接著來測試 Push Image 到私有 Registry，操作程序是用 docker tag 為己載入容器加上 &lt;code&gt;localhost:5000/aspnetapp&lt;/code&gt; 這種標籤，此時等同同一個 IMAGE ID 有兩個 Tag，由於 Registry 需要登入，用剛才的設定帳號密碼以指令 &lt;code&gt;docker login&lt;/code&gt; 登入，接著 &lt;code&gt;docker push localhost:5000/aspnetsapp&lt;/code&gt; 就能完成上傳：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52660686773_ffdc70fc45_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;檢查資料夾 &lt;code&gt;/var/registry/data/docker/registry/v2/repositories/aspnetapp/_layers/sha256&lt;/code&gt; 可看見上傳過程出現的四個 Layer ID，成功！&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52659697842_ef4b89b360_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;接下來試試從 Windows Docker Desktop 從 VM 的對外網域名稱下載容器 Image 執行，一樣輕鬆秒殺：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://live.staticflickr.com/65535/52660476229_bfa8528053_o.png"
max-width="100%"
max-height="100%"
class="gallery-image"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;以上，一台自建 Docker Registry 便上線服役囉～&lt;/p&gt;
&lt;hr&gt;</description></item></channel></rss>