名詞解釋
Container
Docker Container 是一種軟體容器,它可以在其中運行應用程式和其他服務。它使用操作系統級別的虛擬化,可以在單一的物理主機上運行多個容器,並且每個容器都有自己的運行環境和資源。
Docker Container 是一種軟體容器,它可以在其中運行應用程式和其他服務。容器具有輕量級、可移植性和隔離性等特點。容器是通過在操作系統內核中運行的容器引擎來實現的。
Docker容器主要是使用了Linux 的 Namespaces 和 Control groups(cgroups) 技術來實現隔離,這兩種技術可以將一個實體主機上的資源限制給每個容器,而容器內部則是共用一個kernel,因此容器比虛擬機器輕量且速度較快。
Docker容器是基於鏡像(Image)來建立與運行的,一個鏡像可以是一個基礎鏡像或是由其他鏡像所建立而來。當執行docker run 指令時,會從鏡像建立一個容器,並在容器內執行指定的應用程式或服務。
不僅如此,Docker容器還支援網路、儲存卷的映射,使得容器可以與外部通訊,也可以存取本地端的資料。
透過Docker容器,我們可以將應用程式、服務和其所需的環境打包在一起,並且可以在不同的環境中運行,提高了應用程式的可移植性和彈性。
Image
Docker Image 是 Docker 容器的基礎,它是一個只讀的模板,包含了容器運行所需的所有檔案、設定和程式。當執行 docker run
指令時,Docker 會從 Image 建立一個新的容器並在其中執行指定的應用程式或服務。
Docker Image 可以通過構建或下載的方式創建,構建的方式可以使用 Dockerfile 來描述如何構建一個 Image。而下載的方式則可以從 Docker Hub 或其他的 registry 下載。
Volume
Docker Volume 是 Docker 的一種功能,用於管理容器中的數據。容器本身是輕量級的,數據是不能永久存在的,而 Volume 則是可以永久存在的。
Docker Volume 可以被掛載到容器上,並且可以在容器內部存儲數據。當容器停止運行或者被刪除時,Volume 中的數據仍然可以保留下來。這樣就可以在重啟容器或建立新容器時,繼續使用之前存儲的數據。
並且Volume裡面的資料是可以和Host分享的,兩邊的資料呈現鏡像的雙向對應,在Host新增的東西會在Container出現,Container新增的資料也會在Host裡面出現
常用指令
搜尋Image
|
|
查看目前的image
|
|
或是
|
|
執行docker image
|
|
後面的:latest是版本號,可加可不加,沒加的話預設就是latest
刪除images
|
|
在Detached mode下執行
|
|
所謂的Detached mode亦即啟動後會不會占用你的terminal,可以看一下下面的git,可以比較兩者間的差異
- 沒有-d
- 有-d
打包成docker image
|
|
-t 是 tag的縮寫,hello-docker是這個tag的名稱,.代表dockerfile在當前的目錄下,如果Dockerfile不在當前目錄,則這邊要改變。latest則是版本號,可加可不加,不加的話預設是latest
查看目前運行的Container
|
|
ps是process status的意思
或是
|
|
查看目前運作中(running)的Container
|
|
或是
|
|
查看底下全部的Container不論啟動與否
進入Container與之互動
|
|
bash有可能沒有,有可能是sh,要自己到/bin裡面看
停止Container
|
|
啟動停止的Container
|
|
刪除Container
|
|
也可以輸入很多個Id,一次刪個爽
還有更猛的
|
|
直接用參數的方式全刪。
暴露port
|
|
前面的5432是你自定義的localhost:5432,而後面的5432則是容器裡面的port號
暴露已經Running,Stopping 的Container的Port
沒有這個方法,只有
|
|
的這個時候你才可以把port暴露出來
一次性查看Container的Log紀錄
|
|
這條指令只會顯示過去的紀錄,後續的logs不會更新
持續查看Container的Log紀錄
|
|
這條不只會顯示過去的,還會動態更新現在的log
執行Docker-compose
|
|
-d 代表是否背景執行,不佔用terminal
停止並刪除Docker-compose的Container
|
|
將Dokcer Images Push至Dockerhub
首先先登入dockerhub
|
|
再將想要推上去的docker Image重新命名
|
|
接著push上去 dockerhub
|
|
想要使用image的話就執行pull
|
|
在Docker啟動Ubuntu
下載 ubuntu 的image
1
docker pull ubuntu
或是可以
1
docker run ubuntu
就會自動從docker hub載下來了,但這樣只是把ubuntu的image拉到我們的docker裡面,它本身是沒有啟動的
在docker中運行ubuntu
1
docker run -it ubuntu
使用apt(advanced package tool)安裝nano(Linux text editor)
用apt載任何東西前都建議先update
1
apt update
1
apt install nano
Exposing Port
輸入docker ps
可以看到以下資訊
其中的PORTS 80/tcp的意思,容器對外公開的網路端口是 80/tcp,表示這個容器對外公開的網路端口是80,並且是基於TCP協議的。這意味著當外部網路瀏覽器連接到http://localhost或http://
我們可以使用以下的方式將8080 連接到80/TCP
|
|
其中的8080:80的意思是指將主機的 8080 端口映射到容器的 80 端口。也就是說,當外部網路瀏覽器連接到 http://localhost:8080 時,將會連接到容器內部的 Nginx Web 伺服器。
你也可以不只Exposing一個Port,可以Exposing多個port給80
|
|
Container的管理
當我們啟動、並Stop一個Container,實際上如果依照我們剛剛的作法,我們是不斷的創造新的Container,輸入docker ps -a 即可看到目前存在的Container(不論running or Stopping),或是在Desktop docker裡面也都可以看到
我們可以透過
|
|
來真正意義上的移除Container,而不是停止它
可以使用docker ps -aq ,這個指令只會秀出ContainerId,可以刪更爽,直接複製貼上就好
還有更爽的方式,用$(docker ps -aq)的方式 傳遞參數
為Container命名
建議命名一下,比較好找,只支援英文,不支援中文
|
|
Volume的使用
讓資訊可以在host與Container共享的一個功能
範例:
首先在桌面上創建一個名為website的資料夾,裡面有個index.html,內容如下
|
|
接著將terminal切至/website底下,然後輸入
|
|
-v是 Docker 中的 volume 指令,它用於將主機上的目錄或檔案掛載到容器中。配合後面的${PWD}:/usr/share/nginx/html,意思就是將當前目錄的內容掛載(Mount)到容器中的 /usr/share/nginx/html 目錄下。這樣設定後,當主機上的目錄內容變更時,容器中的 /usr/share/nginx/html 目錄內的內容也會隨之更新。
:ro 是指將主機上的目錄或檔案掛載到容器中的目錄或檔案,並設定為只讀模式。
這意味著在容器中將無法寫入或修改掛載的目錄或檔案,只能讀取。這可以避免對主機上的檔案造成損壞或不 必要的變更。
- /usr/share/nginx/html 是 Nginx 預設的網站根目錄。
結果如下:
也因為Volume是鏡像對應,因此修改host的檔案,container的內容物也會同步更新
我們可以用以下的指令來訪問看看Nginx的檔案
|
|
- docker exec 是 Docker 的命令行工具,用於在運行中的容器內執行命令。
- -it 這兩個選項表示要互動式地執行命令,並且讓輸入和輸出保持連接。
- website 是容器的名稱或 ID。
- bash 是要在容器內執行的命令,這裡是啟動 Bash shell。也可以改成ls,就變成ls了,玩法很多,自行摸索
在Nginx裡面新增檔案,移除檔案,會發現host的資料夾檔案也同步更新
不同的Container使用相同的Volume
|
|
- –volumes-from [ContainerName]:將這次要啟動的Container使用和website一樣的Volume
Dockerfile
Dockerfile是一個文本文件,它包含了創建Docker image所需的指令。這些指令可以包括例如:
- 從哪個基礎鏡像建立新鏡像
- 安裝需要的軟體
- 設置環境變量
- 添加應用程序文件
- 定義容器啟動時執行的命令
透過Dockerfile, 可以自動化的建立一個環境,方便在不同的環境上部署,使用者可以更方便的管理環境,以及減少部署錯誤的機會。
例如,如果你有一個Java應用程序需要在多個不同的服務器上運行,你可以使用Dockerfile創建一個包含Java執行時環境的镜像,然後在每個服務器上執行這個镜像,這樣就能保證每個服務器上都有相同的環境。
試著把剛剛寫的volume打包成一個image,首先在/website的資料夾裡面新增一個名稱一定要是dockerfile的檔案
裡面的檔案結構長的像這樣子
|
|
- FROM:指定了基礎Image是nginx,後面的latest是指版本號。Dockerfile中必須要有FROM指令,它是一切的根本,它指定了基礎Image環境。舉例來說,這邊指定用最新版的nginx,那麼我們的鏡像會基於這個最新版的nginx環境運行
- ADD:將本地目錄中的文件複製到鏡像中的指定目錄。在這個例子中是將本地目錄中的所有文件複製到鏡像中的/usr/share/nginx/html目錄。它的功能跟COPY有點像,但是COPY用法比較單純,只能複製本地文件和目錄到鏡像中,而ADD指令還可以解壓縮tar文件並將其中的文件複製到鏡像中。
值得注意的是
|
|
這行指的是,將當前目錄的所有東西(以一個.表示)加入至Container中的/usr/share/nginx/html目錄中。在這個例子中就是將
這些東西ADD進/usr/share/nginx/html裡面。
當Dockerfile寫好後,要開始bulid它,步驟如下
|
|
- 這個命令是在使用 Docker 建立一個新的鏡像檔,並標記為 “website:latest”。 “.” 表示當前目錄下的 Dockerfile 檔案將會被用來建立映像檔。這個命令將會建立一個名為 “website” 並且標記為 “latest” 的鏡像檔。
Build完後就會出現一個image了,輸入
|
|
就可以查看目前擁有的images
並且可以這個image可以運行我們剛剛對index的設定,輸入
|
|
注意:這邊不需要再為website設置volume,因為我們已經將需要的東西打包進image裡面了。
輸入完後,在URL的地方輸入localhost:8080就可以看到我們剛剛設置的東西了。
gif如下
實際演練 NodeJs
前置作業
安裝Node.js,這邊安裝為了快速,就直接用Choco來裝了,Choco的安裝如下
在Terminal中輸入,記得要以系統管理員身分輸入
1
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
安裝完後安裝nodeJs
1
choco install nodejs
安裝完後隨便創一個資料夾,這邊命名叫做user-service-api
切換到該資料夾底下,並且npm init它
1
npm init
接著安裝express
1
npm install --save express
用好後檔案結構長這樣
在該目錄底下新增一個index.js的檔案,內容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
const express = require('express') const app = express() const port = 3000 app.get('/', (req, res) => { res.json([{ name: 'Bod', email: 'bob@gmail.com' }]) }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })
使用nodejs運行
1
node index.js
成功後進入localhost:3000即可看到下列畫面
如此一來前置作業就完成了
製作DockerFile
在目錄底下新增dockerfile,內容如下
|
|
- WORKDIR:若Container有/app這個資料夾,則使用它,若沒有,則創造它。
- ADD . .: 将当前目录中的文件复制到镜像中的 /app 目录。
- RUN npm install: 在鏡像中运行 npm install 命令,安装应用程序所需的依赖项。
- CMD node index.js:设置镜像启动时运行的命令,这里是运行 node index.js。
為什麼要分成RUN跟CMD呢?有幾個原因,首先RUN跟CMD的用途本身就不一樣,RUN主要是在創建image中執行命令,並將結果保存在image中,它主要用來安裝依賴、配置應用程式或其他操作。則是用來說明Image創建完成後要執行的動作。簡而言之,RUN是在創建Image中過程所執行的,而CMD則是在Image創建完成後所執行的。
並且,一個DockerFile可以有很多RUN指令,但只能有一個CMD指令,因為Container只能運行一個CMD指令
使用Image
接著創建鏡像
|
|
創建完之後啟動鏡像
|
|
這邊的8080:3000
是指,將我們容器裡面原本配置的3000端口暴露出來,以8080來接收。
因為3000是指在Container裡面的端口,host想要讀到它,必須將Container的端口暴露出來。因此localhost:3000會找不到東西,只有打localhost:8080才會有我們要的內容
DockerIgnore
做完上面這些操作後,我們的檔案結構長這樣
然後我們的Dockerfile長這樣
|
|
比較之後發現一件事情,RUN npm install會創建node_modules資料夾,但我們在ADD時已經把node_modules加入進去,等於說我們重複創建了兩次node_modules,這種情況就類似gitIgnore,需要排除掉重複的資料夾
dockerIgnore的寫法
|
|
這樣就可以把這些檔案排除在外了
Caching & Layers
DockerFile裡面的每一個CML都是一個Layer,每個Layer都用來Caching`
|
|
可以看到這邊的Step1, Step2都對應著CML的指令…
而Cache的點就在於,其實除了ADD . . 以外(原始碼每次打包時都會有更動),其實WORKDIR, RUN npm install這些指令其實都是重複的,我們每次打包都需要再重複執行一次,這樣很沒**效率 **
於是Dokcer就會把這些重複的事情Caching起來,只要沒有改變就不會重複再做,就會看到上面的Using Cache了
ALPINE
翻譯的意思是高山
我剛剛打包的Image檔案已經快逼近一個G了,很明顯我們其實不需要那麼多的東西,Alpine版本的就是一個非常小的鏡像。
實際安裝ALPINE
|
|
兩者的Size差了快十倍
Docker Compose 將後端與資料庫一起包一包
一個Project不可能只由一個後端組成,肯定是要由後端、前端、以及資料庫三者組合,甚至更甚者可能會有10,20個的部件需要去組合,那麼一個一個run container這件事情就變得相當缺乏效率。為了處理這件事情,於是有了Docker-Compose的概念出現。
Docker-Compose大概就像這樣,
|
|
和dockerfile一樣存在於專案根目錄中
他類似於一個配置檔,用以告訴Docker要啟動哪些Container,以及它們之間的交互關係,以上面的Docker-compose.yml來說明
|
|
▲第四行的db與services的名稱有對應。
▲上述的docker-compose啟動後顯示的樣子。
常用的指令
啟動當前目錄的docker-compose
|
|
關閉當前目錄的docker-compose
|
|