最强微服务部署工具——Docker内容详解

在前面的内容中我们已经学习了SpringCloud的微服务搭建

但是微服务由于被分成多个部分,所以部署起来需要不同的条件环境甚至不同的操作系统从而十分繁琐

下面我们来介绍Docker部署工具,Docker可以帮助我们快速便捷得部署常用微服务以及重复安全部署我们自己的Java项目

我们将从下面角度介绍Docker:

  • Docker概述
  • Docker基本操作
  • Docker自定义镜像
  • Docker-Compose
  • Docker镜像仓库

Docker概述

首先我们先来简单介绍一下Docker

Docker问题概述

我们分别从几个角度来讲述Docker

正常项目部署问题

那么既然说Docker可以快速部署,那么我们平时的部署都会存在什么问题:

  • 首先我们一个项目需要部署的组件是非常多的,常见的包括有Java项目本身,MySQL,Nignx,GateWay,Nacos等
  • 不同的组件之间可能会存在冲突问题,比如Nacos和Eureka两个注册中心存在冲突问题
  • 此外在数百上千台服务中重复部署,环境不一定一致,会遇到各种问题

此外我们还需要注意环境问题:

  • 我们不同版本的组件都需要对应不同版本的其他兼容性组件,如果版本不同可能导致无法兼容
  • 我们在进行开发,测试,生产时需要采用不同的环境,每种环境的配置都需要一一配置

Docker处理兼容问题

因而我们的Docker就采用一种巧妙的方式去解决这个问题:

  • Docker直接将组件所需要的所有内容,包括Deps(依赖)、配置与应用一起打包
  • 并且将其封装起来,单独放在一个容器中,各个组件之间不会相互干扰

由此我们可以解决其兼容性问题

Docker处理操作系统问题

但是我们需要注意到不同的组件所需要的底层操作系统环境可能不同,我们该如何去解决

首先我们需要了解操作系统的原理:

  • 我们目前项目部署都是基于Linux系统,但Linux又细分为CentOS、Ubuntu、Fedora等众多版本
  • 操作系统整体分为计算机硬件(CPU、内存),系统内核(Linux系统,负责与底层交互),系统应用(应用、函数库)
  • 组件主要使用系统应用的函数库,利用函数库调用系统内核对计算机硬件进行操作,也就是说根本区别只有不同环境的函数库不同
  • 如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或者不匹配,就会报错

所以解决方法其实很简单:

  • Docker将用户程序与所需要调用的系统函数库一起打包
  • Docker运行到不同操作系统时,直接基于打包的函数库,借助于操作系统的Linux内核来运行

Docker简述

首先我们先给出Docker的简单定义:

  • Docker将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包并将每个应用放到一个隔离容器去运行,避免互相干扰
  • Docker是一个市面上常用的Linux系统的项目部署工具,可以采用网络拉取或者自己搭建来快速部署项目

Docker是一个快速交付应用、运行应用的技术,具备下列优势:

  • 可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
  • 运行时利用沙箱机制形成隔离容器,各个应用互不干扰
  • 启动、移除都可以通过一行命令完成,方便快捷

Docker重要概念

我们需要介绍到Docker的两个重要概念:

  • 镜像:Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像
  • 容器:镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。

我们可以把镜像简单理解为类,把容器理解为对象:

  • 我们需要注意镜像一旦形成是无法修改的
  • 我们可以基于镜像生成多个容器,且容器内部可以修改

DockerHub

DockerHub本质上是一个Docker镜像的线上网站:

  • DockerHub:官方的Docker镜像的托管平台,这样的平台称为Docker Registry
  • DockerHub同GitHub一样用于存储程序员间开源的优质Dokcer镜像,其中大多是官方提供的镜像
  • 为了避免这些重复劳动,人们就会将自己打包的应用镜像,例如Redis、MySQL镜像放到网络上,共享使用

除了国外的DockerHub外,国内也有很多优秀的Docker镜像存储网站:

我们可以在Docker Registry网站上进行镜像的拉取和上传:

Docker架构

Docker是一个Linux工具,需要进行安装后使用,这里就不写Docker的安装步骤了~

Docker是一个CS架构的程序,由两部分组成:

  • 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等

  • 客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。

Docker基本操作

下面我们来介绍Docker的基本操作

Docker镜像操作

我们首先需要了解镜像的组成元素:

  • 构造:[repository]:[tag]

  • 案例:mysql:5.7

  • 说明:其中repository是指镜像名称,tag是指版本号

我们首先给出一张docker镜像整体操作图:

然后我们给出Docker的相关操作:

# Docker查看目前存在镜像
docker images

# Docker存在两种获取方式(pull云端获取,build构建我们后续详细讲述)

## Docker的pull拉取镜像,直接在云服务器上拉取(一般可以在云服务器上搜索对应的组件,然后获得其对应版本号或对应拉取代码)
## 若标记版本号则为对应版本号,若未标记则为最新版本lastest
docker pull [repository]
docker pull [repository]:[tag]

# Docker的删除rmi(remove images)
docker rmi [repository]:[tag]

# Docker也可以通过push方法推送到服务器上
docker push [repository]:[tag]

# Docker可以采用save将其变换为jar包
docker save -o [保存的目标文件名称] [镜像名称]

# Docker可以采用load将jar包转换回镜像
docker load -i [目标文件名称]

# 最后我们给出一个help方法可以用于查看所有的Docker参数设置
docker 操作名称 --help

Docker容器操作

我们首先给出一张dokcer容器操作的整体流程图:

首先我们需要知道容器的三个状态:

  • 运行:进程正常运行
  • 暂停:进程暂停,CPU不再运行,并不释放内存
  • 停止:进程终止,回收进程占用的内存、CPU等资源

然后我们来简单介绍一下上述操作:

# ------------------------------------------

# 创建并运行一个容器,处于运行状态
docker run 

# 容器暂停
docker pause

# 容器运行
docker unpause

# 容器停止
docker stop

# 容器运行
docker start

# 删除容器
docker rm

# 进入容器执行命令
docker exec

# 查看容器运行日志
docker logs

# 查看所有运行中的容器
docker ps

# ------------------------------------------

下面我们挑几个常用操作去详细介绍:

# run 执行方法
docker run --name containerName -p 80:80 -d nginx

docker run:		语法
--name:		后面跟容器名称
--p:		后面跟对应端口号,第一个端口号是宿主机端口,第二个是容器端口
			(第一个是虚拟机端口号,你需要从这个虚拟机端口号进入,然后进入到对应容器端口号中去调用该组件)
			 默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx。
			现在,将容器的80与宿主机的80关联起来,当我们访问宿主机的80端口时,就会被映射到容器的80,这样就能访问到nginx了:
-d:			后台执行
nginx:		镜像名称

# ps 执行方法
docker ps # 查看运行中容器
docker -a ps # 查看所有容器

# logs 执行方法
docker logs # 获取当前日志
docker -f logs # 实时查看日志

# exec 执行方法
docker exec -it containerName bash

docker exec :	进入容器内部,执行一个命令
-it : 			 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互(可以看成默认格式)
containerName :	要进入的容器的名称
bash:			进入容器后执行的命令,bash是一个linux终端交互命令

注意:在进入容器之后,容器内部实际上是一个小的虚拟机环境,但很多高端命令都无法使用,例如vim等内容书写命令无法使用
注意:我们在进入容器修改信息,例如修改Nginx的html文件等,我们可以在Docker线上网站找到对应的位置直接进入书写(不推荐)

Docker数据卷操作

我们首先来介绍一下数据卷:

  • 数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。
  • 我们可以采用数据卷挂卷,通过对宿主机的某个文件进行修改从而修改容器中的数据或者保存容器的数据

下面我们来介绍数据卷的具体操作:

# 数据卷基本格式
docker volume [command]

docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:

- create 	创建一个volume
- inspect 	显示一个或多个volume的信息(显示数据卷存在位置)
- ls 		列出所有的volume
- prune 	删除未使用的volume
- rm 		删除一个或多个指定的volume

# 创建数据卷
docker volume create [name]

docker volume create html

# 查看当前数据卷
docker volume ls

# 查看某个数据卷的详细信息(具体包括:名字,地址等)
docker volume inspect [name]

docker volume inspect html

# 删除指定数据卷
docker rm [name]

# 删除所有未使用的数据卷
docker prune

我们在了解数据卷操作后还需要了解如何挂卷:

# 挂卷一般在创建docker容器时(下面的"\"是换行符)
docker run \
  --name mn \
  -v html:/root/html \ # -v就是挂卷,:前是数据卷名称,:后是具体的容器文件位置
  -p 8080:80
  nginx \
  
# 挂卷后我们就可以对其进行修改

# 查看html数据卷的位置
docker volume inspect html
# 进入该目录
cd /var/lib/docker/volumes/html/_data
# 修改文件
vi index.html

除了直接挂卷数据卷外我们也可以直接挂卷文件位置:

# 我们可以直接指定文件位置从而减少一次数据卷创建过程同时直接指定创建位置便于管理
docker run \
  --name mysql \
  -v /var/lib/docker/volumes/html:/root/html \ # -v就是挂卷,:前是数据卷名称,:后是具体的容器文件位置
  -p 8080:80
  mysql \
  
# - -v [宿主机目录]:[容器内目录]
# - -v [宿主机文件]:[容器内文件]

我们这里给出两种挂卷方式的优劣点:

  • 数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
  • 目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看

Dockerfile自定义镜像

这小节我们来介绍Dokcerfile,也就是docker的Build创建方法

镜像结构介绍

首先我们需要了解镜像:

  • 镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成
  • 镜像是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,编写好启动脚本打包在一起形成的文件

我们这里给出MySQL的镜像图示:

Dockerfile介绍

我们首先来介绍一下dockerfile:

  • dockerfile实际上是一个shell脚本,里面书写了镜像构造的具体条件
  • 镜像构造的基本条件就是基于镜像的结构一层层书写得出的,也就是说dockerfile实际上就是搭配各个环境创建镜像
  • 我们需要告诉Docker,我们的镜像的组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么
  • Dockerfile就是一个文本文件,其中包含一个个的指令,用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。

我们给出dockerfile的基本语法:

指令 说明 示例
FROM 指定基础镜像 FROM centos:6
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./mysql-5.7.rpm /tmp
RUN 执行Linux的shell命令,一般是安装过程的命令 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的 EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar

我们这里给出Dockerfile的官方传送门:

Dockerfile具体使用

我们给出一个Dockerfile案例来详细解释Dockerfile语法:

# 注意:我们首先需要一个dockerfile文件夹,里面装有/jdk8.tar.gz以及/docker-demo.jar,当然还有下述的dockerfile

# --------------下述为dockerfile文件--------------

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local

# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar

# 安装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 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar

# 最后我们需要build该文件
docker build -t javaweb:1.0 .

-t:		给镜像加一个Tag
javaWeb:镜像名
1.0:	镜像版本
.:		Dockerfile所在目录(.就是指当前目录)

我们可以注意到上述dockerfile中配置了jdk8环境和java项目,而我们可以通过更换基础镜像省略jdk8的配置:

# 我们直接基于java:8-alpine环境(该环境下已配置jdk8),我们只需要将java项目拷贝拷贝并启动
FROM java:8-alpine
COPY ./app.jar /tmp/app.jar
EXPOSE 8090
ENTRYPOINT java -jar /tmp/app.jar

Docker-Compose

下面我们来详细介绍一下Docker-Compose

Docker-Compose简述

首先我们先来简单介绍一下Docker-Compose:

  • Docker Compose可以基于Compose文件帮我们快速的部署分布式应用
  • Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行
  • DockerCompose文件可以看做是将多个docker run命令写到一个文件,语法格式类似于yml格式

我们给出一个简单的Docker-Compose文件:

# -----------------文件概述-----------------
描述一个项目,其中包含两个容器:

- mysql:一个基于`mysql:5.7.25`镜像构建的容器,并且挂载了两个目录
- web:一个基于`docker build`临时构建的镜像容器,映射端口时8090

# -----------------文件内容-----------------

version: "3.8" 	# 版本号
 services:		# 服务标识(下述均为服务名称)
  mysql:		# MySQL服务(不需要映射端口,因为只在内部使用)
    image: mysql:5.7.25	# 依赖镜像
    environment:		# 环境变量,这里直接书写了MySQL密码
     MYSQL_ROOT_PASSWORD: 123 
    volumes:			# 挂卷
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
  web:			# Web服务
    build: .	# 依赖当前文件夹下的dockerfile文件
    ports:		# 映射端口
     - "8090:8090"
    

DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/

温馨提醒:Docker-Compose也需要下载使用,这里不做赘述

Docker-Compose部署微服务集群

下面我们详细介绍一下微服务集群的部署:

  1. 创建spring-cloud的文件夹,包含我们需要部署的组件文件夹和Docker-Compose文件,每个文件夹包含本身的Dockerfile文件

  1. 展示docker-compose和dockerfile文件
# ------------------docker-compose文件------------------

version: "3.2"	# 版本

services:
  nacos:	# nacos注册中心
    image: nacos/nacos-server	# 镜像
    environment:
      MODE: standalone	# 环境:单点模式启动
    ports:
      - "8848:8848"	# 映射端口
  mysql:	# MySQL数据库
    image: mysql:5.7.25		# 镜像
    environment:
      MYSQL_ROOT_PASSWORD: 123	# 环境:设置密码
    volumes:		# 挂卷,方便修改配置
      - "$PWD/mysql/data:/var/lib/mysql"
      - "$PWD/mysql/conf:/etc/mysql/conf.d/"
  userservice:		# 基于dockerfile创建临时项目
    build: ./user-service
  orderservice:		# 基于dockerfile创建临时项目
    build: ./order-service
  gateway:			# 基于dockerfile创建临时项目
    build: ./gateway
    ports:
      - "10010:10010"
      
# ------------------dockerfile文件------------------

FROM java:8-alpine	# 基于java:8-alpine环境
COPY ./app.jar /tmp/app.jar	# 拷贝当前文件夹中的app.jar至虚拟机中的/tmp文件夹下命名为app.jar
ENTRYPOINT java -jar /tmp/app.jar # 启动该镜像时以java -jar启动/tmp/app.jar
  1. 修改微服务配置
# 因为微服务将来要部署为docker容器,而容器之间互联不是通过IP地址,而是通过容器名。
# 这里我们将order-service、user-service、gateway服务的mysql、nacos地址都修改为基于容器名的访问。

spring:
  datasource:
    url: jdbc:mysql://mysql:3306/cloud_order?useSSL=false
    username: root
    password: 123
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: orderservice
  cloud:
    nacos:
      server-addr: nacos:8848 # nacos服务地址
  1. 修改打包名称
<!--在pom.xml中修改,注意:需要部署的项目都需要修改,因为我们的docker-compose中将app.jar复制并创建容器时启动-->

<build>
  <!-- 服务打包的最终名称 -->
  <finalName>app</finalName>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>
  1. 将jar包复制到对应文件夹下,并将spring-cloud文件夹复制到虚拟机中启动即可
# 启动docker-compose
docker-compose up -d

Docker镜像仓库

最后我们介绍一下Docker的私服镜像搭建

Docker搭建私有镜像仓库(简单版)

不管如何搭建镜像仓库,我们都是基于Docker官方提供的DockerRegistry来实现的:

搭建简单版的私有镜像仓库很简单,我们只需要输入下述docker run代码即可:

docker run -d \
    --restart=always \
    --name registry	\
    -p 5000:5000 \
    -v registry-data:/var/lib/registry \ # 这是私有镜像库存放数据的目录
    registry

Docker搭建私有镜像仓库(图形化)

图形化私有镜像的搭建不是官方所提供的,而是基于一个组件,我们需要使用到Docker-Compose:

version: '3.0'
services:
  registry:	# 服务名
    image: registry	# 镜像名
    volumes:	# 挂卷,存放数据的位置
      - ./registry-data:/var/lib/registry
  ui:	# ui就是图形化界面组件
    image: joxit/docker-registry-ui:static	# 镜像名
    ports:
      - 8080:80	# 开放端口,系统通过8080访问到ui的80端口,然后进入到registry的5000端口
    environment:
      - REGISTRY_TITLE=私有仓库	# 仓库名称
      - REGISTRY_URL=http://registry:5000	# registry是服务名称,5000是registry的服务端口号
    depends_on:
      - registry

Docker信任地址配置

我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:

# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://192.168.150.101:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker

Docker推送、拉取镜像

私服的拉取需要我们提前设置tag才能够进行推送:

# 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.150.101:8080/
docker tag nginx:latest 192.168.150.101:8080/nginx:1.0 

# 推送镜像
docker push 192.168.150.101:8080/nginx:1.0 

# 拉取镜像
docker pull 192.168.150.101:8080/nginx:1.0 

结束语

这篇文章中介绍了Docker的内容及其附属产品Dockerfile和Docker-Compose,希望能为你带来帮助

附录

该文章属于学习内容,具体参考B站黑马程序员的微服务课程

这里附上视频链接:01-今日课程介绍3_哔哩哔哩_bilibili