ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Docker与微服务实战:容器化部署、编排与镜像瘦身

Docker与微服务实战:容器化部署、编排与镜像瘦身 简介一份名为《Docker和微服务技术的崛起》的入门文档面向软件开发者、运维人员以及云原生技术爱好者系统梳理了IT基础设施从硬件虚拟化到容器化的演进过程。文档以SOA面向服务架构为起点分析了单体架构在扩展性、维护性方面的痛点进而引出微服务架构的核心思想并重点讲解Docker如何通过容器化封装微服务以及Kubernetes在服务编排、负载均衡、容错恢复中的重要作用。文中配有架构示意图和关键概念对照图直观展示不同模式下的服务关系可帮助读者快速理解微服务拆分、容器隔离、水平扩展等核心知识。资源为单个docx文档压缩包仅112KB下载后即可用常见办公软件打开阅读。目前已有120人学习浏览适合希望建立现代应用开发知识体系的技术人员作为轻量级入门参考。1. Docker与微服务技术的崛起容器解决了微服务部署的什么问题一个大版本迭代后测试环境里20个微服务要重新部署最耗时的不是改代码而是安装各自依赖的JDK、Python、Redis客户端和动态链接库。开发配好了环境测试机器又对不上最后只能甩一句“在我机器上能跑”。Docker和微服务技术的崛起本质上是用镜像把“环境”变成可携带、可校验的构建产物让容器的资源隔离和秒级启动支撑起微服务的独立发布。容器不是虚拟机它共享宿主机内核却拥有独立的文件系统、网络和进程命名空间。一台8C16G的机器单靠虚拟机只能起几个实例用Docker可以同时跑上百个轻量容器这也让微服务按需扩容成为可能。下面从安装、编排、排错到镜像瘦身直接给出可复现的命令和参数。2. Docker安装与核心操作从零跑通本地容器环境2.1 Linux和Docker Desktop的安装命令与前提条件Linux上安装Docker常见做法是配置官方或云厂商的Docker CE源再安装docker-ce、docker-ce-cli和containerd.io。以Ubuntu 22.04为例sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo systemctl enable --now docker这段命令先安装证书和gnupg用--dearmor把GPG密钥转成keyringsigned-by指定密钥路径规避apt-key已废弃的告警。发行版代号从/etc/os-release动态读取不需要额外安装lsb-release。enable --now同时完成启动和开机自启。安装完成后执行docker --version能看到Docker version开头的版本号输出。CentOS或RHEL用户把仓库文件换成https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo再yum install -y docker-ce docker-ce-cli containerd.io即可注意CentOS 7的旧内核需要额外安装container-selinux。Windows和macOS一般用Docker Desktop安装包Windows下依赖WSL2。启动失败最常见的报错是Virtualization support not detected需要先到BIOS打开Intel VT-x或AMD SVM Mode然后在“Windows功能”里勾选“虚拟机平台”和“适用于Linux的Windows子系统”。macOS要求4GB以上内存和硬件虚拟化安装后首次启动需要授权管理员权限。如果内网环境无法联网离线安装可以先在有网机器下载对应版本的Docker Engine二进制包或Docker Desktop安装器再通过U盘拷入内网但要注意内核版本和glibc兼容性。2.2 镜像、容器、仓库三个核心对象与常用命令微服务镜像的传递依赖仓库容器是镜像的运行实例。镜像是模板容器是模板创建出的隔离进程仓库是存放模板的中央位置。docker pull从仓库拉取docker build生成本地镜像docker run启动容器。常用命令如下操作命令说明拉取镜像docker pull nginx:1.27-alpine显式带tag避免latest不确定性启动容器docker run -d -p 8080:80 nginx-d后台-p映射端口查看容器docker ps -a-a包含已退出容器进入容器docker exec -it id /bin/sh排错常用容器内可能没有bash查看日志docker logs -f id-f实时跟踪删除容器docker rm -f id-f强制删除运行中容器删除镜像docker rmi image先删容器或显式加-f执行docker run时Docker先检查本地是否有镜像没有则从仓库拉取再创建可写层并启动进程。两个容易踩的坑-d后容器立刻退出多半是前台进程没启动立即docker logs看原因-p端口冲突时改映射到别的端口或先docker ps找出占用容器的id再处理。这里也会遇到docker权限错误典型症状是每次都要加sudo解决办法放到第4章因为它和daemon权限强相关单独处理更容易定位。2.3 用Dockerfile构建一个微服务镜像以Node.js写的用户服务为例项目里必须提交package-lock.json然后在根目录写DockerfileFROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY src ./src ENV NODE_ENVproduction EXPOSE 3000 USER node CMD [node, src/server.js]构建并启动docker build -t user-service:1.0.0 . docker run -d --name user-service -p 8080:3000 user-service:1.0.0 curl http://localhost:8080/healthnode:18-alpine比node:18小一半以上但缺少部分原生库遇到node-gyp编译失败时改回slim版本。COPY package*.json单独放在RUN npm ci之前是为了利用构建缓存只有依赖变化时才重跑npm ci源码改动不会重新下载依赖。USER node避免以root运行进程降低容器被攻破后的提权风险。EXPOSE 3000只是声明真正对外暴露端口靠docker run -p。很多IDE也提供“右键打包Docker镜像”的入口比如IDEA的Docker插件会自动生成临时Dockerfile但CI流水线里更建议直接执行docker build保证参数和基础镜像版本可控。3. 用Docker Compose编排微服务依赖网络、健康检查与私有仓库3.1 为什么单机编排离不开docker compose微服务项目拆开后本地至少需要API服务、MySQL、Redis有时还有消息队列。手动执行多个docker run端口、数据卷、网络和启动顺序全凭记忆换个人就重演一遍依赖安装的混乱。常见做法是使用docker compose把这些声明在一个YAML中一条docker compose up -d拉起全部服务。docker compose解决的问题是单机多容器编排Kubernetes解决跨主机集群调度。很多团队先拿Compose在开发机复现生产拓扑再转换到Kubernetes清单这也是Compose能成为微服务技术里最常见过渡工具的原因。Compose内的服务默认在同一个自定义网络服务之间直接用服务名当主机名不需要关心容器IP这比一堆docker run --link干净得多。3.2 一个用户服务加MySQL和Redis的最小compose文件在项目根目录放docker-compose.ymlservices: db: image: mysql:8.0 container_name: order-db environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: order_db ports: - 3306:3306 volumes: - db_data:/var/lib/mysql command: --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -proot123] interval: 5s timeout: 3s retries: 10 redis: image: redis:7-alpine ports: - 6379:6379 volumes: - redis_data:/data command: [redis-server, --appendonly, yes] user-service: build: ./user-service ports: - 8080:3000 environment: - DB_HOSTdb - REDIS_HOSTredis depends_on: db: condition: service_healthy redis: condition: service_started volumes: db_data: redis_data:启动命令为docker compose up -d状态查看用docker compose ps日志用docker compose logs -f user-service。这个示例使用了Docker Compose v2注意depends_on下的condition在旧版docker-composev1里不支持直接用会报配置错误。depends_on只保证启动顺序并不能保证服务真正就绪所以db加healthcheckmysqladmin ping成功后user-service才启动redis是service_started意味着容器一进入运行态就继续。DB_HOSTdb和REDIS_HOSTredis是Compose网络自动解析的服务名应用代码不需要写死IP。command里的--character-set-serverutf8mb4是MySQL 8推荐的编码配置避免中文乱码。如果同一套编排里还有另一个Redis要做主从的话从节点启动命令直接写redis-server --replicaof redis 6379。常用子命令如下表子命令作用常用参数up创建并启动服务-d后台--build强制重新构建logs查看服务日志-f跟踪--tail50只看尾部exec在运行中的容器执行命令服务名后用/bin/shdown停止并移除容器和网络-v连同数据卷删除谨慎使用restart重启一个或多个服务后跟具体服务名3.3 配置私有仓库与镜像源解决镜像拉取慢开发阶段用Docker Hub没问题生产环境微服务镜像通常放到私有仓库。自建仓库最简单的是跑一个registry容器docker run -d -p 5000:5000 --name registry -v /opt/registry:/var/lib/registry registry:2 docker tag user-service:1.0.0 192.168.1.10:5000/user-service:1.0.0 docker push 192.168.1.10:5000/user-service:1.0.0其它机器拉取时内网HTTP仓库需要在/etc/docker/daemon.json中声明insecure-registries镜像拉取慢也常见配置registry-mirrors指向云厂商提供的专属镜像源{ registry-mirrors: [https://your-mirror-id.mirror.aliyuncs.com], insecure-registries: [192.168.1.10:5000] }保存后执行sudo systemctl restart docker。registry-mirrors只对Docker Hub生效私有仓库和第三方仓库还是直连。不要使用来历不明的公共加速地址防止镜像被篡改或内容过期。团队规模超过几十人后registry自带的扁平目录没有权限管理和界面常见做法是部署Harbor作为私有仓库Harbor支持项目隔离、LDAP和漏洞扫描本身也是一套微服务。如果只是内部小团队直接跑registry:2更省事重点是把docker push权限收敛到CI避免开发机手动推送绕过扫描。4. 微服务生产排错Docker启动失败、权限异常与资源控制4.1 Docker服务启动失败与Virtualization Support Not DetectedDocker Desktop在Windows上启动失败时如果弹窗写着Docker Desktop failed to start because virtualization support wasnt detected先打开任务管理器性能页面看虚拟化是否“已启用”。如果没有重启进BIOS把Intel Virtualization Technology或AMD SVM Mode设为Enabled保存退出。回到Windows后在“启用或关闭Windows功能”中勾选“虚拟机平台”和“适用于Linux的Windows子系统”再执行wsl --set-default-version 2 wsl --shutdownLinux上docker服务启动失败多数和containerd或iptables相关。先看状态和日志systemctl status docker --no-pager -l journalctl -u docker --no-pager -n 80如果看到提示failed to start daemon: Devices cgroup isnt mounted老内核需要重新挂载cgroup现代发行版通常不会出现如果错误指向iptables执行systemctl restart containerd后清理/var/lib/docker里的残留锁文件再启动。另一个隐藏原因是磁盘写满df -h /var/lib/docker确认可用空间镜像和日志堆积都会撑爆分区。4.2 Failed to connect to the Docker API权限与daemon异常Windows端出现failed to connect to the docker api at npipe:////./pipe/dockerDesktopLinuxEngine本质是Docker Desktop的Linux后端没有启动或客户端与引擎的命名管道没有就绪。确认托盘图标没有停留在“Docker Desktop正在启动”仍未解决就重启Docker Desktop或者执行wsl --shutdown后重新打开。升级Docker Desktop后出现该问题卸载并清空%AppData%\Docker目录再重装。Linux上的等价报错是Cannot connect to the Docker daemon at unix:///var/run/docker.sock。先确认daemon是否启动再判断是不是docker权限错误。让当前用户免sudo进入docker组sudo groupadd docker 2/dev/null || true sudo usermod -aG docker $USER newgrp dockernewgrp docker只对当前会话生效重新登录后长期有效。执行docker run hello-world验证能拉取并运行就说明权限已生效。注意docker组权限等同于root能操作Docker就能挂载宿主机目录并运行特权容器生产服务器上不要随意添加用户必要时用Rootless模式。4.3 给微服务设置资源限制与日志轮转容器默认不受CPU和内存限制多个微服务共享一台机器时某个服务内存泄漏会拖垮全部进程。常见做法是启动时显式限定docker run -d --name user-service \ --cpus1.0 \ --memory512m \ --memory-swap1g \ --pids-limit128 \ --log-opt max-size10m \ --log-opt max-file3 \ -p 8080:3000 user-service:1.0.0参数含义如下表参数作用建议值--cpus限制可占用CPU核心数支持小数1.0、2.0--memory容器内存硬上限超过触发OOM Kill实测峰值上浮50%--memory-swap内存加swap总上限必须大于memory内存值的2倍--pids-limit限制进程数量防止fork炸弹128或256--log-opt max-size单个日志文件达到大小后切割10m、50m--log-opt max-file保留的日志文件个数3或5在docker-compose.yml里对应的写法是deploy.resources.limits.cpus和memory但deploy字段在Swarm模式和普通docker compose up之间一直有兼容性坑旧版docker-compose需要--compatibility才生效某些Compose v2版本在单机模式也会直接忽略。所以单机部署不要依赖Compose里的deploy字段做资源限制把服务启动参数收敛到启动脚本或Makefiledocker run的--memory和--cpus是最可靠的。观察是否生效用docker stats --no-stream看实时CPU和内存然后docker inspect id | grep -A6 Memory确认限制写入。容器反复退出时先看docker inspect id里的OOMKilled字段是true说明内存不足优先调大资源而不是只查业务日志。5. 进阶用Docker多阶段构建压缩微服务镜像的实战参数5.1 构建阶段与运行阶段分离单阶段构建Java微服务时JDK、Maven依赖、编译中间文件全塞进镜像体积动辄1GB。多阶段构建把编译和打包拆成两个阶段最终镜像只保留JAR和运行时。以Spring Boot服务为例FROM maven:3.9-eclipse-temurin-17 AS build WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -Dmaven.test.skiptrue FROM eclipse-temurin:17-jre-alpine RUN addgroup -S app adduser -S app -G app WORKDIR /app COPY --frombuild /build/target/order-service-*.jar app.jar USER app ENV JAVA_OPTS-XX:MaxRAMPercentage75.0 -Xss512k EXPOSE 8080 ENTRYPOINT [sh, -c, java $JAVA_OPTS -jar app.jar]FROM maven:3.9-eclipse-temurin-17 AS build为构建阶段命名后面COPY --frombuild只拷贝JAR包省去JDK、Maven本地仓库和编译中间文件。eclipse-temurin:17-jre-alpine只带JREalpine进一步压缩体积。addgroup和adduser创建非root用户避免容器主进程是root。JAVA_OPTS里的-XX:MaxRAMPercentage75.0很关键JVM默认堆上限是物理内存的四分之一容器限制512m时这个参数让堆按容器限额的75%自动调整不会因为固定-Xmx过大导致OOMKill。构建后执行docker images | grep order-service通常能从1GB降到250MB以内。项目根目录的.dockerignore也要补上.git target .idea *.iml Dockerfile .dockerignore否则docker build会把整个项目目录发给daemon构建上下文可能几百MB拉低打包速度。5.2 镜像瘦身的三个检查点先看层大小docker history image输出每一层的创建命令和占用空间发现某层异常大就回看Dockerfile对应的RUN比如RUN npm install后存在/tmp缓存应该在同一条命令里清理。再看基础镜像标签node:18-alpine和node:18相差几百MB能选slim或alpine就别用全量版。最后看缓存顺序把COPY pom.xml和RUN mvn dependency:go-offline放在COPY src前面保证源码变更后依赖层仍可复用。验证瘦身后的镜像是否可用先docker run --rm -it image /bin/sh进入容器检查工作目录和启动脚本权限再正常启动容器并请求健康检查接口。生产CD阶段接入trivy image image做静态漏洞扫描高危漏洞直接让流水线失败这一步放在推送私有仓库之前多阶段构建的镜像瘦身效果才真正落到交付流程里。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进