目錄
引入
入門使用
部署對比
鏡像倉庫
命令解釋
基礎
常見命令
示例
?數據卷的使用
數據卷的概念
數據卷的使用
掛載本地目錄文件
鏡像
結構
Dockerfile
容器網絡
部署
DockerCompose
語法
?編輯
基礎命令
引入
當我們在 Linux 上部署一個集成了很多中間件的單體項目的時候,會發現命令太多不易記住,軟件安裝包的名字復雜,安裝和部署步驟復雜,容易出錯。在部署微服務項目的時候,由于需要部署的服務器數量眾多,并且每一臺服務器的運行環境也不一樣,寫好的安裝流程,部署腳本并不一定在每個服務器都能正常運行,經常出錯。這就給系統的部署運維帶來了諸多困難,那有沒有一種技術能夠避免部署對服務器環境的依賴,減少復雜的部署流程呢?有的兄弟,有的,這就是即將要介紹的 Docker 技術。
Docker 是一種 容器化技術,它可以把應用程序及其所有依賴(庫、環境、配置)打包到一個 輕量級、可移植的容器 中,這個容器可以在任何支持 Docker 的環境中快速運行。那 Docker 除了簡化部署流程還有那些有點呢?
- 解決“在我的電腦上可以跑”的問題:Docker 容器打包了完整的運行環境,確保無論是在開發、測試還是生產環境,運行效果一致
- 輕量級:容器比虛擬機(VM)更輕,因為它們共用宿主機的操作系統內核,不需要額外的系統資源
- 快速部署與啟動:容器啟動速度快,適合微服務、自動化部署、持續集成/持續交付(CI/CD)場景。
-
易于拓展和管理:配合 Kubernetes 等編排工具,Docker 容器可以方便地進行擴縮容和負載均衡。
-
方便版本控制和回滾:Docker 鏡像支持版本化,能快速切換或回退到指定版本。
入門使用
部署對比
我們先對比?Docker 部署 MySQL 和傳統部署 MySQL 的方式:
傳統部署 MySQL 的話需要:
- 搜索并下載 MySQL 的安裝包
- 上傳至 Linux 環境
- 編譯和配置環境
- 安裝,之后還要進行一些權限設置
Docker 部署 MySQL 只需要在命令行中輸入一下命令:
docker run -d \--name mysql \-p 3306:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \mysql:8.0(版本號)
輸入完之后 Docker 就會去自動搜索并下載了 MySQL ,然后自動運行 MySQL??,這個時候你可以通過任意客戶端工具去連接它了。
并且,這種方式完全不用考慮運行的操作系統的環境,這條命令在所有的操作系統中,比如 CentOS,Ubuntu,MacOS,Kali 等等,都是可以用這一條命令運行的。
不同的操作系統下,一個軟件的安裝包,運行環境都是不相同的。如果是手動安裝,必須手動解決安裝包不同,運行環境不同,配置環境不同的問題。比如你安裝軟件的時候是不是下面都會有 Windows ,MacOS,Linux 這三個版本的安裝包供你下載,并且每一個系統的安裝包版本還可以細分更多別的版本,這個時候你要手動選擇適合你電腦系統的版本安裝包,而 Docker 就不需要這些,直接“一鍵式”安裝,有點像 Mac 里面的 Homebrew。
在使用 Docker 的時候,以上說的這些完全不用考慮,因為 Docker 會自動搜索并下載 MySQL。但是這里的下載的并不是安裝包,而是它的“鏡像”。鏡像中不僅僅包含了 MySQL 本身,還包含了它運行所需要的環境,配置,系統級函數庫,因此在它運行的時候就會有自己獨立的環境,就可以跨系統運行,也不需要手動配置環境了。這套獨立的隔離環境被稱為“容器”。所以,Docker 安裝軟件的過程,就是自動搜索下載鏡像,然后創建并運行容器的過程。
鏡像倉庫
Docker 官方提供了一個專門管理,存儲鏡像的網站,并對外開放了鏡像上傳,下載的權利。Docker 官方提供了一些基礎的鏡像,然后各大軟件公司又在基礎鏡像的基礎上,制作了自家軟件的鏡像,全部存放在這個網站,并成了 Docker 鏡像的交流社區(這套機制有點像 Maven 倉庫):Docker 鏡像交流社區。因此 Docker 會根據命令中的鏡像名稱自動去這里面搜索并下載鏡像。
像這種提供存儲,管理 Docker 鏡像的服務器,被稱為 “DockerRegistry”。DockerHub 網站是官方倉庫,但是官方倉庫在國外,下載速度較慢,一般都會使用阿里云,華為云等等的第三方倉庫提供鏡像加速功能,提高下載速度。我們也可自己搭建私有的鏡像倉庫,而企業內部的機密項目,往往就會采用私有鏡像倉庫,所以鏡像來源有:
- 基于官方基礎鏡像自己制作
- 直接去 DockerRegistry 下載
總結:Docker 本身包含一個后臺服務,我們可以利用 Docker 命令告訴服務,幫我們快速部署指定應用。Docker 服務部署應用的時候,首先去搜索并下載應用對應的鏡像,然后根據鏡像創建并運行容器,應用就部署完成了:
命令解釋
我們就先針對上面的那一條部署 MySQL 命令去解釋一下:
- docker run -d:創建并運行一個容器,-d 則是讓容器以后臺進程運行
- --name mysql:給容器起個名字叫 mysql,這個名字也是可以起別的
- -p 3306:3306:設置端口映射。容器是隔離環境,外界不可訪問,但是可以將宿主機端口映射到容器內的端口,當訪問宿主機指定端口的時候,就是在訪問容器內端口了。容器內端口往往是由容器內進程決定的,例如 MySQL 進程默認端口是3306,因此容器內端口一定是3306,而宿主機端口可以任意綁定;格式: -p 宿主機端口:容器內端口,示例端口就是將宿主機的3306映射到容器內的3306端口
- -e TZ=Asia/Shanghai:配置容器內進程運行的一些參數,格式:-e KEY = VALUE ,KEY 和 VALUE 都是由容器內進程決定,上面的 TZ = Asia/Shanghai 是設置時區;MYSQL_ROOT_PASSWORD=123 是設置 MYSQL 的默認密碼
- mysql:設置鏡像名稱,Docker 會根據這個名字搜索并下載鏡像;格式:REPOSITORY:TAG,例如 mysql:8.0 ,其中 REPOSITORY 可以理解鏡像名稱,TAG 是版本號。在未指定TAG的情況下,默認是最新版本,也就是 mysql:latest
鏡像名稱不是隨意的,而是要到 DockerRegistry 中尋找,鏡像運行時的配置也不是隨意的,要參考鏡像的幫助文檔,這些 DockerHub 網站或者軟件的官方網站中都能找到;如果我們要安裝其他軟件,也可以到 DockerRegistry 中尋找對應的鏡像名稱和版本,閱讀相關配置。
基礎
常見命令
圖片表示:
默認情況下,每次重啟虛擬機都需要手動重啟 Docker 和 Docker 中的容器,通過一下命令可以實現開機自啟動:
# Docker 開機自啟動 systemctl enable docker@ Docker 容器開機自啟 docker update --restart=always [容器名/容器id]
示例
接下來就是通過 Docker 部署一個 Nginx 來演示:
# 去 DockerHub 查看 Nginx 鏡像倉庫以及信息
# 拉取 Nginx 鏡像
docker pull nginx#查看鏡像,如果成功拉取的話就會顯示在下面中
docker images# 創建并允許 Nginx 容器
docker run -d --name nginx -p 90:80 nginx# 查看運行中的容器(格式化訪問)
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Name}}"# 訪問對應的網頁
# 停止容器
docker stop nginx# 查看所有容器
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Name}}"# 再次啟動 Nginx 容器
docker start nginx# 再次查看容器
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Name}}"# 查看容器的詳細信息
docker inspect nginx# 進入容器,查看容器內的目錄
docker exec -it nginx bash
# 這是進入 mysql 容器并進入 mysql
docker exec -it mysql mysql -uroot -p# 刪除容器
docker rm nginx# 強制刪除容器
docker rm -f nginx
由于格式化查看容器的命令較長,但是不格式話查看的話無用信息又很多,所以我們可以給命令取別名,方便以后進行訪問:
# 修改/root/.bashrc文件 vi /root/.bashrc 內容如下: # .bashrc# User specific aliases and functionsalias rm='rm -i' alias cp='cp -i' alias mv='mv -i' alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"' alias dis='docker images'# Source global definitions if [ -f /etc/bashrc ]; then. /etc/bashrc fi# 退出去之后 source ./.bashrc 使得命令生效
?數據卷的使用
數據存儲一直是不管軟件還是硬件都需要考慮的重要問題,而容器也需要考慮自己的數據存儲,但是由于容器是隔離環境,容器內的程序的文件,配置,運行時產生的數據都在容器內部,所以讀寫容器內的文件不是很方便,因此,容器提供程序的運行環境,但是程序運行時產生的數據,依賴的配置都應該與容器進行解耦合。
數據卷的概念
數據卷(volume)是一個虛擬目錄,是容器內目錄與宿主機目錄之間映射的橋梁。以剛剛拉取的 Nginx 為例,Nginx 中有兩個關鍵的目錄:
- html:放置靜態資源
- conf:放置配置文件
如果要讓 Nginx 代理靜態資源,最好是放在 html 目錄;如果要修改 Nginx 的配置,最好在 conf 目錄下面的 nginx.conf 文件,但是容器運行的 Nginx 所有文件都在容器內部,所以需要利用數據卷將兩個目錄與宿主機目錄進行關聯,方便修改操作,而且他們還是雙向綁定的,就是你宿主機的目錄中文件修改的話,對應的容器內的數據也會被修改。
假設我們創建了兩個數據卷:conf,html;Nginx 容器內部的 conf 目錄和 html 目錄分別與兩個數據卷關聯,而數據卷 conf 和 html 分別指向了宿主機對應的 /var/lib/docker/volumes/conf/_data 目錄和 /var/lib/docker/volumes/html /_data 目錄;
這樣的話,容器內的 conf 和 html 目錄就和宿主機的 conf 和 html 目錄關聯起來了,稱為掛載。此時,在宿主機操作 /var/lib/docker/volumes/html/_data 就是在操作容器內的 /usr/share/nginx/html/_data 目錄。只要將靜態資源放入宿主機對應目錄,就可以被 Nginx 代理。
/var/lib/docker/volumes 就是 Linux 下默認存放所有容器數據卷的目錄,其下再根據數據卷名稱創建新目錄,格式為 /數據卷名/_data
之所以不讓容器目錄直接指向宿主機目錄,而是通過數據卷當中間橋梁間接訪問,是因為如果直接指向宿主機目錄就與宿主機強耦合了,如果切換了環境,宿主機目錄就可能會發生改變,但由于容器一旦創建,目錄掛載就無法修改,這樣容器就無法正常工作;但是容器指向數據卷,一個邏輯名稱,而數據卷再指向宿主機目錄,就不存在強耦合。如果宿主機目錄發生改變,只要改變數據卷與宿主機之前的映射關系就行。
數據卷的使用
注意:容器與數據卷的掛載要在創建容器時配置,對于創建好的容器,是不能設置數據卷的。而且創建容器的過程中,數據卷會自動創建。
1,下面可以看一下 Nginx 的目錄掛載:
# 首先創建容器并指定數據卷,注意通過 -v 參數在最后來指定數據卷
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx# 然后查看數據卷
docker volume ls# 查看數據卷詳情
docker volume inspect html
# 結果
[{"CreatedAt": "2024-05-17T19:57:08+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/html/_data","Name": "html","Options": null,"Scope": "local"}
]# 查看 /var/lib/docker/volumes/html/_data 目錄
ls /var/lib/docker/volumes/html/_data# 進入目錄并修改 index.html 內容
cd /var/lib/docker/volumes/html/_data
vim index.html# 進入容器內部。查看 /var/share/nginx/html 目錄內的文件是否變化
docker exec -it nginx bash
2,當我們拉取 MySQL 的鏡像沒有 -v 指定參數時候,但是通過 docker inspect mysql 命令查看容器信息的時候,會發現在 .Config.Volumes 這個信息的下面會發現 MySQL 這個容器聲明了一個本地目錄,需要掛載數據卷,但是數據卷未定義,這就是匿名卷:
{"Config": {// ... 略"Volumes": {"/var/lib/mysql": {}}// ... 略}
}
然后再觀察 .Mounts 部分:
{"Mounts": [{"Type": "volume","Name": "29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f","Source": "/var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data","Destination": "/var/lib/mysql","Driver": "local",}]
}
可以發現:
- Name:數據卷名稱。由于定義容器未設置容器名稱,這里就是匿名卷自動生成的名字,一串 hash 值。
- Source:宿主機目錄
- Destination:容器內的目錄
解釋:上面的配置是將容器內的 /var/lib/mysql 這個目錄,與數據卷?29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f 掛載,于是在宿主機中就有了 /var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data 這個目錄。這就是匿名數據卷對應的目錄。然后就可以切換到改目錄下面查看 MySQL 的 data 文件.
掛載本地目錄文件
我們除了使用數據卷進行間接訪問,還可以直接將容器文件掛載到本地文件,只不過正如上訴所說有一定的弊端,但是數據卷的目錄結構較深,直接去操作數據卷目錄不是很方便,在很多情況下,會直接將容器目錄與宿主機指定目錄掛載。掛載語法與數據卷類似:
- 掛載本地目錄:-v 本地目錄:容器內目錄
- 掛載本地文件:-v 本地文件:容器內文件
注意:本地目錄或者文件必須以 / 或 ./ 開頭,如果直接以名字開頭,會被識別為數據卷名字。
- -v mysql:/var/lib/mysql? 會被識別為一個數據卷名字叫 mysql ,并在運行時創建這個數據卷
- -v ./mysql:/var/lib/mysql 會被識別為當前目錄下面的 mysql 目錄,運行時不存在會自動創建
演示:掛載 MySQL 的數據和配置目錄
# 創建并運行新mysql容器,掛載本地目錄
docker run -d \--name mysql \-p 3306:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \-v ./mysql/data:/var/lib/mysql \-v ./mysql/conf:/etc/mysql/conf.d \-v ./mysql/init:/docker-entrypoint-initdb.d \mysql
鏡像
之前所說我們拉取的鏡像,有部分是官方提供的,有部分是一些廠商提供的,而我們也可以自定義一些鏡像供別人進行部署,這就叫打包鏡像。
結構
鏡像之所以能夠跨操作系統部署應用而忽略其運行環境,配置,就是因為鏡像中包含了程序運行需要的系統函數庫,環境,配置,依賴。因此自定義鏡像本質就是一次準備好程序運行的基礎環境,依賴,應用本身,運行配置等文件,并且打包而成,一下以部署 Java 項目為例:
傳統部署:
- 準備 Linux 服務器
- 安裝配置 JDK?
- 上傳 jar 包
- 運行 jar 包
打包鏡像:
- 準備 Linux 運行環境
- 暗轉配置 JDK
- 拷貝 jar 包
- 配置啟動腳本
上訴操作中每一步都是在生產一些文件(系統運行環境,函數庫,配置,最后都是磁盤文件),所以鏡像就是一堆文件的集合。但是,鏡像文件不是隨意堆放的,而是按照操作的步驟分層疊加的,每一層形成的文件都會單獨打包并標記一個唯一 id ,稱為 Layer (層) 。這樣,如果我們構建時用到某些層其他人已經或者已經拉取過了,就可以直接拷貝使用這些層,而不用重復拉取。
在第一步中需要的 Linux 運行環境就有很強的通用性,所以 Docker 官方就制作了這樣的只包含 Linux 運行環境的鏡像,在制作 Java 鏡像的時候,就不需要重復制作,直接使用 Docker 官方提供的 Linux (CentOS/Ubuntu) 鏡像作為基礎鏡像,然后再搭建其他層:
Dockerfile
在制作鏡像的過程中,需要逐層打包較為復雜,所以 Docker 就提供了自動打包鏡像的功能。只需將打包的過程,每一層要做的事情用固定的語法寫下來,交給 Docker 執行,而這種記錄鏡像結構的文件就是 Dockerfile(注意區分下面要要講解的Docker-compose)?。下面是基礎語法:
一下是基于 Ubuntu 構建 Java 應用的 Dockerfile:
# 指定基礎鏡像
FROM ubuntu:16.04# 配置環境變量,JDK 安裝目錄,容器時區
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai# 拷貝 JDK 和 Java 項目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar# 設定時區
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 安裝 JDK
RUN cd $JAVA_DIR && tar -xf ./jdk8.tar.gz && mv ./jdk1.8.0_144 ./java8# 配置環境變量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin# 指定項目監聽的端口
EXPOSE 8080# 入口,Java 項目的啟動命令
ENTRYPOINT ["java","-jar","/app.jar"]
我們每次構建 Java 鏡像的時候,都需要拉取 Linux 和 JDK 環境,所以有人提供了基礎的系統加 JDK 環境,在此基礎上制作 Java 鏡像,就可以省去 JDK 的配置了:
# 基礎鏡像
FROM openjdk:11.0-jre-buster
# 設定時區
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷貝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
構建鏡像:
# 進入鏡像目錄
cd /root/demo# 開始構建
docker build -t docker-demo:1.0 .
- docker build:就是構建一個 docker 鏡像
- -t docker-demo:1.0: -t 參數是指定鏡像的名字
- .:最后的點是構建鏡像的時候 Dockerfile 所在的目錄
運行完之后再次查看鏡像列表就可以查看到對應的鏡像了,運行之后就可以正常訪問對應的 Java 項目了。
容器網絡
在 Java 項目中需要訪問其它各種中間件,例如 MySQL ,Redis 等,容器之間也能通過 IP:Port 進行相互訪問,每個容器都有自己的獨立的 IP 地址(每次重啟的時候隨機分配):
# 用基本命令,尋找 Networks.bridge.IPAddress 屬性并使用 format 過濾結果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql# 得到 IP 地址:
172.17.0.2# 通過命令進入其它任意容器
docker exec -it anyone bash# 通過 ping 命令進行測試網絡
ping 172.17.0.2# 正常返回結果
雖然可以訪問,但是容器內的網絡 IP 其實是一個虛擬 IP ,這個 IP 并不固定的與某一個容器進行綁定,如果在開發的時候寫死某一個容器 IP,在部署的時候可以那個容器的 IP 就會發生變化導致連接失敗。Docker 就有一個方法解決這個問題:在容器中創建一個虛擬的局域網絡,然后讓需要被部署的容器都加入到這個網絡中,雖然每一個容器的 IP 仍然會發送變化,但是在同一個網絡中,容器之間相互通訊就不是通過 IP 了,而是通過容器名字進行查找連接,這樣就不會因為 IP 的變化而找不到對應的容器了,畢竟每一個容器的名字是唯一的;常見命令:
自定義網絡:
# 創建網絡
docker network create chase# 查看網絡
docker network ls# 讓 nginx 和 mysql 都加入到該網絡中,并取別名(可選)
docker network connect chase mysql --alias db
docker network connect chase nginx# 進入 nginx 容器,并嘗試訪問 db 容器
docker exec -it nginx bash
ping db
# 也可以通過容器名字訪問
ping mysql
部署
DockerCompose
當我們部署的 Java 項目較為復雜的時候,可能包含很多容器(中間件),如果手動部署的話就顯得很麻煩了。這時,DockerCompose 就可以幫助我們實現多個相互關聯的 Docker 容器快速部署。它允許用戶通過一個單獨的 docker-compose.yml 模版文件(yaml 格式)(與上面說的Dockerfile區分)來定義一組相關聯的應用。
語法
docker-compose 文件中可以定義多個相互關聯的應用容器,每一個應用容器被稱為服務(service)。由于 service 就是定義某個應用的運行時參數,因此與 docker run 參數類似;
我們將用 docker run 部署 MySQL 和用 docker-compose.yml 來定義 MySQL 對比一下:
# 用 docker run
docker run -d \--name mysql \-p 3306:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \-v ./mysql/data:/var/lib/mysql \-v ./mysql/conf:/etc/mysql/conf.d \-v ./mysql/init:/docker-entrypoint-initdb.d \--network chasemysql:3.8# 用 docker-compose.yml
version: "3.8"services:mysql:image: mysqlcontainer_name: mysqlports:- "3306:3306"environment:TZ: Asia/ShanghaiMYSQL_ROOT_PASSWORD: 123volumes:- "./mysql/conf:/etc/mysql/conf.d"- "./mysql/data:/var/lib/mysql"networks:- new
networks:new:name: chase
基礎命令
在編寫好 docker-compose.yml 文件之后就可以通過一些常見命令來部署項目了:
# 基本語法
docker compose options command
# 進入 root 目錄
cd /root# 刪除舊容器
docker rm -f contrainer_name# 刪除 chase 鏡像
docker rmi chase# 清空 MySQL 數據
rm -rf mysql/data# 啟動所有
docker compose up -d# 查看鏡像
docker compose images# 查看容器
docker compose ps
以上就是本文的全部內容,涵蓋了幾乎日常中使用 Docker 所需要的全部命令,也不用全部背下來,可以點個收藏加關注,需要的時候在來這邊找對應的命令就行了,我也是用的時候才去找對應的命令 (^ - ^ )。