有下面两个字典:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

想要合并成一个字典:

z = {'a': 1, 'b': 3, 'c': 4}

有以下几种方法:

Python 3.9 以上:

z = x | y

Python 3.5 以上:

z = {**x, **y}

Python 3.4 以上、Python 2:

def merge_two_dicts(x, y):
    z = x.copy()  # 复制 x
    z.update(y)   # 合并 y
    return z

z = merge_two_dicts(x, y)

背景

今天服务器 Redis 服务突然报错

MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.

网上搜索到解决方案是修改 redis.conf 配置文件,把 stop-writes-on-bgsave-error 设置为 no 即可。结果在保存配置文件的时候报错,提示磁盘已满。

后来发现罪魁祸首原来是 Docker 日志,有一个日志文件居然占了 23GB!

$ du -h $(find /var/lib/docker/containers/ -name *-json.log)
164M    /var/lib/docker/containers/1111/1111-json.log
60M     /var/lib/docker/containers/2222/2222-json.log
100M    /var/lib/docker/containers/3333/3333-json.log
12M     /var/lib/docker/containers/4444/4444-json.log
52K     /var/lib/docker/containers/5555/5555-json.log
23G     /var/lib/docker/containers/6666/6666-json.log

解决

1、清空日志文件

$ cat /dev/null > /var/lib/docker/containers/xxx/xxx-json.log
直接删除日志文件不能立即释放磁盘空间

2、全局日志设置

Docker 支持全局日志设置,但只对之后新建的容器有效。

$ vim /etc/docker/daemon.json
{
    .
    .
    .
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "100m",
        "max-file": "3"
    }
}

背景

Docker 是一个非常棒的工具,能够让我们的开发工作更简单、更快、更高效。它的优点就不多说了,但是凡事都有两面性,对于 macOS 和 Windows 系统的用户来说,Docker 还是有一些问题的。最大的问题还是它在这两大操作系统上的网络和 I/O 性能瓶颈。这里我们就讲讲数据卷缓存方法,它们可以加速我们的 Docker 运行速度。

在使用 Docker 时,我们经常会用到数据卷,如共享代码、链接资源、提供备份等。数据卷有很多使用场景,它的 I/O 操作有时很轻,有时很重很密集。在 Linux 系统上,因为可以使用原生内核和系统资源,运行情况比在 macOS 和 Windows 下好很多。在默认配置中,数据卷始终确保主机和容器之间的数据一致性:如果您在主机或容器上保存一些内容,更改会立即反映在另一端,这样可以防止数据丢失。

Docker 提供了两种方法来缓存和加速数据卷,它们都牺牲了一些一致性,但是可以大幅提升运行速度。这两种方法就是:cached 和 delegated。下面是 Docker 官方文档介绍。

delegated

delegated 模式提供最弱保障。对于这类文件夹,容器对文件系统具有权威性。由容器执行的写操作可能不会立即反映在主机文件系统上。在一些场景下,如 NFS 异步模式,如果挂载了 delegated 模式数据卷的容器在运行中崩溃,写入的数据可能会丢失。

cached

cached 配置包含 delegated 配置的所有保障,并且还提供了容器写操作可见性的额外保障。因此,cached 模式通常可以提升频繁读操作场景时的性能,代价是在容器和主机之间牺牲一些临时的不一致性。

它们使用起来非常简单:

  • delegated-volume:/var/volume1:delegated
  • cached-volume:/var/volume2:cached

区别和选择

这两种模式的 I/O 操作都会比默认模式快很多,并且不会影响容器和主机之间的性能。问题是我们该使用哪一种?那当然要看场景了。文档虽然介绍的很清楚,但是没有提供任何实际应用案例。那我们就举例子说明一下。

案例一

我们有一个数据库容器(MySQL、Mongo 或者 PostgreSQL),我们把数据存储在数据卷上,这样容器重启的时候数据不会丢失,同时我们可以备份数据,这些主机操作都是只读的,因为我们不会在主机环境下去改变数据,所以我们不需要把主机的写操作同步到容器中。那么最好的选择就是 delegated 模式。容器能够写操作并且立刻看到结果,而主机方面就没那么重要了,因为它不会进行写操作,忽略它就好了。

案例二

当我们频繁的在主机上的修改代码,然后在容器里读取这些更改。在这个场景下,最好的选择是 cached。为什么?因为在容器方面大部分都是只读操作,而我们在主机上进行写操作。

总结

delegated 和 cached 的区别其实不是特别大,但是也需要注意。他们的性能都比 default 模式要好,但是 default 模式却是最安全的。鱼与熊掌不可兼得,就看怎么取舍了。如果你有什么更好的建议,可以在评论区留言。

PS:在 PHP 开发时,xdebug 也会影响性能,如果你觉得 Docker 很慢,不妨试试关闭 xdebug。

问题

Laravel Sail 环境下,运行 sail composer update 时报错:

  - Upgrading laravel/sail (v1.14.8 => v1.14.11): Extracting archive
    Update of laravel/sail failed

解决

删除 .env 里下面两行:

WWWGROUP=1000
WWWUSER=1000

重启容器后运行 sail composer update 就可以了。

更新成功以后别忘了把这两行再加回去。

分析

这两行是安装 sail 时自动加上去的,用来设置容器内执行权限。sail composer update 的时候因为权限不够导致更新失败。删除这两行以后获得 root 权限就可以正常更新了。

我的阿里云账号下有三台服务器:

深圳 可用区 D
A:Ubuntu 服务器,内网:172.28.119.31
B:Windows 服务器,内网:172.28.119.32

深圳 可用区 E
C:Ubuntu 服务器,内网:172.17.230.3

三台服务器在同一个 VPC 专有网络下,也在同一个安全组下。按照 阿里云官方说明,三台服务器是默认内网互通的。

问题

A 和 C 之间内网无法 ping 通,但是 A 和 C 内网都能 ping 通 B。

检查

查看 A 服务器路由表:

root@ubuntu-20220304:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.28.127.253  0.0.0.0         UG    100    0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.21.0.0      0.0.0.0         255.255.0.0     U     0      0        0 br-72f5d0a4f9cd
172.22.0.0      0.0.0.0         255.255.0.0     U     0      0        0 br-bf5ce375897e
172.28.112.0    0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.28.127.253  0.0.0.0         255.255.255.255 UH    100    0        0 eth0

查看 C 服务器路由表:

root@ubuntu-20220616:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.239.253  0.0.0.0         UG    100    0        0 eth0
172.17.224.0    0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.17.239.253  0.0.0.0         255.255.255.255 UH    100    0        0 eth0

发现 A 服务器路由表里 172.17.0.0 被 docker 占用,而 C 服务器 IP 正好是 172.17.230.3

查看 docker bridge 网络配置:

{
    "Name": "bridge",
    "Id": "fdd7ac1fee6e82486d42e53d8a3cce1164411b65de6a963bf5342aab019f7f7e",
    "Created": "2022-06-16T13:22:29.147640187+08:00",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
        "Driver": "default",
        "Options": null,
        "Config": [
            {
                "Subnet": "172.17.0.0/16",
                "Gateway": "172.17.0.1"
            }
        ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
        "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {},
    "Options": {
        "com.docker.network.bridge.default_bridge": "true",
        "com.docker.network.bridge.enable_icc": "true",
        "com.docker.network.bridge.enable_ip_masquerade": "true",
        "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
        "com.docker.network.bridge.name": "docker0",
        "com.docker.network.driver.mtu": "1500"
    },
    "Labels": {},
    "CreatedTime": 1655356949147
}

解决

方案一

添加 A 服务器路由:

$ route add -net 172.17.230.0/24 gw 172.28.127.253 dev eth0

这是临时方案,重启服务器会失效。

方案二

更换 C 服务器虚拟交换机,使用其他内网网段

方案三

更换 A 服务器 docker 的默认网段

编辑 /etc/docker/daemon.json(不存在则创建一个)

$ vim /etc/docker/daemon.json

添加以下代码:

{
    "bip": "172.20.0.1/16"
}

重启 docker 服务:

$ systemctl restart docker

参考:Configure the default bridge network