docker-docker-compose基础

Docker基础

image-20241222210224661

Docker是什么

Docker 是一个开源的容器化平台,用于开发、交付和运行应用程序。如图所示,简单来说,docker是一艘运载着许多相互隔离、互相独立的集装箱,每个箱子内部运行着独立的程序。

为什么使用Docker

​ Develop faster. Run anywhere,这是docker官网上的docker原话,更快的开发,到处运行。

1、解决传统环境差异导致的兼容性问题

按照传统开发模式,一个产品有2套部署环境:本地开发,线上环境,存在大量的运维部署问题和环境差异,费事费力。最典型的问题是开发人员——我本地明明跑起来正常的,为什么线上不行

2、快速部署、快速迭代的兴起

Devops的快速发展,传统的开发模式时,开发人员提供jar包,然后手动上传服务器,然后java -jar启动新的包,有时遇到,依赖的其他组件升级,那就更是麻烦,比如mysql、redis、xxljob等,涉及到了环境依赖、先后问题,运维难度极大。有了docker,结合Jenkins、可以快速实现持续开发、持续部署、编排容器。

3、容器编排技术的兴起

通过容器编排工具和 Docker 的结合,可以轻松地实现复杂的应用架构,如微服务架构,多个微服务可以分别打包成 Docker 容器,通过容器编排工具进行统一管理和调度

4、资源利用和成本优化

Docker 容器相比于传统的虚拟机,具有更轻量级的特点。虚拟机需要为每个实例运行一个完整的操作系统,而 Docker 容器共享主机操作系统内核,多个容器可以在同一主机上运行,大大减少了系统资源的占用。这使得在相同的硬件资源下,可以部署更多的应用,提高了资源利用率,降低了企业的硬件采购和运维成本。

Docker和虚拟机的区别

docker出现之前,虚拟技术主流就是虚拟机,对应的软件有Vmvare,VirtualBox等。

总的来说,docker不像虚拟机那么重,容器共用宿主机的操作系统,每个容器只需要包含自身需要的环境和程序,所以比较小启动也快资源利用率更高

什么是docker,它与虚拟机有什么区别?_docker和虚拟机的区别-CSDN博客

应用更快速的交付和部署
传统:一堆帮助文档,安装程序
Docker:打包镜像发布测试,一键运行

更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样!
项目打包为一个镜像,扩展服务器A!服务器B

更简单的系统运维
在容器化之后,我们的开发,测试环境都是高度一致的。

更高效的计算资源利用:
Docker是内核级别的虚拟化,可以再一个物理机上可以运行很多的容器实例!服务器的性能可以被压榨到极致。

Docker怎么用

docker核心概念

镜像(image)

​ Docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像 => run => tomcat01容器, 通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)

容器(container)

​ Docker利用容器技术,独立运行一个或者一组应用, 通过镜像来创建的。可以理解为容器是镜像的实例

容器可以启动,停止,删除等

仓库(repository)

​ 存放镜像的地方,Docker Hub(默认是国外的),各大厂商也有自己的docker镜像仓库

img

docker底层原理

docker如何工作

​ Docker是一个Client-Server结构的服务器,Docker的守护进程运行在主机上,通过Socket从客户端访问!Docker-Server接收到Docker-Client指令,就会执行这个命令

img

docker为什么比VM快

1、Docker有着比虚拟机更少的抽象层
2、docker利用的是宿主机的内核,vm需要是Guest os

​ 所以说,新建一个容器的时候,docker不需要想虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载 GuestOS,分钟级别的,而docker是利用宿主机的操作系统吗,省略了这个复杂的过程,秒级!

img

docker安装及镜像仓库配置

参考上篇

Docker常用命令

在这里插入图片描述

帮助命令:

1
2
3
docker version		#查看docker版本信息
docker info #显示docker系统信息,包括镜像和容器的数量
docker --help #帮助命令

镜像命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker images 查看所有本地主机上的镜像
-a:列出本地所有镜像
-q:只列出镜像ID
–digests:显示镜像的摘要信息
–no trunc:显示完整的镜像信息
-s:列出收藏数不小于指定值的镜像

docker search 查找镜像

docker pull 镜像名[:tag] 下拉镜像 如果不写tag,默认就是latest

docker rmi 删除镜像

docker rmi $(docker images -aq) # 删除所有镜像

容器命令

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
docker run [可选参数] image #运行镜像

# 参数说明
--name=“Name” 容器名字 tomcat01 tomcat02 用来区分容器
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-p 随机指定端口


# 测试,启动并进入容器
[root@iZ2zeg4ytp0whqtmxbsqiiZ ~]# docker run -it centos /bin/bash
[root@74e82b7980e7 /]# ls # 查看容器内的centos,基础版本,很多命令是不完善的
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr

# 从容器中退回主机
[root@77969f5dcbf9 /]# exit
exit

列出所有的运行的容器
# docker ps #列出本地运行的容器
# 列出当前正在运行的容器
-a # 列出正在运行的容器包括历史容器
-n=? # 显示最近创建的容器
-q # 只显示当前容器的编号
退出容器
exit # 直接退出容器并关闭
Ctrl + P + Q # 容器不关闭退出

删除容器
docker rm -f 容器id # 删除指定容器
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q|xargs docker rm -f # 删除所有的容器

启动和停止容器的操作
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止当前的容器

显示日志
docker logs -tf --tail number 容器id
-tf # 显示日志
--tail number # 显示日志条数
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker logs -tf --tail 10 a0d580a21251

查看容器中进程信息ps
# 命令 docker top 容器id
[root@iZ2zeg4ytp0whqtmxbsqiiZ /]# docker top df358bc06b17

查看镜像的元数据
docker inspect 容器id


进入当前正在运行的容器
docker exec -it 容器id /bin/bash
docker attach 容器id

# docker exec # 进入容器后开启一个新的终端,可以在里面操作
# docker attach # 进入容器正在执行的终端,不会启动新的进程


从容器中拷贝文件到主机
docker cp 容器id:容器内路径 目的地主机路径
#docker cp 7af535f807e0:/home/Test.java /home

提交容器成为一个新的副本
docker commit
docker commit -m="提交的描述信息" -a="作者名" 容器id 目标镜像名:[tag]

Docker部署软件实战

1、安装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
# 1. 搜索镜像 search 建议去docker hub搜索,可以看到帮助文档
# 2. 下载镜像 pull
# 3. 运行测试
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 0d120b6ccaa8 32 hours ago 215MB
nginx latest 08393e824c32 7 days ago 132MB

# -d 后台运行
# -name 给容器命名
# -p 宿主机端口:容器内部端口
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d --name nginx01 -p 3344:80 nginx # 后台方式启动启动镜像
fe9dc33a83294b1b240b1ebb0db9cb16bda880737db2c8a5c0a512fc819850e0
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe9dc33a8329 nginx "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 0.0.0.0:3344->80/tcp nginx01
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# curl localhost:3344 # 本地访问测试

# 进入容器
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker exec -it nginx01 /bin/bash
root@fe9dc33a8329:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@fe9dc33a8329:/# cd /etc/nginx/
root@fe9dc33a8329:/etc/nginx# ls
conf.d koi-utf mime.types nginx.conf uwsgi_params
fastcgi_params koi-win modules scgi_params win-utf

2、安装Tomcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 官方的使用
docker run -it --rm tomcat:9.0

# 我们之前的启动都是后台的,停止了容器之后, 容器还是可以查到,docker run -it --rm 一般用来测试,用完就删

# 下载再启动
docker pull tomcat

# 启动运行
docker run -d -p 3344:8080 --name tomcat01 tomcat

# 测试访问没有问题

# 进入容器
docker exec -it tomcat01 /bin/bash

# 发现问题:1.linux命令少了, 2. webapps下内容为空 默认是最小的镜像,所有不必要的都剔除了,保证最小可运行环境即可

3、ES+Kibana

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
# es 暴露的端口很多
# es 十分的耗内存
# es 的数据一般需要放置到安全目录! 挂载
# --net somenetwork 网络配置

# 启动elasticsearch
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2

[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
a920894a940b354d3c867079efada13d96cf9138712c76c8dea58fabd9c7e96f

# 启动了linux就卡主了,docker stats 查看cpu状态

# 测试一下es成功了
[root@iZ2zeg4ytp0whqtmxbsqiiZ home]# curl localhost:9200
{
"name" : "a920894a940b",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "bxE1TJMEThKgwmk7Aa3fHQ",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date" : "2020-03-26T06:34:37.794943Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}


# 增加内存限制,修改配置文件 -e 环境配置修改
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2

镜像讲解

​ 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含某个软件所需的所有内容,包括代码、库、环境变量和配合文件。

UnionFS(联合文件系统)

​ UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

​ UnionFS 文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

分层理解

​ 所有的 Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。举一个简单的例子,假如基于 Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

image-20250105214333487

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层,当我们对启动后的容器继续做什么操作后打包成镜像,相当于在原镜像层的基础上,再堆叠一层。

image-20250105214632792

容器数据卷

问题:mysql作为容器启动产生数据后,容器重启或者关闭怎么办,数据怎么保存

什么是容器数据卷

​ 容器之间可以有一个数据共享的技术,docker容器中产生的数据,同步到本地

img

使用数据卷

方式一:直接使用命令来挂载,-v

1
2
docker run -it -v 主机目录:容器目录
[root@localhost ~]# docker run -it -v /home/ceshi:/home centos01:1.0 /bin/bash

实战:安装MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 获取镜像
# docker pull mysql:5.7

# 运行容器, 需要做数据挂载! # 安装启动mysql,需要配置密码(注意)
# 官方测试, docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag

# 启动我们的
-d # 后台运行
-p # 端口隐射
-v # 卷挂载
-e # 环境配置
--name # 容器的名字
# docker run -d -p 3344:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

# 启动成功之后,我们在本地使用navicat链接测试一下
# navicat链接到服务器的3344 --- 3344 和 容器的3306映射,这个时候我们就可以连接上mysql喽!

# 在本地测试创建一个数据库,查看下我们的路径是否ok!

具名挂载与匿名挂载

1
2
3
4
5
6
7
8
9
10
11
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载

# 通过 -v 容器内路径:ro rw 改变读写权限
ro(只读) rw(可读可写)
# 一旦设置了容器权限,容器对我们挂载出来额内容就有限定了
[root@localhost home]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
[root@localhost home]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的

所有docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxxx/_data我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下使用的是具名挂载

多个mysql同步数据

1
2
3
4
5
[root@localhost /]# docker run -it --name docker01 syw/centos:1.0
[root@9998f8ab8c5a /]# ls
bin etc lib lost+found mnt proc run srv tmp var volume02
dev home lib64 media opt root sbin sys usr volume01
[root@localhost /]# docker run -it --name docker02 --volumes-from docker01 syw/centos:1.0

​ 关键命令 –volumes-from 只要子容器挂载到父容器上就可以实现容器间的数据互相同步,第三个容器的数据也可以同步到第一、第二个容器中,可以同步多个容器就算删除docker01,docker02依然可以访问这个文件.这是容器间的拷贝的概念

img

Dockerfile介绍

dockerfile 就是用来构建docker镜像的构建文件,命令脚本,使用docker build

简单的Dockerfile例子

1
2
3
4
5
6
7
8
9
10
11
12
# 自己手写一个镜像
# 创建dockerfile,名字可以随机,建议dockerfile
# 文件中的内容 指令(大写) 参数
[root@localhost docker-test-volume]# cat dockerfile1
FROM centos #
#这里的每一个命令,就是镜像的一层
VOLUME ["/volume01","/volume02"]
CMD echo "----end------"
CMD /bin/bash
# 生成镜像,docker build -f 镜像文件 -t 镜像名:[tag] .
[root@localhost docker-test-volume]# docker build -f dockerfile1 -t syw/centos:1.0 .
docker images 就能查到刚刚构建的镜像

如何使用dockerfile

1
2
3
4
1、编写一个dockerfile文件
2、docker build 构建成为一个镜像
3、docker run运行镜像
4、docker push 发布镜像

img

dockerfile搭建过程

1
2
3
4
1、每个保留关键字(指令)都是必须是大写字母
2、执行从上到下顺序执行
3、#表示注释
4、每个指令都会创建提交一个新的镜像层,并提交

Dockerfile基本命令

1
2
3
4
5
6
7
8
9
10
11
12
FROM			#基础镜像,一切从这里开始构建
MAINTAINER #镜像是谁写的,姓名+邮箱
RUN #镜像运行的时候需要运行的命令
ADD #步骤,tomcat镜像,这个tomcat压缩包!添加内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #保留端口配置
CMD #指定这个容器启动的时候需要运行的命令
ENTRYPOINT #指定这个容器启动的时候需要运行的命令,可以追加命令
ONBUILD #当构建一个被继承dockerfile 这个的时候就会运行ONBUILD的指令
COPY #类似ADD,我们文件拷贝到镜像中
ENV #构建的时候设置环境变量

img

CMD和ENTRYPOINT的区别

1
2
3
CMD	#指定这个容器启动的时候需要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT #指定这个容器启动的时候需要运行的命令,可以追加命令
一般用ENTRYPOINT结尾

DIY自制Centos镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM centos:7
MAINTAINER handsome<handsome@126.com>

RUN rm -rf /etc/yum.repos.d/CentOS-Base.repo
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum clean all
RUN yum makecache
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 100
CMD echo $MYPATH
CMD echo "--------end----------"
CMD /bin/bash

DIY自制Tomcat镜像

image-20250126163106092

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM centos:7
ADD jdk-8u152-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.98.tar.gz /usr/local

RUN rm -rf /etc/yum.repos.d/CentOS-Base.repo
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN yum clean all
RUN yum makecache
RUN yum -y install net-tools



ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk1.8.0_152
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.98
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib #设置环境变量 分隔符是:
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.98/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.98/logs/catalina.out # 设置默认命令

SpringBoot微服务打包Docker镜像

1、定义controller

1
2
3
4
5
6
7
8
@RestController("/test")
public class TestController {
@GetMapping("/aaa")
public Object aaa(){
return "bbb";
}
}

pom.xml

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>dockerfile-test</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/>
</parent>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

2、定义Dockerfile

1
2
3
4
5
FROM openjdk:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

3、上传到linux

4、构建镜像

1
2
3
4
5
6
7
8
[root@localhost Dockerfiletest]# ll
总用量 8
-rw-r--r--. 1 root root 112 123 22:14 Dockerfile
-rw-r--r--. 1 root root 2892 123 22:14 dockerfile-test-1.0-SNAPSHOT.jar
[root@localhost Dockerfiletest]# docker build -t testdockerfile .
[root@localhost Dockerfiletest]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
testdockerfile latest f584d98b0712 About a minute ago 526MB

5、启动容器

1
docker run -d -p 8080:8080 --name testdockerfile testdockerfile

image-20250123230935986

img

Docker网络

image-20250106222458708

如上所示,只要装了docker,就会给本机新增一个docker0的网卡。

容器内网络情况

思考: 宿主机能不能ping通容器?可以相互ping

原理:我们每启动一个docker容器, docker就会给docker容器分配一个ip, 我们只要安装了docker,就会有一个网卡 docker0桥接模式,使用的技术是veth-pair技术!

1
2
3
4
# 我们发现这个容器带来的网卡都是一对对的,
# evth-pair 就是一对虚拟设备接口,它们都是成对出现的,一段连着协议,一段彼此相连
# 正因为这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备
# OpenStack,docker容器间的连接,OVS的连接,都是使用evth-pair 技术

思考:tomcat01、tomcat02 是否可以ping通?可以

结论:tomcat01和tomcat02是共用一个路由器,docker0

所有容器不指定网络的情况下,都是docker0来路由的,docker会给我们的容器分配一个默认的可用ip,因此tomcat01可以ping通tomcat02

img

Docker使用的是linux的桥接,宿主机是一个docker容器的网桥

img

link(不适用当前)

思考一个场景,我们编写了一个微服务,database url=ip: ,项目不重启,数据库ip换掉,我们希望可以处理这个问题,可以名字来进行访问容器。不用网络地址可以ping通

1
2
[root@localhost ~]# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known

如何解决这个问题?

添加了–link 实现容器名ping通,不需要通过网路ip

1
2
3
4
5
[root@localhost ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
[root@localhost ~]# docker exec -it tomcat03 ping tomcat02
64 bytes from tomcat02 (172.17.0.3): icmp_seq=74 ttl=64 time=0.134 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=75 ttl=64 time=0.156 ms
# 反向不能ping

–link就是我们在hosts配置中增加了一个 172.17.0.3 tomcat02 容器id 这种格式的host配置,但是这样写的太死了,不可持续,难以维护。

自定义网络

1
2
3
4
5
6
7
8
9
查看所有的docker网络
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
cd0629f7a781 bridge bridge local
c797f67cfda9 flink_default bridge local
83848de4da79 graylog_graylog bridge local
0d408d098017 host host local
345eb395330e kafka_default bridge local
6591455a2f45 none null local

网络模式

bridge:桥接docker (默认,自己创建的网络也是用bridge 模式)

none:不配置网络

host:和宿主机共享网络

container:容器网络连通(用的少,局限很大)

我们默认启动时其实就是默认 –net bridge,默认使用桥接的docker0这个网络

1
2
3
# 我们直接启动命令,--net bridge,而这个就是我们的docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat #等同于上一个命令

自定义网络

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
docker network create mynetwork --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1
mynetwork是网络名
--driver bridge 是网络模式
--subnet 192.168.0.0/16 子网是192.168.0.016位是网络号,后16位是主机号
--gateway 192.168.0.1 网关是192.168.0.1


#查看刚刚自定义的网络
[root@localhost ~]# docker network list
NETWORK ID NAME DRIVER SCOPE
cd0629f7a781 bridge bridge local
c797f67cfda9 flink_default bridge local
83848de4da79 graylog_graylog bridge local
0d408d098017 host host local
345eb395330e kafka_default bridge local
6cea02844ca7 mynetwork bridge local
6591455a2f45 none null local

#启动容器并绑定到自定义的网络
docker rm -f $(docker ps -aq)
docker run -d -p 3344:8080 --name tomcat01 --network mynetwork tomcat
docker run -d -p 3345:8080 --name tomcat02 --network mynetwork tomcat
[root@localhost ~]# docker network inspect mynetwork
[
{
"Name": "mynetwork",
"Id": "6cea02844ca734056bc69f14e79fdce871d2b7f610a402d259345f7dcf294daf",
"Created": "2025-01-21T22:07:18.289053597+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"567797a34578a54334a6bb4ac39cf2fc06fc9a322b3cfb49d7956f5b04da73a8": {
"Name": "tomcat01",
"EndpointID": "0e85ead22dc53f01ed2fdae970d740a5811e7f6d5a51575a6c69f2514b879c1e",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"a69aeaa3bfef9e7a17b6b77538a1b9e8b9d5d67f457f1181194c3213e0ea46ab": {
"Name": "tomcat02",
"EndpointID": "1b45e31f53e87aed935068e6761b35488d1164f2ac9d7f45942d7f551dc427d4",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

# 不使用--link都可以ping
[root@localhost ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
[root@localhost ~]# docker exec -it tomcat-net-01 ping tomcat-net-02

网络连接

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
[root@localhost ~]# docker network connect --help

Usage: docker network connect [OPTIONS] NETWORK CONTAINER

Connect a container to a network

Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., "172.30.100.104")
--ip6 string IPv6 address (e.g., "2001:db8::33")
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container

[root@localhost ~]# docker run -d -p 3346:8080 --name tomcat03 tomcat
# 连接tomcat03 到自定义网络 mynetwork
[root@localhost ~]# docker network connect mynetwork tomcat03
[root@localhost ~]# docker inspect network mynetwork
[
{
"Name": "mynetwork",
"Id": "6cea02844ca734056bc69f14e79fdce871d2b7f610a402d259345f7dcf294daf",
"Created": "2025-01-21T22:07:18.289053597+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"1f85ca4a00dbf7d502ae16564d9694830c5fcff782e2afe3f28590915b8ed789": {
"Name": "tomcat03",
"EndpointID": "e44f71b65dc14944103448e094dfe4e085cd070495eb006c4cd848e78fa8d10a",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
},
"567797a34578a54334a6bb4ac39cf2fc06fc9a322b3cfb49d7956f5b04da73a8": {
"Name": "tomcat01",
"EndpointID": "0e85ead22dc53f01ed2fdae970d740a5811e7f6d5a51575a6c69f2514b879c1e",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"a69aeaa3bfef9e7a17b6b77538a1b9e8b9d5d67f457f1181194c3213e0ea46ab": {
"Name": "tomcat02",
"EndpointID": "1b45e31f53e87aed935068e6761b35488d1164f2ac9d7f45942d7f551dc427d4",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
# 实现了跨网段用名称进行通信--这样就能ping通了
[root@localhost ~]# docker exec -it tomcat01 ping tomcat-net-01

结论

我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络

好处:

Redis- 不同的集群使用不同的网络,保证集群是安全和健康的

mysql- 不同的集群使用不同的网络,保证集群是安全和健康的

实战:部署Redis集群

img

测试六台服务器的集群,r-m3宕机,r-s3替补

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
# 通过脚本创建六个redis配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

# 创建结点
for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

done

[root@localhost ~]# docker exec -it redis-1 /bin/sh
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379
--cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 259ef84d4826a362bdc73ad25345513c9ebf9447 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 911abefbfae1d70df6d76003c361f25878ce0e77 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: 5bec694e52aa2bae43dc2755e325e1b0aaf87552 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: e240a6400ca97fc7de11d9d356e88bcc51515508 172.38.0.14:6379
replicates 5bec694e52aa2bae43dc2755e325e1b0aaf87552
S: fd596021d140108df6dda7f2cbcf76261e7aa74f 172.38.0.15:6379
replicates 259ef84d4826a362bdc73ad25345513c9ebf9447
S: fcd076bcd8314f935c1d1b99c47bf5a7b444e443 172.38.0.16:6379
replicates 911abefbfae1d70df6d76003c361f25878ce0e77
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 259ef84d4826a362bdc73ad25345513c9ebf9447 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 911abefbfae1d70df6d76003c361f25878ce0e77 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: fcd076bcd8314f935c1d1b99c47bf5a7b444e443 172.38.0.16:6379
slots: (0 slots) slave
replicates 911abefbfae1d70df6d76003c361f25878ce0e77
S: fd596021d140108df6dda7f2cbcf76261e7aa74f 172.38.0.15:6379
slots: (0 slots) slave
replicates 259ef84d4826a362bdc73ad25345513c9ebf9447
S: e240a6400ca97fc7de11d9d356e88bcc51515508 172.38.0.14:6379
slots: (0 slots) slave
replicates 5bec694e52aa2bae43dc2755e325e1b0aaf87552
M: 5bec694e52aa2bae43dc2755e325e1b0aaf87552 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

#至此docker搭建redis集群完成!

测试节点宕机,从节点提供服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#进入redis-1
[root@localhost ~]# docker exec -it redis-1
#查看集群信息
172.38.0.11:6379> cluster nodes
259ef84d4826a362bdc73ad25345513c9ebf9447 172.38.0.11:6379@16379 myself,master - 0 1737557695000 1 connected 0-5460
911abefbfae1d70df6d76003c361f25878ce0e77 172.38.0.12:6379@16379 slave fcd076bcd8314f935c1d1b99c47bf5a7b444e443 0 1737557695528 7 connected
fcd076bcd8314f935c1d1b99c47bf5a7b444e443 172.38.0.16:6379@16379 master - 0 1737557695000 7 connected 5461-10922
fd596021d140108df6dda7f2cbcf76261e7aa74f 172.38.0.15:6379@16379 slave 259ef84d4826a362bdc73ad25345513c9ebf9447 0 1737557694514 5 connected
e240a6400ca97fc7de11d9d356e88bcc51515508 172.38.0.14:6379@16379 slave 5bec694e52aa2bae43dc2755e325e1b0aaf87552 0 1737557694109 4 connected
5bec694e52aa2bae43dc2755e325e1b0aaf87552 172.38.0.13:6379@16379 master - 0 1737557695629 3 connected 10923-16383

#设置a的值
172.38.0.15:6379> set a bbb
-> Redirected to slot [15495] located at 172.38.0.13:6379

停用redis-3的容器服务
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"bbb"

image-20250122225932814

Docker Compose

简介

​ 简单来说,Docker Compose是Docker开源的另一个项目,来轻松高效的管理容器,定义运行多个容器。

Dockerfile build run 只能启动一个容器,如果有多个容器,可以借助docker-compose.yaml定义文件和docker-compose up -d 命令启动来编排和定义先后依赖关系,做到批量启动。

安装

​ 参考前文

命令

只构建镜像

当构建文件(Dockerfile)出错时,修改后,加build会重新构建

1
docker-compose build

只下载镜像docker-compose.yml文件中的镜像

1
docker-compose pull

启动容器,-d后台启动

执行此命令会先pull后启动

1
docker-compose up -d

查看使用docker-compose.yml启动的容器

1
docker-compose ps

临时进入docker-compose.yml启动的容器(查看不到)

1
docker-compose run  容器名 /bin/sh

启动docker-compose.yml中的容器

1
docker-compose start

停止docker-compose.yml中的容器

1
docker-compose stop

删除使用docker-compose.yml运行的容器(需要先stop)

1
docker-compose rm

YAML规则

https://docs.docker.com/compose/compose-file/#compose-file-structure-and-examples

docker-compose.yaml 的语法规则,包括版本、服务(含众多服务关键字)、网络(含网络关键字)、卷、配置项、密钥等的定义

docker-compose.yaml举例

主要有几个部分组成:version、services、volumes、networks,每个service的内容与每个dockerFile定义镜像类似的去describe每个服务的信息

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
version: "3"  # 指定docker-compose语法版本
services: # 从以下定义服务配置列表
server_name: # 可将server_name替换为自定义的名字,如mysql/php都可以
container_name: container_name # 指定实例化后的容器名,可将container_name替换为自定义名
image: xxx:latest # 指定使用的镜像名及标签
build: # 如果没有现成的镜像,需要自己构建使用这个选项
context: /xxx/xxx/Dockerfile # 指定构建镜像文件的路径
dockerfile: .... # 指定Dockerfile文件名,上一条指定,这一条就不要了
ports:
- "00:00" # 容器内的映射端口,本地端口:容器内端口
- "00:00" # 可指定多个
volumes:
- test1:/xx/xx # 这里使用managed volume的方法,将容器内的目录映射到物理机,方便管理
- test2:/xx/xx # 前者是volumes目录下的名字,后者是容器内目录
- test3:/xx/xx # 在文件的最后还要使用volumes指定这几个tests
volumes_from: # 指定卷容器
- volume_container_name # 卷容器名
restarts: always # 设置无论遇到什么错,重启容器
depends_on: # 用来解决依赖关系,如这个服务的启动,必须在哪个服务启动之后
- server_name # 这个是名字其他服务在这个文件中的server_name
- server_name1 # 按照先后顺序启动
links: # 与depend_on相对应,上面控制容器启动,这个控制容器连接
- mysql # 值可以是- 服务名,比较复杂,可以在该服务中使用links中mysql代替这个mysql的ip
networks: # 加入指定的网络,与之前的添加网卡名类似
- my_net # bridge类型的网卡名
- myapp_net # 如果没有网卡会被创建,建议使用时先创建号,在指定
environment: # 定义变量,类似dockerfile中的ENV
- TZ=Asia/Shanghai # 这里设置容器的时区为亚洲上海,也就解决了容器通过compose编排启动的 时区问题!!!!解决了容器的时区问题!!!
变量值: 变量名 # 这些变量将会被直接写到镜像中的/etc/profile
command: [ #使用 command 可以覆盖容器启动后默认执行的命令
'--character-set-server=utf8mb4', #设置数据库表的数据集
'--collation-server=utf8mb4_unicode_ci', #设置数据库表的数据集
'--default-time-zone=+8:00' #设置mysql数据库的 时区问题!!!! 而不是设置容器的时区问题!!!!
]
server_name2: # 开始第二个容器
server_name:
stdin_open: true # 类似于docker run -d
tty: true # 类似于docker run -t
volumes: # 以上每个服务中挂载映射的目录都在这里写入一次,也叫作声明volume
test1:
test2:
test3:
networks: # 如果要指定ip网段,还是创建好在使用即可,声明networks
my_net:
driver: bridge # 指定网卡类型
myapp_net:
driver: bridge

Docker Swarm

docker:容器

docker-compose:一个节点上的一系列容器

docker-swarm:多个主机或者集群上的多套环境容器的管理大规模容器化应用,

docker-swarm对标K8S

不过业界用docker-swarm比较少,学习意义不大