Redis在Docker中运行比在本机运行慢的原因是什么?

huangapple go评论53阅读模式
英文:

Why does Redis run slower in Docker than it does natively?

问题

我注意到Redis在Docker中的性能比在本地运行要慢得多。这可能与以下因素有关:

  • Docker的虚拟化:Docker使用容器技术,这会在一定程度上引入性能开销,因为容器在主机操作系统上运行,并且需要一些虚拟化层来管理资源。这可能会导致性能下降。

  • 资源分配:Docker容器通常会共享主机的资源,如CPU和内存。这可能导致在高负载情况下性能下降,因为多个容器可能争夺有限的资源。

  • 网络开销:如果你在容器之间进行通信,网络开销可能会导致性能下降,特别是在容器之间频繁传输数据时。

  • Docker镜像层:Docker使用镜像层来管理容器的文件系统。在读取/写入大量数据时,这些镜像层可能会导致性能下降。

  • 容器配置:容器的配置也可能影响性能。你可以尝试调整容器的配置参数,例如CPU和内存分配,来提高性能。

总之,Docker提供了便携性和隔离性,但在某些情况下可能会牺牲一些性能。你可以尝试调整Docker容器的配置以优化性能,但需要权衡这些因素以找到最佳性能和便携性之间的平衡点。

英文:

I noticed that Redis works much slower with docker than it does natively. What could this be related to?

Here is my tests on VPS with 1 CPU core / 1 GB RAM / Ubuntu 22.04.

Native Redis on the host (the fastest)

sudo apt-get update
sudo apt-get install redis (redis_version:6.0.16)
redis-benchmark -q -n 100000 
PING_INLINE: 23004.37 requests per second
PING_BULK: 21915.41 requests per second
SET: 23792.53 requests per second
GET: 22867.60 requests per second
INCR: 23894.86 requests per second
LPUSH: 25252.53 requests per second
RPUSH: 24551.93 requests per second
LPOP: 24414.06 requests per second
RPOP: 24307.24 requests per second
SADD: 23512.82 requests per second
HSET: 24746.35 requests per second
SPOP: 22758.31 requests per second
ZADD: 23969.32 requests per second
ZPOPMIN: 22701.47 requests per second
LPUSH (needed to benchmark LRANGE): 24113.82 requests per second
LRANGE_100 (first 100 elements): 17531.56 requests per second
LRANGE_300 (first 300 elements): 7954.18 requests per second
LRANGE_500 (first 450 elements): 6106.12 requests per second
LRANGE_600 (first 600 elements): 5296.89 requests per second
MSET (10 keys): 30012.00 requests per second

Redis in Docker tested outside the container (the slowest)

docker pull redis:6.0.17
docker run --name redis -p 6378:6379 -d redis:6.0.17
redis-benchmark -q -n 100000 -p 6378
PING_INLINE: 7548.31 requests per second
PING_BULK: 7623.69 requests per second
SET: 7474.96 requests per second
GET: 7474.96 requests per second
INCR: 7488.95 requests per second
LPUSH: 7443.25 requests per second
RPUSH: 7487.27 requests per second
LPOP: 7401.92 requests per second
RPOP: 7163.84 requests per second
SADD: 7252.16 requests per second
HSET: 7192.17 requests per second
SPOP: 7217.61 requests per second
ZADD: 7331.38 requests per second
ZPOPMIN: 7597.63 requests per second
LPUSH (needed to benchmark LRANGE): 7392.62 requests per second
LRANGE_100 (first 100 elements): 6248.05 requests per second
LRANGE_300 (first 300 elements): 6377.55 requests per second
LRANGE_500 (first 450 elements): 5748.45 requests per second
LRANGE_600 (first 600 elements): 4578.75 requests per second
MSET (10 keys): 6895.60 requests per second

Running the test inside the container (slightly slower than on the host, but I don't run my app inside the container)

docker exec -it redis sh
redis-benchmark -q -n 100000
PING_INLINE: 22416.50 requests per second
PING_BULK: 21654.40 requests per second
SET: 23413.72 requests per second
GET: 22351.36 requests per second
INCR: 22784.23 requests per second
LPUSH: 24467.83 requests per second
RPUSH: 23651.84 requests per second
LPOP: 23781.21 requests per second
RPOP: 23691.07 requests per second
SADD: 22747.95 requests per second
HSET: 24301.34 requests per second
SPOP: 22172.95 requests per second
ZADD: 24301.34 requests per second
ZPOPMIN: 22578.46 requests per second
LPUSH (needed to benchmark LRANGE): 24177.95 requests per second
LRANGE_100 (first 100 elements): 13817.88 requests per second
LRANGE_300 (first 300 elements): 7212.93 requests per second
LRANGE_500 (first 450 elements): 5898.31 requests per second
LRANGE_600 (first 600 elements): 4890.45 requests per second
MSET (10 keys): 29154.52 requests per second

Running the test inside other container with docker compose (average performance)

Dockerfile
FROM redis:6.0.17
CMD redis-benchmark -q -n 100000 -h redis

docker-compose.yml
version: "3.9"
services:
  web:
    build: .
  redis:
    image: "redis:6.0.17"
PING_INLINE: 16173.38 requests per second, p50=1.495 msec
PING_MBULK: 15172.20 requests per second, p50=1.503 msec
SET: 15989.77 requests per second, p50=1.503 msec
GET: 15701.05 requests per second, p50=1.511 msec
INCR: 16121.23 requests per second, p50=1.487 msec
LPUSH: 16428.46 requests per second, p50=1.495 msec
RPUSH: 14630.58 requests per second, p50=1.639 msec
LPOP: 15542.43 requests per second, p50=1.567 msec
RPOP: 15518.31 requests per second, p50=1.551 msec
SADD: 14900.91 requests per second, p50=1.567 msec
HSET: 15176.81 requests per second, p50=1.575 msec
SPOP: 14918.69 requests per second, p50=1.583 msec
ZADD: 15855.40 requests per second, p50=1.543 msec
ZPOPMIN: 15248.55 requests per second, p50=1.551 msec
LPUSH (needed to benchmark LRANGE): 16466.33 requests per second, p50=1.495 msec
LRANGE_100 (first 100 elements): 10330.58 requests per second, p50=2.423 msec
LRANGE_300 (first 300 elements): 5361.07 requests per second, p50=4.671 msec
LRANGE_500 (first 500 elements): 3863.99 requests per second, p50=6.191 msec
LRANGE_600 (first 600 elements): 3718.99 requests per second, p50=6.559 msec
MSET (10 keys): 20618.56 requests per second, p50=1.415 msec

As you can see from the tests, every time you use Docker, the Redis performance slows down.
But the main selling point of Docker is that it does not have a negative impact on performance.

答案1

得分: 1

不同的结果可能是由容器的网络设置引起的。

对于发布端口的主机 -> 容器通信,Docker需要执行网络地址转换(NAT)来路由您的请求。如果运行 ss -tlpn,您会看到发布端口上并不是 Redis 在监听,而是 dockerd。然后,Docker守护进程会将连接转发到正确的容器端口。为了做到这一点,它需要跟踪所有连接信息,并重新编写发送到端口的每个IP数据包。

对于容器 -> 容器通信,Docker不需要执行整个NAT,但仍然有一些相当复杂的路由逻辑。根据您的配置,这可以在iptables中进行配置。

为了让您快速了解工作原理:
Docker为每个容器设置了一个网络命名空间。一个裸网络命名空间在没有进一步修改的情况下没有网络连接。
为了为容器添加网络连接,Docker创建了一个veth对,并将一端移到网络命名空间中,并为veth接口配置IP地址。
这对于两个容器都是这样做的。
现在,如果这些容器想互相通信,Docker需要为两个veth接口在iptables中设置正确的路由。

我不太确定所有细节,因为我对所有这些的调查已经过去一段时间了。

正如@sks147建议的那样,您可以尝试使用主机网络来运行容器,以禁用网络隔离。

英文:

I would assume the different results are due to the network setup of the containers.

For host -> container communication with published ports, docker needs to do network address translation(NAT) to route your request. If you do ss -tlpn you will see not redis listening on the published port but dockerd. The docker daemon then will forward the connection to the correct container port. To do this it needs to keep track of all the connection information and rewrite every IP packet send to the port.

For container -> container communication docker does not need to do the whole NAT but still has some considerable routing logic. Depending on your configuration this could be for example configured in iptables.

To give you a quick overview over how this works:
Docker sets up a network namespace for every container. A bare network namespace has no network connectivity without further modification.
To add connectivity to the container Docker creates a veth pair and moves one end into the network namespace and configures IP addresses for the veth interfaces.
This is done for both containers.
Now if those containers want to talk to each other docker needs to set up correct routing in iptables for both veth interfaces.

I am not sure about all the details, because my investigation into all of this was quite a while back.

As already suggested by @sks147 you could try to run the container with host networking to disable network isolation.

huangapple
  • 本文由 发表于 2023年2月6日 16:42:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359027.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定