news 2026/10/2 18:45:36

Docker进阶实战:数据卷、网络、Compose与Swarm避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker进阶实战:数据卷、网络、Compose与Swarm避坑指南

很多人学Docker的思路是这样的:先pull一个镜像,docker run跑起来,–p映射个端口,然后就开始用了。等用了两三个月,麻烦事全来了——容器一删,数据跟着没了;服务器重启,容器IP变了连不上了;镜像越build越大,几百MB甚至上GB;服务一多,光启动命令就能写满一屏。说实话,这些痛点我在实践里一个都没落下,所以这篇我把Docker进阶里最绕不开的五块内容——存储、网络、Dockerfile、Compose、Swarm——从头到尾过一遍,把我踩过的坑和现在稳定使用的方案都写出来。适合已经会基本docker命令、想往生产方向走的同学,也适合正在被“docker网络不通”“容器数据丢了”“compose不会写”这类问题折磨的人。这篇文章不会教你背命令,而是告诉你每个选择背后的原因和避坑思路。

1. 存储篇:数据持久化不能只靠“运气”

1.1 三种存储方式的选择逻辑

Docker容器默认有个可写层,进程改文件、写日志都在那层。但容器一删,这层就跟着销毁了。刚开始用Docker的人大概率经历过“MySQL容器删了,库没了”这种事故,本质就是没搞懂容器生命周期和文件系统是绑定的。

Docker提供三类持久化方式:bind mount、volume、tmpfs。我直接用表格对比一下,这是我平时做技术选型最常用的一份参考:

类型数据存哪适合场景特点推荐度
bind mount宿主机指定路径配置目录、日志目录、开发热更新直接映射宿主机路径,改动立即可见看情况
volumeDocker管理目录 /var/lib/docker/volumes数据库数据、应用核心数据隔离性好、可备份、跨主机迁移方便最推荐
tmpfs宿主机内存缓存、临时文件容器停止数据即消失,速度极快特殊场景

bind mount最直白,就是宿主机的目录挂到容器里,比如把宿主机/opt/redis/data挂到容器/data。它的优点是修改宿主机文件容器立刻能看到,适合代码开发、配置修改这种需要频繁变的场景。但问题也明显:宿主机目录的权限、目录结构直接暴露给容器,时间长了容易乱,多台机器同步也麻烦。

volume是官方推荐的方式。它的核心价值在于数据由Docker统一管理,你不需要关心底层路径。指定一个卷名,Docker把它放在自己的管理目录里,容器删除后卷还在,只要挂载同一个卷名,新容器直接接手旧数据。我举一个实际例子:升级MySQL镜像时,先停容器,再启动新版本镜像并挂载同一个volume,数据无缝衔接,完全不用导出导入。这就是生产环境最常用的升级方式。

tmpfs我平时用得少,它写进内存,速度极快,但容器一停数据就没了,应用场景很窄——比如写测试环境、跑一次性的计算任务。不要把重要的业务数据放里面,这个我倒是踩过:把一个临时生成的证书放tmpfs,容器重启后证书不见了,排查好久才发现问题。

1.2 数据卷备份恢复与权限深坑

volume备份这事,光看官方文档很多人都迷迷糊糊。我直接给一套实测稳定的做法,核心思路是借助一个临时容器把卷内容打包出来。

备份命令(把volume名为mysql_data的卷打包到当前目录):

docker run --rm -v mysql_data:/data -v "$(pwd)":/backup alpine tar czf /backup/mysql_data_$(date +%F).tar.gz -C /data .

恢复命令(把备份包解压回卷):

docker run --rm -v mysql_data:/data -v "$(pwd)":/backup alpine tar xzf /backup/mysql_data_2025-06-01.tar.gz -C /data

这里有个很关键的细节:tar打包时用了-C /data .,把卷目录本身作为相对路径打包,解压的时候才不会有“嵌套一层目录”的问题。我见过有人直接tar czf /backup/data.tar.gz /data,恢复出来的结构是data/xxx而不是xxx,挂载后路径就全乱了。

再一个非常大的坑是权限。容器里的进程跑在什么用户下,文件写入卷时就是以这个用户的UID写的。宿主机上的UID和容器内UID大概率不一致。比如容器内用用户ID 999写了一个mysql卷,宿主机上看这个目录归属是999,你普通用户在宿主机上想直接改这个目录的文件,权限会报错。解决办法一般两种:一是启动容器时指定用户参数--user "$(id -u):$(id -g)",让容器进程以宿主机当前用户身份运行;二是在挂载后手动chown -R,把卷目录所有权改成需要的UID。生产环境更推荐前者,这样权限语义清晰,日志文件和数据文件归属统一。

2. 网络篇:容器与容器之间怎么“说话”

2.1 网络模式逐个拆解

Docker默认网络模式是bridge,也就是桥接模式。在这个模式下,容器通过虚拟网卡接入一个虚拟交换机(docker0),容器与容器之间可以通过IP互通,外部访问则需要端口映射。-p 8080:80做的事情就是NAT转发:宿主机8080端口接入的数据,转发给容器80端口。

bridge模式的问题在于IP的不确定性。我经常遇到的情况是:开发机上容器重启后IP变了,代码里写死的IP全废了。这就是为什么我强烈建议用自定义网络而不是默认的bridge网络。默认bridge下,容器之间只能靠IP访问,不支持DNS解析。

host模式就是容器直接共享宿主机的网络命名空间,不走NAT,不会有端口映射这一步。性能开销最低,适合对网络性能敏感的服务,比如高并发UDP、视频流服务。但代价是没有隔离性,容器里监听端口就是宿主机的端口,端口冲突就直接报错。我用host模式跑过Nginx,发现宿主机上别的服务同端口就起不来了,所以这个模式最好是用在明确有独占需求的场景。

none模式就是没有网络,适合离线计算、安全要求高的任务。overlay模式是Swarm集群的核心,跨节点的服务通过VXLAN隧道通信,这个后面的Swarm章节会展开。

2.2 自定义网络才是一劳永逸的正解

真实部署的时候,我几乎每个服务都放在自定义网络里跑。自定义网络带有一个隐藏福利——内建DNS解析。意思是你在同一个自定义网络里用容器名或服务名互相访问,不需要查IP,直接用名字就能通。

举个例子。假设有一个Web服务和一个MySQL数据库,分别叫web和db。我把它们放到同一个自定义网络app-net里:

docker network create app-net docker run -d --network app-net --name db -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0 docker run -d --network app-net --name web -p 8080:80 my-web-image

在web容器里,可以直接用jdbc:mysql://db:3306来连数据库,不用关心db容器实际IP是多少。这种机制的原理是Docker内置DNS解析,容器启动时DNS被设为Docker内建的127.0.0.11,容器名会被自动注册成一条DNS记录。实测下来这个方案非常稳定,不要说容器重启,整个宿主机重启后IP变了,名字也不会变。

创建自定义网络时还有一个参数值得说:--driver bridge指定驱动类型,默认就是bridge;如果加--internal则这个网络不接外部,容器只能内部互通,适合把数据库这类不愿意暴露的服务放进去。

2.3 端口映射与服务发现的取舍

端口映射是单机部署里最常用的暴露手段。-p 3307:3306意思就是把宿主机3307端口映射到容器3306。这里有一个常见认知误区:端口映射只解决“从宿主机外部访问容器内部”的问题,如果你有两个同类型容器需要共享宿主机端口,映射就会冲突。比如你想在同一台机器跑两个MySQL,容器内部都是3306,宿主机端口必须一个用3306一个用3307,否则第二个起不来。

另一条路是服务发现。单机时代可以在应用里配置用容器名访问,跨机器就得引入Consul、etcd这类注册中心,或者直接把服务跑在Swarm模式里,让Swarm内置的DNS去解决。我建议单体服务阶段先把端口映射和自定义网络这两个技能练熟,再去碰服务发现,否则容易一步迈太大,排查问题时反而更懵。

docker网络不通是大家反馈最多的一个问题。我总结几个常见的排查路径:先看容器能不能访问外网,在容器里curl baidu,不行就用docker run --net=host跑一个临时命令测试宿主机网络本身是否正常。接着看防火墙,很多发行版默认开启firewalld或者ufw,会拦掉Docker的端口映射。如果使用systemctl restart docker重启过Docker服务,默认bridge网络下已运行的容器会断线,要重启容器才能恢复。还有一种情况是容器里用的DNS不对,常见的坑是容器内解析不了外网域名,解决方法是给容器加--dns 8.8.8.8或者--dns 114.114.114.114参数。

3. Dockerfile篇:镜像瘦身与构建加速

3.1 多阶段构建让镜像体积腰斩

讲Dockerfile之前,先说一个最令我头疼的问题:镜像体积。早期我构建一个Java项目的镜像,FROM maven:3.8-jdk-11,一个基础镜像就快400MB,再加上编译产物和依赖,最后打成镜像小700MB。传到镜像仓库慢,拉下来也慢,磁盘占得也多。

多阶段构建就是解决这个问题的标准姿势。核心思路是:用一阶段装全量工具去编译,然后把编译好的产物拷贝到第二阶段的全新轻量基础镜像里,前面阶段产生的垃圾最终不会进入最终镜像。以Java项目为例:

# 构建阶段 FROM maven:3.8-jdk-11 AS builder COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # 运行阶段 FROM eclipse-temurin:11-jre-alpine COPY --from=builder /target/app.jar /app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar"]

最终镜像我实测下来不到120MB,比原来小近五倍。这里的关键是COPY --from=builder,它把构建阶段的产物拷过来,后面所有中间层在最终镜像里都不会出现。我给前端项目做Nginx部署也用了同样的方案,Node构建完再COPY到Nginx镜像,最终镜像只有几十MB。

如果你要跑的是Python这类解释型语言,没那么复杂,但有个地方容易踩坑:把requirements.txt和源码一起COPY进镜像后,pip install的缓存层会让镜像体积暴涨。解决办法是设置环境变量PIP_NO_CACHE_DIR=1,pip不要本地缓存,实测能省不少空间。

3.2 层缓存优化:指令顺序决定构建速度

Docker构建镜像是按层来的,每条指令生成一层,有缓存命中就用缓存,不重新执行。这意味着把“经常变的指令”放后面,把“不常变的指令”放前面,可以最大化复用缓存。

举一个最典型的例子。项目里pom.xml(或package.json、requirements.txt)不常动,源码天天改。如果你把COPY . .放在前面,再RUN mvn package,那么每次改一行代码,后面所有指令缓存全部失效,整个构建过程跑一遍,时间长得你想掀桌。正确做法是先把依赖清单COPY进去,安装依赖,再把源码放进去:

COPY package.json package-lock.json ./ RUN npm install COPY src ./src RUN npm run build

这样只要package.json不变,后面两条指令就算改了源码,npm install也会命中缓存,构建时间大幅缩短。我自己实际体感是,有缓存时整个构建不到30秒,没缓存时十分钟起步。另外提一句,.dockerignore和.gitignore一样重要,把node_modules、target、.git这类目录排除掉,否则构建上下文大可能拖慢构建,甚至把敏感文件带进镜像。

3.3 安全与稳定性:非root运行、时区、apt缓存

细节决定上线质量,下面三个点我全都在生产环境踩过。

第一,容器内默认以root运行,一旦容器被攻破或者挂载了宿主机目录,权限就是宿主机root。怎么看都觉得很危险。我现在的方案是在Dockerfile里创建普通用户,然后用USER切过去:

RUN useradd -r -u 1001 app && mkdir /app && chown app:app /app USER 1001

这样容器内进程以非root身份跑,配合1.2节讲到的权限语义,宿主机目录挂载后也不会有UID混乱。

第二,时区。官方镜像默认UTC,日志时间经常比北京时间慢8小时,排查问题的时候一头雾水。我一般直接在Dockerfile里做配置:

ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

Debian系需要先安装tzdata,Alpine需要apk add tzdata,这个细节不要省。

第三,apt缓存。RUN apt-get update后如果再apt-get install,会在镜像里留下/var/lib/apt/lists缓存,白白增大体积。标准做法是同一条RUN里连起来,最后rm -rf清理:

RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*

还有一个小技巧:基础镜像优先选Alpine或者distroless,Alpine官方镜像极小,适合跑静态编译产物或简单的运维工具;distroless更极致,里面没有包管理器、没有shell,攻击面极小,但调试很不方便,适合对安全要求特别高的场景。

4. Compose篇:一键拉起整套服务

4.1 Compose文件结构与服务编排思路

单机部署几个容器时,手动docker run还勉强能撑,服务一多(比如Nginx + 后端 + MySQL + Redis + 消息队列),命令就完全记不住了,而且启动顺序、依赖关系、网络、卷的配置散落在各处。Compose的价值就是用一份YAML把整套服务定义好,docker compose up -d一键全起,docker compose down一键全停。

一个生产环境相对完整的docker-compose.yml长这样:

services: web: image: my-web:1.2 build: context: . dockerfile: Dockerfile args: - ENV=production ports: - "8080:80" networks: - appnet volumes: - web_logs:/var/log/nginx depends_on: mysql: condition: service_healthy environment: - DB_HOST=mysql - DB_PASSWORD=${MYSQL_PASSWORD} mysql: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} networks: - appnet healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"] interval: 5s retries: 10 volumes: mysql_data: web_logs: networks: appnet: driver: bridge

注意这里没有写顶层version字段,新版docker compose已经不推荐写死version了,默认使用插件版本能力。services下面的环境变量直接用${MYSQL_ROOT_PASSWORD}从.env文件读取,这个比写在YAML里硬编码要好得多。我一般让.env进.gitignore,密码、端口这类敏感配置不进仓库。

Compose创建的网络是自动的,所有services都接入同一个默认网络,容器之间直接用service名(web、mysql)互相访问,这解决了我前面说的“IP老是变”的问题。不需要再手动跑docker network create,Compose自动处理。

4.2 depends_on的局限与健康检查的正确用法

很多人以为depends_on能保证依赖服务“可用”,其实它只保证“启动顺序”。我一开始也被坑了:web服务启动时MySQL还没初始化好,连接直接报错。depends_on看起来web在mysql之后启动,但MySQL容器起来后内部做数据初始化仍需要几十秒,web启动时照样连不上。

真正靠谱的方案是给依赖服务加healthcheck,然后在depends_on里用condition: service_healthy。上面YAML里已经写了MySQL的healthcheck,用mysqladmin ping探活,探活成功才认为服务healthy,然后Compose才会放行web启动。Redis也可以用redis-cli ping,Nginx用curl -f http://localhost/healthz,总之探活命令要简单且可靠。

我用这套方案跑了小半年,几乎没有再遇到“服务之间互相抢跑”的问题。如果你的服务代码有重试机制,那可以不用这个配置,但多一层保险总是好的。

4.3 配置复用、环境隔离与常见报错

Compose在生产里比较实用的两个扩展点:多个环境用不同env文件,以及服务配置复用。环境隔离的做法是用--env-file参数指定不同的环境文件,比如docker compose --env-file .env.prod up -d,这样同一份compose文件可以在测试和生产环境跑不同配置。服务配置复用用YAML的anchor语法,但老实说用多了反而难维护,我更推荐用环境变量覆盖关键参数。

还有两个兼容性问题值得单独说。第一个是命令本身:新版Docker推荐直接docker compose,老版本是docker-compose。如果你执行docker compose报“unknown command: docker compose”,大概率是Docker版本太老(20.10之前的Docker没有集成Compose V2插件),需要升级Docker或者改用docker-compose二进制。第二个是YAML格式,我见过有人不小心把缩进换成Tab,Compose解析直接报错,所有缩进必须是空格,建议IDE里装个YAML校验插件。

Compose除了最常用的up、down,我还经常用docker compose exec,进到正在运行的容器里执行命令,比docker exec不用手动拼容器名方便很多。诊断问题常用docker compose logs --tail=50 -f,实时看多个服务的日志,不用一个个容器切。

4.4 实际案例:Compose部署MySQL 8.0 + Redis 主从

这个案例是我在开发环境跑了好几个月的组合,直接可以照着用。MySQL 8.0数据目录挂载volume,初始化时通过环境变量设置账号密码和数据库,Redis用一主一从两个service,从节点用redis-server --replicaof redis-master 6379启动。注意Redis端口不需要暴露给宿主机——多个service内部通过自定义网络就能访问,只把需要外部访问的端口映射出来。

我遇到过的最典型的坑是:MySQL 8.0挂载volume时,如果宿主机目录权限不对,MySQL容器起不来,日志里报“Permission denied”。用volume而不是bind mount能避开很大一部分权限问题,因为Docker管理的volume初始owner就是容器内MySQL用户。

5. Swarm篇:从单机到集群,Docker原生的答案

5.1 Swarm是给什么人准备的

Swarm是Docker自带的内置集群编排方案。它和Kubernetes的关系,简单说:K8s功能全面但门槛高,Swarm轻量、原生、上手快。我个人的建议是,如果你所在团队就三五个人,服务总量不过四五个,没有大规模集群调度需求,Swarm的性价比非常高。K8s那一套得搭etcd、kubelet、控制面,一个最小集群的维护成本远远高于Swarm。

Swarm的核心概念是节点和服务。管理节点负责调度和集群控制,工作节点跑实际容器;服务就是你要部署的长期运行的应用,指定镜像、副本数、网络后,Swarm负责把容器调度到节点上,并保证副本数恒满足。

5.2 三节点集群搭建实录

搭建Swarm集群比我想象中简单很多。在三台机器上都装好Docker后,在第一个节点初始化:

docker swarm init --advertise-addr 192.168.1.10

输出里有一段docker swarm join命令,是加了token的,把它在另外两个节点上执行就能加入集群。我用的是下面这种:

docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377

之后再在管理节点上跑docker node ls,能看到三个节点的状态都是Ready,角色一个是Leader,两个是Worker。如果想让第二台机器也成为管理节点,就用docker node promote那台机器名。这里有一个小坑:--advertise-addr必须填其他节点能访问到的IP,我一开始填了localhost,结果其他节点根本连不上,初始化就要出问题。跨云环境还要记得在安全组放行2377/tcp(集群管理通信)和7946/tcp、7946/udp(节点间网络通信)、4789/udp(overlay VXLAN数据面)。

5.3 服务创建、扩缩容与滚动更新

创建一个带3个副本的Nginx服务:

docker service create --name web --replicas 3 -p 8080:80 --network appnet nginx:alpine

Swarm会在集群里的工作节点上自动分配3个容器,控制面会维护这个副本数。扩缩容也很直接:

docker service scale web=5 docker service scale web=2

滚动更新这个功能在生产环境特别值钱。假设你更新镜像到新版本:

docker service update --image nginx:1.25 --update-parallelism 1 --update-delay 10s web

Swarm会按配置一次更新一个副本,等10秒再更新下一个,保证整个过程中服务一直有可用副本,不会出现全部重启导致的短暂不可用。我部署周末发布的版本时专门把update-delay调到了30s,这样能先观察更新后日志是否正常再继续发布。期间发现新版本有问题,一个docker service rollback web就能回滚到上一个版本。

5.4 服务发现与配置管理

Swarm的网络和跨主机DNS是天然的。在Swarm模式下,服务名会被自动注册到Swarm内置的负载均衡层。比如我的web服务内部要访问mysql服务,直接在配置里写mysql地址,Swarm内部会把它解析到某个运行MySQL容器的节点IP。外部请求则通过ingress网络进入,Swarm会根据服务名自动做负载均衡。这个体验几乎是零配置的。

Swarm还内置了secret和config,可以安全地管理敏感信息和配置文件。用法是:

echo "my-secret-value" | docker secret create db_password - docker service create --secret db_password --name app my-image

secret挂载在容器内的/run/secrets/db_password,不会持久化到镜像层,也不会进环境变量,安全性比直接在compose里写明文强太多。我过去把数据库密码直接写环境变量,跑了一次日志采集,密码全部打进日志里,后来改成secret读文件,测试发现日志里干净了。

6. 避坑手册:这些坑我替你踩过了

下面这些是用Docker过程中几乎躲不掉的高频问题,我做成一个速查表,建议先收藏,等遇到对应现象再回来对照。

症状根因解决方案
docker compose命令报unknow commandDocker版本太老,未集成Compose V2升级Docker版本,或改用docker-compose
容器删了,数据全丢了没有挂载volume或bind mount改用volume持久化,如mysql_data:/var/lib/mysql
宿主机和容器文件权限报错UID不匹配--user $(id -u):$(id -g),或在Dockerfile中创建对应UID用户
容器能启动但外网访问不了端口映射配置或防火墙拦截检查-p参数、firewalld/ufw规则、云安全组
多个服务之间无法通过容器名访问不在同一自定义网络使用docker network create,让服务接入同一网络
镜像构建时间巨长COPY . .把依赖缓存全打乱先COPY依赖清单,再RUN安装,最后COPY源码
MySQL容器起不来Permission deniedbind mount目录权限问题使用volume,或调整宿主机目录owner
Swarm节点worker状态显示Down节点间端口未放行检查2377/7946/4789端口,重新join
Docker Desktop提示存储已损坏WSL2虚拟磁盘异常wsl --shutdown后重启Docker Desktop,必要时压缩vhdx
MySQL 8.0连不上报认证失败8.0默认caching_sha2_password客户端驱动升级,或用mysql_native_password兼容

再补充几个我在Windows开发机上用Docker Desktop的经验。Windows下容器数据和镜像都存在WSL2的虚拟磁盘里,Linux开发机直接看/var/lib/docker就能找到,Windows上路径在ext4.vhdx文件里。这个文件会越用越大,明明删除镜像后还占着空间,解决办法是在PowerShell里执行wsl --shutdown,然后diskpart工具压缩vhdx。另外Windows下挂载本地目录到容器有个性能问题,大量文件读写会慢不少,如果开发时绑定挂载卡顿,改用volume通常有明显改善。

最后的几点个人心得

Docker这套东西,我断断续续用了四五年,最大的体会是:命令本身一点不复杂,复杂的是理解它背后的存储生命周期、网络模型、实例调度逻辑。很多人在单机环境跑得顺,一上集群就懵,多半是没想清楚“容器会被调度到哪台机器”这件事——本地bind mount的路径到了另一台机器根本不存在,这在Swarm里就会直接翻车。

如果你刚开始着手生产实践,我的建议是分三步走:先把数据卷和自定义网络这两个基础打牢,再花一周时间把所有服务都改成Compose管理,最后才考虑引入Swarm。不要一上来就上集群,先把分布式的概念在单机上彻底理解透。越往深处用,越会发现Docker更像一套操作系统级的哲学——资源隔离、声明式配置、不可变基础设施,想通了这些,后面的K8s、云原生都是一层窗户纸。

上面提到的问题排查表,是我根据这些年做运维排障时最常看到的场景整理的。你在实操中遇到别的坑,最好的办法就是打开docker logs看日志,大多数问题都会在里面说出真相。别急着改配置,先看日志,这个习惯能帮你节省至少一半的排查时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 18:44:52

Keepalived+HAProxy高可用负载均衡:原理、配置与故障转移实战

凌晨两点半,值班手机震了。“你们的服务是不是挂了?”我揉着眼睛登录跳板机看了一眼:后端某台机器其实已经宕机快两小时了,入口请求却一直正常,用户完全无感。那一瞬间我就知道,之前搭的那套高可用负载均衡…

作者头像 李华
网站建设 2026/10/2 18:43:22

群晖Web Station搭建个人导航站:从零到日常维护全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 18:42:40

Docker入门踩坑指南:镜像、容器、数据卷与MySQL/Redis部署实战

最近被问到的 Docker 相关问题的密度有点高:有人在 Windows 上装了 Docker Desktop,双击图标后直接报 “virtualization support was not detected”;有人在 Ubuntu 上把 docker 装好了,结果 docker ps 却提示权限不够&#xff…

作者头像 李华
网站建设 2026/10/2 18:41:39

VGG为什么仍是CNN理解的必经路标

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 18:41:34

Kiwi Syslog服务器:Windows中小团队轻量级日志中枢实战指南

1. Kiwi Syslog服务器:不是“又一个日志工具”,而是中小团队的运维神经中枢 Kiwi Syslog服务器,这个名字在Windows系统管理员圈子里,几乎等同于“稳定”和“省心”的代名词。它不像ELK(ElasticsearchLogstashKibana&am…

作者头像 李华
网站建设 2026/10/2 18:41:02

DnCNN图像去噪实战:从DnCNN-B到DnCNN-3的PyTorch复现与避坑指南

简介:本资源面向图像去噪方向的深度学习学习者与研究者,提供基于PyTorch的DnCNN完整复现代码,并在原始DnCNN基础上扩展实现了DnCNN-B、CDnCNN-B与DnCNN-3的训练与测试流程,适合具备一定PyTorch基础、希望系统复现论文实验的读者。…

作者头像 李华