Docker

docker文档:https://docs.docker.com/get-started/

image-20230921084052480

docker是什么

为什么用?

image-20230921084059948

传统部署手法可能因为环境问题出现乱七八糟的问题。

概念

  • Docker 是一个开源的应用容器引擎

  • 诞生于 2013 年初,基于 Go 语言实现, dotCloud 公司出品(后改名为Docker Inc)

  • Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux 机器上。

  • 容器是完全使用沙箱机制,相互隔离

  • 容器性能开销极低。

  • Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版,免费) 和 EE(Enterprise Edition: 企业版,收费)

安装 Docker

Docker可以运行在MAC、Windows、CentOS、UBUNTU等操作系统上,本课程基于CentOS 7 安装

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

image-20211129120536752

1
2
3
4
5
6
7
8
9
10
# 1、yum 包更新到最新 
yum update
# 2、安装需要的软件包, yum‐util 提供yum‐config‐manager功能,另外两个是devicemapper驱动依赖的
yum install -y yum‐utils device-mapper-persistent-data lvm2
# 3、 设置yum源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 4、 安装docker,出现输入的界面都按 y
yum install -y docker-ce
# 5、 查看docker版本,验证是否验证成功
docker -v

配置 Docker 镜像加速器

默认情况下,将来从docker hub(https://hub.docker.com/)上下载docker镜像,太慢。一般都会配置镜像加速器:

1
2
3
{
"registry-mirrors": ["https://xbmcwm0u.mirror.aliyuncs.com"]
}
  • 网易云
  • 腾讯云
1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://xbmcwm0u.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

docker命令

Docker 进程相关命令

1
2
3
4
5
6
7
8
9
10
# 启动docker服务: 
systemct start docker
# 停止docker服务:
systemct stop docker
# 重启docker服务:
systemct restart docker
# 查看docker服务状态:
systemct status docker
# 设置开机启动docker服务:
systemctl enable docker

镜像相关操作

查看镜像 : 查看本地所有的镜像

1
2
docker images 
docker images -q # 查看所用镜像的id

搜索镜像:从网络中查找需要的镜像

1
docker search 镜像名称

拉取镜像:从Docker仓库下载镜像到本地,镜像名称格式为 名称:版本号,如果版本号不指定则是最新的版本。如果不知道镜像版本,可以去docker hub 搜索对应镜像查看。

1
docker pull 镜像名称

删除镜像: 删除本地镜像

1
2
3
4
5
6
7
docker rmi 镜像id # 删除指定本地镜像 
docker rmi `docker images -q` # 删除所有本地镜像

# 删除所有镜像
service docker stop
rm -rf /var/lib/docker
service docker start

删除所有<none>版本的镜像

1
docker images|grep none|awk '{print $3}'|xargs docker rmi

容器相关命令

  • 查看容器

docker ps # 查看正在运行的容器

docker ps -a # 查看所有容器

  • 创建并启动容器

docker run 参数

参数说明:

1
2
3
4
5
-i:保持容器运行。通常与 -t 同时使用。加入it这两个参数后,容器创建后自动进入容器中,退出容器后,容器自动关闭。 
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用。
-d:以守护(后台)模式运行容器。创建一个容器在后台运行,需要使用docker exec 进入容器。退出后,容器不会关闭。
-it 创建的容器一般称为交互式容器,-id 创建的容器一般称为守护式容器
--name:为创建的容器命名。
  • 进入容器

docker exec 参数 # 退出容器,容器不会关闭

  • 停止容器

docker stop 容器名称

  • 启动容器

docker start 容器名称

  • 删除容器:如果容器是运行状态则删除失败,需要停止容器才能删除

docker rm 容器名称

  • 查看容器信息

docker inspect 容器名称

  • 查看容器日志
1
docker logs 容器名
  • 查看容器挂载(比如想看docker redis集群挂载的配置文件路径)
1
docker inspect ab2f940d19ca | grep Mounts -A 20

image-20220829211525668

  • 容器自启动和取消自启动
1
2
3
4
5
6
7
8
9
10
11
12
# 将正在运行的容器设为自启动
# docker update --restart=always 容器名或容器ID
docker update --restart=always <CONTAINER ID>
# 例如将tomcat设为自启动
docker update --restart=always tomcat


# 将自启动的容器取消自启动
# docker update --restart=no 容器名或容器ID
docker update --restart=no <CONTAINER ID>
# 例如取消tomcat的自启动
docker update --restart=no tomcat

docker 数据卷

思考:Docker 容器删除后,在容器中产生的数据还在吗?

image-20211129122041002

1
2
3
4
5
6
# 创建启动容器时,使用 –v 参数 设置数据卷
docker run ... -v 宿主机目录(文件):容器内目录(文件) ...
注意事项:
1. 目录必须是绝对路径
2. 如果目录不存在,会自动创建
3. 可以挂载多个数据卷

多容器进行数据交换

  • 多个容器挂载同一个数据卷

image-20211129124621114

数据卷容器

image-20211129122153359

配置数据卷容器

  • 创建启动c3数据卷容器,使用 –v 参数 设置数据卷
1
docker run -it --name=c3 -v /volume centos:7 /bin/bash
  • 创建启动 c1 c2 容器,使用 –-volumes-from 参数 设置数据卷
1
2
docker run -it --name=c1 --volumes-from c3 centos:7 /bin/bash 
docker run -it --name=c2 --volumes-from c3 centos:7 /bin/bash

docker应用部署

MySQL部署

  • 容器内的网络服务和外部机器不能直接通信
  • 外部机器和宿主机可以直接通信
  • 宿主机和容器可以直接通信
  • 当容器中的网络服务需要被外部机器访问时,可以将容器中提供服务的端口映射到宿主机的端口上。外部机器访问宿主机的该端口,从而间接访问容器的服务。
  • 这种操作称为:端口映射

image-20211129125405460

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
1. 搜索mysql镜像
docker search mysql
2. 拉取mysql镜像
docker pull mysql:5.6
3. 创建容器,设置端口映射、目录映射
# 在/root目录下创建mysql目录用于存储mysql数据信息
mkdir ~/mysql
cd ~/mysql
docker run -id \
-p 3307:3306 \
--name=c_mysql \
-v $PWD/conf:/etc/mysql/conf.d \
-v $PWD/logs:/logs \
-v $PWD/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
mysql:5.6

参数说明:
-p 3307:3306:将容器的 3306 端口映射到宿主机的 3307 端口。
-v $PWD/conf:/etc/mysql/conf.d:将主机当前目录下的 conf/my.cnf 挂载到容器的 /etc/mysql/my.cnf。配置目录
-v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的 /logs。日志目录
-v $PWD/data:/var/lib/mysql :将主机当前目录下的data目录挂载到容器的 /var/lib/mysql 。数据目 录
-e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码。

4. 进入容器,操作mysql
docker exec -it c_mysql /bin/bash
5. 使用外部机器连接容器中的mysql

Tomcat部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 搜索tomcat镜像 
docker search tomcat
2. 拉取tomcat镜像
docker pull tomcat
3. 创建容器,设置端口映射、目录映射
# 在/root目录下创建tomcat目录用于存储tomcat数据信息
mkdir ~/tomcat
cd ~/tomcat
docker run ‐id ‐‐name=c_tomcat \
‐p 8080:8080 \
‐v $PWD:/usr/local/tomcat/webapps \
tomcat

参数说明:
-p 8080:8080:将容器的8080端口映射到主机的8080端口
-v $PWD:/usr/local/tomcat/webapps:将主机中当前目录挂载到容器的webapps
4. 使用外部机器访问tomcat

Nginx部署

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
1. 搜索nginx镜像
docker search nginx
2. 拉取nginx镜像
docker pull nginx
3. 创建容器,设置端口映射、目录映射
# 在/root目录下创建nginx目录用于存储nginx数据信息
mkdir ~/nginx
cd ~/nginx
mkdir conf
cd conf
# 在~/nginx/conf/下创建nginx.conf文件,粘贴下面内容
vim nginx.conf
------------------ nginx.conf -------------------
user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;


events {
worker_connections 1024;
}


http {
include /etc/nginx/mime.types;
default_type application/octet‐stream;

log_format main '$remote_addr ‐ $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

启动容器

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run ‐id ‐‐name=c_nginx \
‐p 80:80 \
‐v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf \
‐v $PWD/logs:/var/log/nginx \
‐v $PWD/html:/usr/share/nginx/html \
nginx

参数说明:
-p80:80:将容器的80端口映射到宿主机的80端口。
-v$PWD/conf/nginx.conf:/etc/nginx/nginx.conf:将主机当前目录下的/conf/nginx.conf挂载到容器的:/etc/nginx/nginx.conf。配置目录
-v$PWD/logs:/var/log/nginx:将主机当前目录下的logs目录挂载到容器的/var/log/nginx。日志目录

4.使用外部机器访问nginx

Redis部署

1
2
3
4
5
6
7
8
1.搜索redis镜像
docker search redis
2.拉取redis镜像
docker pull redis:5.0
3.创建容器,设置端口映射
docker run ‐id ‐‐name=c_redis ‐p 6379:6379 redis:5.0
4.使用外部机器连接redis
./redis‐cli.exe ‐h 192.168.149.135 ‐p 6379

docker镜像原理

思考:

• Docker 镜像本质是什么?=> 是一个分层文件系统

• Docker 中一个centos镜像为什么只有200MB,而一个centos操作系统的iso文件要几个个G? => Centos的iso镜像文件包含bootfs和rootfs,而docker的centos镜像复用操作系统的bootfs,只有rootfs和其他镜像层

• Docker 中一个tomcat镜像为什么有500MB,而一个tomcat安装包只有70多MB? => 由于docker中镜像是分层的,tomcat虽然只有70多MB,但他需要依赖于父镜像和基础镜像,所有整个对外暴露的 tomcat镜像大小500多MB

操作系统组成部分:

  • 进程调度子系统
  • 进程通信子系统
  • 内存管理子系统
  • 设备管理子系统
  • 文件管理子系统
  • 网络通信子系统
  • 作业控制子系统

Linux文件系统由bootfs和rootfs两部分组成,

  • bootfs:包含bootloader(引导加载程序)和kernel(内核)

  • rootfs:root文件系统,包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件

  • 不同的linux发行版,bootfs基本一样,而rootfs不同,如ubuntu,centos等

  • Docker镜像是由特殊的文件系统叠加而成

  • 最底端是bootfs,并使用宿主机的bootfs

  • 第二层是root文件系统rootfs,称为baseimage

  • 然后再往上可以叠加其他的镜像文件

  • 统一文件系统(UnionFileSystem)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。

  • 一个镜像可以放在另一个镜像的上面。位于下面的镜像称

    为父镜像,最底部的镜像成为基础镜像。

  • 当从一个镜像启动容器时,Docker会在最顶层加载一个读写文件系统作为容器

复用

image-20211129145100819

镜像制作

1
2
3
docker commit 容器id 镜像名称:版本号 
docker save -o 压缩文件名称 镜像名称:版本号
docker load -i 压缩文件名称

image-20211129145338238

dockerfile

Dockerfile 是一个文本文件 ,包含了一条条的指令 ,每一条指令构建一层,基于基础镜像,最终构建出一个新的镜像

  • 对于开发人员:可以为开发团队提供一个完全一致的开发环境
  • 对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了
  • 对于运维人员:在部署时,可以实现应用的无缝移植

Dochub网址:https://hub.docker.com

image-20230921094048915

image-20230921094054022

自定义centos7镜像

要求:

  • 默认登录路径为 /usr (官方的默认为/)
  • 可以使用vim (官方的默认不可用vim)

① 定义父镜像:FROM centos:7

② 定义作者信息:MAINTAINER itheima itheima@itcast.cn

③ 执行安装vim命令:RUN yum install -y vim

④ 定义默认的工作目录:WORKDIR /usr

⑤ 定义容器启动执行的命令:CMD /bin/bash

⑥ 通过dockerfile构建镜像:docker bulid –f dockerfile文件路径 –t 镜像名称:版本

定义dockerfile,发布springboot项目

① 定义父镜像:FROM java:8

② 定义作者信息:MAINTAINER itheima itheima@itcast.cn

③ 将jar包添加到容器:ADD springboot.jar app.jar

④ 定义容器启动执行的命令:CMD java-jar app.jar

⑤ 通过dockerfile构建镜像:docker bulid -f dockerfile文件路径 -t 镜像名称:版本

Docker 服务编排

微服务架构的应用系统中一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停 ,维护的工作量会很大。

• 要从Dockerfile build image 或者去dockerhub拉取image

• 要创建多个container

• 要管理这些container(启动停止删除)

服务编排: 按照一定的业务规则批量管理容器

Docker Compose

Docker Compose是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建,启动和停止。使用步骤:

  1. 利用 Dockerfile 定义运行环境镜像
  2. 使用 docker-compose.yml 定义组成应用的各服务
  3. 运行 docker-compose up 启动应用

image-20211129151448840

安装

1
2
3
4
5
6
 # Compose目前已经完全支持Linux、Mac OS和Windows,在我们安装Compose之前,需要先安装Docker。下面我 们以编译好的二进制包方式安装在Linux系统中。 
curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker‐compose
# 设置文件可执行权限 
chmod +x /usr/local/bin/docker-compose
# 查看版本信息 
docker-compose -version

卸载

1
2
 # 二进制包方式安装的,删除二进制文件即可
 rm /usr/local/bin/docker-compose

编排nginx+springboot项目

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
1.创建docker-compose目录
mkdir ~/docker-compose
cd ~/docker-compose
2.编写docker-compose.yml文件
version: '3'
services:
  nginx:
   image: nginx
   ports:
    ‐ 80:80
   links:
    - app
   volumes:
    - ./nginx/conf.d:/etc/nginx/conf.d   
  app:
    image: app
    expose:
      - "8080"
3.创建./nginx/conf.d目录      
mkdir ‐p ./nginx/conf.d
4.在./nginx/conf.d目录下编写itheima.conf文件
 server {
    listen 80;
    access_log off;
    location / {
        proxy_pass http://app:8080;    
    }
}
5.在~/docker-compose目录下使用docker-compose启动容器
docker‐compose up
6.测试访问
http://192.168.149.135/hello

Docker 私有仓库

搭建

1
2
3
4
5
6
7
8
9
10
11
12
# 1、拉取私有仓库镜像 
docker pull registry
# 2、启动私有仓库容器 
docker run -id --name=registry -p 5000:5000 registry
# 3、打开浏览器 输入地址http://私有仓库服务器ip:5000/v2/_catalog,看到{"repositories":[]} 表示私有仓库 搭建成功
# 4、修改daemon.json   
vim /etc/docker/daemon.json    
# 在上述文件中添加一个key,保存退出。此步用于让 docker 信任私有仓库地址;注意将私有仓库服务器ip修改为自己私有仓库服务器真实ip 
{"insecure‐registries":["私有仓库服务器ip:5000"]} 
# 5、重启docker 服务 
systemctl restart docker
docker start registry

上传

1
2
3
4
 # 1、标记镜像为私有仓库的镜像     
docker tag centos:7 私有仓库服务器IP:5000/centos:7  
# 2、上传标记的镜像     
docker push 私有仓库服务器IP:5000/centos:7

拉取

1
docker pull 私有仓库服务器ip:5000/centos:7

Docker 二次学习

轻量容器引擎Docker

Docker是什么

Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。

​ 它基于 Google 公司推出的 Go 语言实现,项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在GitHub 上进行维护

​ Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc,Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。

​ Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案,Docker 的基础是 Linux 容器(LXC)等技术。

了解Docker的前生LXC

Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。

​ LXC为Linux Container的简写,可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性,相当于C++中的NameSpace,容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。

与传统虚拟化技术相比,它的优势在于:

  • 与宿主机使用同一个内核,性能损耗小;
  • 不需要指令级模拟;
  • 不需要即时(Just-in-time)编译;
  • 容器可以在CPU核心的本地运行指令,不需要任何专门的解释机制;
  • 避免了准虚拟化和系统调用替换中的复杂性;
  • 轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享

​ Linux Container提供了在单一可控主机节点上支持多个相互隔离的server container同时执行的机制,Linux Container有点像chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。

LXC与docker的关系

​ docker并不是LXC替代品,docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间相互隔离,并且能够可控限制各进程的资源分配,在LXC的基础之上,docker提供了一系列更强大的功能。

image-20230921095725781

Docker 的特点

容器化越来越受欢迎,因为容器是:

  • 灵活:即使是最复杂的应用也可以集装箱化。
  • 轻量级:容器利用并共享主机内核。
  • 可互换:您可以即时部署更新和升级。
  • 便携式:您可以在本地构建,部署到云,并在任何地方运行。
  • 可扩展:您可以增加并自动分发容器副本。
  • 可堆叠:您可以垂直和即时堆叠服务。

为什么使用Docker

一款产品:开发-生产2套环境,每个机器都要部署环境(集群redis,es,hadoop…)费时费力,发布一个项目(jar+redis mysql jdk es),能不能带上环境安装的打包。

image-20211129120337365

传统部署手法可能因为环境问题出现乱七八糟的问题。

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

​ 首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多,其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。

Docker的优势

具体说来,Docker 在如下几个方面具有较大的优势。

更高效的利用系统资源

​ 由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高,无论是应用执行速度,内存消耗以及文件存储速度,都要比传统虚拟机技术更高效,因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

更快速的启动时间

​ 传统的虚拟机技术启动应用服务往往需要数分钟,而Docker容器应用,由于直接运行与宿主内核,无序启动完整的操作系统,因此可以做到秒级,甚至毫秒级的启动时间,大大的节约了开发,测试,部署的时间。

一致的运行环境

​ 开发过程中一个常见的问题是环境一致性问题,由于开发环境,测试环境,生产环境不一致,导致有些bug并未在开发过程中被发现,而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。

持续交付和部署

对于开发和运维人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

image-20230921095748323

​ 使用Docker可以通过定制应用镜像来实现持续集成,持续交付,部署,开发人员可以通过Dockerfile来进行镜像构建,并结合持续集成系统进行集成测试,而运维人员则可以在生产环境中快速部署该镜像,甚至结合持续部署系统进行自动部署

更轻松的迁移

​ 由于Docker确保了执行环境的一致性,使得应用的迁移更加容易,Docker可以在很多平台上运行,无论是物理机,虚拟机,公有云,私有云,甚至是笔记本,其运行结果是一致的,因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

缺点

隔离性

​ 基于hypervisor的虚机技术,在隔离性上比容器技术要更好,它们的系统硬件资源完全是虚拟化的,当一台虚机出现系统级别的问题,往往不会蔓延到同一宿主机上的其他虚机,但是容器就不一样了,容器之间共享同一个操作系统内核以及其他组件,所以在收到攻击之类的情况发生时,更容易通过底层操作系统影响到其他容器。

性能

​ 不管是虚机还是容器,都是运用不同的技术,对应用本身进行了一定程度的封装和隔离,在降低应用和应用之间以及应用和环境之间的耦合性上做了很多努力,但是随机而来的,就会产生更多的网络连接转发以及数据交互,这在低并发系统上表现不会太明显,而且往往不会成为一个应用的瓶颈(可能会分散于不同的虚机或者服务器上),但是当同一虚机或者服务器下面的容器需要更高并发量支撑的时候,也就是并发问题成为应用瓶颈的时候,容器会将这个问题放大,所以,并不是所有的应用场景都是适用于容器技术的。

存储方案

​ 容器的诞生并不是为OS抽象服务的,这是它和虚机最大的区别,这样的基因意味着容器天生是为应用环境做更多的努力,容器的伸缩也是基于容器的这一disposable特性,而与之相对的,需要持久化存储方案恰恰相反。 这一点docker容器提供的解决方案是利用volume接口形成数据的映射和转移,以实现数据持久化的目的,但是这样同样也会造成一部分资源的浪费和更多交互的发生,不管是映射到宿主机上还是到网络磁盘,都是退而求其次的解决方案。

一致性怎么理解?

对比传统虚拟机

特性 Docker 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

Docker版本

​ 随着Docker的不断流行与发展,docker公司(或称为组织)也开启了商业化之路,Docker 从 17.03版本之后分为 CE(Community Edition) 和 EE(Enterprise Edition),我们来看看他们之前的区别于联系。

版本区别
Docker EE

Docker EE由公司支持,可在经过认证的操作系统和云提供商中使用,并可运行来自Docker Store的、经过认证的容器和插件。

Docker EE提供三个服务层次:

服务层级 功能
Basic 包含用于认证基础设施的Docker平台 Docker公司的支持 经过 认证的、来自Docker Store的容器与插件
Standard 添加高级镜像与容器管理 LDAP/AD用户集成 基于角色的访问控制(Docker Datacenter)
Advanced 添加Docker安全扫描 连续漏洞监控

大家可在该页查看各个服务层次的价目:https://www.docker.com/pricing

Docker CE

Docker CE是免费的Docker产品的新名称,Docker CE包含了完整的Docker平台,非常适合开发人员和运维团队构建容器APP

​ Docker公司认为,Docker CE和EE版本的推出为Docker的生命周期、可维护性以及可升级性带来了巨大的改进。

版本说明

在此之前docker的最新版本更新到docker1.13,而在1.13的基础之上,在2017年的3月1号开始,版本的格式变为如下

项目 说明
版本格式 YY.MM
stable版本 每个季度发行
edge版本 每个月发行
当前CE版本 17.03.0-ce
小结
  • Docker从17.03开始分为企业版与社区版,社区版并非阉割版,而是改了个名称;企业版则提供了一些收费的高级特性。
  • EE版本维护期1年;CE的stable版本三个月发布一次,维护期四个月;另外CE还有edge版,一个月发布一次。

Docker使用场景

  1. Web 应用的自动化打包和发布。
  2. 自动化测试和持续集成、发布。
  3. 在服务型环境中部署和调整数据库或其他的后台应用。
  4. 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境

Docker安装

配置要求:

image-20220514115856431

卸载旧版本

较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。

1
2
3
4
5
6
7
8
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装Docker依赖环境

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库,之后,您可以从仓库安装和更新 Docker。

1
2
3
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

设置 Docker 安装地址

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库,之后,您可以从仓库安装和更新 Docker,使用以下命令来设置稳定的仓库(阿里云)

1
2
3
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装 Docker Engine-Community

安装最新版本的 Docker Engine-Community 和 containerd

1
sudo yum install -y docker-ce docker-ce-cli containerd.io

如果提示您接受 GPG 密钥,请选是。

Docker 安装完默认未启动,并且已经创建好 docker 用户组,但该用户组下没有用户。

Docker 镜像加速

阿里云镜像获取地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,登陆后,左侧菜单选中镜像加速器就可以看到你的专属地址了:

image-20230921100901178

配置daemon.json

您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

启动 Docker

1
systemctl start docker
验证docker服务

通过运行 hello-world 映像来验证是否正确安装了 Docker Engine-Community 。

1
docker run hello-world

运行该代码后首先会从仓库拉取hello-world镜像,然后运行该镜像,运行后会输出一个Hello from Docker!

image-20230921100920605

开启Docker自动补全

使用docker时无法自动补全镜像名和其他参数,这样使用效率大大降低,下面是解决方法

bash-complete
1
yum install -y bash-completion
刷新文件
1
2
source /usr/share/bash-completion/completions/docker
source /usr/share/bash-completion/bash_completion
测试

输入docker p后docker会将可选项列出来 [tab键]

1
docker p

image-20230921100914275

Docker架构

Docker总体架构为c/s架构,模块之间松耦合,包含了Client, Daemon, Registry, Graph, Driver, Libcontainer以及Docker Container

image-20230921100934592

Docker的组件

  • Docker Client 是用户界面,它支持用户与Docker Daemon之间通信
  • Docker Daemon Docker最核心的后台进程,运行于主机上,处理服务请求
  • Docker registry是中央registry,支持拥有公有与私有访问权限的Docker容器镜像的备份
  • Docker Containers负责应用程序的运行,包括操作系统、用户添加的文件以及元数据
  • Docker Images是一个只读模板,用来运行Docker容器
  • DockerFile是文件指令集,用来说明如何自动创建Docker镜像

Docker 基本概念

Docker 包括三个基本概念:

镜像

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等),镜像不包含任何动态数据,其内容在构建之后也不会被改变。

分层存储

​ 镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。

Union FS

​ 联合文件系统是(Union FS)是linux的存储技术,也是Docker镜像的存储方式, 它是分层的文件系统,将不同目录拉到同一个虚拟目录下,下图展示了Docker用Union FS 搭建的分层镜像:(比如最下层是操作系统的引导,上一层是Linux操作系统,再上一层是Tomcat,jdk,再上一层是应用代码)

这些层是只读的,加载完后这些文件会被看成是同一个目录,相当于只有一个文件系统。

image-20230921100940815

​ 我们可以通过docker info查看镜像的一些信息,可以看到存储驱动用的并不是Union FS 而是overlay2,overlay也是一个联合文件系统,所以上述主要介绍联合文件系统的概念,至于这些存储驱动的演变过程和优缺点

image-20230921100945881

镜像构建时,会一层层构建,前一层是后一层的基础,每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。(比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除,在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像,因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉)

容器

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体,容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间,因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。

​ 容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样,这种特性使得容器封装的应用比直接在宿主运行更加安全,也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。

​ 前面讲过镜像使用的是分层存储,容器也是如此,每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层

​ 容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡,因此,任何保存于容器存储层的信息都会随容器删除而丢失。

仓库

镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。

​ 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本,我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像,如果不给出标签,将以latest作为默认标签。

​ 以 Ubuntu 镜像为例,ubuntu是仓库的名字,其内包含有不同的版本标签,如,14.04,16.04,我们可以通过ubuntu:14.04,或者ubuntu:16.04来具体指定所需哪个版本的镜像,如果忽略了标签,比如ubuntu,那将视为ubuntu:latest

docker.io/library + nginx 默认拉取

​ 仓库名经常以三段式路径形式出现,比如heima.com/nginx-proxy:tag,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名,但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。

Docker部署微服务

场景介绍

我们使用Docker完成一个微服务的搭建过程

整体架构如下

image-20230921100952820

使用多个微服务进行项目部署测试

整体服务说明

我们总共涉及到三个微服务以及两个中间件

服务名称 描述
mysql 数据库服务
nacos 注册中心
learn-docker-gateway 网关服务
learn-docker-web API接口服务
learn-docker-storage 存储服务
配置文件提取

因为我们在开发中需要频繁修改application.yml文件我们将配置项配置到pom文件中打包时自动打到配置文件,这样可以用一个pom文件控制多个不同的服务的配置文件项的修改

pom文件定义属性

我们需要在总pom文件定义全局配置,例如nacosmysql等配置

1
2
3
4
<properties>
<mysql.addr>192.168.64.153:3306</mysql.addr>
<nacos.addr>192.168.64.153:8848</nacos.addr>
</properties>
配置编译选项

在子项目的pom文件的build构建配置中使用<filtering>true</filtering>配置,这样就可以将我们的总pom中的配置编译进配置文件了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
配置文件配置

在子项目的application.yml配置文件中注意使用@xxx@占位符来配置编译占位配置,下面的配置就是用了@@占位符编译后会将pom中的配置编译到配置文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server:
port: @project.server.prot@
spring:
application:
name: learn-docker-storage
######################### 数据源连接池的配置信息 #################
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://@db.addr@/employees?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
initialSize: 10
minIdle: 20
maxActive: 100
maxWait: 60000
#### nacos 配置#######
cloud:
nacos:
server-addr: @nacos.addr@
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.heima.module.p
编译测试

配置完后编译项目后,可以进入target目录下查看编译后的配置文件

image-20230921101713657

我们看到maven已经帮我们将配置编译进了配置文件中了

安装MySQL

MySQL简介

​ MySQL 是世界上最受欢迎的开源数据库。凭借其可靠性、易用性和性能,MySQL 已成为 Web 应用程序的数据库优先选择。

查找MySQL镜像

我们可以使用 docker search 镜像名称,命令来搜索镜像

1
docker search mysql

image-20230921101027819

参数解释

搜索出来的有这么多镜像,怎么选择呢

  • NAME: 镜像仓库源的名称
  • DESCRIPTION: 镜像的描述
  • OFFICIAL: 是否 docker 官方发布
  • stars: 类似 Github 里面的 star,表示点赞、喜欢的意思。
  • AUTOMATED: 自动构建。

根据参数,我们一般选择 官方发布的,并且stars多的。

下载镜像

可以使用docker pull 镜像名称来拉取镜像,我们选择了第一个Mysql的镜像,我们把它给拉取下来

1
docker pull mysql

image-20230921101033537

注意事项
  • 如果不写版本号默认拉取最新的版本好latest
  • 拉取的时候是多个层一起拉取的,这样可用让其他镜像复用分层
  • 如果拉取的镜像不写仓库地址默认就是docker.io/library/
下载指定版本的镜像

我们上面下载的镜像不符合我们的预期,我们需要Mysql5.7的镜像

查找指定镜像

可以登录 https://hub.docker.com 地址搜索镜像

image-20230921101040215

输入需要搜索的镜像名称,并选择对应镜像名称

image-20230921101046096

选择tag标签,这个就是版本的信息

image-20230921101051902

找到符合的版本 的mysql镜像,这里我们选择5.7.33

image-20230921101058639

下载指定版本镜像

刚才我们已经找到了5.7.33版本的Mysql的镜像,我们使用docker pull命令下载,镜像后面跟的是tag也就是版本名称

1
docker pull mysql:5.7.34

image-20230921101104078

我们发现,我们的新拉取的mysql镜像共用了 部分分层

查看镜像

安装完镜像后,我们需要看一下我们的本地镜像,使用docker images 可用查看本地镜像

1
docker images

image-20230921101109155

这里有两个镜像,一个是最新版本的一个是我们刚才下载的5.7.33版本的

MySQL配置
配置MySQL忽略大小写

创建MySQL挂载目录,等会会解释什么是挂载路径

1
2
3
4
# 创建MySQL配置的文件夹
mkdir -p /tmp/etc/mysql
# 编辑my.cnf配置文件
vi /tmp/etc/mysql/my.cnf

配置MySQL忽略大小写,在我们创建的MySQL配置文件挂载点的目录的my.cnf文件加入如下内容

1
2
[mysqld]
lower_case_table_names=1
创建MySQL数据目录

因为默认MySQL启动后他的文件是在容器中的,如果我们删除容器,数据也将会消失,我们需要将数据挂载出来。

1
2
#创建mysql存储的目录
mkdir -p /tmp/data/mysql
启动MySql

使用docker run启动容器

1
docker run -d -p 3306:3306 -v /tmp/etc/mysql:/etc/mysql/mysql.conf.d/ -v /tmp/data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql mysql:5.7.34

image-20230921101116491

到这里MySQL已经后台运行了

参数解释
  • -d:是指容器后台运行,如果不加-d,当用户断开客户端时容器会结束运行
  • -p:将容器的3306端口映射到主机的3306端口,用来暴漏端口的
  • -v:这个命令是用来挂载目录的,将本地目录挂载到容器中,这样容器操作的就是本地目录
  • -e:这个命令是配置环境参数的,这里MYSQL_ROOT_PASSWORD=root指的是用root用户运行mysql,可以登录Docker容器通过ENV命令查看
  • –name:这个命令是配置Mysql的容器名称的,如果不配置,默认是随机生成的名字
检查MySQL运行状态

现在并不能确认MySQL的运行状态,我们需要去看下

查看容器运行状态

使用docker ps可以看到运行中的容器

1
docker ps

执行命令后,我们看到mysql的服务已经起来了

image-20230921101123400

查看MySQL运行日志

现在MySQL容器起来了,并不代表MySQL已经成功启动,我们需要使用docker logs命令查看MySQL的日志

1
docker logs -f mysql

该命令可以查看容器日志-f是追踪打印日志,可以看到MySQL已经启动了

image-20230921101130739

客户端连接MySQL

因为我们已经暴漏端口了,可以使用客户端工具连接MySQL

image-20230921101137849

检查配置的大小写参数是否生效

1
SHOW VARIABLES LIKE '%case_table%';

image-20230921101143559

查看容器挂载的配置文件

可以通过docker exec -ti mysql /bin/bash命令登录容器,检查挂载的配置文件情况

1
2
# 登录容器
docker exec -ti mysql /bin/bash

该命令含义是在mysql容器中以

image-20230921101150205

我们可以看到我们修改的配置文件已经被挂载到了docker内部,这里面用到了exec命令,主要是在docker容器中运行命令,下面我们介绍下

命令格式

主要是在deocker容器中运行命令

1
docker exec [options] container command [arg...]

命令参数

名称 简写 描述
–detach -d 后台运行模式,在后台执行命令相关命令
–detach-keys 覆盖容器后台运行的一些参数信息
–env -e 设置环境变量
–interactive -i 展示容器输入信息STDIN
–privileged 为命令提供一些扩展权限
–tty -t 命令行交互模式
–user -u 设置用户名(format: <name|uid>[:<group|gid>])
–workdir -w 指定容器内的目录
查看挂载的数据文件

可以看下挂载的数据文件是否存在

1
cd /tmp/data/mysql/ && ll

image-20230921101159774

我们看到数据文件已经写入了

MySQL准备数据

MySql需要导入一些数据用来操作,我们用MySQL官方提供的数据库来操作

下载并导入数据
下载MySQL测试数据库

测试数据官网 下载数据库文件

image-20230921101217509

直接导入无法导入,需要编辑employees.sql文件的一些地方

  1. set storage_engine = InnoDB;修改为: set default_storage_engine = InnoDB;
  2. select CONCAT('storage engine: ', @@storage_engine) as INFO;修改为:select CONCAT('storage engine: ', @@default_storage_engine) as INFO;
1
2
3
4
5
6
7
8
9
  .....
set default_storage_engine = InnoDB;
-- set storage_engine = MyISAM;
-- set storage_engine = Falcon;
-- set storage_engine = PBXT;
-- set storage_engine = Maria;

select CONCAT('storage engine: ',@@default_storage_engine) as INFO;
.....
导入测试数据

下载解压后,在本地执行命令导入到mysql服务器

1
mysql -uroot -h192.168.64.152 -p < employees.sql

image-20230921101223874

客户端检查数据

在登陆客户端就能看到数据库以及表了

image-20230921101231497

安装部署nacos

​ Nacos是阿里巴巴开源的一款支持服务注册与发现,配置管理以及微服务管理的组件。用来取代以前常用的注册中心(zookeeper , eureka等等),以及配置中心(spring cloud config等等),Nacos是集成了注册中心和配置中心的功能,做到了二合一。

直接运行服务

可以直接用docker 启动服务,镜像不存在会自动拉取

1
docker run -d -p 8848:8848 --name nacos --env MODE=standalone nacos/nacos-server

image-20230921101237247

运行容器后可以稍等下,等待nacos启动成功,受环境限制启动可能有些慢

登录页面测试

可以登录服务器测试以下

用户名:nacos 密码:nacos

image-20230921101244436

微服务打包镜像

我们准备将一个微服务打包成Docker镜像,在各种服务器中进行运行,改为服务支持进行查询用户信息

提前说明

因为我们刚开始进行学习Docker,先从单个微服务开始学习,我们就先部署learn-docker-storage服务,后面随着课程的深入在慢慢增加其他的微服务

image-20230921101253035

访问测试

在idea中运行learn-docker-storage

image-20230921101302744

我们配置的端口是8003,我们可以直接访问http://localhost:8003/storage/employe/findByID/10001

image-20230921101307679

打包上传微服务

learn-docker-storage服务打包后上传到服务器

注意配置项

这里需要检查下maven配置的编译属性是否正确

1
2
<mysql.addr>192.168.64.153:3306</mysql.addr>
<nacos.addr>192.168.64.153:8848</nacos.addr>

我们发现是我们容器中启动的地址

上传打包后的微服务

将target目录中打包完成的微服务上传到服务器

image-20230921101317049

创建DockerFile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

创建一个Dockerfile文件

1
vi Dockerfile

具体内容如下

1
2
3
4
5
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD learn-docker-storage-1.0-SNAPSHOT.jar app.jar
EXPOSE 8003
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
命令解释
  • FORM:定制的镜像都是基于 FROM 的镜像,这里的 openjdk 就是定制需要的基础镜像,后续操作都是基于openjdk
  • VOLUME:挂载一个数据卷,这里因为没有名称,所以是一个默认的数据卷(后面详细解释)
  • ADD:添加一层镜像到当前镜像,这里就是添加SpringBootTest镜像到当前层,并改名app.jar
  • EXPOSE:暴漏端口,因为我们的自己的端口是8003,所以我们暴漏8003
  • ENTRYPOINT:设定容器启动时第一个运行的命令及其参数,这里就是容器以启动就执行 java -jar /app.jar
打包镜像

写好DockerFile后就需要用docker build命令来构建我们的镜像了,这样就可以将我们的微服务打包成一个镜像了

构建命令格式

构建一个镜像需要使用以下命令

1
docker bulid -t 仓库名/镜像名:tag .

参数解释

  • -t: 镜像的名字及标签,一般命名规则是 仓库名/镜像名:tag,
    • 仓库名:一般是私服或者dockerhub等地址,如果忽略默认就是dockerhub的地址docker.io.library/
    • 镜像名称:就是我们的自己的服务名称,可以随意命名
    • tag:就是我们的版本号
  • **.**:这个 . 表示当前目录,这实际上是在指定上下文的目录是当前目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
实战操作

​ 一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下,如果该目录下没有所需文件,那么应该把所需文件复制一份过来。

​ 一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

当前目录的结构如下

image-20230921101328254

构建镜像

进入Dockerfile的目录,通过如下命令构建镜像

1
docker build -t learn-docker-storage:0.0.1 .

构建完成如果出现Successfully说明已经构建成功了,

image-20230921101332940

查看我们构建的镜像

使用docker images命令查看我们构建完成的镜像

1
docker images

我们可以看到我们的镜像就在第一个位置

image-20230921101506116

运行镜像

刚才已经打包完成了镜像,现在我们运行我们自己的镜像

1
2
3
4
# 运行容器
docker run -d -p 8003:8003 learn-docker-storage:0.0.1
# 查看运行中的容器
docker ps

image-20230921101527064

参数最后的learn-docker-storage:0.0.1是镜像的名称,如果启动容器可以使用镜像名称或者镜像ID

参数解释
  • -d:后台运行
  • -p:映射端口,将宿主机的8080端口映射到docker内部的8080端口上
查看启动日志

使用docker logs 容器ID来查看启动日志

1
docker logs -f 74c239792266

image-20230921101531902

尝试访问服务

通过curl 命令来访问服务

1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230921101537114

我们发现服务调用成功了,我们基本实现了微服务改造为docker方式并运行

删除容器

要删除一个容器首先需要将一个容器停止掉

停止容器

我们要把刚才运行的容器停止掉,使用docker stop 容器ID 停止一个容器

1
docker stop 3752f7088a04

image-20230921101542569

停止容器后,我们在通过进程查看是看不到容器的,但是容器还是存在我们的服务中的

查看全部容器

通过docker ps 只能看到运行中的容器,但是我们停止的容器是看不到的,可以加上-a 查看所有的容器

1
docker ps -a

image-20230921101547151

我们可以看到 加上-a参数可以看到刚才已经停止掉的容器

启动停止的容器

想要启动一个停止的容器可以使用docker start 容器ID

1
docker start 3752f7088a04

image-20230921101551726

这样就把刚才已经停止的容器启动了

删除容器

已经停止的容器可以使用docker rm 容器ID删除容器,但是对于运行中的容器可以加上-f参数强制删除

1
docker rm -f 3752f7088a04

image-20230921101556500

这样可以将一个运行的容器强制删除,如果停止的容器可以通过通过docker rm删除

1
docker rm 3752f7088a04

这个时候就把容器给删除掉了

日志挂载优化

存储卷优化
什么是存储卷

“卷”是容器上的一个或多个“目录”,此类目录可绕过联合文件系统与宿主机上的某个目录“绑定(关联)”;

image-20230921101602200

​ 在Docker中,要想实现数据的持久化(所谓Docker的数据持久化即数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中。

​ Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;(最常用的方式

存储卷优化写入速度

Docker镜像由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部加一个读写层;

image-20230921101611384

​ 如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW)”机制

image-20230921101617918

为了避免这种情况,构建Dockerfile的时候应该加入一个存储卷

Dockerfile增加存储卷
增加存储卷

编写Dockerfile增加存储卷,增加日志存储卷/logs,这会是一个匿名存储卷

1
2
3
4
5
FROM openjdk:8-jdk-alpine
VOLUME /tmp /logs
ADD learn-docker-storage-1.0-SNAPSHOT.jar app.jar
EXPOSE 8003
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
构建镜像

因为我们原来已经构建了镜像,这次使用版本号是 0.0.2

1
2
3
4
#构建镜像
docker build -t learn-docker-storage:0.0.2 .
# 查看镜像列表
docker images

image-20230921104602240

运行容器

通过run命令运行容器

1
2
3
4
# 运行容器
docker run -d -p 8003:8003 learn-docker-storage:0.0.2
# 查看运行中的容器
docker ps

image-20230921104610131

查看存储卷

通过docker volume ls可以看到存储卷

1
docker volume ls

image-20230921104615409

查看容器信息

我们发现都是匿名的存储卷,如何来确定都是那个呢,可以通过docker inspect 容器ID来查看存储新鲜

1
docker inspect 2041965c3e87|grep Mounts -A20

image-20230921104620479

通过这个命令可以看到数据卷的名称以及宿主机的存储路径,我们可以直接到宿主机打印日志

1
2
3
4
 # 进入文件挂载目录
cd /var/lib/docker/volumes/d35de1b7e4631908b05635db4c1f114ab3aafbdf25a9843c068696e66a779c75/_data
# 输出日志
tail -f learn-docker-storage.log

image-20230921104627572

这样就看到了我们的日志文件

验证存储卷

删除容器检查存储卷释放消失

1
2
3
4
5
6
# 查看运行的容器列表
docker ps
#删除容器
docker rm -f 2041965c3e87
#查看所有的容器列表(运行+停止)
docker ps -a

image-20230921104634120

我们看到容器已经被删除了,检查我们的存储卷

1
docker volume ls

image-20230921104639879

发现存储卷还存在,数据还是存在的,并且数据也存在

1
2
3
4
# 查看存储卷列表
docker volume ls
# 查看存储卷详情
docker inspect 296ccc64d919e86bb8329bf6b08447c2ea6a118458d3fcb86d5c7c9a3177dfe0

image-20230921104646356

重新运行镜像启动一个新的容器

1
2
3
4
# 运行容器
docker run -d -p 8080:8080 e1222496c69f
# 查看运行中的容器
docker ps

image-20230921104658720

启动容器后查看存储卷列表

1
2
# 查看存储卷列表
docker volume ls

image-20230921104703680

我们发现有创建了两个存储卷,查看容器详情

1
docker inspect 2041965c3e87|grep Mounts -A20

image-20230921104708977

我们发现新启动的容器新开了一个匿名存储卷

bind挂载共享存储
什么是bind

​ Bind mounts模式和Volumes非常相似,不同点在于Bind mounts模式是将宿主机上的任意文件或文件夹挂载到容器,而Volumes本质上是将Docker服务管理的一块区域(默认是/var/lib/docker/volumes下的文件夹)挂载到容器。

共享存储

经过上面的测试,我们发现每一个容器都是单独用一个存储卷,用于临时文件没有问题的,但是如果要让容器都用同一个存储路径怎么办呢,这个时候就用到了 bind挂载了,可以使用-v进行挂载挂载刚才的存储卷。

1
2
3
4
5
6
# 级联创建文件夹
mkdir -p /tmp/data/logs
# 运行容器,指定挂载路径
docker run -d -v /tmp/data/logs:/logs \
-p 8003:8003 --name learn-docker-storage \
learn-docker-storage:0.0.2

这里面--name是指定docker容器的名称,我们操作容器就可以使用名称进行操作了

image-20230921104718360

然后使用docker inspect命令来检查容器详情

1
docker inspect learn-docker-storage|grep Mounts -A20

image-20230921104826797

我们发现挂载日志的挂载方式已经变了,由原来的volume变为了bind,并且挂载路径变为了我们自己定义的路径,进入目录查看

1
2
3
4
# 进入目录并浏览目录文件
cd /tmp/data/logs/&&ll
# 打印日志详情
tail -f learn-docker-storage.log

image-20230921104831212

验证共享存储

我们也按照上面步骤验证下bind方式挂载的存储,先删除容器,检查日志文件是否存在

1
2
3
4
5
6
# 停止并删除容器
docker rm -f learn-docker-storage
# 查看容器已经被删除了
docker ps -a
# 进入日志挂载路径查看日志是否存在
cd /tmp/data/logs/&&ll

我们发现容器被删除但是日志文件还存在本地

image-20230921104837009

启动一个新的容器

1
2
3
4
5
6
# 运行容器,指定挂载路径 
docker run -d -v /tmp/data/logs:/logs \
-p 8003:8003 --name learn-docker-storage \
learn-docker-storage:0.0.2
# 查看日志文件
cat learn-docker-storage.log

我们发现新的容器的日志文件追加进来了

image-20230921104842353

我们发现日志已经追加,我们让不同的容器挂载同一个目录了

volume和bind的区别

对于多个容器需要共享访问同一数据目录,或者需要持久化容器内数据(如数据库)时,我们都是采用挂载目录形式(bind mounts),将宿主机的某一目录挂载到容器内的指定目录,这种方式能解决问题,但这种方式也一直有一些缺点

  • 容器在不同的服务器部署需要根据实际磁盘挂载目录修改路径
  • 不同操作系统的文件和目录权限会搞得你昏头转向,火冒三丈 ?

​ bind mount和volume其实都是利用宿主机的文件系统,不同之处在于volume是docker自身管理的目录中的子目录,所以不存在权限引发的挂载的问题,并且目录路径是docker自身管理的,所以也不需要在不同的服务器上指定不同的路径,你不需要关心路径。

image-20230921104848426

清理volume挂载

volume挂载方式,会生成很多匿名的目录,我们可以找到对应的没有使用的volume进行删除

1
docker volume ls

image-20230921104853734

通过查看我们发现里面,有很多的volume,我们可以找到没有用的删除

1
docker volume rm volume_name

image-20230921104900165

还可以通过命令将没有引用的全部volume清除掉,但是这个命令很危险

1
docker volume prune

image-20230921104904610

这样就将我们服务器上无效的volume清除掉了

网络优化

Docker网络原理

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

​ Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器,如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。

Docker网络模式
Docker网络模式 配置 说明
host模式 –net=host 容器和宿主机共享Network namespace。
container模式 –net=container:NAME_or_ID 容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。
none模式 –net=none 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。
overlay模式 – driver overlay Docker跨主机通信模式,使用分布式计算机架构后需要使用overlay网络模式
bridge模式 –net=bridge (默认为该模式)
host模式

​ 如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。

​ 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口,但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

​ 使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。

Host模式如下图所示

image-20230921104910768

不推荐,会占用宿主机的端口,其他服务想用用不了

container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享

​ 新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等,同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的,两个容器的进程可以通过 lo 网卡设备通信

Container模式示意图

image-20230921104939736

none模式

​ 使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置,也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

​ 这种网络模式下容器只有lo回环网络,没有其他网卡,none模式可以在容器创建时通过–network=none来指定,这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

None模式示意图

image-20230921105048426

一般用来跑定时任务,不需要外部访问的服务

overlay模式

image-20230927215821171

​ 容器在两个跨主机进行通信的时候,是使用overlay network这个网络模式进行通信,如果使用host也可以实现跨主机进行通信,直接使用这个物理的ip地址就可以进行通信,overlay它会虚拟出一个网络比如10.0.9.3这个ip地址,在这个overlay网络模式里面,有一个类似于服务网关的地址,然后把这个包转发到物理服务器这个地址,最终通过路由和交换,到达另一个服务器的ip地址。

bridge模式

​ 当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上,虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中

​ 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关,在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看

​ bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。

bridge模式如下图所示

image-20230927214601005

我们的网络结构

下图是我们自己的网络结构,我们是通过宿主机访问Mysql容器的,刚才我们学过,默认Docker已经接入了一个名字叫bridge的桥接网络

image-20230927214605474

我们可以让我们的网络直接接入桥接网络,例如下图

image-20230927214609303

创建网络
查看网络列表

可以通过docker network ls命令查看网络列表

1
2
# 查看网络列表
docker network ls

image-20230927214613204

上面就是容器默认几种网络

创建一个桥接网络

默认容器启动会自动默认接入bridge的桥接网络,为了区分我们的服务也防止各种网络问题,我们创建一个专用网络,可以通过docker network create 网络名称来创建一个默认的桥接网络

1
2
3
4
# 创建一个桥接网络
docker network create learn-docker-network
# 查看网络列表
docker network ls

image-20230927214617166

服务接入网络
停止并删除原有容器

停止和删除我们的微服务以及mysql服务

1
2
# 删除当前运行中的容器
docker rm -f learn-docker-storage nacos mysql

image-20230927214620190

创建MySQL

因为我们的微服务依赖MySQL先启动MySQL并接入网络,因为MySQL不需要通过宿主机访问,所有也不需要映射端口了,–network 是配置接入哪一个网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run -d \
-v /tmp/etc/mysql:/etc/mysql/mysql.conf.d/ \
-v /tmp/data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
--name mysql --network=learn-docker-network \
mysql:5.7.34

# 也可以指定端口的

docker run -d -p 3306:3306 \
-v /tmp/etc/mysql:/etc/mysql/mysql.conf.d/ \
-v /tmp/data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
--name mysql --network=learn-docker-network \
mysql:5.7.38

image-20230927214624381

这样我们就把我们的MySQL服务启动起来了并且加入了learn-docker-network的网络

创建Nacos

我们的nacos是需要暴漏端口的,因为我们需要外部能够看到nacos页面,但是我们也需要我们的nacos连接到当前网络

1
2
3
4
docker run -d -p 8848:8848 \
--network=learn-docker-network \
--name nacos --env MODE=standalone \
nacos/nacos-server

image-20230927214628133

查看网络详情

可以通过docker network inspect 网络名称可以查看当前的网络的详细信息

1
docker network inspect learn-docker-network|grep Containers -A 20

image-20230927214632188

修改微服务配置

因为需要使用自定义网络访问mysql容器以及nacos容器,需要修改微服务数据库连接地址,

​ docker 网络访问 可以通过IP或者通过服务名称都是可以的,这里我们通过服务名称访问,因为我们使用了maven打包的方式,我们只需要将pom文件修改就可以

1
2
3
4
<properties>
<mysql.addr>mysql:3306</mysql.addr>
<nacos.addr>nacos:8848</nacos.addr>
</properties>

修改完成后进行编译项目

image-20230927214636685

这里将数据库连接地址改为mysql容器的服务名称mysql,nacos的连接地址变为了nacos

重新打包服务

将打包的文件上传服务器后按照上面步骤同上面打包,打包版本为 0.0.3

1
docker build -t learn-docker-storage:0.0.3 .

image-20230927214642056

创建微服务

下面就按部就班的创建微服务就可以,只是注意需要加入网络,这里这个端口需要映射外网访问

1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
learn-docker-storage:0.0.3

image-20230927214646551

测试微服务

到现在微服务已经启动,我们尝试访问以下

1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230927214650332

访问测试数据没有问题,到现在我们服务已经搭建完成,并且使用网络进行了优化

image-20230921101701800

Docker 集群管理

镜像仓库管理

docker仓库,用来管理镜像

​ 主要分为公共仓库和私人仓库,下面介绍了公共仓库Docker Hub、私人仓库Registry和harbor

DockerHUb仓库管理

什么是DockerHUb

保存和分发镜像的最直接方法就是使用 Docker Hub

​ Docker Hub 是 Docker 公司维护的公共 Registry,用户可以将自己的镜像保存到 Docker Hub 免费的 repository 中,如果不希望别人访问自己的镜像,也可以购买私有 repository

账号注册和登陆

一般,你需要先在docker中心创建一个账户(如果您尚未有),你可以直接在Docker Hub创建你的账户。

image-20230927214657333

如果有已有账号可以点击sign in 进行登录,登陆后是这个样子

image-20230927214701224

Docker客户端登录

使用docker login登录dockerhub

​ 这将提示您输入用户名,这个用户名将成为你的公共存储库的命名空间名称。如果你的名字可用,docker会提示您输入一个密码和你的邮箱,然后会自动登录到Docker Hub,你现在可以提交和==推送镜像==到Docker Hub的你的存储库。

1
docker login

image-20230927214704666

出现 Login Succeeded就说明我们登录成功

注:你的身份验证凭证将被存储在你本地目录的.dockercfg文件中

管理镜像

通过docker images可以看到我们所有的镜像列表

1
docker images

image-20220510201030747

删除镜像

我们现在的learn-docker-storage有三个版本,现在我们把前两个有问题的版本删除,docker rmi 镜像ID可以删除镜像

1
docker rmi learn-docker-storage:0.0.1 learn-docker-storage:0.0.2

image-20230927214711554

这样我们就删除了我们没有用的镜像了,可以节省内存空间

修改镜像命名

修改镜像的 repository 使之与 Docker Hub 账号匹配

​ Docker Hub 为了区分不同用户的同名镜像,镜像的 registry 中要包含用户名,完整格式为:[用户名]/镜像名:tag

我们通过 docker tag 命令重命名镜像

1
docker tag learn-docker-storage:0.0.3 baiyp/learn-docker-storage:0.0.3

image-20230927214715777

这样就将我们的镜像改名了,这个就符合我们的dockerhub的规范了

推送镜像
推送镜像

现在我们要将我们的镜像推送到docker hub

推送镜像的规范是docker push 注册用户名/镜像名:tag,因为我们上面已经把镜像名字改正确了,所以可以直接推送。

1
docker push baiyp/learn-docker-storage:0.0.3

image-20230927214719358

这样我们就将我们的数据推送到docker hub,我们发现只有最顶层的镜像推送了,openjdk的镜像并没有推送,直接复用了仓库的,这就是分层的好处

检查镜像

我们可以到docker hub检查我们的镜像

image-20230927214723331

可以看到我们的镜像已经推送过来了,点开详情可以看到我们镜像的内容以及tag号

image-20230927214727141

仓库镜像测试
删除本地镜像

可以通过docker rmi 镜像ID删除本地镜像

1
docker rmi baiyp/learn-docker-storage:0.0.3 learn-docker-storage:0.0.3

image-20230927214731773

再次查看本地镜像,已经没有了我们的微服务的镜像

1
docker images

image-20230927214736289

从仓库拉取镜像

这个时候可以从docker hub拉取镜像

1
docker pull baiyp/learn-docker-storage:0.0.3

image-20230927214740659

这个时候已经将镜像拉取下来了,我们可以运行镜像了

运行镜像

执行下面的命令进行创建镜像

1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
baiyp/learn-docker-storage:0.0.3

image-20230927214744029

访问测试

我们访问下,检查下是否可以正常运行

1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230927214748806

到这里我们就完成dockerhub仓库的发布与拉取

直接运行测试

在真实环境中,我们一般不会拉取在运行,一般都是直接运行,如果docker检查镜像不存在会自动拉取

停止服务并删除镜像

1
2
docker rm -f learn-docker-storage
docker rmi baiyp/learn-docker-storage:0.0.3

image-20230927214752793

我们直接运行容器

1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
baiyp/learn-docker-storage:0.0.3

image-20230927214756916

这一个run命令就解决了容器的拉取以及容器运行的问题

registry仓库管理

registry简介

​ 官方提供了Docker Hub网站来作为一个公开的集中仓库。然而,本地访问Docker Hub速度往往很慢,并且很多时候我们需要一个本地的私有仓库只供网内使用。

​ Docker仓库实际上提供两方面的功能,一个是镜像管理,一个是认证。前者主要由docker-registry项目来实现,通过http服务来上传下载;后者可以通过docker-index(闭源)项目或者利用现成认证方案(如nginx)实现http请求管理。

​ docker registry 就是管理 docker 镜像的服务, Docker 公司维护的 registry 就是 http://hub.docker.com ,它可以让我们方便的下载预先做好的镜像。

安装registry

我们可以通过获取官方的 registry 镜像来运行。

​ 这将使用官方提供的 registry 镜像来启动私有仓库,默认情况下,仓库会被创建在容器的 /var/lib/registry 目录下。我们可以通过 -v 参数将镜像文件存放在本地的指定路径。

1
2
3
4
5
6
# mkdir /tmp/data/registry
docker run -d \
-p 5000:5000 \
-v /tmp/data/registry:/var/lib/registry \
--restart=always \
registry

image-20230927214802456

这样我们的registry已经启动起来了

访问测试

这时我们可以通过浏览器访问 http://ip:5000/v2/_catalog 查看仓库是否启动成功。

1
curl http://192.168.64.152:5000/v2/_catalog

image-20230927214806760

上传镜像

registry 上传镜像的命名规范是 仓库IP:5000/镜像名称:tag

修改镜像名称

将我们的镜像改成服务规范的名字

1
docker tag chenzhijie2021/learn-docker-storage:0.0.1 124.223.105.177:5000/learn-docker-storage:0.0.1

image-20230927214810488

推送镜像

使用命令推送镜像

1
docker push 124.223.105.177:5000/learn-docker-storage:0.0.1

我们发现推送报错了,这是因为docker推送默认使用的https的方式,而我们的registry只支持http的方式

image-20230927214814362

修改Docker推送配置

对于 Linux 系统,我们可以在 /etc/docker/daemon.jsondaemon.josn 文件不存在则新建该文件)

1
vi /etc/docker/daemon.json

添加下面的配置

1
{ "insecure-registries": ["仓库IP:5000"] }

完整的配置如下

1
2
3
4
{
"insecure-registries": ["192.168.64.153:5000"],
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}

执行以下命令重启重新加载配置并生效

1
2
systemctl daemon-reload
service docker restart

image-20230927214818616

再次进行推送

执行命令再次推送

1
docker push 192.168.64.153:5000/learn-docker-storage:0.0.3

image-20230927214824643

我们发现这次推送成功了

再次访问registry

访问测试检查是否已经推送

1
curl http://192.168.64.153:5000/v2/_catalog | python -m json.tool

image-20230927214833844

我们发现我们的镜像已经推送到了registry,我们通过以下URL访问下tag列表

1
curl http://192.168.64.153:5000/v2/learn-docker-storage/tags/list | python -m json.tool

image-20230927214837816

registry镜像测试
删除本地镜像

可以通过docker rmi 镜像ID删除本地镜像

1
docker rmi baiyp/learn-docker-storage:0.0.3 192.168.64.153:5000/learn-docker-storage:0.0.3

image-20230927214841740

运行registry中的镜像
1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
192.168.64.153:5000/learn-docker-storage:0.0.3

image-20230927214845332

我们发现自己的registry很快就拉取并且运行起来了

访问微服务测试
1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230927214849719

使用Harbor管理仓库

什么是Harbor

harbor是一个由vm公司开源的企业级容器镜像仓库,有以下功能

  • 管理用户界面
  • 基于角色的访问控制
  • LDAP/AD 集成及日志审计等基本运维操作

​ harbor是构建企业级私有docker镜像的仓库的开源解决方案,它是 Docker Registry的更高级封装,它除了提供友好的Web UI界面,角色和用户权限管理,用户操作审计等功能外,它还整合了K8s的插件(Add-ons)仓 库,即Helm通过chart方式下载,管理,安装K8s插件,而chartmuseum 可以提供存储chart数据的仓库。

​ 另外它还整合了两个开源的安全组件,一个是Notary,另一个是Clair,Notary类似 于私有CA中心,而Clair则是容器安全扫描工具,它通过各大厂商提供的 CVE漏洞库来获取最新漏洞信息,并扫描用户上传的容器是否存在已知的 漏洞信息,这两个安全功能对于企业级私有仓库来说是非常具有意义的。

Harbor的三种安装方式

这里我们使用离线安装

  • 在线安装:从Docker Hub下载Harbor相关镜像,因此安装软件包非常小
  • 离线安装:安装包包含部署的相关镜像,因此安装包比较大
  • OVA安装程序(第三方):当用户具有vCenter环境时,使用此安装程序,在部署 OVA后启动Harbor
为什么使用私用仓库

公司的项目一般不予许我们上传到 Docker Hub 这类的公共仓库中,所有学会创建一个私有仓库也是非常必要的

​ 虽然hub.docker.com上可以保存镜像,但是网速相对较慢,在内部环境中搭建一个私有的公共仓库是个更好的方案。

harbor 的基本组件
组件 功能
harbor-adminserver 配置管理中心
harbor-db 数据库
harbor-jobservice 镜像复制
harbor-log 日志操作
harbor-ui Web管理页面和API
nginx 前端代理,负责前端页面和镜像上传/下载转发
redis 会话
registry 镜像存储
前置工作
下载安装包

Harbor官方地址:https://github.com/goharbor/harbor/releases 下载最新版安装包

image-20230927214856372

准备环境

需要安装docker以及docker-compose的环境上面我们已提前安装了

  • 安装Docker环境
  • 安装docker-compose环境
离线安装
解压安装包

解压harbor的安装包

1
tar -zxf harbor-offline-installer-v2.1.4.tgz

进入目录 然后将harbor.yml.tmp复制一份并该命为harbor.yml

1
2
cd harbor
cp harbor.yml.tmpl harbor.yml

image-20230927214902255

注意: 这里跟老版本不一样,没有了harbor.cfg文件,我们需要手动复制harbor.yml.tmpl在做修改即可

修改harbor.yml

harbor作为私有仓库作用在公司内网,一般都是信任关系,没多大必要做https,使用http即可! 所以 把https相关的已经注释掉

image-20230927214905343

并注意配置文件的用户名密码配置,默认是 用户名是:admin,密码是:Harbor12345,如果修改需要安装前修改

image-20220510204043388

加载本地镜像

使用docker load命令加载本地镜像,不用再从dockerhub下载了

1
docker load -i harbor.v2.1.4.tar.gz

image-20230927214912897

这样容器镜像就被加载到了本地,我们可以通过docker images命令查看导入的镜像

1
docker images

image-20230927214916752

执行安装命令

先执行预处理命令,会创建一些文件夹,初始化一些文件

1
./prepare

image-20230927214920444

然后开始真正的安装过程

1
./install.sh

image-20230927214924445

如果出现-Harbor has been installed and started successfully表示安装成功,并查看docker进程

1
docker ps

image-20230927214928573

可以看到很多服务已经起来了。

启动和停止harbor

在harbor的安装目录执行以下命令就可以启动和停止了

1
2
3
4
5
6
# 启动
docker-compose up -d
# 停止
docker-compose stop
# 重新启动
docker-compose restart
harbor使用
访问harbor

输入 http://harborIP就可以直接访问了,这里访问我们的地址http://192.168.64.153/

image-20230927214932956

输入用户名密码就可以登录了 ,如果没有修改配置文件 默认是 用户名是:admin,密码是:Harbor12345

image-20230927214936637

登录后就可以进行操作了

Docker登录harbor

使用docker login命令就可以登录harbor了

1
docker login -u admin -p Harbor12345 192.168.64.153

image-20230927214941725

我们发现登录报错了,这和registry一样,docker模式使用https方式,而我们使用的是http的方式登录

修改Docker配置

对于 Linux 系统,我们可以在 /etc/docker/daemon.jsondaemon.josn 文件不存在则新建该文件)

1
vi /etc/docker/daemon.json

添加下面的配置

1
{ "insecure-registries": ["harbor地址"] }

完整的配置如下

1
2
3
4
5
{
#因为默认端口号是80 所以不需要加端口号
"insecure-registries": ["192.168.64.153"],
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}

执行以下命令重启重新加载配置并生效

1
2
systemctl daemon-reload
service docker restart

image-20230927214946612

再次进行登录
1
docker login -u admin -p Harbor12345 192.168.64.153

这次我们就成功登录了

image-20230927214950262

到这里我们就已经登录成功了

修改镜像tag

我们推送镜像我要把我们的镜像名称改成符合规范的格式

1
2
3
docker tag \
192.168.64.153:5000/learn-docker-storage:0.0.3 \
192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927214953425

这里有一个library路径,是harbor默认的项目名称

image-20230927214956557

推送镜像

可以通过docker push进行推送镜像,注意需要先登录在进行推送

1
docker push 192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215000248

到这里我们已经推送到了harbor,我们可以登录library页面查看

image-20230927215003420

harbor 测试
删除本地镜像

可以通过docker rmi 镜像ID删除本地镜像

1
2
3
docker rmi \
192.168.64.153:5000/learn-docker-storage:0.0.3 \
192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215008189

运行harbor 中的镜像

执行运行命令

1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215011727

访问微服务测试
1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230927215017897

查看harbor详情

详情里面会显示拉取次数

image-20230927215022117

HarBor用户权限说明

HarBor用户角色在项目(项目-成员-新加成员)中为3类:项目管理员、开发人员、访客

  • 项目管理员:增删改查
  • 开发人员:上传和下载
  • 访客:只允许下载
HarBor权限配置
新建用户

点击创建用户可以创建一个用户

image-20230927215026265

我们创建一个itcast的用户

image-20230927215030221

用户授权

创建用户后进入项目模块添加成员

image-20230927215034961

选择成员后并选择权限

image-20230927215038902

访客只能进行拉取不能推送和管理

image-20230927215042987

用户登录测试

我们用itcast用户通过web端登录测试下,我们发现用户是没有修改权限的

image-20230927215046808

docker登录测试

登录docker客户端

1
docker login -u itcast -p Qwert123 192.168.64.153

image-20230927215050183

尝试推送镜像

1
docker push 192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215053676

我们发现是无法进行推送镜像的

拉取镜像测试

先删除本地的容器以及镜像

1
2
docker rm -f learn-docker-storage
docker rmi 192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215057534

尝试拉取并启动本地镜像

1
2
3
4
5
6
docker run -d \
-v /tmp/data/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215101072

微服务访问测试

1
curl http://192.168.64.153:8003/storage/employe/findByID/10001 | python -m json.tool

image-20230927215105396

Harbor支持Https(扩展)

为了支持微服务推送我们需要将HarBor设置为https,可以让HarBor在任何地方使用以及推送

生成SSL证书

前面说了怎么搭建harbor仓库,这里讲一讲harbor实现https访问,因为只需要内网访问,没必要去申请一个ssl证书,所以我就用openssl颁发自签名证书,实现https访问。

创建证书目录
1
mkdir -p /tmp/data/cert && cd /tmp/data/cert && ll

image-20230927215108943

创建 CA 根证书
1
openssl req  -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 365 -out ca.crt -subj "/C=CN/L=beijing/O=itcast/CN=harbor-registry"

这里subj是主题的意思含义如下

1
C=国家,S=省(市),L=区(县、市),O=组织机构,OU=组织单位,CN=通用名称
生成证书签名

生成一个证书签名, 设置访问域名为itcastharbor.com

1
openssl req -newkey rsa:4096 -nodes -sha256 -keyout itcastharbor.com.key -out server.csr -subj "/C=CN/L=beijing/O=itcast/CN=itcastharbor.com"
生成主机证书
1
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out itcastharbor.com.crt
操作步骤如下

image-20230927215113004

配置harbor.yml

然后进入harbor安装目录修改harbor.yml,修改下面几个选项

  • hostname,使用IP或域名,不要用回环地址,localhost等
  • certificate,yourdomain.com.crt的路径/tmp/data/cert/itcastharbor.com.crt
  • private_key,yourdomainr.com.key的路径/tmp/data/cert/itcastharbor.com.key

image-20230927215117489

重新安装harbor服务
停止harbor

停止运行中的服务

1
docker-compose down

运行目录harbor下的prepare完成https的配置

1
./prepare

image-20230927215121945

重新安装

在harbor目录下运行安装命令

1
./install.sh

image-20230927215126213

修改Docker推送配置

我们需要将推送的IP改成域名

1
vi /etc/docker/daemon.json

上文中我们对registry已经操作了,这里需要改用harbor,需要重新配置

1
2
#因为默认端口号是80 所以不需要加端口号
{ "insecure-registries": ["仓库IP或域名"] }

完整的配置如下

1
2
3
4
5
{
"insecure-registries": ["itcastharbor.com"],
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"]
}

执行以下命令重启重新加载配置并生效

1
2
systemctl daemon-reload
service docker restart
修改本地host文件

为了让本机能够正常访问到harbor的web环境需要配置本地的hosts文件增加如下配置

1
192.168.64.153 itcastharbor.com

windows环境下host路径在C:\Windows\System32\drivers\etc

域名访问harbor

通过域名访问harbor,域名就是我们刚才配置的itcastharbor.com域名访

image-20230927215131200

因为我们的证书是自签的,不是第三方认证的,素以有安全性提示,点击继续就可以访问

image-20230927215135268

到这里登录后就可以访问了

image-20230927215139446

微服务Docker打包

现在的微服务时代,你的代码没个微服务、分布式人家都会觉得低端,当然!对于我们开发人员来说,掌握这些技术意味着涨薪。

​ 我们项目中用到了多个微服务,我们上一节课程打包用的是手动上传,但是很麻烦,有没有更好的方式呢,是有的,我们可以直接通过idea将我们的微服务打包成Docker镜像,并推送到Docker仓库中

​ 这里我们采用jib-maven-plugin 来进行来构建容器化的spring boot应用程序,Jib可以让不写Dockerfile就能实现Docker打包

什么是Jib

Jib 是 Google 开发的可以直接构建 Java 应用的 Docker 和 OCI 镜像的类库,以 Maven 和 Gradle 插件形式提供。

​ Jib带来的是,它允许您通过简单地将插件添加到您选择的构建工具(Maven或Gradle)来创建容器,没有额外的文件,只需几行配置,它处理将应用程序打包到容器映像的所有步骤。

​ Jib是来自Google的开源Java容器,它允许Java开发人员使用他们所知道的Java工具构建容器,它不需要您编写Dockerfile或安装了docker,它直接集成到MavenGradle中

和传统的插件区别

Docker 构建流程

在“传统”Java到Docker映像构建流程中,我们需要安装Dockerfile和docker守护进程,在Jib构建流程中,您只需要插件项目的构建文件。

image-20230927215144378

Jib构建流程

​ 通过 Jib,Java 开发者可以使用他们熟悉的 Java 工具来构建容器。Jib 是一个快速而简单的容器镜像构建工具,它负责处理将应用程序打包到容器镜像中所需的所有步骤。它不需要你编写 Dockerfile 或安装 Docker,而且可以直接集成到 Maven 和 Gradle中 —— 只需要将插件添加到构建中,就可以立即将 Java 应用程序容器化。

image-20230927215150027

准备工作

设置Horbor用户权限

我们要将idea的微服务推送到Harbor,并且用itcast的用户,所有我们要设置我们的itcast用户是开发者

image-20230927215153342

pom文件配置jib

对于应用程序的基本本地存储镜像,请在pom.xml以下内容中配置jib-maven-plugin

公共属性配置

在properties中配置harbor的共有配置

1
2
3
4
5
6
7
8
9
10
<properties>
<!--harbor 仓库地址-->
<docker.registry.url>itcastharbor.com</docker.registry.url>
<!--harbor 的项目名称-->
<docker.registry.name>library</docker.registry.name>
<!--harbor账号-->
<docker.registry.username>itcast</docker.registry.username>
<!--harbor密码-->
<docker.registry.password>Qwert123</docker.registry.password>
</properties>
编译配置插件配置
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
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>

<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.8.0</version>
<configuration>
<allowInsecureRegistries>true</allowInsecureRegistries>
<!--from节点用来设置镜像的基础镜像,相当于Docerkfile中的FROM关键字-->
<from>
<!--使用openjdk官方镜像,tag是:8-jdk-alpine,表示镜像的操作系统是alpine,装好了jdk8-->
<image>openjdk:8-jdk-alpine</image>
</from>
<to>
<!--镜像名称和tag,使用了mvn内置变量${project.version},表示当前工程的version-->
<image>${docker.registry.url}/${docker.registry.name}/${project.artifactId}:${project.version}
</image>
<tags>
<!--版本号-->
<tag>${project.version}</tag>
</tags>
<!--harbor的认证信息-->
<auth>
<username>${docker.registry.username}</username>
<password>${docker.registry.password}</password>
</auth>
</to>
<!--容器相关的属性-->
<container>

<jvmFlags>
<!--一些启动参数-->
<jvmFlag>-Djava.security.edg=file:/dev/./urandom</jvmFlag>
</jvmFlags>
<!--挂载volume的配置-->
<volumes>
<volume>/tmp</volume>
<volume>/logs</volume>
</volumes>
<ports>
<!--暴漏端口号-->
<port>8080</port>
</ports>
<!--微服务的启动类-->
<mainClass>com.heima.test.Application</mainClass>
<format>OCI</format>
<!--使用该参数将镜像的创建时间与系统时间对其-->
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<!--执行打包配置-->
<execution>
<id>jib-maven-plugin</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Docker maven plugin -->
</plugins>
</build>

执行构建

然后在项目根目录执行mvn clean compile jib:build就可以了

image-20230927215158927

我们看到已经推送成功了

harbor仓库中查看

通过域名访问harbor,我们看我们的library里面的镜像仓库

image-20230927215222035

点进去就可以看到我们刚刚推送的镜像

image-20230927215225483

以及镜像的详细信息

image-20230927215229168

其他的微服务上传

微服务打包

这里我们也将其他微服务上传到仓库,步骤同上

image-20230927215233005

仓库中查看镜像

在我们的仓库中查看镜像,我们看到镜像都已经上传到仓库中了

image-20230927215236774

harbor 测试

删除本地镜像

可以通过docker rmi 镜像ID删除本地镜像

1
2
docker rm -f learn-docker-storage
docker rmi 192.168.64.153/library/learn-docker-storage:0.0.3

image-20230927215240635

运行harbor 中的镜像

我们把我们的所有微服务都上传到了仓库中,我们以一个完整的项目运行docker

image-20230927215244460

运行learn-docker-storage服务

执行运行命令

1
2
3
4
5
docker run -d \
-v /tmp/data/logs:/logs \
--name learn-docker-storage \
--network=learn-docker-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT

因为我们是基于内部网络访问 不需要暴漏接口了

image-20230927215247569

访问微服务测试

1
curl http://192.168.64.152:8080/userinfo/10001 | python -m json.tool

image-20230927215250697

运行learn-docker-web服务

执行运行命令

1
2
3
4
docker run -d \
--name learn-docker-web \
--network=learn-docker-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT

该微服务也是内部服务不需要暴漏端口,并且没有配置日志输出所有不挂载日志路径

image-20230927215253940

运行learn-docker-gateway服务

执行运行命令

1
2
3
4
5
docker run -d \
-p 8888:8888 \
--name learn-docker-gateway \
--network=learn-docker-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT

因为网关对外需要暴漏端口,所有需要开放8888端口

image-20230927215256870

查看nacos注册的微服务

我们发现我们的三个服务都已经注册进去了

image-20230927215259967

访问测试微服务

因为我们存储服务的8003端口没有暴漏出来,无法访问,我们需要通过网关进行访问

1
curl http://192.168.64.153:8888/employeapi/find/10001| python -m json.tool

image-20230927215303675

任务编排工具

我们发现我们现在管理微服务比较麻烦,现在只是三个微服务,如果更多会更加麻烦,怎么办呢,下面我们就来学习下docker编排工具

什么是任务编排

编排是一个新的词汇,经过阅读才明白编排指的是容器的集群化和调度。另一类含义指的是容器管理,负责管理容器化应用和组件任务。

​ docker毫无疑问是一个优秀的开源工具。但是,仅靠docker引擎和容器就不能进行复杂的应用程序部署。对于部署复杂的应用程序体系结构的容器群集,必须进行适当的配置。容器化的应用程序应该能够根据应用程序资源需求进行扩展和缩小。

需要考虑的因素

我们需要一个有效管理容器的良好框架。容器的生命周期很短,在进行容器编排时,要考虑的主要因素是

  1. 联网
  2. 高可用性
  3. 易于部署
  4. 良好的服务发现。

常见的任务编排工具

docker-compose

image-20230927215308593

​ docker-compose是基于docker的编排工具,使容器的操作能够批量的,可视的执行,是一个管理多个容器的工具,比如可以解决容器之间的依赖关系,当在宿主机启动较多的容器时候,如果都是手动操作会觉得比较麻烦而且容器出错,这个时候推荐使用 dockerd的单机编排工具 docker-compose。

Kubernetes

image-20230927215311949

​ Kubernetes是一个开源的,开箱即用的容器集群管理器和业务流程,它具有出色的构建 调度器 和资源管理器,用于以更有效和高度可用的方式部署容器。Kubernetes已成为许多组织事实上的容器编排工具,kubernetes项目由google与世界各地的贡献者维护,它提供了本机Docker工具不提供的许多功能。而且,使用kubernetes很容易上手。

OpenShift

image-20230927215316799

​ Openshift建立在kubernetes之上。Openshift项目由Redhat维护。它同时具有开源(openshift orgin)和企业版(openshift容器平台)。连同核心的Kubernetes功能,它提供了用于容器管理和编排的开箱即用组件。

Docker Swarm

image-20230927215321131

​ Docker生态系统包括从开发到生产部署框架的工具。在该列表中,docker swarm适用于集群管理。可以使用docker-compose,swarm,overlay网络和良好的服务发现工具(例如etcd或consul)的组合来管理Docker容器集群。

​ 与其他开源容器集群管理工具相比,Docker swarm在功能方面仍日趋成熟。考虑到庞大的Docker贡献者,Docker swarm拥有其他工具拥有的所有最佳功能不会太久。Docker记录了在生产中使用docker swarm 的良好生产计划。

环境准备

我们这里面主要讲解docker-composeswarm的编排工具

搭建Horbor仓库

我们刚才讲解了本地搭建Horbor仓库,但是我们本地搭建很占用资源,我们用了一台服务器专门来做Horbor的仓库,地址是https://manager-hongbaoyu-java.itheima.net:8443/

image-20230927215325114

停止本地Harbor

因为使用了单独的Harbor服务器,本地的Harbor就可以停掉了

1
cd /usr/local/harbor/harbor/ && docker-compose down

image-20230927215329949

清理Docker环境
清理本地环境

因为使用任务编排,本地的服务都可以删除掉了

1
2
3
4
# 停止并删除所有容器
docker rm -f $(sudo docker ps -a -q)
# 删除所有镜像
docker rmi $(docker images -q)

image-20230927215334379

查看本地环境

可以查看下本地的Docker环境

1
2
docker ps -a
docker images

image-20230927215338348

删除网络配置

因为我们自己配置了Docker网络,我们删除掉

1
docker network rm learn-docker-network

image-20230927215342609

修改Docker配置

因为我们使用了独立的Horbor仓库,可以将配置到Docker中的本地仓库地址替换为新的仓库地址

修改daemon
1
2
# 修改daemon文件删除本地仓库地址
vi /etc/docker/daemon.json

daemon.json中增加如下内容

1
"insecure-registries": ["manager-hongbaoyu-java.itheima.net:8443"],
查看修改
1
2
# 查看daemon配置
cat /etc/docker/daemon.json

新的仓库地址是manager-hongbaoyu-java.itheima.net:8443

image-20230927215347494

重启Docker
1
2
systemctl daemon-reload
service docker restart
初始化镜像

将服务器的需要的镜像初始化

1
2
3
4
5
docker pull mysql:5.7.33;\
docker pull nacos/nacos-server;\
docker pull manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT;\
docker pull manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT;\
docker pull manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT

docker-compose容器编排

image-20230927215351144

为什么使用docker-compose

​ 我们学会了使用 dockerfile 构建 docker 镜像,看起来已经能够满足我们的日常需求了,无论需要什么环境,在 dockerfile 里逐步构建,然后 build、run,就 ok 了,也满足了我们docker 隔离性、快速部署的要求,为什么还需要docker-compose呢?

​ 我们来看一个网站开发最常见的场景:我们要有数据库,网站应用,nginx,互相配合才是完整的环境。是的,我们完全可以以 ubuntu 为基础镜像,把这些一股脑全装进去,然后运行。但是这样有很多缺点,比如我们每次都要重新装 mysql 而不是直接利用 mysql 官方的基础镜像,升级维护不方便;如果我们的应用要扩展也很难,因为每个应用都连接的自己内部的数据库,无法共享数据;事实上,这种方式是典型的虚拟机的使用方式,不是 docker 的正确打开方式

​ docker 是轻量化的应用程序,docker 官方推荐每个 docker 容器中只运行一个进程,那么就是说,我们需要分别为我们的应用、数据库、nginx 创建单独的 docker 容器,然后分别启动它。想象一下,构建好 docker 之后,每次启动我们的网站,都要至少 docker run 三次,是不是很繁琐?而且此时这几个 docker 是分散独立的,很不方便管理。既然这几个 docker 都是为了同一个网站服务,是不是应该把它们放到一起?这就引出了 docker-compose 项目。

什么是docker-compose

​ docker-compose是 docker 官方的开源项目,使用 python 编写,实现上调用了 Docker 服务的 API 进行容器管理。其官方定义为为 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)),其实就是上面所讲的功能。

安装Docker-Compose

Docker Compose是一个用来定义和运行复杂应用的Docker工具,一个使用Docker容器的应用,通常由多个容器组成。使用Docker Compose不再需要使用shell脚本来启动容器。

​ Compose 通过一个配置文件来管理多个Docker容器,在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动,停止和重启应用,和应用中的服务以及所有依赖服务的容器,非常适合组合使用多个容器进行开发的场景。

查找最新版的docker-compose

https://github.com/docker/compose/releases/ 地址查找最新的docker-compose版本

image-20230927215355184

安装docker-compose
下载最新版的docker-compose文件
1
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
添加可执行权限
1
sudo chmod +x /usr/local/bin/docker-compose
测试安装结果
1
docker-compose --version

docker-compose概述

类似 docker 的Dockerfile文件,docker-compose使用 YAML 文件对容器进行管理。

相关概念

对于 docker-compose 有两个基本的概念:

  • 服务(service):一个应用容器,即 docker 容器,比如之前所说的mysql 容器、nginx 容器
  • 项目(project):由一组关联的应用容器组成的一个完整业务单元,比如上面所讲的由 mysql、web app、nginx 容器组成的网站。docker-compose 面向项目进行管理。
YAML 文件格式
  1. 大小写敏感,缩进表示表示层级关系
  2. 缩进空格数不重要,相同层级左侧对齐即可。(不允许使用 tab 缩进!)
  3. 由冒号分隔的键值对表示对象;一组连词线开头的行,构成一个数组;字符串默认不使用引号
Compose和Docker兼容性

Docker Engine 与docker-compose version 之间的有以下关系

compose文件格式版本 docker版本
3.4 17.09.0+
3.3 17.06.0+
3.2 17.04.0+
3.1 1.13.1+
3 1.13.0+
2.3 17.06.0+
2.2 1.13.0+
2.1 1.12.0+
2 1.10.0+
1 1.9.1.+

基本使用

接下来我们使用 docker-compose 构建我们的微服务以及mysql,并逐步讲解其使用。

准备工作

在项目文件夹下创建 docker-compose.yml文件

1
cd /usr/local/docker-learn/ && touch docker-compose.yml && ll

image-20230927215401274

编写配置文件

先在 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
version: '2'   
services:
mysql:
image: mysql:5.7.33
hostname: mysql
container_name: mysql
restart: always
networks:
- learn-docker-network
volumes:
- "/tmp/etc/mysql:/etc/mysql/mysql.conf.d/"
- "/tmp/data/mysql:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: 'root'
nacos:
image: nacos/nacos-server
hostname: nacos
container_name: nacos
restart: always
ports:
- "8848:8848"
networks:
- learn-docker-network
environment:
MODE: 'standalone'
JVM_XMS: '128m'
JVM_XMX: '128m'

learn-docker-web:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT
restart: always
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
volumes:
- "/tmp/data/logs:/logs"
learn-docker-storage:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT
restart: always
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
volumes:
- "/tmp/data/logs:/logs"
learn-docker-gateway:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT
restart: always
ports:
- "8888:8888"
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
volumes:
- "/tmp/data/logs:/logs"
networks:
learn-docker-network:
driver: bridge
运行测试

在项目的文件中执行docker-compose up -d命令就可以启动了

1
docker-compose up -d

image-20230927215405537

微服务访问测试

通过网关地址访问测试微服务

1
curl http://192.168.64.153:8888/employeapi/find/10001| python -m json.tool

image-20230927215409035

参数解释(手册)

version

指定 docker-compose.yml 文件的写法格式

docker-compose.yml的version版本号应该和docker的版本进行匹配,如果不匹配可能出现问题。

services

表示多个容器的集合

服务对象

​ docker-compose.yml管理是以服务为单位管理的,一个services下面可以有多个服务,mysql,app都代表一个服务

image

​ image是指定服务的镜像名称或镜像 ID,如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。

hostname

配置容器的host名称,在容器的hosts文件中加入了映射

image-20230927215412259

container_name

​ 配置启动后的容器名称和docker的--name xxx效果是一样的

restart

restart参数能够使我们在重启docker时,自动启动相关容器,和docker的--restart效果一致

Docker容器的重启策略如下

  • no,默认策略,在容器退出时不重启容器
  • on-failure,在容器非正常退出时(退出状态非0),才会重启容器
  • on-failure:3,在容器非正常退出时重启容器,最多重启3次
  • always,在容器退出时总是重启容器
  • unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
ports

映射端口的标签,对外暴露的端口定义,和 expose 对应,和docker的-p效果一致

depends_on

​ 这个是依赖配置的选项,意思是如果 服务启动是如果有依赖于其他服务的,先启动被依赖的服务,启动完成后在启动该服务

networks

配置容器所使用的网络

volumes

挂载一个目录或者一个已存在的数据卷容器,和docker -v效果一致

environment

配置环境变量,和docker的 -e效果一致

常用命令

docker-compose up

用于部署一个 Compose 应用

​ 默认情况下该命令会读取名为 docker-compose.yml 或 docker-compose.yaml 的文件

,用户也可以使用 -f 指定其他文件名。通常情况下,会使用 -d 参数令应用在后台启动。

docker-compose stop

停止 Compose 应用相关的所有容器,但不会删除它们。

​ 被停止的应用可以很容易地通过 docker-compose restart 命令重新启动。

如果带有服务命则停止该服务 ,否则停止所有服务

image-20230927215417557

docker-compose rm

用于删除已停止的 Compose 应用。

​ 它会删除容器和网络,但是不会删除卷和镜像。

如果带有服务命则删除该服务 ,否则删除所有服务

image-20230927215420676

docker-compose restart

重启已停止的 Compose 应用。

​ 如果用户在停止该应用后对其进行了变更,那么变更的内容不会反映在重启后的应用中,这时需要重新部署应用使变更生效。

docker-compose ps

用于列出 Compose 应用中的各个容器。

​ 输出内容包括当前状态、容器运行的命令以及网络端口。

image-20230927215427758

docker-compose down

停止并删除运行中的 Compose 应用。

​ 它会删除容器和网络,但是不会删除卷和镜像。

image-20230927215423708

扩缩容

nacos查看集群情况

我们可以查看nacos,查看当服务器集群的一个部署情况

image-20230927215431276

扩容节点

我们现在对learn-docker-storage节点进行扩容

语法格式:docker-compose up -d --scale 服务名=节点数

1
docker-compose up -d --scale learn-docker-storage=2

image-20230927215434855

启动后查看nacos节点信息

image-20230927215439806

缩容节点

和扩容一样指定节点数量就可以的

1
docker-compose up -d --scale learn-docker-storage=1

image-20230927215443769

停止后后查看nacos节点信息

image-20230927215446792

Swarm集群编排

image-20230927215451301

什么是Swarm

​ Swarm是Docker公司自研发的容器集群管理系统,Swarm在早期是作为一个独立服务存在,在Docker Engine v1.12中集成了Swarm的集群管理,和编排功能。可以通过初始化Swarm或加入现有Swarm来启用Docker引擎的Swarm模式。

​ Docker Engine CLI和API包括了管理Swarm节点命令,比如添加、删除节点,以及在Swarm中部署和编排服务,也增加了服务栈(Stack)、服务(Service)、任务(Task)概念

Swarm能干什么

​ Swarm是Docker 引擎内置(原生)的集群管理和编排工具。Docker Swarm是 Docker 官方三剑客项目之一,swarm是基于docker平台实现的集群技术,他可以通过几条简单的指令快速的创建一个docker集群,接着在集群的共享网络上部署应用,最终实现分布式的服务。

swarm节点

swarm是一系列节点的集合,而节点可以是一台裸机或者一台虚拟机。一个节点能扮演一个或者两个角色,manager或者worker。

manager节点

Docker Swarm集群需要至少一个manager节点,节点之间使用**Raft consensus protocol**进行协同工作。

​ 通常,第一个启用docker swarm的节点将成为leader,后来加入的都是follower。当前的leader如果挂掉,剩余的节点将重新选举出一个新的leader。

​ 每一个manager都有一个完整的当前集群状态的副本,可以保证manager的高可用。

worker节点

​ worker节点是运行实际应用服务的容器所在的地方。理论上,一个manager节点也能同时成为worker节点,但在生产环境中,我们不建议这样做。

​ worker节点之间,通过control plane进行通信,这种通信使用gossip协议,并且是异步的。

运行机制

名词解释

集群中经常谈到的stacks, services, tasks,他们之间的关系。

下面简单解释一下这三者的含义:

task

​ 在Docker Swarm中,task是一个部署的最小单元,task与容器是一对一的关系。

services

​ swarm service是一个抽象的概念,它只是一个对运行在swarm集群上的应用服务,所期望状态的描述。它就像一个描述了下面物品的清单列表一样:

  • 服务名称
  • 使用哪个镜像来创建容器
  • 要运行多少个副本
  • 服务的容器要连接到哪个网络上
  • 应该映射哪些端口
stack

​ stack是描述一系列相关services的集合。我们通过在一个YAML文件中来定义一个stack。

工作原理
服务、任务和容器

​ 当将服务部署到集群时,管理者将服务定义视为服务所需状态。然后将服务调度为一个或多个副本任务。这些任务在集群的节点上彼此独立运行。

例如下图有三个副本的HTTP服务,每个服务实例就是一个任务。

image-20230927215542306

​ 容器是一个独立的进程,在swarm模型中,每个任务调用一个容器。任务类似于插槽,调度器将容器放入其中。一旦容器运行,调度器认为该任务处于运行状态。如果容器出现健康监测失败或者终止,那么任务也终止。

副本和全局服务

有两种类型的服务部署:副本和全局。

​ 对于副本服务,指定要运行的相同任务的数量,每个副本都是相同的内容。

​ 全局服务是在每个节点上运行一个任务的服务。不需要预先指定任务数量。每当将一个节点添加到集群中,协调者将创建一个任务,并且调度器将任务分配给该新加入的节点。全局服务最好是监控代理、反病毒扫描程序等等想要在集群中每个节点上运行的容器。

下图显示三个副本服务(黄色)和全局服务(灰色):

image-20230927215550386

功能特点(了解)

集成的集群管理

​ 使用Docker Engine CLI创建一组Docker引擎,您可以在其中部署应用程序服务。您不需要其他编排软件来创建或管理群集。

节点分散式设计

​ Docker Engine不是在部署时处理节点角色之间的差异,而是在运行时处理角色变化。您可以使用Docker Engine部署两种类型的节点,管理节点和工作节点。这意味着您可以从单个服务器构建整个群集。

声明性服务模型

​ Docker Engine使用声明性方法来定义应用程序堆栈中各种服务的所需状态。例如,您可以描述由具有消息队列服务和数据库后端的Web前端服务组成的应用程序。

可扩容与缩放容器

​ 对于每个服务,您可以声明要运行的任务数。当您向上或向下缩放时,swarm管理器通过添加或删除任务来自动适应,以保持所需的任务数量来保证集群的可靠状态。

容器容错状态协调

​ 群集管理器节点不断监视群集状态,并协调您表示的期望状态的实际状态之间的任何差异。

​ 例如,如果设置一个服务以运行容器的10个副本,并且托管其中两个副本的工作程序计算机崩溃,则管理器将创建两个新副本以替换崩溃的副本。 swarm管理器将新副本分配给正在运行和可用的worker节点上。

多主机网络

​ 您可以为服务指定覆盖网络。当swarm管理器初始化或更新应用程序时,它会自动为覆盖网络上的容器分配地址。

服务发现

​ Swarm管理器节点为swarm中的每个服务分配唯一的DNS名称,并负载平衡运行的容器。您可以通过嵌入在swarm中的DNS服务器查询在群中运行的每个容器。

负载平衡

​ 您可以将服务的端口公开给外部负载平衡器。在内部,swarm允许您指定如何在节点之间分发服务容器。

缺省安全

​ 群中的每个节点强制执行TLS相互验证和加密,以保护其自身与所有其他节点之间的通信。您可以选择使用自签名根证书或来自自定义根CA的证书。

滚动更新

​ 在已经运行期间,您可以增量地应用服务更新到节点。 swarm管理器允许您控制将服务部署到不同节点集之间的延迟。如果出现任何问题,您可以将任务回滚到服务的先前版本。

准备环境

服务器准备

我的三台测试机

IP地址 角色 主机名
192.168.64.153 manager node1
192.168.64.154 worker node2
192.168.64.155 worker node3
服务器端口开放

在创建集群前,如果开启了防火墙,请确认三台主机的防火墙能让swarm需求的端口开放,需要打开主机之间的端口,以下端口必须可用。在某些系统上,这些端口默认为打开。

  • 2377:TCP端口2377用于集群管理通信
  • 7946:TCP和UDP端口7946用于节点之间的通信
  • 4789:TCP和UDP端口4789用于覆盖网络流量

可以直接禁用系统防火墙来让这些端口通信不受限制,一般测试环境我们都会禁用防火墙

1
2
systemctl stop firewalld(立即生效)
systemctl disable firewalld(重启生效)

搭建Swarm集群

当首次安装并使用Docker Engine时,默认情况下swarm模式是禁用的。当启用swarm模式时,可以使用docker service 服务管理命令。

有两种方式在swarm模式下运行引擎:

  • 创建一个新的集群

  • 加入现有集群

    在生成环境中,集群模式提供具有集群管理功能的容错平台,以保证服务的可靠运行。

下面我们就来搭建一个swarm集群

初始化集群
1
docker swarm init --advertise-addr 192.168.64.153(本机地址)

image-20230927215557016

生成口令
生成管理节点口令
1
docker swarm join-token manager

image-20230927215600356

生成执行节点口令
1
docker swarm join-token worker

image-20230927215605243

其他节点加入集群

在第一个从节点执行加入 work的指令

1
docker swarm join --token SWMTKN-1-53p5t2rt9ud5j0owkl14boj2z8im6r60ddlzotgc4a8y93u1c2-8f6crxgyc9umayhxva1jv9t1w 192.168.64.153:2377

image-20230927215609517

第二个节点执行加入work的命令

1
docker swarm join --token SWMTKN-1-53p5t2rt9ud5j0owkl14boj2z8im6r60ddlzotgc4a8y93u1c2-8f6crxgyc9umayhxva1jv9t1w 192.168.64.153:2377

image-20230927215613942

查看swarm的节点

执行docker node ls 查看swarm节点信息

1
docker node ls

image-20230927215617799

AVAILABILITY状态说明
  • Active 意味着调度程序可以将任务分配给节点。
  • Pause 意味着调度程序不会将新任务分配给节点,但现有任务仍在运行。
  • Drain 意味着调度程序不会向节点分配新任务。调度程序关闭所有现有任务并在可用节点上调度它们。
MANAGER STATUS状态说明

显示节点是属于manager或者worker

  • 没有值 :表示不参与群管理的工作节点。
  • Leader :意味着该节点是使得群的所有群管理和编排决策的主要管理器节点。
  • Reachable: 意味着节点是管理者节点正在参与Raft共识。如果领导节点不可用,则该节点有资格被选为新领导者。
  • Unavailable :意味着节点是不能与其他管理器通信的管理器。如果管理器节点不可用,您应该将新的管理器节点加入群集,或者将工作器节点升级为管理器。
修改主机名

默认centos的主机名是localhost,我们看上面,节点的主机名都是localhost,我们修改以下

查看主机名

hostnamectl status可以查看主机名

1
hostnamectl status

image-20230927215623144

修改主机名

修改主机名使用hostnamectl set-hostname NAME命令可以进行修改,我们使用 node1,node2…方式命名我们的节点

1
2
3
4
#修改主机名
hostnamectl set-hostname node1
# 查看主机名
hostnamectl status

image-20230927215627374

其他节点依次操作就可以

再次查看节点

再次查看swarm节点信息

1
docker node ls

image-20230927215631868

添加节点标签

因为我们用到了节点约束,所有启动服务之前需要添加节点标签

1
2
3
4
# 添加标签
docker node update --label-add role=data node1
#查看节点标签信息
docker node inspect node1|grep role

image-20230927215634996

管理节点

升降级节点

无论您升级或降级节点,您应该始终在群中维护奇数个管理器节点,

​ 升降级节点角色只能在管理节点上运行,应先升级工作节点为被选举者,再降级领导者为工作节点,然后被选举者成为领导者完成替换;

​ 您可以将工作程序节点提升为manager角色。这在管理器节点不可用或者您希望使管理器脱机以进行维护时很有用。 类似地,您可以将管理器节点降级为worker角色。

升级节点

要降级一个节点或一组节点,请从管理器节点运行

1
docker node promote 节点名称
1
docker node promote pbui0rdry85e25i3bvhzmqw8h

image-20230927215639069

升级节点后不会马上生效,会进入Reachable状态,如果leader节点关掉,当前节点会参与主节点竞争

降级节点

要升级一个节点或一组节点,请从管理器节点运行

1
docker node demote 节点名称
1
docker node demote r7cv7prw1h2to9h1cpwxs9jhl

image-20230927215642338

swam将节点降级后,再次查看节点命令不生效,需要到管理节点查看

节点退出swarm集群

​ docker swarm leave 命令可在所有节点上运行,值得注意的是,工作节点退出swarm集群后,在管理节点上依然保存着工作节点的节点信息,状态为down,要删除节点信息,可使用docker node rm 命令,当所有的节点都退出并且被删除时,在管理节点上使用docker swarm leave,然后退出整个集群;

工作节点

在工作节点执行以下命令可以退出swarm节点

1
docker swarm leave

image-20230927215645788

管理节点

在管理节点查看节信息

1
docker node ls

根据退出节点前后查看节点信息,可以发现退出的节点是down的状态,并没有删除节点

image-20230927215648891

删除节点信息

在管理节点执行删除命令docker node rm 节点ID

1
docker node rm r7cv7prw1h2to9h1cpwxs9jhl

image-20230927215653210

管理集群服务

管理集群服必须在manager角色的主机上

创建overlay网络

我们需要载多个服务器中运行Docker容器集群,需要使用overlay网络,overlay网络用于连接不同机器上的docker容器,允许不同机器上的容器相互通信,同时支持对消息进行加密

1
docker network create --driver overlay learn-docker-overlay-network
创建服务

使用docker service create命令来创建服务

创建MySQL服务
1
2
3
4
5
6
7
8
9
10
docker service create \
-e MYSQL_ROOT_PASSWORD=root \
--mount type=bind,source=/tmp/etc/mysql,destination=/etc/mysql/mysql.conf.d/ \
--mount type=bind,source=/tmp/data/mysql,destination=/var/lib/mysql \
--replicas 1 \
--constraint 'node.labels.role == data' \
--name mysql \
--network learn-docker-overlay-network \
mysql:5.7.33

–replicas 1 表示在集群中创建1个服务

node.labels.role == data表示节点需要创建在标签是data的节点上

image-20230927215657458

可以查看swarm的进程

1
2
3
docker service ls

docker service ps mysql

image-20230927215700953

创建nacos服务

nacos也是需要创建一个,但是节点是可以漂移的,不需要固定在某一台机器

1
2
3
4
5
6
docker service create \
-e MODE=standalone \
--replicas 1 \
--name nacos \
--network learn-docker-overlay-network \
nacos/nacos-server

image-20230927215704220

可以查看swarm的进程

1
2
3
docker service ls

docker service ps nacos

image-20230927215707590

我们发现nacos运行在了 node3节点上

创建learn-docker-storage服务

我们创建learn-docker-storage服务,我们将该服务部署两个节点

1
2
3
4
5
docker service create \
--name learn-docker-storage \
--replicas 2 \
--network learn-docker-overlay-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT

image-20230927215710550

可以查看swarm的进程

1
2
3
docker service ls

docker service ps nacos

我们发现我们的存储服务运行在两个节点上

image-20230927215713512

创建learn-docker-web服务

我们创建learn-docker-web服务,我们将该服务同样部署两个节点

1
2
3
4
5
docker service create \
--name learn-docker-web \
--replicas 2 \
--network learn-docker-overlay-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT

image-20230927215717168

可以查看swarm的进程

1
2
3
docker service ls

docker service ps nacos

image-20230927215720653

创建learn-docker-gateway服务

我们创建learn-docker-gateway服务,因为是网关服务,我们只创建一个节点,因为需要对外暴漏端口,需要开放8888端口

1
2
3
4
5
6
docker service create \
-p 8888:8888 \
--name learn-docker-gateway \
--replicas 1 \
--network learn-docker-overlay-network \
manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT

image-20230927215723714

可以查看swarm的进程

1
2
3
docker service ls

docker service ps nacos

image-20230927215726972

测试访问微服务

因为在node2节点上,node2节点IP是192.168.64.154 我们可以请求URL访问

1
curl http://192.168.64.154:8888/employeapi/find/10001| python -m json.tool

image-20230927215731212

查看某个服务日志

通过docker service logs 服务命可以看到当前服务的日志,但是这个服务有两个容器在运行,所有能同时看到两个容器的日志

1
docker service logs learn-docker-storage

image-20230927215734986

扩缩容服务

可以通过集群操作对集群进行扩缩容

扩容操作

我们将learn-docker-storage由两个容器变为三个容器

1
docker service scale learn-docker-storage=3

image-20230927215739331

这样我们就把存储服务变成了三台服务

缩容操作

同样,使用该命令对learn-docker-storage进行缩容

1
docker service scale learn-docker-storage=2

image-20230927220038649

删除服务

我们可以尝试把learn-docker-gateway删除掉,删除操作将会把整个服务的所有容器删除

1
docker service rm learn-docker-gateway

image-20230927220035171

Docker Stack管理服务

我们上面使用swarm部署服务,单个服务还好,如果很多个服务怎么来解决呢,这里就用到了Docker Stack管理服务。

​ 在上面我们学会了如何配置一个swarm集群,并且知道如何在swarm集群上部署应用,现在,我们开始了解Docker层级关系中的最高一个层级——stack。一个stack就是一组有关联的服务的组合,可以编排在一起,一起管理。

​ 单机模式下,我们可以使用 Docker-Compose来编排多个服务,而 Docker Swarm 只能实现对单个服务的简单部署。于是就引出了本文的主角 Docker Stack ,通过 Docker Stack 我们只需对已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群环境下的多服务编排。

集群搭建案例

应用部署情况
服务名称 数量
mysql 1
nacos 1
learn-docker-gateway 1
learn-docker-web 2
learn-docker-storage 2
创建docker-compose.yml

首先创建一个 docker-compose.yml 文件,使用 Docker Compose v3 语法

我们把我们原来单机版的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
version: '3.9'   
services:
mysql:
image: mysql:5.7.33
networks:
- learn-docker-network
volumes:
- "/tmp/etc/mysql:/etc/mysql/mysql.conf.d/"
- "/tmp/data/mysql:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: 'root'
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- 'node.labels.role == data'
restart_policy:
condition: on-failure
delay: 5s

nacos:
image: nacos/nacos-server
ports:
- "8848:8848"
networks:
- learn-docker-network
environment:
MODE: 'standalone'
JVM_XMS: '128m'
JVM_XMX: '128m'
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s

learn-docker-web:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-web:1.0-SNAPSHOT
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
deploy:
mode: replicated
replicas: 2
restart_policy:
condition: on-failure
delay: 5s


learn-docker-storage:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
deploy:
mode: replicated
replicas: 2
restart_policy:
condition: on-failure
delay: 5s
learn-docker-gateway:
image: manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-gateway:1.0-SNAPSHOT
ports:
- "8888:8888"
networks:
- learn-docker-network
depends_on:
- nacos
- mysql
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s

visualizer:
image: dockersamples/visualizer
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s

networks:
learn-docker-network:
driver: overlay

配置介绍

​ Stack文件就是Docker Compose文件。唯一的要求就是version:一项需要是3.0或者更高的值。在Docker根据某个Stack文件部署应用的时候,首先会检查并创建networks:关键字对应网络。如果网络不存在,Docker会进行创建。下面我们详细看下这几个模块。

overlay网络

​ 这里定义了1个网络,默认情况下网络都是使用overlay驱动,新建对应的覆盖类型的网络。

1
2
3
networks:
learn-docker-network:
driver: overlay
部署节点副本数

接下来我们进一步了解deploy关键字新增的内容

1
2
3
4
5
6
deploy:
mode: replicated
replicas: 2
restart_policy:
condition: on-failure
delay: 5s
  • replicas: 2 设置了期望服务的副本数量为2,默认为1.如果服务正在运行,需要调整副本数。可以调整stack文件中的 replicas 的数值,然后重新部署stack。重新部署stack并不会影响那些没有改动的服务。
  • restart_policy: 定义了Swarm针对容器异常退出的重启策略。当前服务的重启策略是:如果某个副本以非0返回值退出,会立即重启当前副本。重启最多尝试3次,每次都是等待之多120s来检测是否成功。每次重启的间隔是5s。
节点约束

因为我们的数据库节点只能部署在数据节点,因为需要挂载本地的数据文件以及数据库文件,所有需要使用标签进行节点约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql:
image: mysql:5.7.33
networks:
- learn-docker-network
volumes:
- "/tmp/etc/mysql:/etc/mysql/mysql.conf.d/"
- "/tmp/data/mysql:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: 'root'
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- 'node.labels.role == data'
restart_policy:
condition: on-failure
delay: 5s

这里的 'node.labels.role == data含义就是将当前mysql节点约束在标签名字是role,并且值是data的数据节点,更多操作请参考下文

部署服务
部署应用

使用docker stack deploy 命令部署

1
docker stack deploy -c docker-compose.yml learn-docker-test

这里我们指定了docker-compose文件,并把stack命名为 learn-docker-test。

image-20230927220026573

查看部署情况

可以通过docker stack ls命令查看集群部署情况,会列出 Swarm 集群中的全部 Stack,包括每个 Stack 拥有多少服务

image-20230927220023281

服务部署情况
查看nacos节点信息

访问nacos服务,发现我们的服务都已经注册

image-20230927220019042

测试访问服务

访问服务接口测试

1
curl http://192.168.64.153:8888/employeapi/find/10001| python -m json.tool

image-20230927220014087

集群管理

更新服务

docker service upadte可以对swarm服务进行升级

参数详解
  • –force 强制更新重启服务,无论是否配置或镜像改变都更新
  • –image image:tag 制定更新的镜像
  • –with-registry-auth 向 Swarm 代理发送 Registry 认证详细信息,私有仓库需要携带该参数
更新镜像
1
2
3
4
#查看服务详情
docker service ls
# 更新服务
docker service update --image manager-hongbaoyu-java.itheima.net:8443/library/learn-docker-storage:1.0-SNAPSHOT learn-docker-test_learn-docker-storage

image-20230927220010621

删除应用
查看部署集群

docker stack ls可以查看部署的服务列表

image-20230927220006978

执行删除

docker stack rm stack名称命令会删除整个stack集群,注意移除操作执行前并不会进行二次确认。

1
docker stack rm learn-docker-test

image-20230927220002730

相关命令(手册)
docker stack 常用命令
命令 说明
docker stack deploy 部署新的堆栈或更新现有堆栈
docker stack ls 列出现有堆栈
docker stack ps 列出堆栈中的任务
docker stack rm 删除堆栈
docker stack services 列出堆栈中的服务
docker stack down 移除某个堆栈(不会删除数据)
docker service 常用命令
命令 说明
docker service create 部署服务
docker service inspect 查看服务详情
docker service logs 产看某个服务日志
docker service ls 查看所有服务详情
docker service rm 删除某个服务(-f强制删除)
docker service scale 设置某个服务个数
docker service update 更新某个服务
docker node 常用命令
命令 说明
docker node ls 查看所有集群节点
docker node rm 删除某个节点(-f强制删除)
docker node inspect 查看节点详情
docker node demote 节点降级,由管理节点降级为工作节点
docker node promote 节点升级,由工作节点升级为管理节点
docker node update 更新节点
docker node ps 查看节点中的 Task 任务
docker swarm 常用命令
命令 说明
docker swarm init 初始化集群
docker swarm join-token worker 查看工作节点的 token
docker swarm join-token manager 查看管理节点的 token
docker swarm join 加入集群中

portainer集群管理(扩展)

Portainer介绍

Portainer是一个可视化的容器镜像的图形管理工具,利用Portainer可以轻松构建,管理和维护Docker环境。 而且完全免费,基于容器化的安装方式,方便高效部署。

​ Portainer 的目的是部署和使用一样简单。它由一个可以在任何 Docker 引擎上运行的单一容器组成(可以部署为Linux容器或Windows本地容器,也支持其他平台)。Portainer允许你管理所有的Docker资源(容器、镜像、卷、网络等等)。它与独立的Docker引擎和Docker Swarm模式兼容。

swarm集群安装Portainer

使用swarm集群安装Portainer,用Portainer来管理swarm集群

1
2
3
4
# 下载部署配置文件
curl -L https://downloads.portainer.io/portainer-agent-stack.yml -o portainer-agent-stack.yml
# 部署节点
docker stack deploy -c portainer-agent-stack.yml portainer

注意:此方法将自动部署Portainer服务器的单个实例,并将Portainer代理作为全局服务部署到集群中的每个节点上。

image-20230927215956592

portainer使用

注册用户

默认访问接口是9000端口,可以通过浏览器进行访问,首次登陆需要注册用户,给admin用户设置密码

image-20230927215952378

查看管理服务

点击home节点,当前这个节点就是我们的swarm集群

image-20230927215949014

点进去就可以看到我们能操作的菜单了

image-20230927215945030

查看swarm节点

点击swarm菜单就可以看到swarm节点了

image-20230927215941068

管理微服务

服务部署情况

我们要将我们的服务交给portainer管理

服务名称 数量
mysql 1
nacos 1
learn-docker-gateway 1
learn-docker-web 2
learn-docker-storage 2
准备工作
管理节点标签

我们MySQL需要部署在数据节点,我们添加节点标签

image-20230927215936395

在swarm管理节点,点击节点信息进入下面详情页面进行配置标签

image-20230927215931954

添加网络信息

因为我们的节点需要一个共有的overlay网络,我们需要配置下,在network节点点击添加

image-20230927215927515

在添加页面选择overlay网络类型,名字叫做learn-docker-network

image-20230927215923018

然后点击创建就可以

创建仓库配置

因为我们的微服务需要从我们自己的harbor镜像仓库拉取,需要将我们的仓库配置

image-20230927215918974

在仓库节点填写我们的镜像地址就可以https://manager-hongbaoyu-java.itheima.net:8443

image-20230927215915347

创建stack任务

在stack界面点击stack菜单进行添加stck任务

image-20230927215911730

在stack管理界面将我们的docker-compose.yml复制进我们的stack界面

image-20230927215908252

点击创建节点信息就可以,等待部署就可以

image-20230927215904440

稍等下节点就部署完成了

image-20230927215900145

点击进去就可以单到节点详情了

image-20230927215855840

查看节点部署情况

进入swarm管理界面

image-20230927215851301

点击Go to cluster visualizer查看服务部署情况

image-20230927215845543

docker 问题

1.docker image报错

1
2
cannot connect to the docker daemon at unix:///var/run/docker.sock.Is the docker daemon running?、
没在跑

image-20221006223152743

重启一下就行