柚子快報激活碼778899分享:Docker與微服務(wù)實戰(zhàn)
柚子快報激活碼778899分享:Docker與微服務(wù)實戰(zhàn)
來自尚硅谷周陽老師的docker教程。。
docker官網(wǎng):http://www.docker.comocker Hub官網(wǎng): https://hub.docker.com/
1. 基礎(chǔ)篇(零基小白)
1.1 Docker簡介
1.1.1 是什么
Docker之所以發(fā)展如此迅速,也是因為它對此給出了一個標(biāo)準(zhǔn)化的解決方案-----系統(tǒng)平滑移植,容器虛擬化技術(shù)。
開發(fā)需要清楚的告訴運(yùn)維部署團(tuán)隊,用的全部配置文件+所有軟件環(huán)境。不過,即便如此,仍然常常發(fā)生部署失敗的狀況。Docker的出現(xiàn)使得Docker得以打破過去「程序即應(yīng)用」的觀念。透過鏡像(images)將作業(yè)系統(tǒng)核心除外,運(yùn)作應(yīng)用程式所需要的系統(tǒng)環(huán)境,由下而上打包,達(dá)到應(yīng)用程式跨平臺間的無縫接軌運(yùn)作。
1.1.2 docker理念
Docker是基于Go語言實現(xiàn)的云開源項目。 Docker的主要目標(biāo)是“Build,Ship and Run Any App,Anywhere”,也就是通過對應(yīng)用組件的封裝、分發(fā)、部署、運(yùn)行等生命周期的管理,使用戶的APP(可以是一個WEB應(yīng)用或數(shù)據(jù)庫應(yīng)用等等)及其運(yùn)行環(huán)境能夠做到“一次鏡像,處處運(yùn)行”。
Linux容器技術(shù)的出現(xiàn)就解決了這樣一個問題,而 Docker 就是在它的基礎(chǔ)上發(fā)展過來的。將應(yīng)用打成鏡像,通過鏡像成為運(yùn)行在Docker容器上面的實例,而 Docker容器在任何操作系統(tǒng)上都是一致的,這就實現(xiàn)了跨平臺、跨服務(wù)器。只需要一次配置好環(huán)境,換到別的機(jī)子上就可以一鍵部署好,大大簡化了操作。 一句話:解決了運(yùn)行環(huán)境和配置問題的軟件容器, 方便做持續(xù)集成并有助于整體發(fā)布的容器虛擬化技術(shù)。
1.1.3 容器與虛擬機(jī)比較
(1)傳統(tǒng)虛擬機(jī)技術(shù):
傳統(tǒng)虛擬機(jī)技術(shù)預(yù)計安裝在主操作系統(tǒng)上的虛擬機(jī)管理系統(tǒng)(如:VirtualBox和VMWare),創(chuàng)阿金虛擬機(jī)(虛擬出各種硬件),在虛擬機(jī)上安裝從操作系統(tǒng),在從操作系統(tǒng)中安裝部署各種應(yīng)用。虛擬機(jī)的缺點(diǎn):
1)資源占用多;2)冗余步驟多3)啟動慢
(2)容器虛擬化技術(shù)
Linux容器(Linux Containers,縮寫為 LXC):Linux容器是與系統(tǒng)其他部分隔離開的一系列進(jìn)程,從另一個鏡像運(yùn)行,并由該鏡像提供支持進(jìn)程所需的全部文件。容器提供的鏡像包含了應(yīng)用的所有依賴項,因而在從開發(fā)到測試再到生產(chǎn)的整個過程中,它都具有可移植性和一致性。
Linux 容器不是模擬一個完整的操作系統(tǒng)而是對進(jìn)程進(jìn)行隔離。有了容器,就可以將軟件運(yùn)行所需的所有資源打包到一個隔離的容器中。容器與虛擬機(jī)不同,不需要捆綁一整套操作系統(tǒng),只需要軟件工作所需的庫資源和設(shè)置。系統(tǒng)因此而變得高效輕量并保證部署在任何環(huán)境中的軟件都能始終如一地運(yùn)行。 Docker容器是在操作系統(tǒng)層面上實現(xiàn)虛擬化,直接復(fù)用本地主機(jī)的操作系統(tǒng),而傳統(tǒng)的虛擬機(jī)則是在硬件層面實現(xiàn)虛擬化。Docker有事體現(xiàn)為啟動速度快、占用體積小。
- 對比
關(guān)系對比 - 指向 底層原理
比較了 Docker 和傳統(tǒng)虛擬化方式的不同之處: *傳統(tǒng)虛擬機(jī)技術(shù)是虛擬出一套硬件后,在其上運(yùn)行一個完整操作系統(tǒng),在該系統(tǒng)上再運(yùn)行所需應(yīng)用進(jìn)程; *容器內(nèi)的應(yīng)用進(jìn)程直接運(yùn)行于宿主的內(nèi)核,容器內(nèi)沒有自己的內(nèi)核且也沒有進(jìn)行硬件虛擬。因此容器要比傳統(tǒng)虛擬機(jī)更為輕便。
每個容器之間互相隔離,每個容器有自己的文件系統(tǒng) ,容器之間進(jìn)程不會相互影響,能區(qū)分計算資源。
1.1.4 能干嘛
技術(shù)職級變化
coderprogrammersoftware engineerDevOps engineer 開發(fā)/運(yùn)維(DevOps)新一代開發(fā)工程師
一次構(gòu)建、隨處運(yùn)行更快速的應(yīng)用交付和部署更簡單的系統(tǒng)運(yùn)維更高效的計算資源利用 Docker應(yīng)用場景
1.2 Docker安裝
1.2.1 前提說明
Docker必須部署在Linux內(nèi)核的系統(tǒng)上。
1)CentOS Docker 安裝 2)前提條件 目前,CntOS 僅發(fā)行版本中的內(nèi)核支持 Docker。Docker 運(yùn)行在CentOS 7 (64-bit)上, 要求系統(tǒng)為64位、Linux系統(tǒng)內(nèi)核版本為 3.8以上,這里選用Centos7.x
3)查看自己的內(nèi)核 uname命令用于打印當(dāng)前系統(tǒng)相關(guān)信息(內(nèi)核版本號、硬件架構(gòu)、主機(jī)名稱和操作系統(tǒng)類型等)。
1.2.2 Docker的基本組成
(1)鏡像(image) Docker 鏡像(Image)就是一個只讀的模板。鏡像可以用來創(chuàng)建 Docker 容器,一個鏡像可以創(chuàng)建很多容器。 它也相當(dāng)于是一個root文件系統(tǒng)。比如官方鏡像 centos:7 就包含了完整的一套 centos:7 最小系統(tǒng)的 root 文件系統(tǒng)。 相當(dāng)于容器的“源代碼”,docker鏡像文件類似于Java的類模板,而docker容器實例類似于java中new出來的實例對象。
** (2)容器(container)**
從面向?qū)ο蠼嵌龋篋ocker 利用容器(Container)獨(dú)立運(yùn)行的一個或一組應(yīng)用,應(yīng)用程序或服務(wù)運(yùn)行在容器里面,容器就類似于一個虛擬化的運(yùn)行環(huán)境,容器是用鏡像創(chuàng)建的運(yùn)行實例。就像是Java中的類和實例對象一樣,鏡像是靜態(tài)的定義,容器是鏡像運(yùn)行時的實體。容器為鏡像提供了一個標(biāo)準(zhǔn)的和隔離的運(yùn)行環(huán)境,它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的、保證安全的平臺。從鏡像容器角度:可以把容器看做是一個簡易版的 Linux 環(huán)境(包括root用戶權(quán)限、進(jìn)程空間、用戶空間和網(wǎng)絡(luò)空間等)和運(yùn)行在其中的應(yīng)用程序。
3)倉庫(repository) 倉庫(Repository)是集中存放鏡像文件的場所。 類似于Maven倉庫,存放各種jar包的地方;github倉庫,存放各種git項目的地方; Docker公司提供的官方registry被稱為Docker Hub,存放各種鏡像模板的地方。
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。
最大的公開倉庫是 Docker Hub(https://hub.docker.com/),存放了數(shù)量龐大的鏡像供用戶下載。國內(nèi)的公開倉庫包括阿里云 、網(wǎng)易云等
4)小總結(jié) 需要正確的理解倉庫/鏡像/容器這幾個概念:
Docker 本身是一個容器運(yùn)行載體或稱之為管理引擎。我們把應(yīng)用程序和配置依賴打包好形成一個可交付的運(yùn)行環(huán)境,這個打包好的運(yùn)行環(huán)境就是image鏡像文件。只有通過這個鏡像文件才能生成Docker容器實例(類似Java中new出來一個對象)。image文件可以看作是容器的模板。Docker 根據(jù) image 文件生成容器的實例。同一個 image 文件,可以生成多個同時運(yùn)行的容器實例。鏡像文件:image 文件生成的容器實例,本身也是一個文件,稱為鏡像文件。容器實例:一個容器運(yùn)行一種服務(wù),當(dāng)我們需要的時候,就可以通過docker客戶端創(chuàng)建一個對應(yīng)的運(yùn)行實例,也就是我們的容器。倉庫:就是放一堆鏡像的地方,我們可以把鏡像發(fā)布到倉庫中,需要的時候再從倉庫中拉下來就可以了。
1.2.3 Docker平臺架構(gòu)圖解(架構(gòu)版)
Docker運(yùn)行的基本流程為:
用戶使用docker client與docker daemon簡歷通信,并發(fā)送請求給后者。
docker daemon作為docker架構(gòu)中的主體部分,首先提供docker server的功能使其可以接受docker client的請求。
docker engine執(zhí)行docker內(nèi)部的一系列工作,每項工作都是以一個Job的形式存在
Job的運(yùn)行過程中,黨需要容器鏡像時,則從docker registry中下載鏡像,并通過鏡像管理驅(qū)動graph driver將下載鏡像以graph的形式存儲。
當(dāng)需要為docker創(chuàng)建網(wǎng)絡(luò)環(huán)境時,通過網(wǎng)絡(luò)管理驅(qū)動NetWork driver創(chuàng)建并配置docker容器網(wǎng)絡(luò)環(huán)境。
當(dāng)需要限制docker容器運(yùn)行資源或執(zhí)行用戶指令等操作時,則通過exec driver來完成。
Libcontainer是一項獨(dú)立的容器管理包,NetWork driver以及Exec driver都是通過Libcontainer來實現(xiàn)具體對容器進(jìn)行的操作。
Docker 是一個 C/S 模式的架構(gòu),后端是一個松耦合架構(gòu),眾多模塊各司其職。
1.2.4 安裝步驟
確定你是CentOS7及以上版本:cat /etc/redhat-release CentOS7安裝Docker:https://docs.docker.com/engine/install/centos/
1.2.4.1 卸載舊版本
https://docs.docker.com/engine/install/centos/
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
1.2.4.2 yum安裝gcc相關(guān)
yum -y install gcc
yum -y install gcc-c++
yum install -y yum-utils
1.2.4.3 設(shè)置stable鏡像倉庫(大坑)
| 官網(wǎng)要求: 執(zhí)行:yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 報錯:
1)[Errno 14] curl#35 - TCP connection reset by peer2)[Errno 12] curl#35 - Timeout | | — |
| 推薦:使用國內(nèi)我們自己的 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
1.2.4.4 更新yum軟件包索引
yum makecache fast
1.2.4.5 安裝DOCKER CE
yum -y install docker-ce docker-ce-cli containerd.io | 官網(wǎng)要求: 執(zhí)行結(jié)果: | | — |
1.2.4.6 hello word
啟動docker:systemctl start docker測試:docker versionhello word:docker run hello-world
1.2.4.7 卸載
systemctl stop docker
yum remove docker-ce docker-ce-cli containerd.io
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
1.2.5 阿里云鏡像加速
https://promotion.aliyun.com/ntms/act/kubernetes.html 注冊一個屬于自己的阿里云賬戶(可復(fù)用淘寶賬號),獲得加速器地址連接。
登陸阿里云開發(fā)者平臺
點(diǎn)擊控制臺
選擇容器鏡像服務(wù)
獲取加速器地址
粘貼腳本直接執(zhí)行 | mkdir -p /etc/docker | | — | | tee /etc/docker/daemon.json <<-‘EOF’ { “registry-mirrors”: [“https://aa25jngu.mirror.aliyuncs.com”] } EOF |
重啟服務(wù)器
systemctl daemon-reloadsystemctl restart docker
1.2.6 國內(nèi)鏡像加速器 Docker中國區(qū)官方鏡像:https://registry.docker-cn.com
1.2.6 為什么Docker會比VM虛擬機(jī)快
(1)docker有著比虛擬機(jī)更少的抽象層 由于docker不需要Hypervisor(虛擬機(jī))實現(xiàn)硬件資源虛擬化,運(yùn)行在docker容器上的程序直接使用的都是實際物理機(jī)的硬件資源。因此在CPU、內(nèi)存利用率上docker將會在效率上有明顯優(yōu)勢。 (2)docker利用的是宿主機(jī)的內(nèi)核,而不需要加載操作系統(tǒng)OS內(nèi)核 當(dāng)新建一個容器時,docker不需要和虛擬機(jī)一樣重新加載一個操作系統(tǒng)內(nèi)核。進(jìn)而避免引尋、加載操作系統(tǒng)內(nèi)核返回等比較費(fèi)時費(fèi)資源的過程,當(dāng)新建一個虛擬機(jī)時,虛擬機(jī)軟件需要加載OS,返回新建過程是分鐘級別的。而docker由于直接利用宿主機(jī)的操作系統(tǒng),則省略了返回過程,因此新建一個docker容器只需要幾秒鐘。
1.3 Docker常用命令
1.3.1 幫助啟動類命令
啟動docker: systemctl start docker停止docker: systemctl stop docker重啟docker: systemctl restart docker查看docker狀態(tài): systemctl status docker開機(jī)啟動: systemctl enable docker查看docker概要信息: docker info查看docker總體幫助文檔: docker --help查看docker命令幫助文檔: docker 具體命令 --help
1.3.2 鏡像命令
1)列出本地主機(jī)上的鏡像:docker images
| 各個選項說明: REPOSITORY:表示鏡像的倉庫源 TAG:鏡像的標(biāo)簽版本號 IMAGE ID:鏡像ID CREATED:鏡像創(chuàng)建時間 SIZE:鏡像大小
同一倉庫源可以有多個 TAG版本,代表這個倉庫源的不同個版本,我們使用 REPOSITORY:TAG 來定義不同的鏡像。如果你不指定一個鏡像的版本標(biāo)簽,例如你只使用 ubuntu,docker 將默認(rèn)使用 ubuntu:latest 鏡像
OPTIONS說明:
-a :列出本地所有的鏡像(含歷史映像層) -q :只顯示鏡像ID。
2)docker search 某個XXX鏡像名字
docker search [OPTIONS] 鏡像名字
| OPTIONS說明: –limit : 只列出N個鏡像,默認(rèn)25個
例如:docker search --limit 5 redis
3)下載鏡像:docker pull 鏡像名字
docker pull 鏡像名字[:TAG] docker pull 鏡像名字 沒有TAG就是最新版
4)查看鏡像/容器/數(shù)據(jù)卷所占的空間:docker system df
5)刪除鏡像:docker rmi
刪除單個:docker rmi -f 鏡像ID刪除多個:docker rmi -f 鏡像名1:TAG 鏡像名2:TAG刪除全部:docker rmi -f $(docker images -qa)
面試題:談?wù)刣ocker虛懸鏡像是什么?
倉庫名、標(biāo)簽都是的鏡像,俗稱虛懸鏡像dangling image
1.3.3 容器命令
有鏡像才能創(chuàng)建容器, 這是根本前提(下載一個CentOS或者ubuntu鏡像演示)
本次演示用ubuntu演示
1)新建+啟動容器
docker run [OPTIONS] **IMAGE **[COMMAND] [ARG…] | OPTIONS說明(常用):有些是一個減號,有些是兩個減號 –name=“容器新名字” 為容器指定一個名稱; -d: 后臺運(yùn)行容器并返回容器ID,也即啟動守護(hù)式容器(后臺運(yùn)行);
-i:以交互模式運(yùn)行容器,通常與 -t 同時使用; -t:為容器重新分配一個偽輸入終端,通常與 -i 同時使用; 也即啟動交互式容器(前臺有偽終端,等待交互);
-P: 隨機(jī)端口映射,大寫P -p: 指定端口映射,小寫p
2)列出當(dāng)前所有正在運(yùn)行的容器
docker ps [OPTIONS] | OPTIONS說明(常用):
-a :列出當(dāng)前所有正在運(yùn)行的容器+歷史上運(yùn)行過的 -l :顯示最近創(chuàng)建的容器。 -n:顯示最近n個創(chuàng)建的容器。
-q :靜默模式,只顯示容器編號。
3)啟動、停止、重啟命令組
啟動已停止運(yùn)行的容器:docker start 容器ID或者容器名重啟容器:docker restart 容器ID或者容器名停止容器:docker stop 容器ID或者容器名強(qiáng)制停止容器:docker kill 容器ID或容器名刪除已停止的容器:docker rm 容器ID一次性刪除多個容器實例(慎用):
docker rm -f $(docker ps -a -q)docker ps -a -q | xargs docker rm
4)查詢?nèi)萜魅罩?、?nèi)部進(jìn)程和內(nèi)部細(xì)節(jié)命令
查看容器日志:docker logs 容器ID查看容器內(nèi)運(yùn)行的進(jìn)程:docker top 容器ID查看容器內(nèi)部細(xì)節(jié):docker inspect 容器ID
5)啟動前臺交互式容器(前臺命令行)
docker run -it centos /bin/bash
| 參數(shù)說明: -i: 交互式操作。 -t: 終端。 centos : centos 鏡像。 /bin/bash:放在鏡像名后的是命令,這里我們希望有個交互式 Shell,因此用的是 /bin/bash。
要退出終端,直接輸入 exit:
6)啟動后臺守護(hù)式容器(后臺服務(wù)器)
docker run -d 容器名
7)退出容器(針對前臺交互式容器)
兩種退出方式
exit:run進(jìn)去容器,exit退出,容器停止ctrl+p+q:run進(jìn)去容器,ctrl+p+q退出,容器不停止
8)進(jìn)入正在運(yùn)行的容器并以命令行交互(重要)
兩種方式:
docker exec -it 容器ID bashShelldocker attach 容器ID
兩者區(qū)別:
attach 直接進(jìn)入容器啟動命令的終端,不會啟動新的進(jìn)程 用exit退出,會導(dǎo)致容器的停止。exec 是在容器中打開新的終端,并且可以啟動新的進(jìn)程 用exit退出,不會導(dǎo)致容器的停止。推薦大家使用 docker exec 命令,因為退出容器終端,不會導(dǎo)致容器的停止。
用之前的redis容器實例進(jìn)入試試
進(jìn)入redis服務(wù)
docker exec -it 容器ID /bin/bash
docker exec -it 容器ID redis-cli
一般用-d后臺啟動的程序,再用exec進(jìn)入對應(yīng)容器實例
演示: attach 直接進(jìn)入容器啟動命令的終端,不會啟動新的進(jìn)程 用exit退出,會導(dǎo)致容器的停止。
exec 是在容器中打開新的終端,并且可以啟動新的進(jìn)程 用exit退出,不會導(dǎo)致容器的停止。
9)從容器內(nèi)拷貝文件到主機(jī)上
容器→主機(jī) 公式:docker cp 容器ID:容器內(nèi)路徑 目的主機(jī)路徑
10)導(dǎo)入和導(dǎo)出容器
export 導(dǎo)出容器的內(nèi)容留作為一個tar歸檔文件[對應(yīng)import命令] import 從tar包中的內(nèi)容創(chuàng)建一個新的文件系統(tǒng)再導(dǎo)入為鏡像[對應(yīng)export] 操作案例:
| docker export 容器ID > 文件名.tar
cat 文件名.tar | docker import - 鏡像用戶/鏡像名:鏡像版本號
1.3.4 小總結(jié)
常用命令
attach Attach to a running container # 當(dāng)前 shell 下 attach 連接指定運(yùn)行鏡像 build Build an image from a Dockerfile # 通過 Dockerfile 定制鏡像 commit Create a new image from a container changes # 提交當(dāng)前容器為新的鏡像 cp Copy files/folders from the containers filesystem to the host path #從容器中拷貝指定文件或者目錄到宿主機(jī)中 create Create a new container # 創(chuàng)建一個新的容器,同 run,但不啟動容器 diff Inspect changes on a container’s filesystem # 查看 docker 容器變化 events Get real time events from the server # 從 docker 服務(wù)獲取容器實時事件 exec Run a command in an existing container # 在已存在的容器上運(yùn)行命令 export Stream the contents of a container as a tar archive # 導(dǎo)出容器的內(nèi)容流作為一個 tar 歸檔文件[對應(yīng) import ] history Show the history of an image # 展示一個鏡像形成歷史 images List images # 列出系統(tǒng)當(dāng)前鏡像 import Create a new filesystem image from the contents of a tarball # 從tar包中的內(nèi)容創(chuàng)建一個新的文件系統(tǒng)映像[對應(yīng)export] info Display system-wide information # 顯示系統(tǒng)相關(guān)信息 inspect Return low-level information on a container # 查看容器詳細(xì)信息 kill Kill a running container # kill 指定 docker 容器 load Load an image from a tar archive # 從一個 tar 包中加載一個鏡像[對應(yīng) save] login Register or Login to the docker registry server # 注冊或者登陸一個 docker 源服務(wù)器 logout Log out from a Docker registry server # 從當(dāng)前 Docker registry 退出 logs Fetch the logs of a container # 輸出當(dāng)前容器日志信息 port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口對應(yīng)的容器內(nèi)部源端口 pause Pause all processes within a container # 暫停容器 ps List containers # 列出容器列表 pull Pull an image or a repository from the docker registry server # 從docker鏡像源服務(wù)器拉取指定鏡像或者庫鏡像 push Push an image or a repository to the docker registry server # 推送指定鏡像或者庫鏡像至docker源服務(wù)器 restart Restart a running container # 重啟運(yùn)行的容器 rm Remove one or more containers # 移除一個或者多個容器 rmi Remove one or more images # 移除一個或多個鏡像[無容器使用該鏡像才可刪除,否則需刪除相關(guān)容器才可繼續(xù)或 -f 強(qiáng)制刪除] run Run a command in a new container # 創(chuàng)建一個新的容器并運(yùn)行一個命令 save Save an image to a tar archive # 保存一個鏡像為一個 tar 包[對應(yīng) load] search Search for an image on the Docker Hub # 在 docker hub 中搜索鏡像 start Start a stopped containers # 啟動容器 stop Stop a running containers # 停止容器 tag Tag an image into a repository # 給源中鏡像打標(biāo)簽 top Lookup the running processes of a container # 查看容器中運(yùn)行的進(jìn)程信息 unpause Unpause a paused container # 取消暫停容器 version Show the docker version information # 查看 docker 版本號 wait Block until a container stops, then print its exit code # 截取容器停止時的退出狀態(tài)值
1.4 Docker鏡像
1.4.1 什么是鏡像
**鏡像:**是一種輕量級、可執(zhí)行的獨(dú)立軟件包,它包含運(yùn)行某個軟件所需的所有內(nèi)容,我們把應(yīng)用程序和配置依賴打包好形成一個可交付的運(yùn)行環(huán)境(包括代碼、運(yùn)行時需要的庫、環(huán)境變量和配置文件等),這個打包好的運(yùn)行環(huán)境就是image鏡像文件。 只有通過這個鏡像文件才能生成Docker容器實例(類似Java中new出來一個對象)。
1)UnionFS(聯(lián)合文件系統(tǒng))
UnionFS(聯(lián)合文件系統(tǒng)):Union文件系統(tǒng)(UnionFS)是一種分層、輕量級并且高性能的文件系統(tǒng),它支持對文件系統(tǒng)的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統(tǒng)下(unite several directories into a single virtual filesystem)。Union 文件系統(tǒng)是 Docker 鏡像的基礎(chǔ)。鏡像可以通過分層來進(jìn)行繼承,基于基礎(chǔ)鏡像(沒有父鏡像),可以制作各種具體的應(yīng)用鏡像。 特性:一次同時加載多個文件系統(tǒng),但從外面看起來,只能看到一個文件系統(tǒng),聯(lián)合加載會把各層文件系統(tǒng)疊加起來,這樣最終的文件系統(tǒng)會包含所有底層的文件和目錄
2) Docker鏡像加載原理:
docker的鏡像實際上由一層一層的文件系統(tǒng)組成,這種層級的文件系統(tǒng)UnionFS。 bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引導(dǎo)加載kernel, Linux剛啟動時會加載bootfs文件系統(tǒng),在Docker鏡像的最底層是引導(dǎo)文件系統(tǒng)bootfs。這一層與我們典型的Linux/Unix系統(tǒng)是一樣的,包含boot加載器和內(nèi)核。當(dāng)boot加載完成之后整個內(nèi)核就都在內(nèi)存中了,此時內(nèi)存的使用權(quán)已由bootfs轉(zhuǎn)交給內(nèi)核,此時系統(tǒng)也會卸載bootfs。
rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系統(tǒng)中的 /dev, /proc, /bin, /etc 等標(biāo)準(zhǔn)目錄和文件。rootfs就是各種不同的操作系統(tǒng)發(fā)行版,比如Ubuntu,Centos等等。 。 平時我們安裝進(jìn)虛擬機(jī)的CentOS都是好幾個G,為什么docker這里才200M?? 對于一個精簡的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序庫就可以了,因為底層直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可見對于不同的linux發(fā)行版, bootfs基本是一致的, rootfs會有差別, 因此不同的發(fā)行版可以公用bootfs。
3)為什么 Docker 鏡像要采用這種分層結(jié)構(gòu)呢
鏡像分層最大的一個好處就是共享資源,方便復(fù)制遷移,就是為了復(fù)用。 比如說有多個鏡像都從相同的 base 鏡像構(gòu)建而來,那么 Docker Host 只需在磁盤上保存一份 base 鏡像; 同時內(nèi)存中也只需加載一份 base 鏡像,就可以為所有容器服務(wù)了。而且鏡像的每一層都可以被共享。
1.4.2 重點(diǎn)理解
docker鏡像層都是只讀的,容器層是可寫的 當(dāng)容器啟動時,一個新的可寫層被加載到鏡像的頂部。 這一層通常被稱作“容器層”,“容器層”之下的都叫“鏡像層”。
所有對容器的改動 - 無論添加、刪除、還是修改文件都只會發(fā)生在容器層中。只有容器層是可寫的,容器層下面的所有鏡像層都是只讀的。
1.4.3 Docker鏡像commit操作案例
命令:docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要創(chuàng)建的目標(biāo)鏡像名:[標(biāo)簽名]
案例演示ubuntu安裝vim |從Hub上下載ubuntu鏡像到本地并成功運(yùn)行原始的默認(rèn)Ubuntu鏡像是不帶著vim命令的
外網(wǎng)連通的情況下,安裝vim
docker容器內(nèi)執(zhí)行上述兩條命令: apt-get update apt-get -y install vim
安裝完成后,commit我們自己的新鏡像
啟動我們的新鏡像并和原來的對比
1 官網(wǎng)是默認(rèn)下載的Ubuntu沒有vim命令
2我們自己commit構(gòu)建的鏡像,新增加了vim功能,可以成功使用。
1.5 本地鏡像發(fā)布到阿里云
1.5.1 本地鏡像發(fā)布到阿里云流程
1. 鏡像的生成方法
docker commit [OPTIONS] 容器ID [REPOSITORY[:TAG]] OPTIONS說明: -a :提交的鏡像作者; -m :提交時的說明文字; 本次案例centos+ubuntu兩個,當(dāng)堂講解一個,家庭作業(yè)一個,請大家務(wù)必動手,親自實操。
2. 將本地鏡像推送到阿里云
本地鏡像素材原型
阿里云開發(fā)者平臺:https://promotion.aliyun.com/ntms/act/kubernetes.html
創(chuàng)建倉庫鏡像
選擇控制臺,進(jìn)入容器鏡像服務(wù)
選擇個人實例
- 命名空間
繼續(xù)
倉庫名稱
繼續(xù)
進(jìn)入管理界面獲得腳本
將鏡像推送到阿里云,將鏡像推送到阿里云registry
管理界面腳本 腳本實例
docker login --username=zzyybuy registry.cn-hangzhou.aliyuncs.comdocker tag cea1bb40441c registry.cn-hangzhou.aliyuncs.com/atguiguwh/myubuntu:1.1docker push registry.cn-hangzhou.aliyuncs.com/atguiguwh/myubuntu:1.1上面命令是陽哥自己本地的,你自己酌情處理,不要粘貼我的。
1.5.2 將阿里云上的鏡像下載到本地
下載到本地
docker pull registry.cn-hangzhou.aliyuncs.com/atguiguwh/myubuntu:1.1
1.6 本地鏡像發(fā)布到私有庫
1.6.1 簡介
(1)本地鏡像發(fā)布到私有庫流程
(2)是什么
官方Docker Hub地址:https://hub.docker.com/,中國大陸訪問太慢了且準(zhǔn)備被阿里云取代的趨勢,不太主流。Dockerhub、阿里云這樣的公共鏡像倉庫可能不太方便,涉及機(jī)密的公司不可能提供鏡像給公網(wǎng),所以需要創(chuàng)建一個本地私人倉庫供給團(tuán)隊使用,基于公司內(nèi)部項目構(gòu)建鏡像。Docker Registry是官方提供的工具,可以用于構(gòu)建私有鏡像倉庫Docker Registry
1.6.2 將本地鏡像推送到私有庫
1. 下載鏡像Docker Registry:docker pull registry
2. 運(yùn)行私有庫Registry,相當(dāng)于本地有個私有Docker hub
docker run -d -p 5000:5000 -v /zzyyuse/myregistry/:/tmp/registry --privileged=true registry
默認(rèn)情況,倉庫被創(chuàng)建在容器的/var/lib/registry目錄下,建議自行用容器卷映射,方便于宿主機(jī)聯(lián)調(diào)
3. 案例演示創(chuàng)建一個新鏡像,ubuntu安裝ifconfig命令
a. 從Hub上下載ubuntu鏡像到本地并成功運(yùn)行
b. 原始的Ubuntu鏡像是不帶著ifconfig命令的
c. 外網(wǎng)連通的情況下,安裝ifconfig命令并測試通過
apt-get update
apt-get install net-tools
d. 安裝完成后,commit我們自己的新鏡像
# 命令:在容器外執(zhí)行,記得
# docker commit -m="提交的描述信息" -a="作者" 容器ID 要創(chuàng)建的目標(biāo)鏡像名:[標(biāo)簽名]
docker commit -m="ifconfig cmd add" -a="zzyy" a69d7c825c4f zzyyubuntu:1.2
圖例:
e. 啟動我們的新鏡像并和原來的對比
官網(wǎng)是默認(rèn)下載的Ubuntu沒有ifconfig命令我們自己commit構(gòu)建的新鏡像,新增加了ifconfig功能,可以成功使用。
4. curl驗證私服庫上有什么鏡像
curl -XGET http://192.168.111.162:5000/v2/_catalog
可以看到,目前私服庫沒有任何鏡像上傳過。。。。。。
5. 將新鏡像zzyyubuntu:1.2修改符合私服規(guī)范的Tag
# docker tag 鏡像:Tag Host:Port/Repository:Tag
# 使用命令 docker tag 將zzyyubuntu:1.2 這個鏡像修改為192.168.111.162:5000/zzyyubuntu:1.2
docker tag zzyyubuntu:1.2 192.168.111.162:5000/zzyyubuntu:1.2
6. 修改配置文件使之支持http
| 別無腦照著復(fù)制,registry-mirrors 配置的是國內(nèi)阿里提供的鏡像加速地址,不用加速的話訪問官網(wǎng)的會很慢。 2個配置中間有個逗號 ','別漏了,這個配置是json格式的。 2個配置中間有個逗號 ','別漏了,這個配置是json格式的。
2個配置中間有個逗號 ','別漏了,這個配置是json格式的。
vim命令新增如下紅色內(nèi)容:vim /etc/docker/daemon.json
| { “registry-mirrors”: [“https://aa25jngu.mirror.aliyuncs.com”], “insecure-registries”: [“192.168.111.162:5000”]
}
上述理由:docker默認(rèn)不允許http方式推送鏡像,通過配置選項來取消這個限制。====> 修改完后如果不生效,建議重啟docker
7. push推送到私服庫
docker push 192.168.111.162:5000/zzyyubuntu:1.2
8. curl驗證私服庫上有什么鏡像2
curl -XGET http://192.168.111.162:5000/v2/_catalog
9. pull到本地并運(yùn)行
docker pull 192.168.111.162:5000/zzyyubuntu:1.2 docker run -it 鏡像ID /bin/bash
1.7 Docker容器數(shù)據(jù)卷
【避坑指南1】Docker掛載主機(jī)目錄訪問如果出現(xiàn)cannot open directory .: Permission denied 解決辦法:在掛載目錄后多加一個–privileged=true參數(shù)即可 原因:如果是CentOS7安全模塊會比之前系統(tǒng)版本加強(qiáng),不安全的會先禁止,所以目錄掛載的情況被默認(rèn)為不安全的行為, 在SELinux里面掛載目錄被禁止掉了額,如果要開啟,我們一般使用–privileged=true命令,擴(kuò)大容器的權(quán)限解決掛載目錄沒有權(quán)限的問題,也即 使用該參數(shù),container內(nèi)的root擁有真正的root權(quán)限,否則,container內(nèi)的root只是外部的一個普通用戶權(quán)限。
1. **回顧下上一講的知識點(diǎn),參數(shù)V**
還記得藍(lán)色框框中的內(nèi)容嗎?
1.7.1 簡介
1. 是什么
卷就是目錄或文件,存在于一個或多個容器中,由docker掛載到容器,但不屬于聯(lián)合文件系統(tǒng),因此能夠繞過Union File System提供一些用于持續(xù)存儲或共享數(shù)據(jù)的特性: 卷的設(shè)計目的就是數(shù)據(jù)的持久化,完全獨(dú)立于容器的生存周期,因此Docker不會在容器刪除時刪除其掛載的數(shù)據(jù)卷。
一句話:有點(diǎn)類似我們Redis里面的rdb和aof文件將docker容器內(nèi)的數(shù)據(jù)保存進(jìn)宿主機(jī)的磁盤中運(yùn)行一個帶有容器卷存儲功能的容器實例
docker run -it --privileged=true -v /宿主機(jī)絕對路徑目錄:/容器內(nèi)目錄 鏡像名
2. 能干嘛
為了保證docker數(shù)據(jù)持久化,防止容器實例刪除以后,造成數(shù)據(jù)丟失。 特點(diǎn): 1:數(shù)據(jù)卷可在容器之間共享或重用數(shù)據(jù) 2:卷中的更改可以直接實時生效,爽 3:數(shù)據(jù)卷中的更改不會包含在鏡像的更新中 4:數(shù)據(jù)卷的生命周期一直持續(xù)到?jīng)]有容器使用它為止
1.7.2 數(shù)據(jù)卷案例
1. 宿主vs容器之間映射添加容器卷
a. 掛載命令
# 公式:docker run -it -v /宿主機(jī)目錄:/容器內(nèi)目錄 ubuntu /bin/bash
docker run -it --name myu3 --privileged=true -v /tmp/myHostData:/tmp/myDockerData ubuntu /bin/bash
結(jié)果圖示:
b. 查看數(shù)據(jù)卷是否掛載成功
docker inspect 容器ID
c. 容器和宿主機(jī)之間數(shù)據(jù)共享
docker修改,主機(jī)同步獲得主機(jī)修改,docker同步獲得docker容器stop,主機(jī)修改,docker容器重啟看數(shù)據(jù)是否同步。
2. 讀寫規(guī)則映射添加說明
讀寫(默認(rèn)):rw = read + write
只讀:ro
docker run -it --privileged=true -v /宿主機(jī)絕對路徑目錄:/容器內(nèi)目錄:ro 鏡像名
3. 卷的繼承和共享
a. 容器1完成和宿主機(jī)的映射
docker run -it --privileged=true -v /mydocker/u:/tmp --name u1 ubuntu
b. 容器2繼承容器1的卷規(guī)則
# 命令公式
docker run -it --privileged=true --volumes-from 父類 --name u2 ubuntu
1.8 Docker常規(guī)安裝簡介
1.8.1 總體步驟
搜索鏡像拉取鏡像查看鏡像啟動鏡像
服務(wù)端口映射 停止容器移除容器
1.8.2 安裝tomcat
1. docker hub上面查找tomcat鏡像
docker search tomcat
2. 從docker hub上拉取tomcat鏡像到本地
docker pull tomcat
3. 查看是否有拉取到的tomcat
docker images
4. 使用tomcat鏡像創(chuàng)建容器實例(也叫運(yùn)行鏡像)
docker run -it -p 8080:8080 tomcat
-p 小寫,主機(jī)端口:docker容器端口 -P 大寫,隨機(jī)分配端口 i:交互 t:終端 d:后臺
5. 訪問貓首頁
免修改版說明
docker pull billygoo/tomcat8-jdk8docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8
6. 問題
高版本Tomcat會出現(xiàn)404情況 解決
可能沒有映射端口或者沒有關(guān)閉防火墻
把webapps.dist目錄換成webapps
先成功啟動tomcat 查看webapps 文件夾查看為空
1.8.3 安裝mysql
docker hub上面查找mysql鏡像
從docker hub上(阿里云加速器)拉取mysql鏡像到本地標(biāo)簽為5.7
使用mysql5.7鏡像創(chuàng)建容器(也叫運(yùn)行鏡像)
1. 簡單版
# 啟動容器
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 查看啟動結(jié)果
docker ps
# 進(jìn)入容器
docker exec -it 容器ID /bin/bash
# 執(zhí)行mysql命令
mysql -uroot -p
建庫建表插入數(shù)據(jù) 外部Win10也來連接運(yùn)行在dokcer上的mysql容器實例服務(wù)
a. 簡單版存在的問題
插入中文數(shù)據(jù)試試
為什么報錯? docker上默認(rèn)字符集編碼隱患
docker里面的mysql容器實例查看,內(nèi)容如下:
SHOW VARIABLES LIKE ‘character%’
刪除容器后,里面的mysql數(shù)據(jù)如何辦 容器實例一刪除,你還有什么? 刪容器到跑路。。。。。?
2. 實戰(zhàn)版
a. 啟動容器
docker run -d -p 3306:3306 --privileged=true
-v /zzyyuse/mysql/log:/var/log/mysql
-v /zzyyuse/mysql/data:/var/lib/mysql
-v /zzyyuse/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
b. 新建my.cnf
通過容器卷同步給mysql容器實例
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
c. 重新啟動mysql容器實例再重新進(jìn)入并查看字符編碼
d. 再新建庫新建表再插入中文測試
e. 結(jié)論:docker安裝完MySQL并run出容器后,建議請先修改完字符集編碼后再新建mysql庫-表-插數(shù)據(jù)
1.8.3 安裝redis
1. docker pull 拉取鏡像
2. 入門命令
3. 安裝
命令提醒:容器卷記得加入–privileged=true
a 在CentOS宿主機(jī)下新建目錄/app/redis
b. 將一個redis.conf文件模板拷貝進(jìn)/app/redis目錄下
c. /app/redis目錄下修改redis.conf文件
1)開啟redis驗證 可選 requirepass 123
2)允許redis外地連接 必須 注釋掉 # bind 127.0.0.1
3)daemonize no 將daemonize yes注釋起來或者 daemonize no設(shè)置,因為該配置和docker run中-d參數(shù)沖突,會導(dǎo)致容器一直啟動失敗 4)開啟redis數(shù)據(jù)持久化 appendonly yes 可選
d. 使用redis6.0.8鏡像創(chuàng)建容器(也叫運(yùn)行鏡像)
docker run -p 6379:6379 --name myr3 --privileged=true
-v /app/redis/redis.conf:/etc/redis/redis.conf
-v /app/redis/data:/data
-d redis:6.0.8 redis-server /etc/redis/redis.conf
e. 測試redis-cli連接上來
docker exec -it 運(yùn)行著Rediis服務(wù)的容器ID redis-cli
2 高級篇(大廠進(jìn)階)
2.1 Docker復(fù)雜安裝詳說
2.1.1 安裝mysql主從復(fù)制
主從搭建步驟如下:
1. 新建主服務(wù)器容器實例3307
docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql-master/log:/var/log/mysql \
-v /mydata/mysql-master/data:/var/lib/mysql \
-v /mydata/mysql-master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
2. 進(jìn)入/mydata/mysql-master/conf目錄下新建my.cnf
vim my.cnf
[mysqld]
## 設(shè)置server_id,同一局域網(wǎng)中需要唯一
server_id=101
## 指定不需要同步的數(shù)據(jù)庫名稱
binlog-ignore-db=mysql
## 開啟二進(jìn)制日志功能
log-bin=mall-mysql-bin
## 設(shè)置二進(jìn)制日志使用內(nèi)存大?。ㄊ聞?wù))
binlog_cache_size=1M
## 設(shè)置使用的二進(jìn)制日志格式(mixed,statement,row)
binlog_format=mixed
## 二進(jìn)制日志過期清理時間。默認(rèn)值為0,表示不自動清理。
expire_logs_days=7
## 跳過主從復(fù)制中遇到的所有錯誤或指定類型的錯誤,避免slave端復(fù)制中斷。
## 如:1062錯誤是指一些主鍵重復(fù),1032錯誤是因為主從數(shù)據(jù)庫數(shù)據(jù)不一致
slave_skip_errors=1062
3. 修改完配置后重啟master實例
docker restart mysql-master
4. 進(jìn)入mysql-master容器
docker exec -it mysql-master /bin/bash
5. master容器實例內(nèi)創(chuàng)建數(shù)據(jù)同步用戶
mysql -uroot -proot
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
6. 新建從服務(wù)器容器實例3308
docker run -p 3308:3306 --name mysql-slave \
-v /mydata/mysql-slave/log:/var/log/mysql \
-v /mydata/mysql-slave/data:/var/lib/mysql \
-v /mydata/mysql-slave/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
7. 從服務(wù)實例修改配置文件
進(jìn)入/mydata/mysql-slave/conf目錄下新建my.cnf vim my.cnf
[mysqld]
## 設(shè)置server_id,同一局域網(wǎng)中需要唯一
server_id=102
## 指定不需要同步的數(shù)據(jù)庫名稱
binlog-ignore-db=mysql
## 開啟二進(jìn)制日志功能,以備Slave作為其它數(shù)據(jù)庫實例的Master時使用
log-bin=mall-mysql-slave1-bin
## 設(shè)置二進(jìn)制日志使用內(nèi)存大?。ㄊ聞?wù))
binlog_cache_size=1M
## 設(shè)置使用的二進(jìn)制日志格式(mixed,statement,row)
binlog_format=mixed
## 二進(jìn)制日志過期清理時間。默認(rèn)值為0,表示不自動清理。
expire_logs_days=7
## 跳過主從復(fù)制中遇到的所有錯誤或指定類型的錯誤,避免slave端復(fù)制中斷。
## 如:1062錯誤是指一些主鍵重復(fù),1032錯誤是因為主從數(shù)據(jù)庫數(shù)據(jù)不一致
slave_skip_errors=1062
## relay_log配置中繼日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave將復(fù)制事件寫進(jìn)自己的二進(jìn)制日志
log_slave_updates=1
## slave設(shè)置為只讀(具有super權(quán)限的用戶除外)
read_only=1
8. 修改完配置后重啟slave實例
docker restart mysql-slave
9. 在主數(shù)據(jù)庫中查看主從同步狀態(tài)(需要在mysql命令行查看)
show master status;
10. 進(jìn)入mysql-slave容器
docker exec -it mysql-slave /bin/bash
mysql -uroot -proot
11. 在從數(shù)據(jù)庫中配置主從復(fù)制
change master to master_host='宿主機(jī)ip', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
主從復(fù)制命令參數(shù)說明:
master_host:主數(shù)據(jù)庫的IP地址;master_port:主數(shù)據(jù)庫的運(yùn)行端口;master_user:在主數(shù)據(jù)庫創(chuàng)建的用于同步數(shù)據(jù)的用戶賬號;master_password:在主數(shù)據(jù)庫創(chuàng)建的用于同步數(shù)據(jù)的用戶密碼;master_log_file:指定從數(shù)據(jù)庫要復(fù)制數(shù)據(jù)的日志文件,通過查看主數(shù)據(jù)的狀態(tài),獲取File參數(shù);master_log_pos:指定從數(shù)據(jù)庫從哪個位置開始復(fù)制數(shù)據(jù),通過查看主數(shù)據(jù)的狀態(tài),獲取Position參數(shù);master_connect_retry:連接失敗重試的時間間隔,單位為秒。
12. 在從數(shù)據(jù)庫中查看主從同步狀態(tài)
show slave status \G;
13. 在從數(shù)據(jù)庫中開啟主從同步
14. 查看從數(shù)據(jù)庫狀態(tài)發(fā)現(xiàn)已經(jīng)同步
15. 主從復(fù)制測試
主機(jī)新建庫-使用庫-新建表-插入數(shù)據(jù),ok 從機(jī)使用庫-查看記錄,ok
2.1.2 分布式存儲方案
cluster(集群)模式-docker版 哈希槽分區(qū)進(jìn)行億級數(shù)據(jù)存儲 單機(jī)單臺100%不可能,肯定是分布式存儲,用redis如何落地? 上述問題阿里P6~P7工程案例和場景設(shè)計類必考題目, 一般業(yè)界有3種解決方案
1. 哈希取余分區(qū)
| 原理: 2億條記錄就是2億個k,v,我們單機(jī)不行必須要分布式多機(jī),假設(shè)有3臺機(jī)器構(gòu)成一個集群,用戶每次讀寫操作都是根據(jù)公式:
hash(key) % N個機(jī)器臺數(shù),計算出哈希值,用來決定數(shù)據(jù)映射到哪一個節(jié)點(diǎn)上。優(yōu)點(diǎn):簡單粗暴,直接有效,只需要預(yù)估好數(shù)據(jù)規(guī)劃好節(jié)點(diǎn),例如3臺、8臺、10臺,就能保證一段時間的數(shù)據(jù)支撐。使用Hash算法讓固定的一部分請求落到同一臺服務(wù)器上,這樣每臺服務(wù)器固定處理一部分請求(并維護(hù)這些請求的信息),起到負(fù)載均衡+分而治之的作用。缺點(diǎn):原來規(guī)劃好的節(jié)點(diǎn),進(jìn)行擴(kuò)容或者縮容就比較麻煩了額,不管擴(kuò)縮,每次數(shù)據(jù)變動導(dǎo)致節(jié)點(diǎn)有變動,映射關(guān)系需要重新進(jìn)行計算,在服務(wù)器個數(shù)固定不變時沒有問題,如果需要彈性擴(kuò)容或故障停機(jī)的情況下,原來的取模公式就會發(fā)生變化:Hash(key)/3會變成Hash(key) /?。此時地址經(jīng)過取余運(yùn)算的結(jié)果將發(fā)生很大變化,根據(jù)公式獲取的服務(wù)器也會變得不可控。某個redis機(jī)器宕機(jī)了,由于臺數(shù)數(shù)量變化,會導(dǎo)致hash取余全部數(shù)據(jù)重新洗牌。
2. 一致性哈希算法分區(qū)
是什么 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工學(xué)院中提出的,設(shè)計目標(biāo)是為了解決 分布式緩存數(shù)據(jù)變動和映射問題,某個機(jī)器宕機(jī)了,分母數(shù)量改變了,自然取余數(shù)不OK了。
能干嘛 提出一致性Hash解決方案。 目的是當(dāng)服務(wù)器個數(shù)發(fā)生變動時, 盡量減少影響客戶端到服務(wù)器的映射關(guān)系 3大步驟 算法構(gòu)建一致性哈希環(huán) 一致性哈希環(huán) 一致性哈希算法必然有個hash函數(shù)并按照算法產(chǎn)生hash值,這個算法的所有可能哈希值會構(gòu)成一個全量集,這個集合可以成為一個hash空間[0,2^32-1],這個是一個線性空間,但是在算法中,我們通過適當(dāng)?shù)倪壿嬁刂茖⑺孜蚕噙B(0 = 2^32),這樣讓它邏輯上形成了一個環(huán)形空間。
它也是按照使用取模的方法,前面筆記介紹的節(jié)點(diǎn)取模法是對節(jié)點(diǎn)(服務(wù)器)的數(shù)量進(jìn)行取模。而一致性Hash算法是對232取模,簡單來說,一致性Hash算法將整個哈希值空間組織成一個虛擬的圓環(huán),如假設(shè)某哈希函數(shù)H的值空間為0-232-1(即哈希值是一個32位無符號整形),整個哈希環(huán)如下圖:整個空間按順時針方向組織,圓環(huán)的正上方的點(diǎn)代表0,0點(diǎn)右側(cè)的第一個點(diǎn)代表1,以此類推,2、3、4、……直到232-1,也就是說0點(diǎn)左側(cè)的第一個點(diǎn)代表232-1, 0和232-1在零點(diǎn)中方向重合,我們把這個由232個點(diǎn)組成的圓環(huán)稱為Hash環(huán)。 服務(wù)器IP節(jié)點(diǎn)映射 節(jié)點(diǎn)映射 將集群中各個IP節(jié)點(diǎn)映射到環(huán)上的某一個位置。 將各個服務(wù)器使用Hash進(jìn)行一個哈希,具體可以選擇服務(wù)器的IP或主機(jī)名作為關(guān)鍵字進(jìn)行哈希,這樣每臺機(jī)器就能確定其在哈希環(huán)上的位置。假如4個節(jié)點(diǎn)NodeA、B、C、D,經(jīng)過IP地址的哈希函數(shù)計算(hash(ip)),使用IP地址哈希后在環(huán)空間的位置如下: key落到服務(wù)器的落鍵規(guī)則 當(dāng)我們需要存儲一個kv鍵值對時,首先計算key的hash值,hash(key),將這個key使用相同的函數(shù)Hash計算出哈希值并確定此數(shù)據(jù)在環(huán)上的位置,從此位置沿環(huán)順時針“行走”,第一臺遇到的服務(wù)器就是其應(yīng)該定位到的服務(wù)器,并將該鍵值對存儲在該節(jié)點(diǎn)上。 如我們有Object A、Object B、Object C、Object D四個數(shù)據(jù)對象,經(jīng)過哈希計算后,在環(huán)空間上的位置如下:根據(jù)一致性Hash算法,數(shù)據(jù)A會被定為到Node A上,B被定為到Node B上,C被定為到Node C上,D被定為到Node D上。 優(yōu)點(diǎn) 一致性哈希算法的容錯性 容錯性 假設(shè)Node C宕機(jī),可以看到此時對象A、B、D不會受到影響,只有C對象被重定位到Node D。一般的,在一致性Hash算法中,如果一臺服務(wù)器不可用,則受影響的數(shù)據(jù)僅僅是此服務(wù)器到其環(huán)空間中前一臺服務(wù)器(即沿著逆時針方向行走遇到的第一臺服務(wù)器)之間數(shù)據(jù),其它不會受到影響。簡單說,就是C掛了,受到影響的只是B、C之間的數(shù)據(jù),并且這些數(shù)據(jù)會轉(zhuǎn)移到D進(jìn)行存儲。
一致性哈希算法的擴(kuò)展性 擴(kuò)展性 數(shù)據(jù)量增加了,需要增加一臺節(jié)點(diǎn)NodeX,X的位置在A和B之間,那收到影響的也就是A到X之間的數(shù)據(jù),重新把A到X的數(shù)據(jù)錄入到X上即可, 不會導(dǎo)致hash取余全部數(shù)據(jù)重新洗牌。
缺點(diǎn) 一致性哈希算法的數(shù)據(jù)傾斜問題
Hash環(huán)的數(shù)據(jù)傾斜問題 一致性Hash算法在服務(wù)節(jié)點(diǎn)太少時,容易因為節(jié)點(diǎn)分布不均勻而造成數(shù)據(jù)傾斜(被緩存的對象大部分集中緩存在某一臺服務(wù)器上)問題, 例如系統(tǒng)中只有兩臺服務(wù)器: 小總結(jié) 為了在節(jié)點(diǎn)數(shù)目發(fā)生改變時盡可能少的遷移數(shù)據(jù)
將所有的存儲節(jié)點(diǎn)排列在收尾相接的Hash環(huán)上,每個key在計算Hash后會順時針找到臨近的存儲節(jié)點(diǎn)存放。 而當(dāng)有節(jié)點(diǎn)加入或退出時僅影響該節(jié)點(diǎn)在Hash環(huán)上順時針相鄰的后續(xù)節(jié)點(diǎn)。
優(yōu)點(diǎn) 加入和刪除節(jié)點(diǎn)只影響哈希環(huán)中順時針方向的相鄰的節(jié)點(diǎn),對其他節(jié)點(diǎn)無影響。
缺點(diǎn) 數(shù)據(jù)的分布和節(jié)點(diǎn)的位置有關(guān),因為這些節(jié)點(diǎn)不是均勻的分布在哈希環(huán)上的,所以數(shù)據(jù)在進(jìn)行存儲時達(dá)不到均勻分布的效果。
3. 哈希槽分區(qū)
a. 是什么
哈希槽實質(zhì)就是一個數(shù)組,數(shù)組[0,2^14 -1]形成hash slot空間。
b. 能干什么
解決均勻分配的問題,在數(shù)據(jù)和節(jié)點(diǎn)之間又加入了一層,把這層稱為哈希槽(slot),用于管理數(shù)據(jù)和節(jié)點(diǎn)之間的關(guān)系,現(xiàn)在就相當(dāng)于節(jié)點(diǎn)上放的是槽,槽里放的是數(shù)據(jù)。 槽解決的是粒度問題,相當(dāng)于把粒度變大了,這樣便于數(shù)據(jù)移動。 哈希解決的是映射問題,使用key的哈希值來計算所在的槽,便于數(shù)據(jù)分配。
c. 多少個hash槽
一個集群只能有16384個槽,編號0-16383(0-2^14-1)。這些槽會分配給集群中的所有主節(jié)點(diǎn),分配策略沒有要求??梢灾付男┚幪柕牟鄯峙浣o哪個主節(jié)點(diǎn)。集群會記錄節(jié)點(diǎn)和槽的對應(yīng)關(guān)系。解決了節(jié)點(diǎn)和槽的關(guān)系后,接下來就需要對key求哈希值,然后對16384取余,余數(shù)是幾key就落入對應(yīng)的槽里。slot = CRC16(key) % 16384。以槽為單位移動數(shù)據(jù),因為槽的數(shù)目是固定的,處理起來比較容易,這樣數(shù)據(jù)移動問題就解決了。
d. 哈希槽計算
Redis 集群中內(nèi)置了 16384 個哈希槽,redis 會根據(jù)節(jié)點(diǎn)數(shù)量大致均等的將哈希槽映射到不同的節(jié)點(diǎn)。當(dāng)需要在 Redis 集群中放置一個 key-value時,redis 先對 key 使用 crc16 算法算出一個結(jié)果,然后把結(jié)果對 16384 求余數(shù),這樣每個 key 都會對應(yīng)一個編號在 0-16383 之間的哈希槽,也就是映射到某個節(jié)點(diǎn)上。如下代碼,key之A 、B在Node2, key之C落在Node3上
2.1.3 Redis三主三從集群搭建和擴(kuò)縮容
1. 前置內(nèi)容
關(guān)閉防火墻+啟動docker后臺服務(wù) systemctl start docker
2. 新建6個docker容器redis
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
如果運(yùn)行成功,效果如下: 命令分步解釋
docker run:創(chuàng)建并運(yùn)行docker容器實例–name redis-node-6:容器名字–net host:使用宿主機(jī)的IP和端口,默認(rèn)–privileged=true:獲取宿主機(jī)root用戶權(quán)限-v /data/redis/share/redis-node-6:/data:容器卷,宿主機(jī)地址:docker內(nèi)部地址redis:6.0.8:redis鏡像和版本號–cluster-enabled yes:開啟redis集群–appendonly yes:開啟持久化–port 6386:redis端口號
3.進(jìn)入容器redis-node-1并為6臺機(jī)器構(gòu)建集群關(guān)系
進(jìn)入容器:docker exec -it redis-node-1 /bin/bash
redis-cli --cluster create 192.168.111.147:6381 192.168.111.147:6382 192.168.111.147:6383 192.168.111.147:6384 192.168.111.147:6385 192.168.111.147:6386 --cluster-replicas 1
注意:
進(jìn)入docker容器后才能執(zhí)行一下命令,且注意自己的真實IP地址–cluster-replicas 1 表示為每個master創(chuàng)建一個slave節(jié)點(diǎn)
操作圖示:
一切OK的話,3主3從搞定 鏈接進(jìn)入6381作為切入點(diǎn),查看節(jié)點(diǎn)狀態(tài)
4. 主從容錯切換遷移案例
數(shù)據(jù)讀寫存儲 啟動6機(jī)構(gòu)成的集群并通過exec進(jìn)入。防止路由失效加參數(shù)-c并新增兩個key
redis-cli -p 6831 -c
加入?yún)?shù)-c,優(yōu)化路由
5. 查看集群信息
# redis-cli --cluster check IP:端口
redis-cli --cluster check 192.168.111.147:6381
6. 容錯切換遷移
a. 操作步驟:
主6381和從機(jī)切換,先停止主機(jī)6381:docker stop redis-node-16381主機(jī)停了,對應(yīng)的真實從機(jī)上位6381作為1號主機(jī)分配的從機(jī)以實際情況為準(zhǔn),具體是幾號機(jī)器就是幾號。
b. 查看集群信息
6381宕機(jī)了,6385上位成為了新的master。 備注:本次腦圖筆記6381為主下面掛從6385。每次案例下面掛的從機(jī)以實際情況為準(zhǔn),具體是幾號機(jī)器就是幾號
c. 先還原之前的3主3從
先啟動redis-node-1節(jié)點(diǎn)再停redis-node-1節(jié)點(diǎn)對應(yīng)的從機(jī)(本案例是redis-node-5)最后重啟redis-node-5
先啟6381:docker start redis-node-1
再停6385:docker stop redis-node-5 再啟動6385:docker start redis-node-5
查看集群狀態(tài):redis-cli --cluster check 自己IP:6381
2.1.4 Redis主從擴(kuò)容案例
1. 新建6387、6388兩個節(jié)點(diǎn)
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
# 查看容器
docker ps
2. 將新增的6387節(jié)點(diǎn)(空槽號)作為master節(jié)點(diǎn)加入原集群
進(jìn)入6387容器實例內(nèi)部:docker exec -it redis-node-7 /bin/bash
# 命令公式:redis-cli --cluster add-node 自己實際IP地址:6387 自己實際IP地址:6381
redis-cli --cluster add-node 192.168.56.10:6387 192.168.56.10:6381
6387 就是將要作為master新增節(jié)點(diǎn)6381 就是原來集群節(jié)點(diǎn)里面的領(lǐng)路人,相當(dāng)于6387拜拜6381的碼頭從而找到組織加入集群
操作圖示:
3. 檢查集群情況第1次
redis-cli --cluster check 真實ip地址:6381
4. 重新分派槽號
# 命令:redis-cli --cluster reshard IP地址:端口號
redis-cli --cluster reshard 192.168.56.10:6381
5. 檢查集群情況第2次
redis-cli --cluster check 192.168.56.10:6381
槽號分派說明
| 為什么6387是3個新的區(qū)間,以前的還是連續(xù)?
重新分配成本太高,所以前3家各自勻出來一部分,從6381/6382/6383三個舊節(jié)點(diǎn)分別勻出1364個坑位給新節(jié)點(diǎn)6387
6. 為主節(jié)點(diǎn)6387分配從節(jié)點(diǎn)6388
# 命令:redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主機(jī)節(jié)點(diǎn)ID
redis-cli --cluster add-node 192.168.56.10:6388 192.168.56.10:6387 --cluster-slave --cluster-master-id d8c57543cca389847fa3981e8d201b603e02e809
7. 檢查集群情況第3次
redis-cli --cluster check 192.168.56.10:6382
2.1.5 Redis主從縮容案例
目的:6387和6388下線
1. 檢查集群情況1
redis-cli --cluster check 192.168.111.147:6382
2. 刪除從節(jié)點(diǎn)
# 命令:redis-cli --cluster del-node ip:從機(jī)端口 從機(jī)6388節(jié)點(diǎn)ID
redis-cli --cluster del-node 192.168.56.10:6388 0becca6ec61c7ab61e85c0463158a8911c7f9ea9
3. 檢查集群情況2
redis-cli --cluster check 192.168.56.10:6382
檢查一下發(fā)現(xiàn),6388被刪除了,只剩下7臺機(jī)器了。
4. 將6387的槽號清空,重新分配
本例將清出來的槽號都給6381
redis-cli --cluster reshard 192.168.56.10:6381
5. 檢查集群情況3
redis-cli --cluster check 192.168.56.10:6381
4096個槽位都指給6381,它變成了8192個槽位,相當(dāng)于全部都給6381了,不然要輸入3次,一鍋端
6. 將主節(jié)點(diǎn)6387刪除
redis-cli --cluster del-node 192.168.56.10:6387 d8c57543cca389847fa3981e8d201b603e02e809
7. 檢查集群情況4
2.2 DockerFile解析
官網(wǎng):https://docs.docker.com/engine/reference/builder/
2.2.1 簡介
Dockerfile是用來構(gòu)建Docker鏡像的文本文件,是由一條條構(gòu)建鏡像所需的指令和參數(shù)構(gòu)成的腳本。
2.2.2 構(gòu)建
1. 構(gòu)建三步驟
編寫Dockerfile文件docker build命令構(gòu)建鏡像docker run依鏡像運(yùn)行容器實例
2. DockerFile構(gòu)建過程解析
Dockerfile內(nèi)容基礎(chǔ)知識
1:每條保留字指令都必須為大寫字母且后面要跟隨至少一個參數(shù)2:指令按照從上到下,順序執(zhí)行3:#表示注釋4:每條指令都會創(chuàng)建一個新的鏡像層并對鏡像進(jìn)行提交 Docker執(zhí)行Dockerfile的大致流程
docker從基礎(chǔ)鏡像運(yùn)行一個容器
執(zhí)行一條指令并對容器作出修改
執(zhí)行類似docker commit的操作提交一個新的鏡像層
docker再基于剛提交的鏡像運(yùn)行一個新容器
執(zhí)行dockerfile中的下一條指令直到所有指令都執(zhí)行完成
Dockerfile、Docker鏡像與Docker容器三者關(guān)系 從應(yīng)用軟件的角度來看,Dockerfile、Docker鏡像與Docker容器分別代表軟件的三個不同階段,
Dockerfile是軟件的原材料,需要定義一個Dockerfile,Dockerfile中定義了進(jìn)程需要的一切東西。docker鏡像,在用Dockerfile定義一個文件之后,執(zhí)行docker build會產(chǎn)生一個Docker鏡像,運(yùn)行Docker鏡像時會真正的開發(fā)提供服務(wù)docker容器,執(zhí)行docker run之后會是生成一個docker容器實例,對外提供服務(wù)
2.2.3 DockerFile常用保留字指令
參考tomcat8的dockerfile入門:https://github.com/docker-library/tomcat
1. FROM
基礎(chǔ)鏡像,當(dāng)前新鏡像是基于哪個鏡像的,指定一個已經(jīng)存在的鏡像作為模板,第一條必須是from
2. MAINTAINER
鏡像維護(hù)者的姓名和郵箱地址
3. RUN
容器構(gòu)建時需要運(yùn)行的命令,RUN是在 docker build時運(yùn)行
兩種格式
shell格式:RUN yum -y install vimexec格式:RUN [“可執(zhí)行文件”, “參數(shù)1”, “參數(shù)2”]
4. EXPOSE
當(dāng)前容器對外暴露出的端口
5. WORKDIR
指定在創(chuàng)建容器后,終端默認(rèn)登陸的進(jìn)來工作目錄,一個落腳點(diǎn)
6. USER
指定該鏡像以什么樣的用戶去執(zhí)行,如果都不指定,默認(rèn)是root
7. ENV
用來在構(gòu)建鏡像過程中設(shè)置環(huán)境變量
| ENV MY_PATH /usr/mytest 這個環(huán)境變量可以在后續(xù)的任何RUN指令中使用,這就如同在命令前面指定了環(huán)境變量前綴一樣; 也可以在其它指令中直接使用這些環(huán)境變量,
比如:WORKDIR $MY_PATH
8. ADD
將宿主機(jī)目錄下的文件拷貝進(jìn)鏡像且會自動處理URL和解壓tar壓縮包
9. COPY
類似ADD,拷貝文件和目錄到鏡像中。 將從構(gòu)建上下文目錄中 <源路徑> 的文件/目錄復(fù)制到新的一層的鏡像內(nèi)的 <目標(biāo)路徑> 位置 COPY src dest COPY [“src”, “dest”]
10. VOLUME
容器數(shù)據(jù)卷,用于數(shù)據(jù)保存和持久化工作
11. CMD
指定容器啟動后的要干的事情。 Dockerfile 中可以有多個 CMD 指令,但只有最后一個生效,CMD 會被 docker run 之后的參數(shù)替換 參考官網(wǎng)Tomcat的dockerfile演示講解 官網(wǎng)最后一行命令 我們演示自己的覆蓋操作 CMD和RUN命令的區(qū)別
CMD是在docker run 時運(yùn)行。RUN是在 docker build時運(yùn)行。
12. ENTRYPOINT
用來指定一個容器啟動時要運(yùn)行的命令類似于 CMD 指令,但是ENTRYPOINT不會被docker run后面的命令覆蓋, 而且這些命令行參數(shù)會被當(dāng)作參數(shù)送給 ENTRYPOINT 指令指定的程序命令格式和案例說明
命令格式: ENTRYPOINT可以和CMD一起用,一般是變參才會使用 CMD ,這里的 CMD 等于是在給 ENTRYPOINT 傳參。 當(dāng)指定了ENTRYPOINT后,CMD的含義就發(fā)生了變化,不再是直接運(yùn)行其命令而是將CMD的內(nèi)容作為參數(shù)傳遞給ENTRYPOINT 指令,他兩個組合會變成
案例如下:假設(shè)已通過 Dockerfile 構(gòu)建了 nginx:test 鏡像:
是否傳參按照dockerfile編寫執(zhí)行傳參運(yùn)行Docker命令docker run nginx:testdocker run nginx:test -c /etc/nginx/new.conf衍生出的實際命令nginx -c /etc/nginx/nginx.confnginx -c /etc/nginx/new.conf
優(yōu)點(diǎn):在執(zhí)行docker run的時候可以指定 ENTRYPOINT 運(yùn)行所需的參數(shù)。注意:如果 Dockerfile 中如果存在多個 ENTRYPOINT 指令,僅最后一個生效。
小總結(jié)
2.2.4 案例
自定義鏡像mycentosjava8
要求:Centos7鏡像具備vim+ifconfig+jdk8
JDK的下載鏡像地址 官網(wǎng)
下載地址: https://www.oracle.com/java/technologies/downloads/#java8 https://mirrors.yangxingzhen.com/jdk/
- 編寫
準(zhǔn)備編寫Dockerfile文件
FROM centos
MAINTAINER zzyy
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安裝vim編輯器
RUN yum -y install vim
#安裝ifconfig命令查看網(wǎng)絡(luò)IP
RUN yum -y install net-tools
#安裝java8及l(fā)ib庫
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相對路徑j(luò)ar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安裝包必須要和Dockerfile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java環(huán)境變量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
# docker build -t 新鏡像名字:TAG .
docker build -t centosjava8:1.5 .
注意:不要忘記后面的點(diǎn),表示當(dāng)前目錄
# docker run -it 新鏡像名字:TAG
docker run -it centosjava8:1.5 /bin/bash
2.2.5 再體會下UnionFS(聯(lián)合文件系統(tǒng))
UnionFS(聯(lián)合文件系統(tǒng)):Union文件系統(tǒng)(UnionFS)是一種分層、輕量級并且高性能的文件系統(tǒng),它支持對文件系統(tǒng)的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統(tǒng)下(unite several directories into a single virtual filesystem)。Union 文件系統(tǒng)是 Docker 鏡像的基礎(chǔ)。鏡像可以通過分層來進(jìn)行繼承,基于基礎(chǔ)鏡像(沒有父鏡像),可以制作各種具體的應(yīng)用鏡像。 特性:一次同時加載多個文件系統(tǒng),但從外面看起來,只能看到一個文件系統(tǒng),聯(lián)合加載會把各層文件系統(tǒng)疊加起來,這樣最終的文件系統(tǒng)會包含所有底層的文件和目錄
2.2.6 虛懸鏡像
倉庫名、標(biāo)簽都是的鏡像稱為虛懸鏡像,俗稱dangling image Dockerfile寫一個 vim Dockerfile
| from ubuntu
CMD echo ‘a(chǎn)ction is success’
2 docker build .
- 查看
docker image ls -f dangling=true 命令結(jié)果
docker image prune
2.2.7 案例:自定義鏡像myubuntu
編寫DockerFile文件
| FROM ubuntu MAINTAINER zzyyzzyybs@126.com
ENV MYPATH /usr/local WORKDIR $MYPATH
RUN apt-get update RUN apt-get install net-tools #RUN apt-get install -y iproute2 #RUN apt-get install -y inetutils-ping
EXPOSE 80
CMD echo $MYPATH CMD echo “install inconfig cmd into ubuntu success--------------ok”
CMD /bin/bash
構(gòu)建:docker build -t 新鏡像名字:TAG .運(yùn)行:docker run -it 新鏡像名字:TAG
2.3 Docker微服務(wù)實戰(zhàn)
2.3.1 通過IDEA新建一個普通微服務(wù)模塊
1. 新建Module
docker_boot
2. 改POM
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3. 寫YML
server.port=6001
4. 主啟動
package com.atguigu.docker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic
class DockerBootApplication {
public static void main(String[] args){
SpringApplication.run(DockerBootApplication.class, args);
}
}
5. 業(yè)務(wù)類
package com.atguigu.docker.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* @auther zzyy
* @create 2021-10-25 17:43
*/
@RestControllerpublic
class OrderController{
@Value("${server.port}")
private String port;
@RequestMapping("/order/docker")
public String helloDocker(){
return "hello docker"+"\t"+port+"\t"+ UUID.randomUUID().toString();
}
@RequestMapping(value ="/order/index",method = RequestMethod.GET)
public String index(){
return "服務(wù)端口號: "+"\t"+port+"\t"+UUID.randomUUID().toString();
}
}
2.3.2 通過dockerfile發(fā)布微服務(wù)部署到docker容器
1. IDEA工具里面搞定微服務(wù)jar包
docker_boot-0.0.1-SNAPSHOT.jar
2. 編寫Dockerfile
# 基礎(chǔ)鏡像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定臨時文件目錄為/tmp,在主機(jī)/var/lib/docker目錄下創(chuàng)建了一個臨時文件并鏈接到容器的/tmp
VOLUME /tmp
# 將jar包添加到容器中并更名為zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 運(yùn)行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作為微服務(wù)
EXPOSE 6001
3. 將微服務(wù)jar包和Dockerfile文件上傳到同一個目錄下/mydocker
4. 構(gòu)建鏡像
docker build -t zzyy_docker:1.6 .
5. 運(yùn)行容器
docker run -d -p 6001:6001 zzyy_docker:1.6
6. 訪問測試
2.4 Docker網(wǎng)絡(luò)
2.4.1 是什么
1. docker不啟動,默認(rèn)網(wǎng)絡(luò)情況
在CentOS7的安裝過程中如果有選擇相關(guān)虛擬化的的服務(wù)安裝系統(tǒng)后,啟動網(wǎng)卡時會發(fā)現(xiàn)有一個以網(wǎng)橋連接的私網(wǎng)地址的virbr0網(wǎng)卡(virbr0網(wǎng)卡:它還有一個固定的默認(rèn)IP地址192.168.122.1),是做虛擬機(jī)網(wǎng)橋的使用的,其作用是為連接其上的虛機(jī)網(wǎng)卡提供 NAT訪問外網(wǎng)的功能。
我們之前學(xué)習(xí)Linux安裝,勾選安裝系統(tǒng)的時候附帶了libvirt服務(wù)才會生成的一個東西,如果不需要可以直接將libvirtd服務(wù)卸載,yum remove libvirt-libs.x86_64
2. docker啟動后,網(wǎng)絡(luò)情況
會產(chǎn)生一個名為docker0的虛擬網(wǎng)橋
默認(rèn)創(chuàng)建3大網(wǎng)絡(luò)模式
2.4.2 常用基本命令
All命令
[root@centos7 ~]# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
[root@centos7 ~]#
查看網(wǎng)絡(luò):docker network ls查看網(wǎng)絡(luò)源數(shù)據(jù):docker network inspect XXX網(wǎng)絡(luò)名字刪除網(wǎng)絡(luò):docker network rm XXX網(wǎng)絡(luò)名字案例
2.4.3 能干嘛
容器間的互聯(lián)和通信以及端口映射容器IP變動時候可以通過服務(wù)名直接網(wǎng)絡(luò)通信而不受到影響
2.4.4 網(wǎng)絡(luò)模式
1.容器實例內(nèi)默認(rèn)網(wǎng)絡(luò)IP生產(chǎn)規(guī)則
1)先啟動兩個ubuntu容器實例 2)docker inspect 容器ID or 容器名字 3)關(guān)閉u2實例,新建u3,查看ip變化 4)結(jié)論 docker容器內(nèi)部的ip是有可能會發(fā)生改變的
2. 網(wǎng)絡(luò)模式總體介紹
bridge模式:使用–network bridge指定,默認(rèn)使用docker0host模式:使用–network host指定none模式:使用–network none指定container模式:使用–network container:NAME或者容器ID指定
3. bridge模式
Docker 服務(wù)默認(rèn)會創(chuàng)建一個 docker0 網(wǎng)橋(其上有一個 docker0 內(nèi)部接口),該橋接網(wǎng)絡(luò)的名稱為docker0,它在內(nèi)核層連通了其他的物理或虛擬網(wǎng)卡,這就將所有容器和本地主機(jī)都放到同一個物理網(wǎng)絡(luò)。Docker 默認(rèn)指定了 docker0 接口 的 IP 地址和子網(wǎng)掩碼,讓主機(jī)和容器之間可以通過網(wǎng)橋相互通信。
查看 bridge 網(wǎng)絡(luò)的詳細(xì)信息,并通過 grep 獲取名稱項
docker network inspect bridge | grep name
ifconfig 1)案例說明
Docker使用Linux橋接,在宿主機(jī)虛擬一個Docker容器網(wǎng)橋(docker0),Docker啟動一個容器時會根據(jù)Docker網(wǎng)橋的網(wǎng)段分配給容器一個IP地址,稱為Container-IP,同時Docker網(wǎng)橋是每個容器的默認(rèn)網(wǎng)關(guān)。因為在同一宿主機(jī)內(nèi)的容器都接入同一個網(wǎng)橋,這樣容器之間就能夠通過容器的Container-IP直接通信。docker run 的時候,沒有指定network的話默認(rèn)使用的網(wǎng)橋模式就是bridge,使用的就是docker0。在宿主機(jī)ifconfig,就可以看到docker0和自己create的network(后面講)eth0,eth1,eth2……代表網(wǎng)卡一,網(wǎng)卡二,網(wǎng)卡三……,lo代表127.0.0.1,即localhost,inet addr用來表示網(wǎng)卡的IP地址網(wǎng)橋docker0創(chuàng)建一對對等虛擬設(shè)備接口一個叫veth,另一個叫eth0,成對匹配。
整個宿主機(jī)的網(wǎng)橋模式都是docker0,類似一個交換機(jī)有一堆接口,每個接口叫veth,在本地主機(jī)和容器內(nèi)分別創(chuàng)建一個虛擬接口,并讓他們彼此聯(lián)通(這樣一對接口叫veth pair);每個容器實例內(nèi)部也有一塊網(wǎng)卡,每個接口叫eth0;docker0上面的每個veth匹配某個容器實例內(nèi)部的eth0,兩兩配對,一一匹配。
通過上述,將宿主機(jī)上的所有容器都連接到這個內(nèi)部網(wǎng)絡(luò)上,兩個容器在同一個網(wǎng)絡(luò)下,會從這個網(wǎng)關(guān)下各自拿到分配的ip,此時兩個容器的網(wǎng)絡(luò)是互通的。 2)代碼
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
3)兩兩匹配驗證
4. host模式
直接使用宿主機(jī)的 IP 地址與外界進(jìn)行通信,不再需要額外進(jìn)行NAT 轉(zhuǎn)換。 1)案例說明 容器將不會獲得一個獨(dú)立的Network Namespace, 而是和宿主機(jī)共用一個Network Namespace。容器將不會虛擬出自己的網(wǎng)卡而是使用宿主機(jī)的IP和端口。 2)代碼
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
問題: docke啟動時總是遇見標(biāo)題中的警告 原因: docker啟動時指定–network=host或-net=host,如果還指定了-p映射端口,那這個時候就會有此警告, 并且通過-p設(shè)置的參數(shù)將不會起到任何作用,端口號會以主機(jī)端口號為主,重復(fù)時則遞增。 解決: 解決的辦法就是使用docker的其他網(wǎng)絡(luò)模式,例如–network=bridge,這樣就可以解決問題,或者直接無視。。。。O(∩_∩)O哈哈~ 正確: docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8 無之前的配對顯示了,看容器實例內(nèi)部
沒有設(shè)置-p的端口映射了,如何訪問啟動的tomcat83?? http://宿主機(jī)IP:8080/ 在CentOS里面用默認(rèn)的火狐瀏覽器訪問容器內(nèi)的tomcat83看到訪問成功,因為此時容器的IP借用主機(jī)的, 所以容器共享宿主機(jī)網(wǎng)絡(luò)IP,這樣的好處是外部主機(jī)與容器可以直接通信。
5. none
在none模式下,并不為Docker容器進(jìn)行任何網(wǎng)絡(luò)配置。 也就是說,這個Docker容器沒有網(wǎng)卡、IP、路由等信息,只有一個lo需要我們自己為Docker容器添加網(wǎng)卡、配置IP等。禁用網(wǎng)絡(luò)功能,只有l(wèi)o標(biāo)識(就是127.0.0.1表示本地回環(huán)) 1)案例 docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8 進(jìn)入容器內(nèi)部查看 在容器外部查看 docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
6. container模式
新建的容器和已經(jīng)存在的一個容器共享一個網(wǎng)絡(luò)ip配置而不是和宿主機(jī)共享。新創(chuàng)建的容器不會創(chuàng)建自己的網(wǎng)卡,配置自己的IP,而是和一個指定的容器共享IP、端口范圍等。同樣,兩個容器除了網(wǎng)絡(luò)方面,其他的如文件系統(tǒng)、進(jìn)程列表等還是隔離的。 案例
單圖標(biāo) CustomIcon-663735520; [“”, “4MyPJwAAAAAAAAAAAAAAAA==”]
docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8 docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8 運(yùn)行結(jié)果
相當(dāng)于tomcat86和tomcat85公用同一個ip同一個端口,導(dǎo)致端口沖突
本案例用tomcat演示不合適。。。演示坑。。。。。。o(╥﹏╥)o
換一個鏡像給大家演示, 案例2
單圖標(biāo) CustomIcon–1664269521; [“”, “L0PNnAAAAAAAAAAAAAAAAA==”]
Alpine操作系統(tǒng)是一個面向安全的輕型 Linux發(fā)行版
Alpine Linux 是一款獨(dú)立的、非商業(yè)的通用 Linux 發(fā)行版,專為追求安全性、簡單性和資源效率的用戶而設(shè)計。 可能很多人沒聽說過這個 Linux 發(fā)行版本,但是經(jīng)常用 Docker 的朋友可能都用過,因為他小,簡單,安全而著稱,所以作為基礎(chǔ)鏡像是非常好的一個選擇,可謂是麻雀雖小但五臟俱全,鏡像非常小巧,不到 6M的大小,所以特別適合容器打包。 docker run -it --name alpine1 alpine /bin/sh docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh 運(yùn)行結(jié)果,驗證共用搭橋 假如此時關(guān)閉alpine1,再看看alpine2 15: eth0@if16: 消失了。。。。。。關(guān)閉alpine1,再看看alpine2
2.4.5 自定義網(wǎng)絡(luò)
1. 沒有用自定義網(wǎng)絡(luò)之前
案例
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
上述成功啟動并用docker exec進(jìn)入各自容器實例內(nèi)部 問題 按照IP地址ping是OK的,按照服務(wù)名ping不通。 按照服務(wù)名ping不通
2. 使用自定義網(wǎng)絡(luò)之后
新建自定義網(wǎng)絡(luò) 新建容器加入上一步新建的自定義網(wǎng)絡(luò) docker run -d -p 8081:8080 --network zzyy_network --name tomcat81 billygoo/tomcat8-jdk8 docker run -d -p 8082:8080 --network zzyy_network --name tomcat82 billygoo/tomcat8-jdk8 互相ping測試 問題結(jié)論 自定義網(wǎng)絡(luò)本身就維護(hù)好了主機(jī)名和ip的對應(yīng)關(guān)系(ip和域名都能通)
2.4.6 Docker平臺架構(gòu)圖解
整體說明:從其架構(gòu)和運(yùn)行流程來看,Docker 是一個 C/S 模式的架構(gòu),后端是一個松耦合架構(gòu),眾多模塊各司其職。
Docker 運(yùn)行的基本流程為:
用戶是使用 Docker Client 與 Docker Daemon 建立通信,并發(fā)送請求給后者。Docker Daemon 作為 Docker 架構(gòu)中的主體部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的請求。Docker Engine 執(zhí)行 Docker 內(nèi)部的一系列工作,每一項工作都是以一個 Job 的形式的存在。Job 的運(yùn)行過程中,當(dāng)需要容器鏡像時,則從 Docker Registry 中下載鏡像,并通過鏡像管理驅(qū)動 Graph driver將下載鏡像以Graph的形式存儲。當(dāng)需要為 Docker 創(chuàng)建網(wǎng)絡(luò)環(huán)境時,通過網(wǎng)絡(luò)管理驅(qū)動 Network driver 創(chuàng)建并配置 Docker 容器網(wǎng)絡(luò)環(huán)境。當(dāng)需要限制 Docker 容器運(yùn)行資源或執(zhí)行用戶指令等操作時,則通過 Execdriver 來完成。Libcontainer是一項獨(dú)立的容器管理包,Network driver以及Exec driver都是通過Libcontainer來實現(xiàn)具體對容器進(jìn)行的操作。
整體架構(gòu)
2.5 Docker-compose容器編排
2.5.1 簡介
Docker-Compose是Docker官方的開源項目, 負(fù)責(zé)實現(xiàn)對Docker容器集群的快速編排。 Compose 是 Docker 公司推出的一個工具軟件,可以管理多個 Docker 容器組成一個應(yīng)用。你需要定義一個 YAML 格式的配置文件docker-compose.yml,寫好多個容器之間的調(diào)用關(guān)系。然后,只要一個命令,就能同時啟動/關(guān)閉這些容器
2.5.2 作用
docker建議我們每一個容器中只運(yùn)行一個服務(wù),因為docker容器本身占用資源極少,所以最好是將每個服務(wù)單獨(dú)的分割開來但是這樣我們又面臨了一個問題?
如果我需要同時部署好多個服務(wù),難道要每個服務(wù)單獨(dú)寫Dockerfile然后在構(gòu)建鏡像,構(gòu)建容器,這樣累都累死了,所以docker官方給我們提供了docker-compose多服務(wù)部署的工具
例如要實現(xiàn)一個Web微服務(wù)項目,除了Web服務(wù)容器本身,往往還需要再加上后端的數(shù)據(jù)庫mysql服務(wù)容器,redis服務(wù)器,注冊中心eureka,甚至還包括負(fù)載均衡容器等等。。。。。。
Compose允許用戶通過一個單獨(dú)的docker-compose.yml模板文件(YAML 格式)來定義一組相關(guān)聯(lián)的應(yīng)用容器為一個項目(project)。
可以很容易地用一個配置文件定義一個多容器的應(yīng)用,然后使用一條指令安裝這個應(yīng)用的所有依賴,完成構(gòu)建。Docker-Compose 解決了容器與容器之間如何管理編排的問題。
2.5.3 安裝
官網(wǎng):https://docs.docker.com/compose/compose-file/compose-file-v3/ 官網(wǎng)下載:https://docs.docker.com/compose/install/ 安裝步驟:
# 下載
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 修改目錄權(quán)限
chmod +x /usr/local/bin/docker-compose
# 檢查
docker-compose --version
卸載步驟:
rm /usr/local/bin/docker-compose
2.5.4 Compose核心概念
一文件:docker-compose.yml兩要素
服務(wù)(service):一個個應(yīng)用容器實例,比如訂單微服務(wù)、庫存微服務(wù)、mysql容器、nginx容器或者redis容器工程(project):由一組關(guān)聯(lián)的應(yīng)用容器組成的一個完整業(yè)務(wù)單元,在 docker-compose.yml 文件中定義。 Compose使用的三個步驟
編寫Dockerfile定義各個微服務(wù)應(yīng)用并構(gòu)建出對應(yīng)的鏡像文件使用 docker-compose.yml 定義一個完整業(yè)務(wù)單元,安排好整體應(yīng)用中的各個容器服務(wù)。最后,執(zhí)行docker-compose up命令 來啟動并運(yùn)行整個應(yīng)用程序,完成一鍵部署上線
2.5.4 Compose常用命令
docker-compose -h # 查看幫助 docker-compose up # 啟動所有docker-compose服務(wù) docker-compose up -d # 啟動所有docker-compose服務(wù)并后臺運(yùn)行 docker-compose down # 停止并刪除容器、網(wǎng)絡(luò)、卷、鏡像。 docker-compose exec yml里面的服務(wù)id # 進(jìn)入容器實例內(nèi)部 docker-compose exec docker-compose.yml文件中寫的服務(wù)id /bin/bash docker-compose ps # 展示當(dāng)前docker-compose編排過的運(yùn)行的所有容器 docker-compose top # 展示當(dāng)前docker-compose編排過的容器進(jìn)程
docker-compose logs yml里面的服務(wù)id # 查看容器輸出日志 docker-compose config # 檢查配置 docker-compose config -q # 檢查配置,有問題才有輸出 docker-compose restart # 重啟服務(wù) docker-compose start # 啟動服務(wù) docker-compose stop # 停止服務(wù)
2.5.5 Compose編排微服務(wù)
1.SQL建表建庫
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '用戶名',
`password` varchar(50) NOT NULL DEFAULT '' COMMENT '密碼',
`sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性別 0=女 1=男 ',
`deleted` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '刪除標(biāo)志,默認(rèn)0不刪除,1刪除',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用戶表'
2. 改POM
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3. 寫YML
server.port=6001
# ========================alibaba.druid相關(guān)配置=====================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.111.169:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.druid.test-while-idle=false
# ========================redis相關(guān)配置=====================
spring.redis.database=0
spring.redis.host=192.168.111.169
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相關(guān)配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.atguigu.docker.entities
# ========================swagger=====================
spring.swagger2.enabled=true
4. 主啟動
package com.atguigu.docker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.atguigu.docker.mapper")
//import tk.mybatis.spring.annotation.MapperScan;
public class DockerBootApplication {
public static void main(String[] args) {
SpringApplication.run(DockerBootApplication.class, args);
}
}
5.配置
config配置類
package com.atguigu.docker.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
/**
* @auther zzyy
* @create 2021-10-27 17:19
*/
@Configuration
@Slf4jpublic
class RedisConfig {
/**
* @param lettuceConnectionFactory
* @return
*
* redis序列化的工具配置類,下面這個請一定開啟配置
* 127.0.0.1:6379> keys *
* 1) "ord:102" 序列化過
* 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,沒有序列化過
*/
@Bean
public RedisTemplate
RedisTemplate
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
//設(shè)置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//設(shè)置value的序列化方式j(luò)son
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
package com.atguigu.docker.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @auther zzyy
* @create 2021-05-01 16:18
*/@Configuration
@EnableSwagger2public
class SwaggerConfig {
@Value("${spring.swagger2.enabled}")
private Boolean enabled;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(enabled)
.select()
.apis(RequestHandlerSelectors.basePackage("com.atguigu.docker")) //你自己的package
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("尚硅谷Java大廠技術(shù)"+"\t"+new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
.description("docker-compose")
.version("1.0")
.termsOfServiceUrl("https://www.atguigu.com/")
.build();
}
}
6.業(yè)務(wù)類
package com.atguigu.docker.entities;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(generator = "JDBC")
private Integer id;
/**
* 用戶名
*/
private String username;
/**
* 密碼
*/
private String password;
/**
* 性別 0=女 1=男
*/
private Byte sex;
/**
* 刪除標(biāo)志,默認(rèn)0不刪除,1刪除
*/
private Byte deleted;
/**
* 更新時間
*/
@Column(name = "update_time")
private Date updateTime;
/**
* 創(chuàng)建時間
*/
@Column(name = "create_time")
private Date createTime;
/**
* @return id
*/
public Integer getId() {
return id;
}
/**
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 獲取用戶名
*
* @return username - 用戶名
*/
public String getUsername() {
return username;
}
/**
* 設(shè)置用戶名
*
* @param username 用戶名
*/
public void setUsername(String username) {
this.username = username;
}
/**
* 獲取密碼
*
* @return password - 密碼
*/
public String getPassword() {
return password;
}
/**
* 設(shè)置密碼
*
* @param password 密碼
*/
public void setPassword(String password) {
this.password = password;
}
/**
* 獲取性別 0=女 1=男
*
* @return sex - 性別 0=女 1=男
*/
public Byte getSex() {
return sex;
}
/**
* 設(shè)置性別 0=女 1=男
*
* @param sex 性別 0=女 1=男
*/
public void setSex(Byte sex) {
this.sex = sex;
}
/**
* 獲取刪除標(biāo)志,默認(rèn)0不刪除,1刪除
*
* @return deleted - 刪除標(biāo)志,默認(rèn)0不刪除,1刪除
*/
public Byte getDeleted() {
return deleted;
}
/**
* 設(shè)置刪除標(biāo)志,默認(rèn)0不刪除,1刪除
*
* @param deleted 刪除標(biāo)志,默認(rèn)0不刪除,1刪除
*/
public void setDeleted(Byte deleted) {
this.deleted = deleted;
}
/**
* 獲取更新時間
*
* @return update_time - 更新時間
*/
public Date getUpdateTime() {
return updateTime;
}
/**
* 設(shè)置更新時間
*
* @param updateTime 更新時間
*/
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
/**
* 獲取創(chuàng)建時間
*
* @return create_time - 創(chuàng)建時間
*/
public Date getCreateTime() {
return createTime;
}
/**
* 設(shè)置創(chuàng)建時間
*
* @param createTime 創(chuàng)建時間
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
package com.atguigu.docker.entities;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Data
@ApiModel(value = "用戶信息")
public class UserDTO implements Serializable{
@ApiModelProperty(value = "用戶ID")
private Integer id;
@ApiModelProperty(value = "用戶名")
private String username;
@ApiModelProperty(value = "密碼")
private String password;
@ApiModelProperty(value = "性別 0=女 1=男 ")
private Byte sex;
@ApiModelProperty(value = "刪除標(biāo)志,默認(rèn)0不刪除,1刪除")
private Byte deleted;
@ApiModelProperty(value = "更新時間")
private Date updateTime;
@ApiModelProperty(value = "創(chuàng)建時間")
private Date createTime;
}
package com.atguigu.docker.mapper;
import com.atguigu.docker.entities.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper
}
package com.atguigu.docker.service;
import com.atguigu.docker.entities.User;
import com.atguigu.docker.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @auther zzyy
* @create 2021-05-01 14:58
*/@Service
@Slf4j
public class UserService {
public static final String CACHE_KEY_USER = "user:";
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate redisTemplate;
/**
* addUser
* @param user
*/
public void addUser(User user)
{
//1 先插入mysql并成功
int i = userMapper.insertSelective(user);
if(i > 0)
{
//2 需要再次查詢一下mysql將數(shù)據(jù)撈回來并ok
user = userMapper.selectByPrimaryKey(user.getId());
//3 將撈出來的user存進(jìn)redis,完成新增功能的數(shù)據(jù)一致性。
String key = CACHE_KEY_USER+user.getId();
redisTemplate.opsForValue().set(key,user);
}
}
/**
* findUserById
* @param id
* @return
*/
public User findUserById(Integer id)
{
User user = null;
String key = CACHE_KEY_USER+id;
//1 先從redis里面查詢,如果有直接返回結(jié)果,如果沒有再去查詢mysql
user = (User) redisTemplate.opsForValue().get(key);
if(user == null)
{
//2 redis里面無,繼續(xù)查詢mysql
user = userMapper.selectByPrimaryKey(id);
if(user == null)
{
//3.1 redis+mysql 都無數(shù)據(jù)
//你具體細(xì)化,防止多次穿透,我們規(guī)定,記錄下導(dǎo)致穿透的這個key回寫redis
return user;
}else{
//3.2 mysql有,需要將數(shù)據(jù)寫回redis,保證下一次的緩存命中率
redisTemplate.opsForValue().set(key,user);
}
}
return user;
}
}
package com.atguigu.docker.controller;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReferenceUtil;
import com.atguigu.docker.entities.User;
import com.atguigu.docker.entities.UserDTO;
import com.atguigu.docker.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Random;
/**
* @auther zzyy
* @create 2021-05-01 15:02
*/@Api(description = "用戶User接口")@RestController
@Slf4j
public class UserController {
@Resource
private UserService userService;
@ApiOperation("數(shù)據(jù)庫新增3條記錄")
@RequestMapping(value = "/user/add",method = RequestMethod.POST)
public void addUser()
{
for (int i = 1; i <=3; i++) {
User user = new User();
user.setUsername("zzyy"+i);
user.setPassword(IdUtil.simpleUUID().substring(0,6));
user.setSex((byte) new Random().nextInt(2));
userService.addUser(user);
}
}
@ApiOperation("刪除1條記錄")
@RequestMapping(value = "/user/delete/{id}",method = RequestMethod.POST)
public void deleteUser(@PathVariable Integer id)
{
userService.deleteUser(id);
}
@ApiOperation("修改1條記錄")
@RequestMapping(value = "/user/update",method = RequestMethod.POST)
public void updateUser(@RequestBody UserDTO userDTO)
{
User user = new User();
BeanUtils.copyProperties(userDTO,user);
userService.updateUser(user);
}
@ApiOperation("查詢1條記錄")
@RequestMapping(value = "/user/find/{id}",method = RequestMethod.GET)
public User findUserById(@PathVariable Integer id)
{
return userService.findUserById2(id);
}
}
7.打包
mvn package命令將微服務(wù)形成新的jar包 并上傳到Linux服務(wù)器/mydocker目錄下
8. 編寫Dockerfile
# 基礎(chǔ)鏡像使用java
FROM java:8
# 作者
MAINTAINER zzyy
# VOLUME 指定臨時文件目錄為/tmp,在主機(jī)/var/lib/docker目錄下創(chuàng)建了一個臨時文件并鏈接到容器的/tmp
VOLUME /tmp
# 將jar包添加到容器中并更名為zzyy_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar zzyy_docker.jar
# 運(yùn)行jar包
RUN bash -c 'touch /zzyy_docker.jar'
ENTRYPOINT ["java","-jar","/zzyy_docker.jar"]
#暴露6001端口作為微服務(wù)
EXPOSE 6001
9. 構(gòu)建鏡像
docker build -t zzyy_docker:1.6 .
10. compose服務(wù)編排
version: "3"
services:
microService:
image: zzyy_docker:1.6
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- atguigu_net
depends_on:
- redis
- mysql
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- atguigu_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2021'
MYSQL_USER: 'zzyy'
MYSQL_PASSWORD: 'zzyy123'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- atguigu_net
command: --default-authentication-plugin=mysql_native_password #解決外部無法訪問
networks:
atguigu_net:
11. 第二次修改微服務(wù)工程docker_boot
修改yml文件,修改內(nèi)容為:將寫死的mysql和redis連接地址IP改為服務(wù)名
server.port=6001
# ========================alibaba.druid相關(guān)配置=====================
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#spring.datasource.url=jdbc:mysql://192.168.111.169:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.url=jdbc:mysql://mysql:3306/db2021?useUnicode=true&characterEncoding=utf-8&useSSL=falsespring.datasource.username=rootspring.datasource.password=123456spring.datasource.druid.test-while-idle=false
# ========================redis相關(guān)配置=====================
spring.redis.database=0
#spring.redis.host=192.168.111.169
spring.redis.host=redis
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
# ========================mybatis相關(guān)配置===================
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.atguigu.docker.entities
# ========================swagger=====================
spring.swagger2.enabled=true
12. 重新打包,上傳服務(wù)器制作鏡像
同7,8,9步
13. 執(zhí)行 docker-compose up
或者執(zhí)行 docker-compose up -d
14. 進(jìn)入mysql容器實例并新建庫db2021+新建表t_user
docker exec -it 容器實例id /bin/bash
mysql -uroot -p
create database db2021;
use db2021;
CREATE TABLE `t_user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用戶名',
`password` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '密碼',
`sex` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '性別 0=女 1=男 ',
`deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '刪除標(biāo)志,默認(rèn)0不刪除,1刪除',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';
15. 測試
http://localhost:你的微服務(wù)端口/swagger-ui.html#/
16. 關(guān)停
2.6 Docker輕量級可視化工具Portainer
2.6.1 簡介
Portainer 是一款輕量級的應(yīng)用,它提供了圖形化界面,用于方便地管理Docker環(huán)境,包括單機(jī)環(huán)境和集群環(huán)境。
官網(wǎng)
https://www.portainer.io/https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux
2.6.2 docker命令安裝
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
2.6.3 登錄
第一次登錄需創(chuàng)建admin,訪問地址:xxx.xxx.xxx.xxx:9000
用戶名,直接用默認(rèn)admin密碼記得8位,隨便你寫
設(shè)置admin用戶和密碼后首次登陸 選擇local選項卡后本地docker詳細(xì)信息展示 上一步的圖形展示,能想得起對應(yīng)命令嗎? 登陸并演示介紹常用操作case
2.7 Docker容器監(jiān)控之 CAdvisor+InfluxDB+Granfana
docker自帶監(jiān)控命令:docker stats docker stats命令的結(jié)果
問題:
通過docker stats命令可以很方便的看到當(dāng)前宿主機(jī)上所有容器的CPU,內(nèi)存以及網(wǎng)絡(luò)流量等數(shù)據(jù),一般小公司夠用了。。。。docker stats統(tǒng)計結(jié)果只能是當(dāng)前宿主機(jī)的全部容器,數(shù)據(jù)資料是實時的,沒有地方存儲、沒有健康指標(biāo)過線預(yù)警等功能
2.7.1 容器監(jiān)控簡介
CAdvisor監(jiān)控收集+InfluxDB存儲數(shù)據(jù)+Granfana展示圖表
1. CAdvisor
2. InfluxDB
3. Granfana
5. 總結(jié)
2.7.2 容器編排方式啟動
1. 新建目錄
2. 新建3件套組合的 docker-compose.yml
version: '3.1'
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor
ports:
- "8083:8083"
- "8086:8086"
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
restart: always
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
grafana:
user: "104"
image: grafana/grafana
user: "104"
restart: always
links:
- influxdb:influxsrv
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root
3. 啟動docker-compose文件
docker-compose up
4. 查看三個服務(wù)容器是否啟動
5. 測試
瀏覽cAdvisor收集服務(wù),http://ip:8080/ 第一次訪問慢,請稍等 cadvisor也有基礎(chǔ)的圖形展現(xiàn)功能,這里主要用它來作數(shù)據(jù)采集
瀏覽influxdb存儲服務(wù),http://ip:8083/瀏覽grafana展現(xiàn)服務(wù),http://ip:3000
ip+3000端口的方式訪問,默認(rèn)帳戶密碼(admin/admin) 配置步驟 配置數(shù)據(jù)源 選擇influxdb數(shù)據(jù)源
配置細(xì)節(jié) 1 2
配置面板panel 1 2 3 4 5 6
到這里cAdvisor+InfluxDB+Grafana容器監(jiān)控系統(tǒng)就部署完成了
2.8 終章の總結(jié)
2.8.1 知識回顧簡單串講和總結(jié)
2.8.2 進(jìn)階篇:雷豐陽老師的K8S
柚子快報激活碼778899分享:Docker與微服務(wù)實戰(zhàn)
相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。