初识 Docker

概述

系统平滑移植,容器虚拟化技术。使得Docker得以打破过去「程序即应用」的观念。透过镜像将作业系统核心除外,运作应用所需要的系统环境,由下而上打包,达到应用跨平台间的无缝接轨运作。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。是解决了运行环境和配置问题的软件容器。

Linux 容器不是模拟一个完整的操作系统而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。

官网:https://www.docker.com/

仓库:https://hub.docker.com/

基本组成

  • 镜像(image)

    Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器

  • 容器(container)

    Docker 利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例

  • 仓库(repository)

    仓库是集中存放镜像文件的场所

    仓库分为公开仓库(Public)和私有仓库(Private)两种形式。

    最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。

    国内的公开仓库包括阿里云 、网易云等

工作原理

image-20220902153219473

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。

image-20220902154251949

基本流程

  1. 用户使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者;
  2. Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的请求;
  3. Docker Engine 执行 Docker 内部的一系列工作,每一项工作以一个 Job 的形式存在;
  4. Job 运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graph Driver 将下载的镜像以 Graph 的形式存储;
  5. 当需要为 Docker 创建网络环境时,通过网络管理驱动 Network Driver 创建并配置 Docker 容器网络环境;
  6. 当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Exec Driver 来完成;
  7. Libcontainer 是一项独立的容器管理包,Network Driver 以及 Exec Driver 都是通过 Libcontainer 来实现对容器的具体操作;
  8. 最后各个 Docker Container(容器实例)则运行在 rootfs(类似一个Linux系统)。

HelloWorld

安装步骤

也可查看 https://docs.docker.com/engine/install/centos/

  1. 前提

    目前,CentOS 仅发行版本中的内核支持 Docker。Docker 运行在CentOS 7 (64-bit)上,要求系统为64位、Linux系统内核版本为 3.8以上。

  2. gcc 相关

    1
    2
    yum -y install gcc
    yum -y install gcc-c++
  3. 安装需要软件包

    1
    yum install -y yum-utils
  4. 设置 stable 镜像仓库

    1
    yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  5. 更新 yum 软件包索引

    1
    yum makecache fast 
  6. 安装 Docker CE

    1
    yum -y install docker-ce docker-ce-cli containerd.io
  7. 启动 Docker

    1
    systemctl start docker
  8. 测试

    查看版本

    1
    docker version

    hello-world

    1
    docker run hello-world
  9. 卸载

    1
    2
    3
    4
    systemctl stop docker
    yum remove docker-ce docker-ce-cli containerd.io
    rm -rf /var/lib/docker
    rm -rf /var/lib/containerd

阿里云镜像加速

使用加速器可以提升获取Docker官方镜像的速度

image-20220902164802900

常用命令

帮助启动类命令

  • 启动docker: systemctl start docker
  • 停止docker: systemctl stop docker
  • 重启docker: systemctl restart docker
  • 查看docker状态: systemctl status docker
  • 开机启动: systemctl enable docker
  • 查看docker概要信息: docker info
  • 查看docker总体帮助文档: docker —help

镜像命令

  • docker images

    列出本地主机上的镜像

    各个选项说明:

    REPOSITORY:表示镜像的仓库源

    TAG:镜像的标签版本号

    IMAGE ID:镜像ID

    CREATED:镜像创建时间

    SIZE:镜像大小

    OPTIONS说明:

    -a :列出本地所有的镜像(含历史映像层)

    -q :只显示镜像ID

    虚悬镜像

    仓库名、标签都是<none>的镜像,俗称虚悬镜像 dangling image

  • docker search [OPTIONS] 镜像名字

    搜索镜像

    OPTIONS说明:

    —limit: 只列出N个镜像,默认25个

  • docker pull 镜像名字[:TAG]

    下载镜像,没有指定TAG默认最新版。

  • docker system df 查看镜像/容器/数据卷所占的空间

  • docker rmi 镜像名字/ID

    -f 强制删除

    删除单个

    docker rmi -f 镜像ID

    删除多个

    docker rmi -f 镜像名1:TAG 镜像名2:TAG

    删除全部

    docker rmi -f $(docker images -qa)

容器命令

有镜像才能创建容器,这是根本前提!!

1
docker pull ubuntu

本次使用 ubuntu 演示

  • 新建+启动容器

    1
    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

    OPTIONS说明:

    —name=”容器名字”: 为容器指定一个名称

    -d:后台运行容器并返回容器ID,也即启动守护式容器(后台运行)

    -i:以交互模式运行容器,通常与 -t 同时使用

    -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用

    -P:随机端口映射,大写P

    -p:指定端口映射,小写p

    hostPort 表示主机访问端口,containerPort 表示容器实例端口。

    也就是用户访问本机对应端口,docker 再去访问对应实例端口。

    | 参数 | 说明 |
    | ——————————————- | ————————————————— |
    | -p hostPort:containerPort | 端口映射 -p 8080:80 |
    | -p ip:hostPort:containerPort | 配置监听地址 -p 10.0.0.100:8080:80 |
    | -p ip::containerPort | 随机分配端口 -p 10.0.0.100::80 |
    | -p hostPort:containerPort:udp | 指定协议 -p 8080:80:tcp |
    | -p 81:80 -p 443:443 | 指定多个 |

    启动交互式容器(前台命令行)

    docker run -it ubuntu /bin/bash

    image-20220902191301761

    bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。

    要退出终端,直接输入 exit。

    在大部分场景下,我们希望 docker 的服务是在后台运行的,可以通过 -d 指定容器的后台运行模式。

    很重要的一点:容器运行的命令如果不是那些一直挂起的命令,是会自动停止容器的。例如 docker run -d centos 启动运行后台 centos,然后 docker ps 进行查看, 会发现容器已经停止。这个是docker的机制问题。后台运行后有的会自动停止容器,需要自己把握。

  • 列出当前所有正在运行的容器

    1
    docker ps [OPTIONS]

    OPTIONS说明:

    -a:列出当前所有正在运行的容器+历史上运行过的

    -l:显示最近创建的容器

    -n:显示最近n个创建的容器

    -q:静默模式,只显示容器编号

  • 退出容器

    exit:run进去容器,exit退出,容器停止

    ctrl+p+q:run进去容器,ctrl+p+q退出,容器不停止

  • 启动已停止运行的容器

    1
    docker start 容器ID或者容器名
  • 重启容器

    1
    docker restart 容器ID或者容器名
  • 停止容器

    1
    docker stop 容器ID或者容器名
  • 强制停止容器

    1
    docker kill 容器ID或容器名
  • 删除容器

    1
    docker rm [-f] 容器ID/容器名

    容器就算已经停止,没被删除的情况下是还存在的,也就是可以重启进行复原,删除之后容器则不能复原。只能再run一个。

    -f 表示强制删除,如果容器还在运行着,不加 -f 的话docker不会让你删除。

    一次性删除多个容器实例

    1
    2
    3
    docker rm -f $(docker ps -a -q)

    docker ps -a -q | xargs docker rm
  • 查看容器日志

    1
    docker logs 容器ID
  • 查看容器内运行的进程

    1
    docker top 容器ID
  • 查看容器内部细节

    1
    docker inspect 容器ID
  • 进入正在运行的容器并以命令行交互

    1
    2
    3
    docker exec -it 容器ID /bin/bash

    docker attach 容器ID

    区别:

    attach 进入容器启动命令的终端,不会启动新的进程。用 exit 退出,会导致容器的停止。

    exec 是在容器中打开新的终端,并且可以启动新的进程。用 exit 退出,不会导致容器的停止。因为关掉的是自己打开的终端。

    推荐使用 docker exec 命令,因为退出容器终端,不会导致容器的停止。

  • 将容器内拷贝文件到主机上

    1
    docker cp 容器ID:容器内路径 目的主机路径
  • 导入和导出容器

    export 导出容器的内容留作为一个tar归档文件[对应import命令]
    import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]

    1
    2
    docker export 容器ID > 文件名.tar
    cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号

其它命令

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
attach	# 当前 shell 下 attach 连接指定运行镜像

build # 通过 Dockerfile 定制镜像

commit # 提交当前容器为新的镜像

cp # 从容器中拷贝指定文件或者目录到宿主机中

create # 创建一个新的容器,同 run,但不启动容器

diff # 查看 docker 容器变化

events # 从 docker 服务获取容器实时事件

exec # 在已存在的容器上运行命令

export # 导出容器的内容流作为一个 tar 归档文件 [对应 import]

history # 展示一个镜像形成历史

images # 列出系统当前镜像

import # 从tar包中的内容创建一个新的文件系统映像 [对应 export]

info # 显示系统相关信息

inspect # 查看容器详细信息

kill # 杀掉指定 docker 容器

load # 从一个 tar 包中加载一个镜像 [对应 save]

login # 注册或者登陆一个 docker 源服务器

logout # 从当前 Docker registry 退出

logs # 输出当前容器日志信息

port # 查看映射端口对应的容器内部源端口

pause # 暂停容器

ps # 列出容器列表

pull # 从docker镜像源服务器拉取指定镜像或者库镜像

push # 推送指定镜像或者库镜像至docker源服务器

restart # 重启运行的容器

rm # 移除一个或者多个容器

rmi # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]

run # 创建一个新的容器并运行一个命令

save # 保存一个镜像为一个 tar 包[对应 load]

search # 在 docker hub 中搜索镜像

start # 启动容器

stop # 停止容器

tag # 给源中镜像打标签

top # 查看容器中运行的进程信息

unpause # 取消暂停容器

version # 查看 docker 版本号

wait # 截取容器停止时的退出状态值

Docker 镜像

概述

是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。

UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

Docker的镜像实际上是由一层一层的 UnionFS 文件系统组成。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层“,“容器层”之下的都叫“镜像层”。所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。这就表示可以在已有镜像的基础上去制作新的镜像。

image-20220903122427827

构建镜像

演示ubuntu安装vim

  1. 从Hub上下载ubuntu镜像到本地并成功运行,原始的默认Ubuntu镜像是不带着vim命令的

  2. run并安装vim

    1
    2
    apt-get update
    apt-get -y install vim
  3. 安装完成后,commit 成为新镜像

    提交容器副本使之成为一个新的镜像

    1
    docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
  4. 此时执行 docker images 会看到两个ubuntu 镜像,其中大的镜像就是刚刚提交上去的镜像

  5. 总结

    Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。

    新镜像是从 base 镜像一层一层叠加生成的。

这是根据已有镜像构建新镜像,还有一种通过 Dockerfile 构建镜像的方式,之后会讲。

阿里云镜像仓库

将本地镜像保存在云上

image-20220903154046859

  1. 创建命名空间

    image-20220903154225958

  2. 创建镜像仓库

    image-20220903154350433

    image-20220903154411099

    image-20220903154454646

  3. 进入仓库可查看操作指南

    image-20220903154640833

  4. 推送镜像

    image-20220903160112204

    image-20220903160833150

  5. 拉取镜像并测试

    image-20220903161310330

私有镜像仓库

Dockerhub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。Docker Registry 是官方提供的工具,可以用于构建私有镜像仓库。

  1. 下载镜像 Docker Registry

    1
    docker pull registry
  2. 运行私有库

    相当于在本地有个私有仓库

    1
    docker run -d -p 5000:5000 -v /wjuse/myregistry:/var/lib/registry --privileged=true registry

    默认情况,仓库被创建在容器的 /var/lib/registry 目录下,建议使用容器卷映射,方便于宿主机联调。

    : 左边表示主机路径,右边表示容器内路径。也就是将上传到registry的镜像备份一份到本地。

    --privileged=true 使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。

  3. 查看库中镜像

    1
    curl -XGET http://localhost:5000/v2/_catalog

    image-20220903165914295

    上图表示仓库为空。

  4. 将镜像修改成符合私服规范的Tag

    1
    2
    3
    docker tag 镜像:Tag Host:Port/Repository:Tag
    此处
    docker tag hello-world:latest localhost:5000/hello-world:1.0.0

    image-20220903170456350

  5. 修改配置文件使之支持http

    1
    vim /etc/docker/daemon.json
    1
    2
    3
    4
    {
    "registry-mirrors": ["https://gprv3qkn.mirror.aliyuncs.com"],
    + "insecure-registries": ["localhost:5000"]
    }

    注意逗号隔开,上面为之前配置的阿里云仓库。配置完最好重启一下docker。注意重启完记得run仓库。

  6. push 到私有库并验证

    1
    2
    docker push localhost:5000/hello-world:1.0.0
    curl -XGET http://localhost:5000/v2/_catalog

    image-20220903172338731

  7. pull 到本地

    1
    docker pull localhost:5000/hello-world:1.0.0

    image-20220903172920887

容器数据卷

概述

Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。

卷就是目录或文件,由 docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System 提供一些用于持续存储或共享数据的特性。就是将 docker 容器内的数据保存进宿主机的磁盘中。卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。

运行一个带有容器卷存储功能的容器实例,当然运行方式可以是前台或后台:

1
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内路径 镜像名
1
docker run -d -p 5000:5000 -v /wjuse/myregistry/:/tmp/registry --privileged=true registry

Docker挂载主机目录访问如果出现 cannot open directory,因为是权限的问题。所以在挂载目录后多加一个 —privileged=true 参数即可。

特点

  1. 数据卷可在容器之间共享或重用数据
  2. 数据卷的更改可以直接实时生效到容器中,反之同样可以。就算数据卷是在容器停止时修改,容器启动后数据依然会同步。
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

数据共享

  1. 运行带有容器卷的ubuntu

    1
    docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data ubuntu
  2. 查看ubuntu是否挂载数据卷

    1
    docker inspect 容器ID

    image-20220903185738914

  3. 数据共享

    1)docker修改,主机同步更新

    image-20220903190127224

    2)主机修改,docker同步更新

    image-20220903190223102

    3)docker 容器 stop,主机修改,docker 容器重启数据同步更新

    image-20220903190802775

    image-20220903190825208

读写规则

  • 读写(默认)

    数据共享案例使用的就是默认规则。

    1
    docker run -it --privileged=true -v /宿主机目录:/容器内目录:rw 镜像名
  • 只读

    容器实例内部被限制,只能读取不能写。

    1
    docker run -it --privileged=true -v /宿主机目录:/容器内目录:ro 镜像名

    此时如果宿主机写入内容,可以同步给容器内,容器可以读取到。

继承与共享

1
2
docker run -it  --privileged=true -v /mydocker/u:/tmp --name u1 ubuntu
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu

继承:--volumes-from表示继承容器 u1 的规则。

共享:主机、容器u1、容器u2 之间数据共享。

常用安装

Tomcat

  1. 拉取tomcat

    1
    docker pull tomcat
  2. 运行

    1
    docker run -it -p 8080:8080 tomcat
  3. 访问猫首页 ip:8080

    出现404,对应端口已经打开。这是新版 tomcat 的问题。

    1
    2
    3
    4
    5
    # 进入tomcat实例
    docker exec -it 容器ID /bin/bash
    # 发现 webapps 文件夹内容为空
    # 将 webapps.dist 覆盖成 webapps 再重新访问即可
    mv webapps.dist webapps
  4. 免修改版

    安装tomcat8即可。

    1
    2
    docker pull billygoo/tomcat8-jdk8
    docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8

MySQL

  1. 拉取镜像

    1
    docker pull mysql:5.7
  2. 运行镜像

    注意运行前查看本地3306端口是否已经被mysql占用。并且一定要使用数据卷进行备份。

    1
    docker run -d -p 3306:3306 --privileged=true -v /wjuse/mysql/log:/var/log/mysql -v /wjuse/mysql/data:/var/lib/mysql -v /wjuse/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=root --name mysql mysql:5.7
  3. 设置字符集解决插入中文报错等问题

    1
    2
    cd /wjuse/mysql/conf/
    vim my.cnf
    1
    2
    3
    4
    5
    [client]
    default_character_set=utf8
    [mysqld]
    collation_server = utf8_general_ci
    character_set_server = utf8
  4. 重新启动mysql容器实例

    1
    2
    docker restart mysql
    docker exec -it mysql /bin/bash
  5. 进入mysql

    1
    mysql -uroot -p
  6. 查看字符集是否更改

    1
    SHOW VARIABLES LIKE 'character%';
  7. 之后即可进行正常数据库操作,就算容器被删除,再新建一个mysql容器实例并使用容器卷将主机路径指向之前的路径即可。

Redis

  1. 拉取redis

    1
    docker pull redis:6.0.8
  2. 宿主机新建目录

    1
    mkdir -p /app/redis
  3. 在上面目录下新建 redis.conf

    去拷贝一份原始配置就行。

  4. 修改redis.conf

    1)允许redis外地连接

    1
    # bind 127.0.0.1

    2)daemonize 设置为 no,因为该配置和docker run中 -d 参数冲突,会导致容器一直启动失败

    1
    daemonize no
  5. 运行redis

    redis-server /etc/redis/redis.conf 指定配置文件为上面配置的conf。

    1
    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
    1
    docker exec -it myr3 /bin/bash
  6. 进入redis

    1
    redis-cli
  7. 测试

    image-20220903220434845

复杂安装

MySQL主从复制

  1. 新建主服务器容器3307

    1
    2
    3
    4
    5
    6
    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. 进入/mydata/mysql-master/conf目录新建my.conf

    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
    27
    28
    29
    30
    31
    [mysqld]

    ## 设置server_id,同一局域网中需要唯一

    server_id=101

    ## 指定不需要同步的数据库名称

    binlog-ignore-db=mysql

    ## 开启二进制日志功能

    log-bin=mall-mysql-bin

    ## 设置二进制日志使用内存大小(事务)

    binlog_cache_size=1M

    ## 设置使用的二进制日志格式(mixed,statement,row)

    binlog_format=mixed

    ## 二进制日志过期清理时间。默认值为0,表示不自动清理。

    expire_logs_days=7

    ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。

    ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致

    slave_skip_errors=1062
  3. 修改完后重启master实例并进入

    1
    2
    3
    docker restart mysql-master
    docker exec -it mysql-master /bin/bash
    mysql -uroot -proot
  4. Master创建数据同步用户

    1
    2
    CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
    GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
  5. 新建从服务器实例3308

    1
    2
    3
    4
    5
    6
    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
  6. 进入/mydata/mysql-slave/conf目录新建my.conf

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    [mysqld]

    ## 设置server_id,同一局域网中需要唯一

    server_id=102

    ## 指定不需要同步的数据库名称

    binlog-ignore-db=mysql

    ## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用

    log-bin=mall-mysql-slave1-bin

    ## 设置二进制日志使用内存大小(事务)

    binlog_cache_size=1M

    ## 设置使用的二进制日志格式(mixed,statement,row)

    binlog_format=mixed

    ## 二进制日志过期清理时间。默认值为0,表示不自动清理。

    expire_logs_days=7

    ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。

    ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致

    slave_skip_errors=1062

    ## relay_log配置中继日志

    relay_log=mall-mysql-relay-bin

    ## log_slave_updates表示slave将复制事件写进自己的二进制日志

    log_slave_updates=1

    ## slave设置为只读(具有super权限的用户除外)

    read_only=1
  7. 重启slave实例并进入

    1
    2
    3
    docker restart mysql-slave
    docker exec -it mysql-slave /bin/bash
    mysql -uroot -proot
  8. 在从数据库中配置主从复制

    1
    change master to master_host='宿主机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;
    1
    2
    3
    4
    5
    6
    7
    master_host:主数据库的IP地址;
    master_port:主数据库的运行端口;
    master_user:在主数据库创建的用于同步数据的用户账号;
    master_password:在主数据库创建的用于同步数据的用户密码;
    master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
    master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
    master_connect_retry:连接失败重试的时间间隔,单位为秒。
  9. 开启主从同步

    1
    start slave;

Redis集群

前置问题

1~2亿条数据需要缓存,如何设计这个存储案例?单机100%无法完成,肯定使用分布式存储,但是应该如何落地?

哈希取余

image-20220904100705182

2亿条记录就是2亿个k,v,用户每次读写操作都是根据公式 hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。

优点

简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到 负载均衡+分而治之 的作用。

缺点

原来规划好的节点,进行扩容或者缩容就比较麻烦了,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key) /?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。

一致性哈希

算法背景

一致性哈希算法在1997年由麻省理工学院中提出的,设计目标是为了解决分布式服务器个数变动所带来的数据变动和映射问题。例如某个机器宕机了,分母数量改变了,自然取余数就不OK了。

三大步骤

  • 构建一致性哈希环

    一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间

    它也是按照使用取模的方法,前面介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对2^32取模。一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环。整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推直到2^32-1。我们把这个由2的32次方个点组成的圆环称为Hash环。

    image-20220904101927248

  • 节点映射

    将集群中各个IP节点映射到环上的某一个位置。将各个服务器进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:

    image-20220904102136819

  • 落键规则

    当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。

    如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:

    image-20220904102558514

优点

  • 容错性

    假设 Node C 宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。

    image-20220904102900401

  • 扩展性

    数据量增加了,需要增加一台节点 NodeX,X 的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。

    image-20220904103014181

缺点

  • 数据倾斜

    一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题。

    image-20220904103323019

哈希槽分区

是为了解决一致性哈希算法的数据倾斜问题而出现的。

哈希槽实质就是一个数组,数组[0,2^14 -1]形成hash slot空间。为解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。

一个集群只能有16384个槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。

image-20220904103857963

Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。

三主三从

  1. 新建6个redis实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    | 命令 | 说明 |
    | ——————————- | —————————— |
    | —net host | 使用宿主机的ip和端口 |
    | —cluster-enabled yes | 开启redis集群 |
    | —appendonly yes | 开启持久化 |
    | —port 6386 | redis端口号 |

  2. 进入redis-node-1

    1
    docker exec -it redis-node-1 /bin/bash
  3. 构建主从关系

    记得打开服务器对应端口。(6381-6386)、(16381-16386),很重要。

    1
    redis-cli --cluster create 120.48.54.126:6381 120.48.54.126:6382 120.48.54.126:6383 120.48.54.126:6384 120.48.54.126:6385 120.48.54.126:6386 --cluster-replicas 1

    --cluster-replicas 1 表示为每个master创建一个slave节点。一切OK的话,三主三从搞定。如下也可以看到redis自动进行了槽分区。

    image-20220904111434738

    image-20220904112057837

  4. 以6381作为切入点,查看集群状态

    1
    2
    3
    redis-cli -p 6381
    cluster info
    cluster nodes

    image-20220904112639321

数据读写存储

问题

进入6381测试

image-20220904113651701

发现有的key能够存进去,有的不行。这是因为redis根据key计算hash值,并落入对应槽分区里头。图中k1、k4的hash值明显不在6381的槽分区内。

解决

表示以集群方式连接集群。

1
redis-cli -p 6381 -c

image-20220904114443451

查看集群信息

1
redis-cli --cluster check 120.48.54.126:6381

image-20220904115143706

可看到各个节点上存储的key数量,以及对应的主从关系。

主从容错切换

  1. 连接6382查看主从情况

    可以看到master节点1的slave节点为4

    image-20220904120144754

  2. 停掉6381主节点

    重新查看节点情况,只剩下5个节点,而节点4成为新的主节点。

    image-20220904120541139

  3. 重启6381节点

    查看节点情况,此时节点4依然是master节点,而节点1变为slave节点

    image-20220904120814807

主从扩容

  1. 新建6387、6388两个节点

    1
    2
    3
    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
  2. 进入6387容器实例内部

    1
    docker exec -it redis-node-7 /bin/bash
  3. 将6387节点作为master节点加入原集群

    当然因为是集群,所以最右边端口随便一个集群机子端口即可。

    1
    redis-cli --cluster add-node 120.48.54.126:6387 120.48.54.126:6381
  4. 查看集群情况

    1
    redis-cli --cluster check 120.48.54.126:6381

    此时新增的6387还没有被分配槽位。

    image-20220904141003902

  5. 重新分配槽位

    1
    redis-cli --cluster reshard 120.48.54.126:6381

    image-20220904141736805

  6. 查看集群情况

    1
    redis-cli --cluster check 120.48.54.126:6381

    此时6387获得从其它三台主机分配过来的槽位。

    image-20220904142319551

  7. 为主节点6487分配从节点6388

    1
    redis-cli --cluster add-node 120.48.54.126:6388 120.48.54.126:6387 --cluster-slave --cluster-master-id 649808db07241bcf8e746d4fa5033c6e8c4568dc
  8. 查看集群情况

    1
    redis-cli --cluster check 120.48.54.126:6382

    image-20220904142647480

主从缩容

目的:下线6387和6388。

  1. 从集群中删除从节点6388

    1
    redis-cli --cluster del-node 120.48.54.126:6388 591b2ebd26202f7f993a472be99c3c9034d34ba2
  2. 重新分配槽位

    将6387的4096个槽位清空,本次全都分配给6384

    1
    redis-cli --cluster reshard 120.48.54.126:6381

    image-20220904144107375

  3. 删除6387

    1
    redis-cli --cluster del-node 120.48.54.126:6387 649808db07241bcf8e746d4fa5033c6e8c4568dc
  4. 查看集群情况

    1
    redis-cli --cluster check 120.48.54.126:6381

    image-20220904144413818

DockerFile

概述

Dockerfile 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。

image-20220904145356798

DockerFile基础知识

  • 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  • 指令按照从上到下,顺序执行
  • 表示注释

  • 每条指令都会创建一个新的镜像层并对镜像进行提交

大体步骤

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器作出修改
  3. 执行类似docker commit的操作提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个新容器
  5. 执行dockerfile中的下一条指令直到所有指令都执行完成

常用保留字

保留字 说明
FROM 指定一个已经存在的镜像作为模板,第一条必须是FROM
MAINTAINER 镜像维护者的姓名和邮箱地址
RUN 容器构建时需要运行的命令,在 docker build 时运行。两种格式:
shell格式:RUN <命令行命令>
exec格式:RUN [“可执行文件”,”参数1”,”参数2”]
EXPOSE 当前容器对外暴露出的端口
WORKDIR 指定在创建容器后,终端默认登陆进来的工作目录,第一个落脚点
USER 指定该镜像以什么样的用户去执行,默认是root
ENV 用来在构建镜像过程中设置环境变量,eg:
ENV MY_PATH /usr/mytest
WORKDIR $MY_PATH
ADD 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
COPY 类似ADD,拷贝文件和目录到镜像中。 将从构建上下文目录中的文件/目录复制到新的一层的镜像内
COPY src dest 或 COPY [“src”,”dest”]
VOLUME 容器数据卷,用于数据保存和持久化工作
CMD 指定容器启动后的要干的事情,也就是在 docker run 时运行,可跟 shell 或 exec 格式。
注意 Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。CMD 会被 docker run 之后的参数替换。
ENTRYPOINT 也是用来指定一个容器启动时要运行的命令,但是ENTRYPOINT不会被 docker run 后面的命令覆盖。
而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。同样如果存在多个 ENTRYPOINT
指令,只有最后一个会生效。

CMD 与 ENTRYPOINT

ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。

image-20220904155603271

按照dockerfile编写执行 传参运行
Docker命令 docker run nginx:test docker run nginx:test -c /etc/nginx/new.conf
实际命令 nginx -c /etc/nginx/nginx.conf nginx -c /etc/nginx/new.conf

案例

自定义镜像mycentosjava8,要求 Centos7 镜像具备vim+ifconfig+jdk8。

  1. 下载一个jdk压缩包

    https://www.oracle.com/java/technologies/downloads/#java8

  2. 编写Dockerfile文件

    1
    vim Dockerfile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    FROM centos
    MAINTAINER wj<wj@123.com>

    ENV MYPATH /usr/local
    WORKDIR $MYPATH

    #安装vim编辑器
    RUN yum -y install vim
    #安装ifconfig命令查看网络IP
    RUN yum -y install net-tools
    #安装java8及lib库
    RUN yum -y install glibc.i686
    RUN mkdir /usr/local/java
    #ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
    ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
    #配置java环境变量
    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 /bin/bash
  3. 构建

    注意最后面有一个.,指定Dockerfile路径

    1
    docker build -t centosjava8:1.5 .
  4. 运行并测试

    1
    docker run -it centosjava8:1.5
    1
    2
    3
    java -version
    ifconfig
    vim a.txt

虚悬镜像

仓库名、标签都为<none>的镜像,俗称 dangling image。

构建时不写仓库名和标签就会生成虚悬镜像,或者构建时出错也会生成。

1
docker build .

查看所有虚悬镜像

1
docker image ls -f dangling=true

删除所有虚悬镜像

1
docker image prune

项目部署

  1. 本地运行并测试通过一个boot项目

    image-20220904172201368

  2. 打包项目

    image-20220904172223572

  3. 将打包后的jar包上传到服务器

    image-20220904172442241

  4. 在jar包目录下新建Dockerfile文件

    1
    vim Dockerfile
    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
    27
    # 基础镜像使用java

    FROM java:8

    # 作者

    MAINTAINER swj

    # VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp

    VOLUME /tmp

    # 将jar包添加到容器中并更名为swj_docker.jar

    ADD boot-docker-0.0.1-SNAPSHOT.jar swj_docker.jar

    # touch 作用就是修改文件的访问时间和修改时间为当前时间

    RUN bash -c 'touch /swj_docker.jar'

    # 运行jar包

    ENTRYPOINT ["java","-jar","/swj_docker.jar"]

    #暴露6001端口作为微服务

    EXPOSE 6001
  5. 下载java:8

    1
    docker pull java:8
  6. 构建镜像

    1
    docker build -t swj_docker:1.0 .
  7. 运行容器

    1
    docker run -d -p 6001:6001 swj_docker:1.0
  8. 测试

    访问 http://120.48.54.126:6001/hello

    image-20220904173705172

Docker 网络

概述

Docker 启动后,会产生一个名为docker0的虚拟网桥。案例说明详解。

image-20220904183101400

能干嘛?

  • 容器间的互联通信以及端口映射
  • 容器ip变动时可以通过服务名进行网络通信而不受到ip变动影响

容器有自身内部ip:

  1. 启动两个ubuntu容器实例

    1
    2
    docker run -it --name u1 ubuntu /bin/bash
    docker run -it --name u2 ubuntu /bin/bash
  2. docker inspect 容器ID or 容器名字

    image-20220904190509237

    image-20220904190551105

    发现容器有自己的ip。

常用命令

  • ALL命令

    1
    docker network --help
  • 查看网络

    1
    docker network ls
  • 查看网络信息

    1
    docker network inspect 网络名字
  • 创建网络

    1
    docker network create 网络名字
  • 删除网络

    1
    docker network rm 网络名字

image-20220904181424840

网络模式

网络模式 简介
bridge 为每个容器分配、设置ip等,并将容器连接到一个docker0虚拟网桥,默认该模式。
host 容器将不会虚拟出自己的网卡,配置自己的ip等,而是使用宿主机的ip和端口。
none 容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair、网桥连接、ip等。
container 新创建的容器不会创建自己的网卡和配置自己的ip,而是和一个指定的容器共享ip、端口范围等。
  • bridge

    1
    --network bridge
  • host

    1
    --network host
  • none

    1
    --network none
  • container

    1
    --network container

    案例说明

bridge

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信

查看 bridge 网络的详细信息,并通过 grep 获取名称项

1
docker network inspect bridge | grep name

image-20220904191054644

image-20220904185655877

  1. Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

  2. docker run 的时候,网桥 docker0 创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。

    整个宿主机的网桥模式都是 docker0,类似一个交换机有一堆接口,每个接口叫 veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);每个容器实例内部也有一块网卡,每个接口叫eth0。

  3. 通过上述,将宿主机上的所有容器都连接到这个内部网络上,各个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时容器间的网络是互通的。

启动两个tomcat实例进行测试:

1
2
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8

宿主机执行ip addr发现新增了两个veth:

image-20220904193553641

进入两个容器内部执行ip addr查看发现各自都有eth并且与veth一一匹配:

image-20220904193401596

image-20220904193425539

host

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口

image-20220904210446991

注意,以下方法启动会发出警告,因为直接使用宿主机的ip和端口就不存在端口映射这一说。

1
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8

直接启动tomcat实例即可,不指定端口就默认就是8080。

1
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8

image-20220904210742086

image-20220904211639828

image-20220904211603110

image-20220904211207002

综上,使用host模式,容器实例内部网络与宿主机一致。

none

在none模式下,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo。需要我们自己为Docker容器添加网卡、配置IP等。禁用网络功能,只有lo标识(就是127.0.0.1本地回环)

1
docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8

容器外部查看:

image-20220904212155659

容器内部查看:

image-20220904212241241

container

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

image-20220904212750326

此处用 centos 演示,因为虽然共享网络,但是 tomcat 之间8080端口冲突,启动会报错。

1
2
docker run -it --name centos1 centos /bin/bash
docker run -it --network container:centos1 --name centos2 centos /bin/bash

image-20220904213528263

image-20220904213624876

注意此时如果停止centos1容器,centos2就会只剩下lo本地回环了。

自定义网络

before

1
2
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8

使用自定义网络之前,容器之间可以 ping ip,但不能通过容器名称去 ping。而容器ip是有可能发生改变,很麻烦的。

image-20220904215044694

image-20220904214937538

after

自定义桥接网络,自定义网络默认使用的是桥接网络bridge。

新建自定义网络

1
docker network create swj_network

image-20220904215440280

新建容器并加入上一步新建的自定义网络

1
2
docker run -d -p 8081:8080 --network swj_network  --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --network swj_network --name tomcat82 billygoo/tomcat8-jdk8

image-20220904215749681

image-20220904215951033

Docker Compose

容器编排。

概述

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。需要定义一个 YAML 格式的配置文件docker-compose.yml,写好容器之间的调用关系。之后只要一个命令,就能同时启动/关闭这些容器。

例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等 … …

Compose 允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。

核心概念

  • 一文件

    docker-compose.yml

  • 两要素

    • 服务(service)

      一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器

    • 工程(project)

      由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

三个步骤

  1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
  2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务
  3. 最后,执行 docker-compose up 命令来启动并运行整个应用程序,完成一键部署上线

安装

1
2
3
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version

image-20220904222823156

卸载

1
rm /usr/local/bin/docker-compose

常用命令

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
docker-compose -h                           # 查看帮助

docker-compose up # 启动所有docker-compose服务

docker-compose up -d # 启动所有docker-compose服务并后台运行

docker-compose down # 停止并删除容器、网络、卷、镜像。

docker-compose exec yml里面的服务id /bin/bash # 进入容器实例内部

docker-compose ps # 展示当前docker-compose编排过的运行的所有容器

docker-compose top # 展示当前docker-compose编排过的容器进程

docker-compose logs yml里面的服务id # 查看容器输出日志

docker-compose config # 检查配置

docker-compose config -q # 检查配置,有问题才有输出

docker-compose restart # 重启服务

docker-compose start # 启动服务

docker-compose stop # 停止服务

编排案例

  1. 改造boot-docker使之连接数据库

    可以连接本地进行测试,再将连接地址ip:3306改成mysql:3306。因为后面在docker打算不通过ip访问,而是自定义网络从而使用容器名访问。

  2. mvn package 命令将项目打成jar包,并上传到服务器

  3. 编写Dockerfile

  4. 构建成镜像

  5. 编写docker-compose.yml文件

    相当于编写项目和mysql两条run命令并定义执行顺序,创建自定义网络swj_net。

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    version: "3"


    services:

    myService:

    image: swj_docker:1.1

    container_name: ms01

    ports:

    - "6001:6001"

    volumes:

    - /app/myService:/data

    networks:

    - swj_net

    depends_on:

    - mysql



    mysql:

    image: mysql:5.7

    environment:

    MYSQL_ROOT_PASSWORD: 'root'

    MYSQL_ALLOW_EMPTY_PASSWORD: 'no'

    MYSQL_DATABASE: 'db2022'

    MYSQL_USER: 'swj'

    MYSQL_PASSWORD: 'swj123'

    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:

    - swj_net

    command: --default-authentication-plugin=mysql_native_password # 解决外部无法访问


    networks:
    swj_net:
  6. 执行以下命令,检查文件是否有语法错误,如果没有任何输出,表示语法正确

    需在compose文件目录下执行

    1
    docker-compose config -q
  7. 执行docker-compose.yml文件

    需在compose文件目录下执行

    1
    2
    3
    docker-compose up

    docker-compose up -d
  8. 进入mysql容器实例并新建库表

  9. 测试

  10. 关停

    1
    docker-compose down

Portainer

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。

  1. docker 命令安装

    --restart=always 表示docker启动它也跟着启动

    1
    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. 访问

    访问 ip:9000,第一次登录需要创建用户。

  3. 登录

    image-20220905001254334

  4. 选择local选项卡后查看本地docker详细信息

    image-20220905001359447

    image-20220905001411433

  5. 仪表盘其实也跟 docker system df 命令差不多

    image-20220905001507088

  6. 其它操作

    等等等 … …,基本上可以舍弃ssh命令行工具了。

    image-20220905001801269

容器监控

通过 docker stats 原生命令可以很方便的看到当前宿主机上所有容器的CPU,内存以及网络流量等数据,一般小公司够用了。

但是,docker stats 统计结果只能是当前宿主机的全部容器,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能。

三剑客

CAdvisor监控收集 + InfluxDB存储数据 + Granfana展示图表

CAdvisor

image-20220905002727889

InfluxDB

image-20220905002740881

Granfana

image-20220905002752953

容器编排

  1. 新建目录

    image-20220905003524463

  2. 新建 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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    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. 检查语法错误

    1
    docker-compose config -q
  4. 启动

    1
    docker-compose up -d
  5. 测试

    注意除了以下端口还需打开8086端口,influxdb要用。

配置

  1. 配置数据源

    image-20220905005240894

    image-20220905005321118

    image-20220905005433846

    image-20220905005554428

  2. 配置面板

    image-20220905005644688

    image-20220905005758201

    image-20220905010256128

    image-20220905010354377