【Docker】快速入門與項目部署實戰

我們在部署一個項目時,會出現一系列問題比如:

  • 命令太多了,記不住
  • 軟件安裝包名字復雜,不知道去哪里找
  • 安裝和部署步驟復雜,容易出錯

其實上述問題不僅僅是新手,即便是運維在安裝、部署的時候一樣會覺得麻煩、容易出錯。
特別是我們即將進入微服務階段學習,微服務項目動輒就是幾十臺、上百臺服務需要部署,有些大型項目甚至達到數萬臺服務。而由于每臺服務器的運行環境不同,你寫好的安裝流程、部署腳本并不一定在每個服務器都能正常運行,經常會出錯。這就給系統的部署運維帶來了很多困難。

那么,有沒有一種技術能夠避免部署對服務器環境的依賴,減少復雜的部署流程呢?
答案是肯定的,這就是我們今天要學習的Docker技術。你會發現,有了Docker以后項目的部署如絲般順滑,大大減少了運維工作量。
即便你對Linux不熟悉,你也能輕松部署各種常見軟件、Java項目。

通過今天的學習,希望大家能達成下面的學習目標:

  • 能利用Docker部署常見軟件
  • 能利用Docker打包并部署Java應用
  • 理解Docker數據卷的基本作用
  • 能看懂DockerCompose文件

1.快速入門

要想讓Docker幫我們安裝和部署軟件,肯定要保證你的機器上有Docker. 由于大家的操作系統各不相同,安裝方式也不同。為了便于大家學習,我們統一在CentOS的虛擬機中安裝Docker,統一學習環境。
注意:使用MacBook的同學也請利用 VMwareFusion來安裝虛擬機,并在虛擬機中學習Docker使用。

安裝方式參考文檔:《安裝Docker》

1.1.部署MySQL

首先,我們利用Docker來安裝一個MySQL軟件,大家可以對比一下之前傳統的安裝方式,看看哪個效率更高一些。
如果是利用傳統方式部署MySQL,大概的步驟有:

  • 搜索并下載MySQL安裝包
  • 上傳至Linux環境
  • 編譯和配置環境
  • 安裝

而使用Docker安裝,僅僅需要一步即可,在命令行輸入下面的命令(建議采用CV大法):

docker run -d \--name mysql \-p 3306:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \mysql

運行效果如圖:

在這里插入圖片描述

MySQL安裝完畢!通過任意客戶端工具即可連接到MySQL.

大家可以發現,當我們執行命令后,Docker做的第一件事情,是去自動搜索并下載了MySQL,然后會自動運行MySQL,我們完全不用插手,是不是非常方便。

而且,這種安裝方式你完全不用考慮運行的操作系統環境,它不僅僅在CentOS系統是這樣,在Ubuntu系統、macOS系統、甚至是裝了WSL的Windows下,都可以使用這條命令來安裝MySQL。
要知道,不同操作系統下其安裝包、運行環境是都不相同的!如果是手動安裝,必須手動解決安裝包不同、環境不同的、配置不同的問題!

而使用Docker,這些完全不用考慮。就是因為Docker會自動搜索并下載MySQL。注意:這里下載的不是安裝包,而是鏡像。鏡像中不僅包含了MySQL本身,還包含了其運行所需要的環境、配置、系統級函數庫。因此它在運行時就有自己獨立的環境,就可以跨系統運行,也不需要手動再次配置環境了。這套獨立運行的隔離環境我們稱為容器。
說明:

  • 鏡像:英文是image
  • 容器:英文是container

因此,Docker安裝軟件的過程,就是自動搜索下載鏡像,然后創建并運行容器的過程。

Docker會根據命令中的鏡像名稱自動搜索并下載鏡像,那么問題來了,它是去哪里搜索和下載鏡像的呢?這些鏡像又是誰制作的呢?

Docker官方提供了一個專門管理、存儲鏡像的網站,并對外開放了鏡像上傳、下載的權利。Docker官方提供了一些基礎鏡像,然后各大軟件公司又在基礎鏡像基礎上,制作了自家軟件的鏡像,全部都存放在這個網站。這個網站就成了Docker鏡像交流的社區:
https://hub.docker.com/
基本上我們常用的各種軟件都能在這個網站上找到,我們甚至可以自己制作鏡像上傳上去。

像這種提供存儲、管理Docker鏡像的服務器,被稱為DockerRegistry,可以翻譯為鏡像倉庫。DockerHub網站是官方倉庫,阿里云、華為云會提供一些第三方倉庫,我們也可以自己搭建私有的鏡像倉庫。
官方倉庫在國外,下載速度較慢,一般我們都會使用第三方倉庫提供的鏡像加速功能,提高下載速度。而企業內部的機密項目,往往會采用私有鏡像倉庫。

總之,鏡像的來源有兩種:

  • 基于官方基礎鏡像自己制作
  • 直接去DockerRegistry下載

總結一下:
Docker本身包含一個后臺服務,我們可以利用Docker命令告訴Docker服務,幫助我們快速部署指定的應用。Docker服務部署應用時,首先要去搜索并下載應用對應的鏡像,然后根據鏡像創建并允許容器,應用就部署完成了。

用一幅圖標示如下:
在這里插入圖片描述

1.2.命令解讀

利用Docker快速的安裝了MySQL,非常的方便,不過我們執行的命令到底是什么意思呢?

docker run -d \--name mysql \-p 3306:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \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中尋找對應的鏡像名稱和版本,閱讀相關配置即可。

2.Docker基礎

接下來,我們一起來學習Docker使用的一些基礎知識,為將來部署項目打下基礎。具體用法可以參考Docker官方文檔:
https://docs.docker.com/

2.1.常見命令

首先我們來學習Docker中的常見命令,可以參考官方文檔:
https://docs.docker.com/engine/reference/commandline/cli/

2.1.1.命令介紹

其中,比較常見的命令有:
在這里插入圖片描述

用一副圖來表示這些命令的關系:
在這里插入圖片描述

補充:
默認情況下,每次重啟虛擬機我們都需要手動啟動Docker和Docker中的容器。通過命令可以實現開機自啟:

# Docker開機自啟
systemctl enable docker# Docker容器開機自啟
docker update --restart=always [容器名/容器id]

2.1.2.演示

教學環節說明:我們以Nginx為例給大家演示上述命令。

# 第1步,去DockerHub查看nginx鏡像倉庫及相關信息# 第2步,拉取Nginx鏡像
docker pull nginx# 第3步,查看鏡像
docker images
# 結果如下:
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        latest    605c77e624dd   16 months ago   141MB
mysql        latest    3218b38490ce   17 months ago   516MB# 第4步,創建并允許Nginx容器
docker run -d --name nginx -p 80:80 nginx# 第5步,查看運行中容器
docker ps
# 也可以加格式化方式訪問,格式會更加清爽
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第6步,訪問網頁,地址:http://虛擬機地址# 第7步,停止容器
docker stop nginx# 第8步,查看所有容器
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第9步,再次啟動nginx容器
docker start nginx# 第10步,再次查看容器
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第11步,查看容器詳細信息
docker inspect nginx# 第12步,進入容器,查看容器內目錄
docker exec -it nginx bash
# 或者,可以進入MySQL
docker exec -it mysql mysql -uroot -p# 第13步,刪除容器
docker rm nginx
# 發現無法刪除,因為容器運行中,強制刪除容器
docker rm -f nginx

2.1.3.命令別名

給常用Docker命令起別名,方便我們訪問:

# 修改/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 /root/.bashrc

接下來,試試看新的命令吧。

2.2.數據卷

容器是隔離環境,容器內程序的文件、配置、運行時產生的容器都在容器內部,我們要讀寫容器內的文件非常不方便。大家思考幾個問題:

  • 如果要升級MySQL版本,需要銷毀舊容器,那么數據豈不是跟著被銷毀了?
  • MySQL、Nginx容器運行后,如果我要修改其中的某些配置該怎么辦?
  • 我想要讓Nginx代理我的靜態資源怎么辦?

因此,容器提供程序的運行環境,但是程序運行產生的數據、程序運行依賴的配置都應該與容器解耦

2.2.1.什么是數據卷

數據卷(volume)是一個虛擬目錄,是容器內目錄宿主機目錄之間映射的橋梁。
以Nginx為例,我們知道Nginx中有兩個關鍵的目錄:

  • html:放置一些靜態資源
  • conf:放置配置文件
    如果我們要讓Nginx代理我們的靜態資源,最好是放到html目錄;如果我們要修改Nginx的配置,最好是找到conf下的nginx.conf文件。
    但遺憾的是,容器運行的Nginx所有的文件都在容器內部。所以我們必須利用數據卷將兩個目錄與宿主機目錄關聯,方便我們操作。如圖:
    在這里插入圖片描述

在上圖中:

  • 我們創建了兩個數據卷:confhtml
  • Nginx容器內部的conf目錄和html目錄分別與兩個數據卷關聯。
  • 而數據卷confhtml分別指向了宿主機的/var/lib/docker/volumes/conf/_data目錄和/var/lib/docker/volumes/html/_data目錄
    這樣以來,容器內的confhtml目錄就 與宿主機的confhtml目錄關聯起來,我們稱為掛載。此時,我們操作宿主機的/var/lib/docker/volumes/html/_data就是在操作容器內的/usr/share/nginx/html/_data目錄。只要我們將靜態資源放入宿主機對應目錄,就可以被Nginx代理了。

小提示:
/var/lib/docker/volumes這個目錄就是默認的存放所有容器數據卷的目錄,其下再根據數據卷名稱創建新目錄,格式為/數據卷名/_data。

為什么不讓容器目錄直接指向宿主機目錄呢?

  • 因為直接指向宿主機目錄就與宿主機強耦合了,如果切換了環境,宿主機目錄就可能發生改變了。由于容器一旦創建,目錄掛載就無法修改,這樣容器就無法正常工作了。
  • 但是容器指向數據卷,一個邏輯名稱,而數據卷再指向宿主機目錄,就不存在強耦合。如果宿主機目錄發生改變,只要改變數據卷與宿主機目錄之間的映射關系即可。

不過,我們通過由于數據卷目錄比較深,不好尋找,通常我們也允許讓容器直接與宿主機目錄掛載而不使用數據卷,具體參考2.2.3小節。


2.2.2.數據卷命令

數據卷的相關命令有:
在這里插入圖片描述

注意:容器與數據卷的掛載要在創建容器時配置,對于創建好的容器,是不能設置數據卷的。而且創建容器的過程中,數據卷會自動創建。

教學演示環節:演示一下nginx的html目錄掛載

# 1.首先創建容器并指定數據卷,注意通過 -v 參數來指定數據卷
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx# 2.然后查看數據卷
docker volume ls
# 結果
DRIVER    VOLUME NAME
local     29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f
local     html# 3.查看數據卷詳情
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"}
]# 4.查看/var/lib/docker/volumes/html/_data目錄
ll /var/lib/docker/volumes/html/_data
# 可以看到與nginx的html目錄內容一樣,結果如下:
總用量 8
-rw-r--r--. 1 root root 497 1228 2021 50x.html
-rw-r--r--. 1 root root 615 1228 2021 index.html# 5.進入該目錄,并隨意修改index.html內容
cd /var/lib/docker/volumes/html/_data
vi index.html# 6.打開頁面,查看效果# 7.進入容器內部,查看/usr/share/nginx/html目錄內的文件是否變化
docker exec -it nginx bash

教學演示環節:演示一下MySQL的匿名數據卷

# 1.查看MySQL容器詳細信息
docker inspect mysql
# 關注其中.Config.Volumes部分和.Mounts部分

我們關注兩部分內容,第一是.Config.Volumes部分:

{"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文件:

ls -l /var/lib/docker/volumes/29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f/_data

注意:每一個不同的鏡像,將來創建容器后內部有哪些目錄可以掛載,可以參考DockerHub對應的頁面

2.2.3.掛載本地目錄或文件

可以發現,數據卷的目錄結構較深,如果我們去操作數據卷目錄會不太方便。在很多情況下,我們會直接將容器目錄與宿主機指定目錄掛載。掛載語法與數據卷類似:

# 掛載本地目錄
-v 本地目錄:容器內目錄
# 掛載本地文件
-v 本地文件:容器內文件

注意:本地目錄或文件必須以 / ./開頭,如果直接以名字開頭,會被識別為數據卷名而非本地目錄名。

例如:

-v mysql:/var/lib/mysql # 會被識別為一個數據卷叫mysql,運行時會自動創建這個數據卷
-v ./mysql:/var/lib/mysql # 會被識別為當前目錄下的mysql目錄,運行時如果不存在會創建目錄

教學演示,刪除并重新創建mysql容器,并完成本地目錄掛載:

  • 掛載/root/mysql/data到容器內的/var/lib/mysql目錄
  • 掛載/root/mysql/init到容器內的/docker-entrypoint-initdb.d目錄(初始化的SQL腳本目錄)
  • 掛載/root/mysql/conf到容器內的/etc/mysql/conf.d目錄(這個是MySQL配置文件目錄)

在資料中已經準備好了mysql的init目錄和 conf 目錄:

在這里插入圖片描述
以及對應的初始化SQL腳本和配置文件:
在這里插入圖片描述

其中,hm.cnf主要是配置了MySQL的默認編碼,改為utf8mb4;而hmall.sql則是后面我們要用到的黑馬商城項目的初始化SQL腳本。

我們直接將整個mysql目錄上傳至虛擬機的/root目錄下:

在這里插入圖片描述

接下來,我們演示本地目錄掛載:

# 1.刪除原來的MySQL容器
docker rm -f mysql# 2.進入root目錄
cd ~# 3.創建并運行新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# 4.查看root目錄,可以發現~/mysql/data目錄已經自動創建好了
ls -l mysql
# 結果:
總用量 4
drwxr-xr-x. 2 root    root   20 519 15:11 conf
drwxr-xr-x. 7 polkitd root 4096 519 15:11 data
drwxr-xr-x. 2 root    root   23 519 15:11 init# 查看data目錄,會發現里面有大量數據庫數據,說明數據庫完成了初始化
ls -l data# 5.查看MySQL容器內數據
# 5.1.進入MySQL
docker exec -it mysql mysql -uroot -p123
# 5.2.查看編碼表
show variables like "%char%";
# 5.3.結果,發現編碼是utf8mb4沒有問題
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+# 6.查看數據
# 6.1.查看數據庫
show databases;
# 結果,hmall是黑馬商城數據庫
+--------------------+
| Database           |
+--------------------+
| hmall              |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
# 6.2.切換到hmall數據庫
use hmall;
# 6.3.查看表
show tables;
# 結果:
+-----------------+
| Tables_in_hmall |
+-----------------+
| address         |
| cart            |
| item            |
| order           |
| order_detail    |
| order_logistics |
| pay_order       |
| user            |
+-----------------+
# 6.4.查看address表數據
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| id | user_id | province | city   | town     | mobile      | street        | contact   | is_default | notes |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| 59 |       1 | 北京     | 北京   | 朝陽區    | 13900112222 | 金燕龍辦公樓   | 李佳誠    | 0          | NULL  |
| 60 |       1 | 北京     | 北京   | 朝陽區    | 13700221122 | 修正大廈       | 李佳紅    | 0          | NULL  |
| 61 |       1 | 上海     | 上海   | 浦東新區  | 13301212233 | 航頭鎮航頭路   | 李佳星    | 1          | NULL  |
| 63 |       1 | 廣東     | 佛山   | 永春      | 13301212233 | 永春武館       | 李曉龍    | 0          | NULL  |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
4 rows in set (0.00 sec)

2.3.鏡像

前面我們一直在使用別人準備好的鏡像,那如果我要部署一個Java項目,把它打包為一個鏡像該怎么做呢?

2.3.1.鏡像結構

要想自己構建鏡像,必須先了解鏡像的結構。
之前我們說過,鏡像之所以能讓我們快速跨操作系統部署應用而忽略其運行環境、配置,就是因為鏡像中包含了程序運行需要的系統函數庫、環境、配置、依賴。
因此,自定義鏡像本質就是依次準備好程序運行的基礎環境、依賴、應用本身、運行配置等文件,并且打包而成。

舉個例子,我們要從0部署一個Java應用,大概流程是這樣:

  • 準備一個linux服務(CentOS或者Ubuntu均可)
  • 安裝并配置JDK
  • 上傳Jar包
  • 運行jar包

那因此,我們打包鏡像也是分成這么幾步:

  • 準備Linux運行環境(java項目并不需要完整的操作系統,僅僅是基礎運行環境即可)
  • 安裝并配置JDK
  • 拷貝jar包
  • 配置啟動腳本

上述步驟中的每一次操作其實都是在生產一些文件(系統運行環境、函數庫、配置最終都是磁盤文件),所以鏡像就是一堆文件的集合。

但需要注意的是,鏡像文件不是隨意堆放的,而是按照操作的步驟分層疊加而成,每一層形成的文件都會單獨打包并標記一個唯一id,稱為Layer(層)。這樣,如果我們構建時用到的某些層其他人已經制作過,就可以直接拷貝使用這些層,而不用重復制作。

例如,第一步中需要的Linux運行環境,通用性就很強,所以Docker官方就制作了這樣的只包含Linux運行環境的鏡像。我們在制作java鏡像時,就無需重復制作,直接使用Docker官方提供的CentOS或Ubuntu鏡像作為基礎鏡像。然后再搭建其它層即可,這樣逐層搭建,最終整個Java項目的鏡像結構如圖所示:
[圖片]

2.3.2.Dockerfile
由于制作鏡像的過程中,需要逐層處理和打包,比較復雜,所以Docker就提供了自動打包鏡像的功能。我們只需要將打包的過程,每一層要做的事情用固定的語法寫下來,交給Docker去執行即可。
而這種記錄鏡像結構的文件就稱為Dockerfile,其對應的語法可以參考官方文檔:
https://docs.docker.com/engine/reference/builder/

其中的語法比較多,比較常用的有:
在這里插入圖片描述

例如,要基于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環境這兩層,只有上面的3層不同(因為jar包不同)。如果每次制作java鏡像都重復制作前兩層鏡像,是不是很麻煩。

所以,就有人提供了基礎的系統加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"]

是不是簡單多了。

2.3.3.構建鏡像

當Dockerfile文件寫好以后,就可以利用命令來構建鏡像了。
在資料中,我們準備好了一個demo項目及對應的Dockerfile:
在這里插入圖片描述

首先,我們將課前資料提供的docker-demo.jar包以及Dockerfile拷貝到虛擬機的/root/demo目錄:

[圖片]

然后,執行命令,構建鏡像:

# 進入鏡像目錄
cd /root/demo
# 開始構建
docker build -t docker-demo:1.0 .

命令說明:

  • docker build : 就是構建一個docker鏡像
  • -t docker-demo:1.0-t參數是指定鏡像的名稱(repositorytag
  • .: 最后的點是指構建時Dockerfile所在路徑,由于我們進入了demo目錄,所以指定的是.代表當前目錄,也可以直接指定Dockerfile目錄:
# 直接指定Dockerfile目錄
docker build -t docker-demo:1.0 /root/demo

結果:
[圖片]

查看鏡像列表:

#查看鏡像docker images
#結果
REPOSITORY   TAG               IMAGE ID       CREATED              SIZE
doker-demo   latest            cd28ba0789d8   About a minute ago   315MB
redis        latest            860da63e75fb   12 days ago          128MB
nginx        latest            be69f2940aaf   7 weeks ago          192MB
mysql        latest            edbdd97bf78b   8 weeks ago          859MB
mysql        8.0               43006ac274fd   8 weeks ago          772MB
openjdk      11.0-jre-buster   0b489110c503   2 years ago          297MB

然后嘗試運行該鏡像:

# 1.創建并運行容器
docker run -d --name dd -p 8080:8080 docker-demo:1.0
# 2.查看容器
dps
# 結果
CONTAINER ID   IMAGE             PORTS                                                  STATUS         NAMES
78a000447b49   docker-demo:1.0   0.0.0.0:8080->8080/tcp, :::8090->8090/tcp              Up 2 seconds   dd
f63cfead8502   mysql             0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   Up 2 hours     mysql# 3.訪問
curl localhost:8080/hello/count
# 結果:
<h5>歡迎訪問黑馬商城, 這是您第1次訪問<h5>

2.4.網絡

默認情況下,所有容易都是以bridge的方式連接到Docker的一個虛擬網橋上:

在這里插入圖片描述

上節課我們創建了一個Java項目的容器,而Java項目往往需要訪問其它各種中間件,例如MySQL、Redis等。現在,我們的容器之間能否互相訪問呢?我們來測試一下

首先,我們查看下MySQL容器的詳細信息,重點關注其中的網絡IP地址:

# 1.用基本命令,尋找Networks.bridge.IPAddress屬性
docker inspect mysql
# 也可以使用format過濾結果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2# 2.然后通過命令進入dd容器
docker exec -it dd bash# 3.在容器內,通過ping命令測試網絡
ping 172.17.0.2
# 結果
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.058 ms

發現可以互聯,沒有問題。

但是,容器的網絡IP其實是一個虛擬的IP,其值并不固定與某一個容器綁定,如果我們在開發時寫死某個IP,而在部署時很可能MySQL容器的IP會發生變化,連接會失敗。

所以,我們必須借助于docker的網絡功能來解決這個問題,官方文檔:
https://docs.docker.com/engine/reference/commandline/network/
常見命令有:
在這里插入圖片描述
教學演示:自定義網絡

# 1.首先通過命令創建一個網絡
docker network create hmall# 2.然后查看網絡
docker network ls
# 結果:
NETWORK ID     NAME      DRIVER    SCOPE
639bc44d0a87   bridge    bridge    local
403f16ec62a2   hmall     bridge    local
0dc0f72a0fbb   host      host      local
cd8d3e8df47b   none      null      local
# 其中,除了hmall以外,其它都是默認的網絡# 3.讓dd和mysql都加入該網絡,注意,在加入網絡時可以通過--alias給容器起別名
# 這樣該網絡內的其它容器可以用別名互相訪問!
# 3.1.mysql容器,指定別名為db,另外每一個容器都有一個別名是容器名
docker network connect hmall mysql --alias db
# 3.2.db容器,也就是我們的java項目
docker network connect hmall dd# 4.進入dd容器,嘗試利用別名訪問db
# 4.1.進入容器
docker exec -it dd bash
# 4.2.用db別名訪問
ping db
# 結果
PING db (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.056 ms
# 4.3.用容器名訪問
ping mysql
# 結果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms
# 實現自定義同一網橋內的容器之間就可以相互訪問了

OK,現在無需記住IP地址也可以實現容器互聯了。

總結:

  • 在自定義網絡中,可以給容器起多個別名,默認的別名是容器名本身
  • 在同一個自定義網絡中的容器,可以通過別名互相訪問

3.項目部署

好了,我們已經熟悉了Docker的基本用法,接下來可以嘗試部署項目了。
在課前資料中已經提供了一個黑馬商城項目給大家,如圖:

項目說明:

  • hmall:商城的后端代碼
  • hmall-portal:商城用戶端的前端代碼
  • hmall-admin:商城管理端的前端代碼

部署的容器及端口說明:

在這里插入圖片描述

在正式部署前,我們先刪除之前的nginx、dd兩個容器:

docker rm -f nginx dd

mysql容器中已經準備好了商城的數據,所以就不再刪除了。

3.1.部署Java項目

hmall項目是一個maven聚合項目,使用IDEA打開hmall項目,查看項目結構如圖:
在這里插入圖片描述

我們要部署的就是其中的hm-service,其中的配置文件采用了多環境的方式:

在這里插入圖片描述

其中的application-dev.yaml是部署到開發環境的配置,application-local.yaml是本地運行時的配置。

查看application.yaml,你會發現其中的JDBC地址并未寫死,而是讀取變量:

這兩個變量在application-dev.yaml和application-local.yaml中并不相同:

在這里插入圖片描述

在dev開發環境(也就是Docker部署時)采用了mysql作為地址,剛好是我們的mysql容器名,只要兩者在一個網絡,就一定能互相訪問。

我們將項目打包:
在這里插入圖片描述

結果:
在這里插入圖片描述

將hm-service目錄下的Dockerfile和hm-service/target目錄下的hm-service.jar一起上傳到虛擬機的root目錄:

部署項目:

# 1.構建項目鏡像,不指定tag,則默認為latest
docker build -t hmall .# 2.查看鏡像
docker images
# 結果
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
hmall         latest    0bb07b2c34b9   43 seconds ago   362MB
docker-demo   1.0       49743484da68   24 hours ago     327MB
nginx         latest    605c77e624dd   16 months ago    141MB
mysql         latest    3218b38490ce   17 months ago    516MB# 3.創建并運行容器,并通過--network將其加入hmall網絡,這樣才能通過容器名訪問mysql
docker run -d --name hmall --network hmall -p 8080:8080 hmall

測試,通過瀏覽器訪問:http://你的虛擬機地址:8080/search/list

3.2.部署前端

hmall-portalhmall-admin是前端代碼,需要基于nginx部署。在課前資料中已經給大家提供了nginx的部署目錄:

在這里插入圖片描述

其中:

  • html是靜態資源目錄,我們需要把hmall-portal以及hmall-admin都復制進去
  • nginx.conf是nginx的配置文件,主要是完成對html下的兩個靜態資源目錄做代理

我們現在要做的就是把整個nginx目錄上傳到虛擬機的/root目錄下:

在這里插入圖片描述

然后創建nginx容器并完成兩個掛載:

  • /root/nginx/nginx.conf掛載到/etc/nginx/nginx.conf
  • /root/nginx/html掛載到/usr/share/nginx/html

由于需要讓nginx同時代理hmall-portalhmall-admin兩套前端資源,因此我們需要暴露兩個端口:

  • 18080:對應hmall-portal
  • 18081:對應hmall-admin
    命令如下:
docker run -d \--name nginx \-p 18080:18080 \-p 18081:18081 \-v /root/nginx/html:/usr/share/nginx/html \-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \--network lk_xiao_du \nginx

測試,通過瀏覽器訪問:http://你的虛擬機ip:18080

在這里插入圖片描述

3.3.DockerCompose

大家可以看到,我們部署一個簡單的java項目,其中包含3個容器:

  • MySQL
  • Nginx
  • Java項目

而稍微復雜的項目,其中還會有各種各樣的其它中間件,需要部署的東西遠不止3個。如果還像之前那樣手動的逐一部署,就太麻煩了。

Docker Compose就可以幫助我們實現多個相互關聯的Docker容器的快速部署。它允許用戶通過一個單獨的docker-compose.yml模板文件(YAML 格式)來定義一組相關聯的應用容器。

3.3.1.基本語法
docker-compose.yml文件的基本語法可以參考官方文檔:
https://docs.docker.com/compose/compose-file/compose-file-v3/

docker-compose文件中可以定義多個相互關聯的應用容器,每一個應用容器被稱為一個服務(service)。由于service就是在定義某個應用的運行時參數,因此與docker run參數非常相似。

舉例來說,用docker run部署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 \--network hmallmysql

如果用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: hmall

對比如下:

在這里插入圖片描述

明白了其中的對應關系,相信編寫docker-compose文件應該難不倒大家。

黑馬商城部署文件:

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"- "./mysql/init:/docker-entrypoint-initdb.d"networks:- hm-nethmall:build: context: .dockerfile: Dockerfilecontainer_name: hmallports:- "8080:8080"networks:- hm-netdepends_on:- mysqlnginx:image: nginxcontainer_name: nginxports:- "18080:18080"- "18081:18081"volumes:- "./nginx/nginx.conf:/etc/nginx/nginx.conf"- "./nginx/html:/usr/share/nginx/html"depends_on:- hmallnetworks:- hm-net
networks:hm-net:name: hmall

3.3.2.基礎命令

編寫好docker-compose.yml文件,就可以部署項目了。常見的命令:
https://docs.docker.com/compose/reference/

基本語法如下:

docker compose [OPTIONS] [COMMAND]

其中,OPTIONS和COMMAND都是可選參數,比較常見的有:
在這里插入圖片描述
教學演示:

# 1.進入root目錄
cd /root# 2.刪除舊容器
docker rm -f $(docker ps -qa)# 3.刪除hmall鏡像
docker rmi hmall# 4.清空MySQL數據
rm -rf mysql/data# 5.啟動所有, -d 參數是后臺啟動
docker compose up -d
# 結果:
[+] Building 15.5s (8/8) FINISHED=> [internal] load build definition from Dockerfile                                    0.0s=> => transferring dockerfile: 358B                                                    0.0s=> [internal] load .dockerignore                                                       0.0s=> => transferring context: 2B                                                         0.0s=> [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster             15.4s=> [1/3] FROM docker.io/library/openjdk:11.0-jre-buster@sha256:3546a17e6fb4ff4fa681c3  0.0s=> [internal] load build context                                                       0.0s=> => transferring context: 98B                                                        0.0s=> CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo   0.0s=> CACHED [3/3] COPY hm-service.jar /app.jar                                           0.0s=> exporting to image                                                                  0.0s=> => exporting layers                                                                 0.0s=> => writing image sha256:32eebee16acde22550232f2eb80c69d2ce813ed099640e4cfed2193f71  0.0s=> => naming to docker.io/library/root-hmall                                           0.0s
[+] Running 4/4? Network hmall    Created                                                             0.2s? Container mysql  Started                                                             0.5s? Container hmall  Started                                                             0.9s? Container nginx  Started                                                             1.5s# 6.查看鏡像
docker compose images
# 結果
CONTAINER           REPOSITORY          TAG                 IMAGE ID            SIZE
hmall               root-hmall          latest              32eebee16acd        362MB
mysql               mysql               latest              3218b38490ce        516MB
nginx               nginx               latest              605c77e624dd        141MB# 7.查看容器
docker compose ps
# 結果
NAME                IMAGE               COMMAND                  SERVICE             CREATED             STATUS              PORTS
hmall               root-hmall          "java -jar /app.jar"     hmall               54 seconds ago      Up 52 seconds       0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
mysql               mysql               "docker-entrypoint.s…"   mysql               54 seconds ago      Up 53 seconds       0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp
nginx               nginx               "/docker-entrypoint.…"   nginx               54 seconds ago      Up 52 seconds       80/tcp, 0.0.0.0:18080-18081->18080-18081/tcp, :::18080-18081->18080-18081/tcp

打開瀏覽器,訪問:http://虛擬機IP地址:8080

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86690.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86690.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86690.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java面試題尚硅谷版第1季

1、寫出如下代碼運行結果 1.1、 使用局部變量表和操作數棧解題 1.2、使用前置和后置遞增解題 2、寫一個單例模式 2.1、考察知識點 2.2、單例模式實現 3、類加載和初始化順序 package classload;public class Father {private int i test();private static int j method();st…

關于Qt阻斷樣式繼承的解決辦法

引言 在使用 Qt 開發桌面應用時&#xff0c;借助樣式表&#xff08;StyleSheet&#xff09;來統一定義界面風格是非常常見的做法。通常&#xff0c;你會在主程序中通過 qApp->setStyleSheet(...) 或者直接給某個父控件設置樣式表&#xff0c;讓所有的子控件都采用相同的配色…

鼠標右鍵添加新建某種文件的方法

場景 我經常用到.emmx&#xff0c;.eddx文件&#xff0c;電腦上裝的是wpsX億圖&#xff08;因為有wps會員&#xff09;&#xff0c;沒有開億圖會員。 然后問題就是&#xff0c;思維導圖和流程圖我都能正常開&#xff0c;正常編輯&#xff0c;但鼠標右鍵沒有新建這兩個文件的按…

Inxpect安全雷達傳感器與控制器:動態檢測 + 抗干擾技術重構工業安全防護體系

Inxpect 推出工業安全領域新型智能傳感器與控制器&#xff0c;其核心產品為雷達掃描儀&#xff0c;具備動態調整檢測區域、抗干擾能力強等特點&#xff0c;可精準檢測危險區域人員進入或存在情況&#xff0c;適用于移動機器人等場景。 Inxpect安全雷達傳感器核心功能 動態檢測…

【AI學習】李廣密與階躍星辰首席科學家張祥雨對談:多模態發展的歷史和未來

仔細閱讀了文章《專訪張祥雨&#xff1a;多模態推理和自主學習是未來的 2 個 「GPT-4」 時刻》 https://mp.weixin.qq.com/s/892QuRPH9uP6zN6dS-HZMw 非常贊嘆的一篇文章&#xff0c;說清楚了NLP、CV發展中的許多重大問題&#xff0c;讀來醍醐灌頂&#xff01;這樣的文章&…

C++中std::deque詳解和實戰工程代碼示例

C中std::deque詳解和實戰工程代碼示例 std::deque&#xff08;雙端隊列&#xff09;是 C 標準庫中的一個序列容器&#xff0c;與 std::vector 類似&#xff0c;但它支持從頭部和尾部高效地插入和刪除元素。它底層采用分段連續空間實現&#xff0c;兼具靈活性與性能。 一、基本…

【AI大模型入門指南】概念與專有名詞詳解 (二)

【AI大模型入門指南】概念與專有名詞詳解 &#xff08;二&#xff09; 一 、前言 當你和聊天機器人聊得天花亂墜時&#xff0c;當你用文字讓AI生成精美圖片時&#xff0c;當手機相冊自動幫你分類照片時 —— 這些看似智能的操作背后&#xff0c;都藏著 AI 大模型的身影。 本…

AIStor 的模型上下文協議 (MCP) 服務器:管理功能

在本系列的上一篇博文中&#xff0c;我們討論了 MinIO AIStor 的模型上下文協議 (MCP) 服務器的基本用戶級功能。我們學習了如何使用人類語言命令查看存儲桶的內容、分析對象并標記它們以便將來處理&#xff0c;以及如何通過 LLM&#xff08;例如 Anthropic Claude&#xff09;…

期權末日輪實值期權盈利未平倉怎么辦?

本文主要介紹期權末日輪實值期權盈利未平倉怎么辦&#xff1f;期權末日輪實值期權盈利未平倉該怎么辦&#xff0c;需要明確幾個關鍵點&#xff1a;末日輪指的是期權到期日臨近的時候&#xff0c;通常指最后一周&#xff0c;尤其是最后一天&#xff0c;這時候時間價值衰減很快&a…

C++/Qt 聯合編程中的定時器使用陷阱:QObject::startTimer 報錯詳解

在 Qt 開發中&#xff0c;QTimer 是一個常用的工具類&#xff0c;用于處理定時事件。但不少開發者在 C/Qt 聯合編程&#xff0c;尤其是在工具類、靜態類、線程中使用定時器時&#xff0c;會遇到如下令人困惑的報錯&#xff1a; QObject::startTimer: Timers can only be used …

CentOS7.9 查詢運維安全日志,排查惡意用戶

1、查看系統版本 cat /etc/redhat-release uname -a 2、查看所有賬號 cat /etc/shadow 3、修改 root 密碼 passwd 3、查看賬號ID id jinzhi 4、查看登錄日志 lastlog 5、查看操作日志 cat .bash_history sudo cat /home/yunwei/.bash_history sudo grep root /va…

多模態大語言模型arxiv論文略讀(117)

Training-free Zero-shot Composed Image Retrieval via Weighted Modality Fusion and Similarity ?? 論文標題&#xff1a;Training-free Zero-shot Composed Image Retrieval via Weighted Modality Fusion and Similarity ?? 論文作者&#xff1a;Ren-Di Wu, Yu-Yen L…

如何正確的配置eureka server集群

將 Eureka Server 實例的 hostname 都配置成相同的值&#xff0c;在 Eureka Server 集群環境下同樣是不推薦且通常會導致嚴重問題的&#xff0c; 核心問題&#xff1a;Eureka Server 集群的工作機制 Eureka Server 集群通過相互注冊&#xff08;Peering&#xff09;來實現高可…

AI支持下的-ArcGIS數據處理、空間分析、可視化及多案例綜合應用

查看原文>>> 從入門到精通-AI支持下的-ArcGIS數據處理、空間分析、可視化及多案例綜合應用 結合ArcGIS和GPT的優勢&#xff0c;本文重點進行AI大模型應用、ArcGIS工作流程及功能、Prompt使用技巧、AI助力工作流程、AI助力數據讀取與處理、AI助力空間分析、AI助力遙感…

vue3-ts: v-model 和 props 的關系

在 Vue.js 中&#xff0c;v-model 是一個語法糖&#xff0c;它實際上是 :value 和 input 事件的組合。 當你使用 v-model 綁定一個組件時&#xff0c;默認情況下&#xff0c;組件會通過 props 接收 value 這個 prop&#xff0c; 并通過觸發 input 事件來更新父組件中的數據。 …

學車筆記 變擋

超15就可以加一檔了 有些人對手動擋的檔位有一些誤解_嗶哩嗶哩_bilibili 獻給所有新司機.開手動檔擺脫頓挫的根本方法.學會看轉速!沒那么復雜!_嗶哩嗶哩_bilibili 減速到怠速降一檔

STM32的DMA簡介

STM32的DMA簡介 一、DMA概述 DMA&#xff08;Direct Memory Access&#xff0c;直接存儲器存取&#xff09;是一種硬件機制&#xff0c;它允許外設和存儲器之間或者存儲器和存儲器之間進行高速數據傳輸&#xff0c;而無需CPU的干預。這種機制可以極大地節省CPU資源&#xff0c…

Spring-AOP知識點

一、AOP簡介 1.AOP概念 2.AOP思想實現方案 3.AOP相關概念 二、基于xml配置AOP 1.快速入門 2.AOP配置詳解 3.AOP原理剖析 三、基于注解配置AOP 1.快速入門 2.注解方式AOP配置詳解 抽取切點表達式

Java@Data 與 @NotNull 注解沖突問題

第一章&#xff1a;核心概念解析 1. Data&#xff08;Lombok 提供&#xff09; 自動生成以下方法&#xff1a; gettersettertoString()equals()hashCode() 簡化實體類編寫&#xff0c;提高開發效率。 示例&#xff1a; import lombok.Data;Data public class User {private…

離線部署openstack 2024.1 glance

控制節點鏡像服務 離線下載 apt install --download-only glancemkdir /controller/glance mv /var/cache/apt/archives/*.deb /controller/glance/ dpkg -i /controller/glance/*.deb在一個控制節點操作 CREATE DATABASE glance; GRANT ALL PRIVILEGES ON glance.* TO glan…