Docker

相关资料可以移步至SpringCloud中的Docker查找,教程主要来源于黑马SpringCloud教程,后面看的GeekHour有部分截图在这里

简介与作用

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 LinuxWindows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口

大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:

  • 依赖关系复杂,容易出现兼容性问题
  • 开发、测试、生产环境有差异

image-20220922204851957

Docker为了解决依赖的兼容问题的,采用了两个手段:

  • 将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包

  • 将每个应用放到一个隔离容器去运行,避免互相干扰

image-20220922204914118

Docker如何解决不同系统环境的问题?

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

image-20220922205211881

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

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

Docker和虚拟机的区别

虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。

Docker仅仅是封装函数库,并没有模拟完整的操作系统

image-20220922205358267

Docker和虚拟机的差异:

  • docker是一个系统进程;虚拟机是在操作系统中的操作系统
  • docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般

Docker架构

镜像与容器

  • 镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。

  • 容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。

image-20210731153059464

DockerHub

开源应用程序非常多,打包这些应用往往是重复的劳动。为了避免这些重复劳动,人们就会将自己打包的应用镜像,例如Redis、MySQL镜像放到网络上,共享使用,就像GitHub的代码共享一样。

image-20210731153743354

Docker架构

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

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

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

image-20220922210001332

Docker在centos的安装

移步安装教程->Centos7安装Docker.md

查看docker运行状态

1
docker version

Docker的基本操作

镜像

镜像名称一般分两部分组成:[repository]:[tag]

在没有指定tag时,默认是latest,代表最新版本的镜像

image-20220923160342406

Docker操作镜像结构图

image-20220923160652105

大致结构图如下

image-20230828161112284

拉取镜像

docker hub查找对应镜像拉取指令

image-20220923161001250

xshell执行该命令

1
docker pull nginx

查看现有镜像

1
docker images

导出与加载本地镜像

  1. 利用docker xx –help命令查看docker save和docker load的语法
  2. 使用docker save导出镜像到磁盘
  3. 使用docker load加载镜像
1
docker save -o nginx.tar
1
docker load -i nginx.tar

镜像操作指令

  • docker images

    查看镜像

  • docker rmi

    移除镜像

  • docker pull

    拉取镜像

  • docker build -t 镜像名:版本 . (.为当前目录)

    构建镜像

  • docker push

    推送镜像

  • docker save

    打包镜像

  • docker load

    加载镜像压缩包

  • docker rmi –force [-f] $(docker images | grep 共有关键字 | awk ‘{print $3}’)

    批量删除含共有关键字的镜像

容器操作

容器操作结构图

image-20220923162139234

以运行一个nginx为例,可以通过dockerHub查找相关容器运行指令

image-20220923162513143

1
docker run --name containerName -p 80:80 -d nginx

命令解读:

  • docker run :创建并运行一个容器

  • –name : 给容器起一个名字,比如叫做mn

  • -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口

    image-20220923162645751

  • -d:后台运行容器

  • nginx:镜像名称,例如nginx

进入容器并修改容器内容(以修改nginx的html为例)

  1. 进入容器

    1
    docker exec -it mn bash

    命令解读:

    1. docker exec :进入容器内部,执行一个命令
    2. -it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
    3. mn :要进入的容器的名称
    4. bash:进入容器后执行的命令,bash是一个linux终端交互命令
  2. 进入html目录

    1
    cd /usr/share/nginx/html
  3. 修改html文件内容

    1
    2
    sed -i 's#Welcome to nginx#你是个什么东西😒#g' index.html
    sed -i 's#<head>#<head><meta charset="utf-8">#g' index.html

注意:xec命令可以进入容器修改文件,但是在容器内修改文件是不推荐的

操作redis容器

运行redis

1
docker run --name rd -p 6379:6379 -d redis redis-server --save 60 1 --loglevel warning

进入redis 并运行redis-cli

1
docker exec -it rd redis-cli

执行redis操作

1
set num 666

退出

1
exit

容器操作指令

  • docker logs

    查看容器日志

    添加 -f 参数可以持续查看日志

  • docker ps

    查看容器状态码

  • docker exec -it [容器名] [要执行的命令]

    进入容器

  • docker rm

    删除容器

    不能删除运行中的容器,除非添加 -f 参数

  • docker rm $(docker ps -aq)

    删除所有未使用的容器

数据卷(容器数据管理)

容器与数据耦合的问题

image-20220923164129251

数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录

我们就可以直接操作宿主机文件系统下的文件,从而使得容器与数据分离,解耦合,方便操作容器内数据,保证数据安全

image-20220923164153594

数据卷操作

数据卷操作的基本语法:docker volume [COMMAND]

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

  • create 创建一个volume

  • inspect 显示一个或多个volume的信息

  • ls 列出所有的volume

  • prune 删除未使用的volume

  • rm 删除一个或多个指定的volume

image-20220923164718854

挂载数据卷

在创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器内目录,没有该数据卷时docker会帮我们创建一个

1
docker run --name mn -v html:/usr/share/nginx/html -p80:80 -d nginx

上述内容是将html数据卷挂载到容器的/usr/share/nginx/html目录

修改容器的数据卷内容

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

目录挂载

可以越过volumes直接将宿主机目录挂载起来

目录挂载与数据卷挂载的语法是类似的:

  • -v [宿主机目录]:[容器内目录]
  • -v [宿主机文件]:[容器内文件]
  • -v [volume名称]:[容器内目录]

image-20220923165507651

两者优劣:

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

创建并运行一个MySQL容器,将宿主机目录直接挂载到容器

  1. 通过拉取(pull)或者本地有镜像直接加载(load)

    1
    docker load -i  mysql.tar
  2. 查看镜像是否加载

    1
    docker images
  3. 创建目录用于挂载

    /tmp/mysql/data

    /tmp/mysql/conf

  4. 将自定义的配置文件放入conf目录下:hmy.cnf

  5. 运行容器,通过docker hub可以查询相关操作

    查看mysql容器conf所在目录

    image-20220923170454842

    通过-e MYSQL_ROOT_PASSWORD=你的密码可以设置mysql登陆密码

    完整执行命令

    1
    2
    3
    4
    5
    6
    7
    8
    docker run \
    --name mysql \
    -e MYSQL_ROOT_PASSWORD=123 \
    -p 3306:3306 \
    -v /tmp/mysql/conf:/etc/mysql/conf.d \
    -v /tmp/mysql/data:/var/lib/mysql \
    -d \
    mysql:5.7.25

    Docker自定义镜像

简介

我们自己也可以构建Docker镜像,但需要首先了解镜像的结构

镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成

image-20220925091143503

构建镜像其实就是将上图所示的各层打包形成一个镜像的过程

DockerFile

Dockerfile reference | Docker Documentation

Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer,一下是常见的指令

指令 说明 示例
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示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 指定基础镜像
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

image-20230828161118876

构建Java项目

一、基于Ubuntu构建

  1. 新建一个空文件夹

    1
    mkdir docker-demo
  2. 拷贝jar文件到这个目录

  3. 拷贝jdk8.tar.gz文件到这个目录

  4. 创建或拷贝Dockerfile到这个目录

    image-20220925092443594

    dockerfile文件内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 指定基础镜像
    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
  5. 使用命令进入目录

    1
    cd /tmp/docker-demo
  6. 运行命令:docker build -t 镜像名:版本 . (.为当前目录)

    image-20220925092700817

  7. 使用docker run创建容器并运行

    1
    docker run --name javaweb -p 8090:8090 -d javaweb:1.0

    二、基于java8构建Java项目

在DockerFile里,很多项目都是基于java8进行环境配置的,也就是说,下图所含内容都是重复性工作,每个项目都这么弄会重复工作,所以docker提供了镜像免去这些配置

image-20220925092946611

  1. 新建一个空的目录,然后在目录中新建一个文件,命名为Dockerfile

    1
    2
    mkdir docker-demo
    touch Dockerfile
  2. 拷贝jar包到这个目录中

    image-20220925092443594

  3. 编写Dockerfile文件:

    1. 基于java:8-alpine作为基础镜像
    2. 将app.jar拷贝到镜像中
    3. 暴露端口
    4. 编写入口ENTRYPOINT
    1
    2
    3
    4
    FROM java:8-alpine
    COPY ./docker-demo.jar /tmp/app.jar
    EXPOSE 8090
    ENTRYPOINT java -jar /tmp/app.jar
  4. 使用docker build命令构建镜像

    1
    docker build -t javaweb:2.0 .
  5. 使用docker run创建容器并运行

    1
    docker run --name javaweb -p 8090:8090 -d javaweb:1.0

    总结:

  6. Dockerfile的本质是一个文件,通过指令描述镜像的构建过程

  7. Dockerfile的第一行必须是FROM,从一个基础镜像来构建

  8. 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine

Docker-Compose

可爱的🐙章鱼镇楼

image-20220925095509889

简介

当我们有多个微服务需要构建时,用自定义镜像的方式去一个个构建难免有点麻烦,Docker-Compose可以帮助我们批量处理,只需要定义好各个包的相关属性即可

Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器

Compose文件是一个文本文件(yaml格式),通过指令定义集群中的每个容器如何运行

DockerCompose的详细语法参考官网

image-20230828161122801

下面的Compose文件就描述一个项目,其中包含两个容器:

  • mysql:一个基于mysql:5.7.25镜像构建的容器,并且挂载了两个目录
  • web:一个基于docker build临时构建的镜像容器,映射端口8090(注意:构建自己的项目默认会去找app.jar,所以打包后的java项目一定要命名为app
1
2
3
4
5
6
7
8
9
10
11
12
13
version: "3.8"
services:
  mysql:
    image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
    volumes:
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
  web:
    build: .
    ports:
     - "8090:8090"

DockerCompose文件可以看做是将多个docker run命令写到一个文件,只是语法稍有差异

image-20230828161127394

安装

移步安装教程->Centos7安装Docker.md

构建微服务集群(重点)

PS:踩了一天的坑,差点🧚其中,😅

实现思路如下:

  1. 创建如下目录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    -项目名

    -微服务名1

    -微服务名2

    -微服务名...

    -mysql

    -conf

    -data
    docker-compose.yml

    我的项目如下所示:

    image-20220925101957961

  2. 每个微服务目录下新建Dockerfile文件,并填入以下内容

    1
    2
    3
    FROM java:8-alpine
    COPY ./app.jar /tmp/app.jar
    ENTRYPOINT java -jar /tmp/app.jar

    含义是根据java:8-alpine进行打包,并构建当前目录下的app.jar包,所以,接下来你知道了吧,将每个微服务打包成名为app.jar包

  3. 用idea在每个微服务的pom.xml文件夹下修改或添加以下内容,用于将当前微服务打包为app.jar,记住,根目录下的就不要加这个东西了,在微服务里加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <build>
    <finalName>app</finalName>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
  4. 既然来到了idea,我们需要再改个东西,每个微服务请求的其他服务理应都该是服务名称,否则一旦部署到其他环境,ip地址改变,我们就无从下手了(当然肯定有办法的只是比较麻烦),这里的nacos以及mysql等一些需要请求的服务ip都改为服务名称

    1
    2
    3
    4
    spring:
    datasource:
    #url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
    url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false #用于docker部署设置的微服务名
    1
    2
    3
    4
    5
    6
    spring:
    cloud:
    nacos:
    #server-addr: localhost:8848 # Nacos地址
    #server-addr: localhost:80 # nginx反向代理Nacos
    server-addr: nacos:8848 # 用于docker部署设置的微服务名
  5. 然后就是进行打包操作了,gogogo,在项目根目录执行package操作

    image-20220925102949437

    然后,将打包后的各个jar包拖至前面创建好的哥哥微服务目录下

  6. 接下来配置下mysql,mysql目录下的conf目录是用于存储相关的配置文件,文件命名为hmy.cnf,文件具体内容可以从网上查找,也可以直接复制下面的

    data目录用于存储一些mysql的数据,这里先不管了,后面构建完再创建也行

    1
    2
    3
    4
    5
    [mysqld]
    skip-name-resolve
    character_set_server=utf8
    datadir=/var/lib/mysql
    server-id=1000
  7. 然后,进到docker-compose.yml文件里,我们需要配置各个微服务的构建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    version: "3.2"

    services:
    nacos:
    image: nacos/nacos-server
    environment:
    MODE: standalone
    ports:
    - "8848:8848"
    mysql:
    image: mysql:5.7.25
    environment:
    MYSQL_ROOT_PASSWORD: 123456
    volumes:
    - "$PWD/mysql/data:/var/lib/mysql"
    - "$PWD/mysql/conf:/etc/mysql/conf.d/"
    ports:
    - "3306:3306"
    userservice:
    build: ./user-service
    orderservice:
    build: ./order-service
    gateway:
    build: ./gateway
    ports:
    - "10010:10010"

    至此,整体文件创建配置完成,下面就要到服务器上的配置了,整体目录结构如下

    image-20220925103638293

  8. 将整个目录上传至虚拟机,在目录根路径下利用 docker-compose up -d 来部署,你可以输入docker-compose --help 去查看相关操作,这时候,项目就会自动化构建并运行了

    image-20220925113905394

  9. 还没完,这时候我们通过docker-compose logs 某个服务名会发现服务会报错,因为在这里,nacos启动比其他微服务慢了,导致其他项目启动时没发现nacos,无法注册服务导致抛出异常,所以在这里我们需要重启下其他微服务,重启前最好重启下nacos服务,保证在浏览器能访问到http://服务器地址:8848/nacos/index.html后再进行下一步

    1
    2
    docker-compose restart nacos
    docker-compose restart 微服务1 微服务2 微服务...
  10. 在上一步完成后,服务相关的设置就已经ok了,但是这时的mysql还没有数据,我们需要在自己电脑上将本地的数据进行sql导出,然后连接上linux里面的数据库,将数据导入

    image-20220925113513578

  11. 大功告成,浏览器输入地址享用吧,在这时你也可以将mysql的data目录进行备份处理,这样在其他的系统就可以直接拷贝过去,无需执行第十步了

Docker镜像仓库

简介

registry - Official Image | Docker Hub

镜像仓库( Docker Registry )有公共的和私有的两种形式:

  1. 公共仓库:例如Docker官方的 Docker Hub,国内也有一些云服务商提供类似于 Docker Hub 的公开服务,比如 网易云镜像服务DaoCloud 镜像服务阿里云镜像服务等。
  2. 私有仓库:用户在本地搭建的私有 Docker Registry,企业自己的镜像最好是采用私有Docker Registry来实现

安装并搭建私有镜像仓库

移步安装教程->Centos7安装Docker.md

推送镜像和拉取镜像

记住:记得重新打包(tag)时,一定要加上镜像仓库地址(如下)

tag本地镜像

1
docker tag nginx:latest 你的ip:8080/nginx:1.0 

推送镜像

1
docker push 你的ip:8080/nginx:1.0 

拉取镜像

1
docker pull 你的ip:8080/nginx:1.0