0%

缓存穿透、击穿、雪崩

缓存穿透

缓存穿透是指缓存和数据库中都没有的数据, 在高并发下对不存在的 key 的操作. 由于缓存是不命中时被动写的, 并且出于容错考虑, 如果存储层查不到数据则不写入缓存, 这将导致这个不存在的数据每次请求都要到存储层去查询, 失去的缓存的意义. 在流量大时, 可能引起数据库崩溃. 或者有人利用不存在的 key 频繁攻击应用, 可能会引起应用的崩溃

解决办法

  • 接口层增加校验, 如用户鉴权校验、id 做基础校验、 id <= 0 的直接拦截
  • 从缓存取不到的数据, 在数据库中也取不到时,可以将 key-value 写为 key-null, 缓存有效时间设置短点, 这样可以防止攻击用户反复用同一个 key 暴力攻击
  • 布隆过滤器, 类似于一个 hash set, 用于快速判断某个元素是否存在于集合中, 其典型的应用场景就是快速判断一个 key 是否存在于某容器, 不存在就直接返回. 布隆过滤器的关键就在于 hash 算法和容器大小

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期), 在高并发下对同一 key 的操作. 如果在缓存中没有获取到数据, 又同时在数据库中获取到数据, 引起数据库压力过大.

解决办法

  • 设置热点数据永不过期
  • 接口限流与熔断、降级, 重要的接口一定要做好限流策略, 防止用户恶意刷接口, 同时要降级准备, 当接口中的某些服务不可用时, 进行熔断, 失败快速返回机制
  • 加互斥锁

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间, 而查询数据量巨大, 引起数据库压力过大甚至崩溃. 和缓存击穿不同的是, 缓存击穿指并发查询同一条数据, 缓存雪崩是不同数据都过期了, 很多数据都查不到从而查询数据库

解决办法

  • 缓存数据的过期时间设置随机, 防止同一时间大量数据过期现象发生
  • 如果缓存数据库是分布式部署, 将热点数据均匀分布在不同的缓存数据库中
  • 设置热点数据永不过期
阅读全文 »

Redis

Remote Dictionary Server 即远程字典服务, 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API, Redis 能读的速度是 11 万次/s,写的速度是 8.1 万次/s
Redis 通常被称为数据结构服务器, 因为它的核心数据类型包括字符串、列表、字典(或哈希)、集合和排序集合等大多编程语言都支持的数据类型. 高版本版的 Redis 还添加了计算基数、地理定位和流处理等高级功能

数据类型

redis-1

阅读全文 »

聚合操作

操作处理多个文档并返回计算结果, 由 一个或多个处理文档的 阶段 组成

  • 每个阶段对输入文档执行一个操作
  • 从一个阶段输出的文档将传递到下一个阶段
  • 一个聚合管道可以返回针对文档组的结果

字段路径, 使用 $ 前缀启用字段路径表达式访问输入文档中的字段.

管道优化, 该阶段会尝试重塑管道以提高性能, 优先使用 $match、$sort、$limit、$skip 阶段限制进入管道的文档

限制

  • 结果大小限制, 每个文档均受 MB16 BSON 文档大小的限制的约束
  • 阶段数量限制, 单个管道中允许的聚合管道阶段数量最大为 1000 个
  • 内存限制, 6.0 开始 allowDiskUseByDefault 参数控制需要 100MB 以上内存容量来执行的管道阶段是否默认会将临时文件写入磁盘

db.[collectionName].aggregate() 方法运行的聚合管道不会修改集合中的文档, 除非管道包含 $merge$out 阶段

1
2
3
4
5
6
7
8
db.orders.aggregate([
// stage 1: Filter pizza order document by pizza size
{$match: {size: 'medium'}},
// stage 2: Group remaining document by pizza name and calcuate total quantity
{$group: {_id: '$name', totalQuantity: {$sum: "$quantity"}}},
// stage 3: Sort document by totalQuantity in descending order
{$sort: {totalQuantity: -1}}
])

变量

变量可以保存任何 BSON 类型的数据, 访问变量时需要使用 $$ 前缀.

用户变量

变量名称可包含 ASCII 字符和任意非 ASCII 字符, 必须以小写 ASCII 字符开头

系统变量

  • NOW 返回当前日期时间值的变量, 为部署的所有成员返回相同的值, 并在聚合管道的所有阶段保持不变.
  • CLUSTER_TIME 返回当前时间戳值的变量
    • 仅适用于副本集和分片的集群
    • 为部署的所有节点返回相同的值, 并在管道的所有阶段保持不变
  • ROOT 引用根文档, 即当前正在聚合管道阶段处理的顶层文档
    • 引用聚合管道中当前正在处理的完整文档
    • 常用于需要保留原始文档内容的场景
    • 在 $group、$project 等阶段特别有用
  • CURRENT 引用聚合管道阶段正在处理的 字段路径 的起始位置
  • REMOVE 一个求值为缺失的变量, 允许排除 $addFields 和 $project 阶段的字段
  • DESCEND $redact 表达式的允许结果之一, 返回当前文档级别的字段, 不包括嵌入式文档
  • PRUNE $redact 表达式的允许结果之一, 排除当前文档/嵌入式文档级别的所有字段, 而不进一步检查任何已排除的字段
  • KEEP $redact 表达式的允许结果之一, 返回或保留此当前文档/嵌入式文档级别的所有字段, 而不进一步检查此级别的字段
  • SEARCH_META search 查询元数据结果的变量, 在所有支持的聚合管道阶段中, 设立变量 $SEARCH_META 的字段会返回查询的元数据结果
  • USER_ROLES 返回分配给当前用户的角色
阅读全文 »

事务隔离级别

mysql 事务隔离级别定义了一个事务在多大程度上能够看到其他并发事务所做的修改

1
2
3
4
5
6
7
8
9
10
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL <level>;

-- 全局设置
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 当前会话设置
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 事务开始时设置
SET TRANSACTION ISOLATION LEVEL <level>;

读未提交(Read Uncommitted)

最低级别的隔离, 允许一个事务读取另一个事务尚未提交的数据, 通常不推荐使用此级别, 除非对数据一致性要求非常低的场景

问题:

  • 脏读(Dirty Read), 一个事务可以读取到另一个事务未提交的数据, 如果该事务回滚, 则会导致数据不一致
  • 不可重复读(Non-repeatable Read), 在一个事务中多次读取同一数据可能会得到不同的结果
  • 幻读(Phantom Read), 在一个事务中执行相同的查询可能会返回不同的行集, 因为其他事务插入了新的行
1
2
3
4
5
6
7
8
9
10
11
12
-- 事务 A
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 暂时不提交
COMMIT;

-- 事务 B
START TRANSACTION;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT balance FROM accounts WHERE id = 1;
-- 可能读取到 900 (脏读)
COMMIT;
阅读全文 »

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
{
"[typescriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
"source.removeUnusedImports": "explicit",
},
},
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
"source.removeUnusedImports": "explicit",
},
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
"source.removeUnusedImports": "explicit",
},
},
"[vue]": {
"editor.defaultFormatter": "Vue.volar",
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
"source.removeUnusedImports": "explicit"
}
},
}

命令行输出内容变身

格式: \033[显示方式;前景色;背景色 m …… \033[0m

  • \033[ 固定格式
  • \033[0m 非必需, 如果省略表示后面输出内容的样式都会应用当前设置的样式

属性集

前景色 背景色 色值
30 40 黑色
31 41 红色
32 42 绿色
33 43 黄色
34 44 蓝色
35 45 紫红色
36 46 青蓝色
37 47 白色

显示方式

显示方式 表现行为
0 默认
1 高亮
4 下划线
5 闪烁
7 反色
8 不可见
1
2
3
4
5
6
7
8
# 输出字体为绿色的 你好hello world
[root@localhost ~]# printf '\033[1;32m你好hello world\033[0m\n'
# 输出字体为绿色并带有下划线的 你好hello world
[root@localhost ~]# printf '\033[4;32m你好hello world\033[0m\n'
# 输出背景色为绿色的 你好hello world
[root@localhost ~]# printf '\033[7;32m你好hello world\033[0m\n'
# 输出内容不可见
[root@localhost ~]# printf '\033[8;32m你好hello world\033[0m\n'
阅读全文 »

systemd

systemd(system daemon)是 linux 下的一种 init 软件, 提供更优秀的框架以表示系统服务间的依赖关系, 并依此实现系统初始化时服务的并行启动, 同时达到降低 shell 的系统开销的效果, 最终代替常用的 System V 与 BSD 风格 init 程序

  • 采用 socket 激活式与总线激活式服务, 以提高相互依赖的各服务的并行运行性能
  • 采用 cgroup 代替 PID 来追踪进程, 依此即使是两次 fork 之后生成的守护进程也不会脱离 systemd 的控制

CGroup

cgroup 是 linux 内核的一个功能, 用来限制、控制与分离一个进程组的资源(如 CPU、内存、磁盘输入输出等)

cgroup 是 linux 内核提供的一种机制, 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内, 从而为系统资源管理提供一个统一的框架. 简单说, cgroup 可以限制、记录任务组所使用的物理资源, 本质上来说, cgroup 是内核附加在程序上的一系列钩子(hook), 通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的.

作用

  • 资源限制: cgroup 可以对任务需要的资源总额进行限制, 例如设定任务运行使用的内存上限, 一旦超出就触发 OOM
  • 优先级分配: 通过分配的 CPU 时间片数量和磁盘 IO 带宽, 实际上就等于控制了任务运行的优先级
  • 资源统计: cgroup 可以统计系统的资源使用量, 例如 CPU 使用时长、内存使用量等
  • 任务控制: cgroup 可以对任务执行挂起、恢复等操作
阅读全文 »

敲黑板

onMenuShareAppMessage (微信提示: 即将废弃)

  • jweixin-1.4.0.js

企微(4.0.6) 中使用此 API 的位置

  • 转发
  • 转发给客户
  • 群发到客户群
  • 发表到客户的朋友圈
  • 分享到同事吧
  • 分享到微信
  • 分享到微信朋友圈使用此 onMenuShareTimeline, 但无法设置分享信息
阅读全文 »

Docker Compose

  • Docker Compose 以服务为单位, 将为每一个服务部署一个容器
  • 默认以 应用名称-服务名称-数字 方式作为容器名称
  • 默认以 应用名称_数据卷名 方式作为数据卷名称
  • 默认以 应用名称_网络名 方式作为网络名称

Docker Compose 是定义和运行多容器 Docker 应用程序的工具, 运行部分命令时需要在 compose.yaml/docker-compose.yaml 文件所在目录中, 以 应用名称-服务名称-数字 编号为规则命名容器, 配置文件使用 yaml 语法, yaml 是一个可读性高,用来表达数据序列化的格式.

yaml 文件中不能使用 tab 缩进, 只能使用空格

1
2
3
4
${VAR:-default}   如果 变量名 对应的环境变量未设置或为空, 则使用 默认值, 否则使用环境变量的值 
${VAR-default} 仅当 变量名 对应的环境变量 完全未定义 时才用默认值(为空时不会替换)
${VAR:?error} 严格模式, 如果 变量名 对应的环境变量 未设置/为空, 则报错并显示错误信息 error
${VAR:+alternate} 反向逻辑, 如果 变量名 已设置且非空, 则使用 alternate
1
2
3
4
# 启动指定服务, 不加参数则默认启动所有服务
docker compose -f -p -c --env-file .env.development up [service_name]

# 以下的命令不带服务名称则默认对所有服务执行相同操作

参数

  • --all-resources 引入所有的资源, 即使未被服务使用

  • -f, --file stringArray 指定配置文件

  • -p, --project-name string 指定项目名称

  • --project-directory string 指定项目工作目录

  • -c, --context 指定上下文环境名称

  • --env-file stringArray 指定环境变量配置文件

  • --parallel 设置并行

  • --compatibility 运行 compose 兼容模式

  • --profile 启用指定的服务, web 服务默认启动

1
docker compose --profile db up -d # 只启动 db, web 服务

命令

  • attach 连接运行服务的标准输入输出

  • build 构建服务

  • commit 从服务容器创建一个新的镜像

    • -a, --author 作者
    • -m, --message string 提交信息
    • -c, --change list 应用 Dockerfile 指令创建镜像
    • --index int 指定如果有多个副本的服务的容器
    • -p, --pause 提交过程中是否中断容器运行, 默认为 true
  • config 解析验证 compose.yaml 配置文件

    • --environment 打印环境变量的插值

    • --format string 格式化输出, 值可选 yaml(default) | json

    • --images 输出镜像名称

    • --hash 输出服务的配置 hash, 一个一行

    • --profiles 输出指定的服务名, 一个一行

    • -o, --output string 保存到指定文件中, 默认是标准输出流

    • -q, --quiet 仅验证配置项, 不输出任何信息

    • --services 输出服务名称

    • --volumes 输出数据卷名称

  • cp 在容器和本地文件系统之间拷贝文件

  • events 接收一个来自容器的真实的事件

  • export 导出容器文件系统为归档文件

  • images 列出创建容器的镜像

  • start 启动服务

  • restart 重启服务

  • wait 阻塞直到所有的服务容器停止

  • stop 停止服务

  • kill 强制停止容器

  • pause 暂停服务

  • unpasue 取消暂停服务

  • rm 移除已经停止的容器, 默认情况下附加到容器上的匿名数据卷不会被移除

    • -f, --force 不询问确认操作直接移除
    • -s, --stop 在移除之前停止容器
    • -v, --volumes 移除所有附加到容器上的匿名数据卷
  • down 停止并移除容器, 网络

    • --rmi string 移除服务使用的镜像
    • -t, --timeout int 延迟关机的时长秒
    • -v, --volumes 移除在 compose.yaml 中 volumes 顶层指令中声明的具名和匿名数据卷
  • logs 查看服务输出日志

    • -f, --follow 监听日志输出
    • --index int 指定哪个容器执行命令, 如果服务有多个副本时
    • -n, --tail 输出容器日志的最后几行
    • --since 显示指定时间开始的日志
    • --until 显示截止到指定时间的日志
  • stats 查看容器资源的使用情况

  • ls 列出正在运行的 compose 项目

  • port 查看公共端口绑定信息

    • --index int 指定如果有多个副本的服务的容器
    • --protocol string 指定协议, tcp(default) | udp
  • ps 查看所有容器

    • -a, --all 列出所有容器
    • -f, --filter <FILTER_TYPE>=<VALUE> 根据指定条件过滤服务容器
    • --format string 使用自定义模板格式化输出, table(default) | table TEMPLATE | json | TEMPLATE
    • --services 显示服务名称
    • --status stringArray 通过状态过滤服务, [paused | restarting | removing | running | dead | created | exited]
  • pull 拉取服务镜像

  • push 推送服务镜像

  • top 显示运行的进程信息

  • exec 在运行的容器中执行命令

    • -d, --detach 后台运行命令
    • -e, --env stringArray 设置环境变量
    • --index int 指定哪个容器执行命令, 如果服务有多个副本时
    • -T, --no-TTY 不分配伪 TTY, 默认每次执行命令时都分配 TTY
    • -w, --workdir string 设置本次命令的工作目录
  • run 在服务上运行一次性命令

    • --build 启动容器之前构建镜像
    • -d, --detach
    • -i, --interactive 交互式运行
    • -e, --env stringArray 设置环境变量
    • -l, --label stringArray 添加或覆盖 label
    • -P, --service-ports 运行命令所有服务的端口都能映射到宿主机
    • --name 给容器定义名字
    • --pull 运行之前拉取镜像
    • -p, --publish stringArray 发布容器的端口到宿主机
    • --rm 当容器退出时自动移除
    • -v, --volume stringArray 挂载数据卷
    • -w, --workdir string 设置容器内的工作目录
  • scale 调整服务

    • --no-deps 不启动关联的服务
  • version 查看版本信息

  • watch 监听文件系统更新时服务容器重构/重启的构建上下文

  • create 为服务创建容器

    • --build 启动容器之前构建镜像
    • --no-build 不构建镜像即使镜像不存在
    • --force-recreate 即使配置项或镜像没有改变也要重新创建容器
    • --no-recreate 如果容器存在则不创建新的容器
    • --pull 创建之前拉取镜像
    • --scale scale 调整服务实例数量, 并覆盖 compose.yaml 配置文件中的 scale 配置
  • up 创建服务并启动容器

    • -d, --detach 后台运行容器

    • --attach 连接服务的输出

    • --no-attach 不连接服务的输出

    • --build 启动容器之前构建镜像

    • --no-build 不构建镜像即使镜像不存在

    • --no-start 创建服务之后不启动它

    • --no-deps 不启动关联的服务

    • --pull 启动之前拉取镜像

    • --scale scale 调整服务实例数量, 覆盖 compose.yaml 配置文件中的 scale 配置

    • --no-log-prefix 打印日志时不适用前缀

    • --no-recreate 如果容器存在则不创建新的容器

    • -y 非交互式运行命令, 所有的提示都回答 yes

    • -t, --timeout int 延迟关闭容器

1
2
3
4
[root@localhost ~]# docker compose up service_id # 启动指定服务

# 调整指定服务实例数量, 先去掉 compose.yaml/docker-compose.yaml 配置文件 service 指定的端口, 在单机中会出现端口占用问题
[root@localhost ~]# docker compose up --scale web=5 -d

配置文件

  • 使用副本不能指定容器名称, Compose 自动使用 应用名称-服务名称-数字 形式命名容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# compose.yaml/docker-compose.yaml
version: 3.9 # 版本, obsolete(已过时)
name: myapp # 定义默认的项目名称, 将以环境变量 ${COMPOSE_PROJECT_NAME} 的方式公开
services:
web: # 服务名称
annotations: # 容器声明, 可以是 arr 或 map
com.example.foo: bar
# - com.example.foo=bar
attach: false # 设置为 false 时不会主动收集服务日志, 默认为 false, v2.20.0 以上支持
build:
context: './web' # 指定构建 web 服务的镜像的上下文环境目录
dockerfile: Dockerfile # 指定构建镜像的配置文件名称
command: ['bundle', 'exec', 'thin', '-p', '3000'] # 覆盖镜像配置文件(Dockerfile)中的CMD指令
ports: # 端口映射
- '5000:5000'
- '0.0.0.0:80:80/tcp' # 指定 ip 地址和协议, 或修改 /etc/docker/daemon.json 配置项"ipv6":false
- target: 80
host_ip: 127.0.0.1
published: 8080
protocol: tcp
mode: host
privileged: true # 配置容器目录权限
read_only: true # 开启容器文件系统只读模式
restart: always # 定义容器重启模式 "no" | always | on-failure | unless-stopped
container_name: my-web # 容器名称, 使用副本不能指定容器名称, Compose 自动使用 应用名称-服务名称-数字 形式命名容器
env_file: .env # 环境变量配置文件
environment: # 设置容器内环境变量
RACK_ENV: development
SHOW: 'true'
USER_INPUT:
entrypoint: # 覆盖镜像配置文件(Dockerfile)中的 ENTRYPOINT 指令
- php
- -d
- vendor/bin/phpunit
external_links: # 将服务容器连接到 compose 应用管理以外的服务, 作用同 links
- redis
- database:mysql
- database:postgresql
extra_hosts: # 添加主机 ip 映射关系到容器网络接口配置中(/etc/hosts)
- 'somehost:162.242.195.82'
- 'otherhost:50.31.209.229'
volumes: # 挂载数据卷
- type: volume
source: db-data
target: /data
read_only: true
volume:
nocopy: true
subpath: sub
tempfs:
size: 1024
mode: 755
- type: bind
source: /home/workspace
target: /home/workVolume
- /home/workspace:/var/workspace # 定义指定路径数据卷
tmpfs:
- /run # 挂载容器内临时文件系统
- /tmp
volumes_from: # 挂载共享数据卷
- service_name
- service_name:ro
- container:container_name
- container:container_name:rw
network_mode: "host|none|service:[service name]" # 设置服务容器的网络模式
networks: # 自定义网络模式
- my-web-network
platform: linux/amd64 # 设置服务容器运行的目标平台
# 当前服务启动的依赖优先于当前服务启动
# 当前服务关闭优先于当前服务的依赖关闭
depends_on: # 服务启动依赖,
db:
condition: service_healthy
restart: true
redis:
condition: service_healthy
# - db
# - redis
deploy: # 部署
# 外部客户端连接服务的方式
# vip(Virtual IP) 为服务分配虚拟 IP, 客户端使用虚拟 IP 连接
# dnsrr 平台配置 dns 条目, 使用服务名称查询 IP 地址列表连接
endpoint_mode: vip
labels:
com.example.description: 'This label will appear on the web server' # 服务元数据
mode: replicated # 服务运行模式, global | replicaated(default) | replicated-job | global-job
replicas: 6 # 实例
restart_policy: # 服务重启策略, 如果缺失, compose 会使用服务 restart 项
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
rollback_config: # 服务回滚设置
update_config: # 服务升级设置
parallelism: 2
delay: 10s
order: stop-first
dns:
- 8.8.8.8 # 自定义网络的 DNS 服务器
extends:
file: common.yml # 当前配置中扩展另一个服务
labels: # 添加容器元数据
- 'com.example.description=Accounting webapp'
develop: # 定义开发模式容器同步
devices:
dns:
dns_opt:
dns_search:
healthcheck: # 服务健康检查
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
start_interval: 5s

redis: # 服务名称
# 当同时使用 image 和 build 指令构建时, 将按照 pull_policy 的定义进行构建,
# 如果未定义 pull_policy 时, Compose 会尝试先拉取镜像, 如果在镜像仓库或缓存中找不到镜像时从源构建
pull_policy: always # Compose 总是从镜像仓库拉取镜像
never # Compose 不会从镜像仓库拉取镜像而是依赖缓存中的镜像, 如果缓存中不存在则报告错误
missing # 默认选项, Compose 仅当缓存中镜像不可用时从镜像仓库拉取
build # Compose 构建镜像如果镜像已经存在则重新构建
image: redis
cpuset: '0,2,4,6' # 绑定 cpu
build:
context: redis
dockerfile: /redis.Dockerfile
volumes: # 挂载数据卷
- /home/workspace # 定义匿名数据卷
- db-data:/var/lib/redis # 挂载公共数据卷 db-data
networks: # 自定义网络模式
- my-web-network
links: # 定义网络连接另一个服务的容器
- db:mysql # 可以直接使用 服务名, 或者使用 服务名:别名 方式
scale: 6 # 设置容器数量, 如果 scale 和 deploy.replicas 同时存在则必须保持一致
profiles: # 指定启动时的 profile
- 'db'

db:
image: mysql
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test123
volumes:
- db-data:/var/lib/mysql # 挂载公共数据卷 db-data
networks:
- my-web-network
profiles: # 指定启动时的 profile
- 'debug'

# 跨服务共享数据卷定义到顶层指令 volumes
volumes:
db-data: # 声明卷名, compose 自动创建该卷名并会添加项目名前缀
data:
name: 'my-app-data'

networks:
front-tier:
back-tier:
backend:
driver: custom-driver
my-web-network: # 声明自定义网络模式, compose 自动创建该网络并会添加项目名前缀
driver: bridge
name: myapp-network # 定义网络名称, 在 docker network 列表中显示
attachable: true # 允许独立的容器连接到此网络
enable_ipv6: true
external: true # 指定此网络的生命周期在应用程序的生命周期之外进行维护, Compose 不会尝试创建这些网络, 如果不存在则返回错误
configs: # 允许服务调整其行为而无须重新构建 docker 镜像
http_config:
file: ./httpd.conf
secrets: # 针对敏感数据的配置
server-certificate:
file: ./server.cert
token:
environment: 'OAUTH_TOKEN'

volumes

  • 使用路径方式挂载数据卷

  • 使用卷名方式挂载数据卷, 需要在 一级配置项 中声明, compose 会自动创建以项目名为前缀的卷名, 如果不需要卷名前缀, 则使用 external: true 指定卷名, 但是需要手动创建该卷名

多实例 Web 应用

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
# compose.yaml
name: myapp
service:
nginx:
image: nginx:latest
port:
- '80:80'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf # 自定义 nginx 配置文件
depends_on:
- web
network:
- webnet
web:
build:
context: web
dockerfile: ./my-web-app.Dockerfile
deploy:
replicas: 3 # 启动 3 个 web 实例
environment:
- ENV=production
network:
- webnet
networks:
webnet:
driver: bridge

nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# nginx.conf
http {
upstream web_backend {
server web:80;
server web:80;
server web:80;
}
server {
listen 80;
location / {
proxy_pass http://web_backend;

proxy_set_header Host $host;
proxy_set_header X-Reap-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}

web 应用

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# my-web-app.Dockerfile
# 使用官方 Python 镜像作为基础镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app

# 复制依赖文件并安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .
EXPOSE 80
CMD ["python", "app.py"]

docker compose 数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
name: myapp # ${COMPOSE_PROJECT_NAME} 以环境变量形式访问项目名称
services:
redis:
image: redis:7
# 使用副本不能指定容器名称, Compose 自动使用 应用名称-服务名称-数字 形式命名容器
# container_name: redis-container
ports:
- '6379:6379'
command: ['redis-server', '--appendonly yes', '--logfile /data/redis.log']
volumes:
- /var/lib/redis:/data
- /var/lib/redis/redis.conf:/usr/local/etc/redis/redis.conf
networks:
- my-app-network
# deploy:
# replicas: 3
# labels:
# com.myapp.redis.description: 'This label will appear on the redis server'
mysql:
image: mysql:latest
container_name: mysql-container
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test123
volumes:
- /var/lib/mysql:/var/lib/mysql
networks:
- my-app-network
mongodb:
# mongodb 6.0 以上 docker 镜像不再包含 mongo shell 工具. 只包含 mongod 数据库服务器
# 手动下载 mongosh 工具, 或者使用 mongodb 6.0 之前的版本
image: mongo:latest
container_name: mongodb-container
ports:
- '27017:27017'
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: 123456
MONGODB_BIND_IP: 0.0.0.0
command: ['mongod', '--logpath', '/var/log/mongodb/mongod.log']
volumes:
- /var/lib/mongodb:/data/db
- /var/lib/mongodb/logs:/var/log/mongodb
networks:
- my-app-network
# 挂载临时文件系统添加初始化脚本安装 mongosh
tmpfs:
- /tmp
# 首次创建容器需要先执行 entrypoint 命令安装 mongosh, 然后再注释 entrypoint
# entrypoint: ['/bin/sh', '-c', 'apt-get update && apt-get install -y wget && wget -qO- https://downloads.mongodb.com/compass/mongosh-1.5.4-linux-x64.tgz | tar -xz -C /usr/local/bin --strip-components 1 mongosh-1.5.4-linux-x64/bin/mongosh && exec docker-entrypoint.sh $$MONGO_INITDB_ROOT_USERNAME $$MONGO_INITDB_ROOT_PASSWORD']
# sqllite:
# image: sqllite3:latest
# command: ['sqllite3', '/data/database.db'] # 启动 sqllite 并指向数据库文件
# volumes:
# - /var/lib/sqllite:/data
# networks:
# - my-app-network
volumes:
db-data:
labels:
- 'com.myapp.volumes.description=share data vaolume'
networks:
my-app-network:
driver: bridge
name: myapp-network # 定义网络名称, 在 docker network 列表中显示
attachable: true # 允许独立的容器连接到此网络
enable_ipv6: true

Docker Swarm

介绍

Swarm 是 Docker 官方提供的一款集群管理工具, 其主要作用是把若干台 Docker 主机抽象为一个整体, 并且通过一个入口统一管理这些 Docker 主机上的各种 Docker 资源

从集群角度来说, 一个 Swarm 由一个或多个 Docker 节点组成. 这些节点可以是物理服务器、虚拟机、树莓派(Raspberry Pi)或云实例. 唯一的前提就是要求所有节点通过可靠的网络相连

节点会被配置为管理节点(Manager)或工作节点(Worker). 管理节点负责集群控制面(Control Plane), 进行诸如监控集群状态、分发任务至工作节点等操作. 工作节点接收来自管理节点的任务并执行.

Swarm 的配置和状态信息保存在一套位于所有管理节点上的分布式 etcd 数据库中. 该数据库运行于内存中, 并保持数据的最新状态. 关于该数据库最棒的是, 它几乎不需要任何配置, 作为 Swarm 的一部分被安装, 无须管理

  • Swarm 和 Kubernetes 比较类似, 但是更加轻, 具有的功能也较 kubernetes 更少一些
  • Docker Swarm 包含两方面:一个企业级的 Docker 安全集群, 以及一个微服务应用编排引擎
  • Swarm 默认内置有加密的分布式集群存储(encrypted distributed cluster store)、加密网络(Encrypted Network)、公用 TLS(Mutual TLS)、安全集群接入令牌 Secure Cluster Join Token)以及一套简化数字证书管理的 PKI(Public Key Infrastructure). 我们可以自如地添加或删除节点
  • 编排方面, Swarm 提供了一套丰富的 API 使得部署和管理复杂的微服务应用变得易如反掌. 通过将应用定义在声明式配置文件中, 就可以使用原生的 Docker 命令完成部署

docker-7

令牌格式

1
2
3
PREFIX - VERSION - SWARM ID - TOKEN

SWMTKN-1-5uqag7ddbx6jp9l273blxmda6308l5cn23487hbwsnw71w6dsh-eh4h7yhzchi0p6cy2ihg539jh
  • PREFIX 令牌前缀,便于区分 固定为 SWMTKN
  • VERSION Swarm 的版本信息
  • SWARM ID Swarm 认证信息的一个哈希值
  • TOKEN 标识管理节点还是工作节点的准入令牌

初始化 init

  • --advertise-addr 指定其他节点用来连接到当前管理节点的 IP 和端口, 当节点上有多个 IP 时指定
  • --listen-addr 指定用于承载 Swarm 流量的 IP 和端口. 其设置通常与 --advertise-addr 相匹配, 但是当节点上有多个 IP 的时候,可用于指定具体某个 IP
  • --autolock 启用锁

开放端口

  • 每个节点都需要安装 Docker, 并且能够与 Swarm 的其他节点通信

需要在路由器和防火墙中开放如下端口

  • 2377/tcp: 用于客户端与 Swarm 进行安全通信
  • 7946/tcp 与 7946/udp: 用于控制面 gossip 分发
  • 4789/udp: 用于基于 VXLAN 的覆盖网络
1
2
3
4
5
6
7
8
[root@localhost ~]# docker swarm init --advertise-addr 192.168.1.2 --listen-addr 192.168.1.2
Swarm initialized: current node (5r1q8c5jaawi9w1wd8yr7w3u2) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-5uqag7ddbx6jp9l273blxmda6308l5cn23487hbwsnw71w6dsh-eh4h7yhzchi0p6cy2ihg539jh 192.168.1.2:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
阅读全文 »