Docker高阶实战:从Redis集群到C++微服务,全面解析镜像优化与生产环境部署+镜像制作常见问题详解

📅 发布时间:2026/9/29 2:26:55
Docker高阶实战:从Redis集群到C++微服务,全面解析镜像优化与生产环境部署+镜像制作常见问题详解
文章目录本篇摘要一.基于dockerfile构建redis集群二.基于dockerfile编写C微服务三.docker镜像制作常见问题四.本篇小结本篇摘要本文基于Dockerfile实战搭建Redis集群与C微服务详解多阶段构建、镜像优化及Nginx负载均衡配置并解析虚悬镜像与中间层镜像的核心区别与处理方法。一.基于dockerfile构建redis集群在这里插入图片描述下面通过容器化技术搭建6个redis的集群然后第七个redis把他们够成集群后退出。搭建过程结构下载配置redis集群编写dockerfile制作镜像启动容器测试镜像能否正常运行编写集群docker-compose.yml启动集群测试功能-清理资源首先下载对应源码Redis源码包然后本地解压拿到对应的conf文件修改成集群相关配置。修改如下:安全认证设置requirepass 123456设置客户端连接密码为123456。masterauth 123456设置主从节点间同步数据的认证密码与连接密码一致确保集群内部通信安全。网络与连接设置bind * -::*允许所有 IPv4 和 IPv6 地址连接这是实现远程访问和集群节点间通信的关键。port 6379保持默认服务端口。集群模式配置cluster-enabled yes启用集群模式这是核心开关。cluster-config-file nodes.conf指定集群节点配置文件由 Redis 自动生成和维护记录集群状态。cluster-node-timeout 5000设置节点超时时间5秒用于判断节点是否故障。数据与运行设置dir /data/redis指定数据持久化文件如 RDB、AOF的存储目录。daemonize no配置为前台运行这是为了适配 Docker 容器的最佳实践。容器需要一个前台进程来保持运行否则会立即退出。对应文件拖转进来。对应目录结构rootiv-ye1i2elts0wh2yp1ahah:/home/youxing/docker-learn/master_slave/rediscluster# tree.├── docker-compose.yml └── redis ├── dockerfile ├── redis-7.0.11.tar.gz └── redis.conf1directory,4files下面看下对应的dockerfile文件# 构建阶段编译Redis FROM ubuntu:22.04as buildstage # 替换阿里云源并更新 RUN sed-i s//.*archive.ubuntu.com//mirrors.aliyun.comg /etc/apt/sources.list \ apt-get update# 安装编译工具 RUN apt-get install-y build-essential # 添加Redis源码和配置 ADD redis-7.0.11.tar.gz/ADD redis.conf/redis/# 编译Redis WORKDIR/redis-7.0.11RUN make # 移动编译好的文件 RUN mv src/redis-server/redis/\ mv src/redis-cli/redis/# 运行阶段生产镜像 FROM ubuntu:22.04# 安装运行时依赖 RUN apt-get update\ apt-get install-y--no-install-recommends \ libc6\ rm-rf/var/lib/apt/lists/* # 创建目录 RUN mkdir -p /data/redis \ mkdir -p /redis # 从构建阶段复制文件 COPY --frombuildstage /redis /redis # 暴露端口 EXPOSE 6379 # 设置启动命令 ENTRYPOINT [/redis/redis-server, /redis/redis.conf]这里在第一个ubuntu环境进行解压下载编译等得到可执行程序然后搞到第二个ubuntu里面由于原先包解压自带对应运行环境因此第二个容器还需要下对应环境实现了多级构建效果大大减少了对应容器占用空间情况。docker-compose.yml:services:redis01:image:myredis:v1.0build:./redis container_name:redis01 ports:-6379:6379healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis02:image:myredis:v1.0container_name:redis02 healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis03:image:myredis:v1.0container_name:redis03 healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis04:image:myredis:v1.0container_name:redis04 healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis05:image:myredis:v1.0container_name:redis05 healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis06:image:myredis:v1.0container_name:redis06 healthcheck:test:[CMD,/redis/redis-cli,ping]interval:10s timeout:5s retries:10redis07:image:myredis:v1.0container_name:redis07 entrypoint:/redis/redis-cli--cluster create redis01:6379redis02:6379redis03:6379redis04:6379redis05:6379redis06:6379--cluster-replicas1-a123456--cluster-yes depends_on:redis01:condition:service_healthy redis02:condition:service_healthy redis03:condition:service_healthy redis04:condition:service_healthy redis05:condition:service_healthy redis06:condition:service_healthy之前build的镜像搞6个redis容器也可以使用docker compose build直接构建启动然后再让第七个把他们搞成集群服务再退出。演示下效果可以发现对应先启动123456全部健康后才启动的7。镜像构建成功容器成功启动。可以成功插入数据发现被重定向到对应槽位所在的主节点了。主从关系也正常。停止服务先停止的7然后剩下的接着。二.基于dockerfile编写C微服务基于nginx容器反向代理对应C微服务通过主机port映射到nginx容器的80端口然后根据nginx的conf反向把对应请求发给对应同一compose网络里的cpp程序容器的8081端口然后它就接收到请求把对应html发给nginx80端口最后通过映射发给对应浏览器。步骤如下1. 编写代码2. 编写微服务Dockerfile3. 配置Nginx负载均衡编写Nginx Dockerfile4. 编写docker-compose.yml构建镜像5. 启动服务6. 测试功能对应工程目录rootiv-ye1i2elts0wh2yp1ahah:/home/youxing/docker-learn/C_micro_server# tree.├── cppweb │?? ├── dockerfile │?? ├── main.cpp │?? └── mycppweb ├── docker-compose.yml └── nginx ├── default.conf └── dockerfile2directories,6filesc代码#includeiostream#includenetinet/in.h#includestring.h#includeassert.h#includeunistd.h#includepthread.husingnamespacestd;structpthread_data{structsockaddr_inclient_addr;intsock_fd;};void*serverForClient(void*arg);intmain(){intsocket_fd;intconn_fd;intres;socklen_t lensizeof(sockaddr_in);structsockaddr_insever_add;memset(sever_add,0,sizeof(sever_add));sever_add.sin_familyAF_INET;sever_add.sin_porthtons(8081);sever_add.sin_addr.s_addrhtonl(INADDR_ANY);socket_fdsocket(AF_INET,SOCK_STREAM,0);assert(socket_fd0);intoption1;setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,option,sizeof(option));resbind(socket_fd,(structsockaddr*)sever_add,len);assert(res!-1);reslisten(socket_fd,5);assert(res!-1);coutserver initendl;while(1){structsockaddr_inclient;socklen_t client_lensizeof(client);conn_fdaccept(socket_fd,(structsockaddr*)client,client_len);if(conn_fd0){perror(accept);continue;}pthread_data*pdatanewpthread_data;pdata-client_addrclient;pdata-sock_fdconn_fd;std::coutin conn_fdendl;pthread_t pt;pthread_create(pt,NULL,serverForClient,(void*)pdata);pthread_detach(pt);}close(socket_fd);return0;}void*serverForClient(void*arg){pthread_data*pdata(pthread_data*)arg;intconn_fdpdata-sock_fd;std::coutprocess conn_fdendl;charrequest[1024];intlenrecv(conn_fd,request,sizeof(request)-1,0);if(len0){close(conn_fd);deletepdata;returnnullptr;}request[len]\0;constchar*http_headerHTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n;send(conn_fd,http_header,strlen(http_header),0);constchar*html_content!DOCTYPE html\nhtml\nhead\ntitleWelcome to C web server!/title\nstyle\n body { width: 35em; margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif; }\n/style\n/head\nbody\nh1Welcome to C web server!/h1\npIf you see this page, the web server is successfully working./p\n/body\n/html;send(conn_fd,html_content,strlen(html_content),0);close(conn_fd);deletepdata;returnnullptr;}这里绑定默认8081端口如果容器里进行就绑定容器的8081端口然后接受到请求就发送对应html。默认nginx的conf#Nginx 负载均衡配置upstream backend{server mycppweb:8081weight1;server mycppweb2:8081weight2;}server{listen80;access_log off;location/{proxy_pass http://backend;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}}这里默认进行反向代理当访问到它的80号端口就转向对应的名字为mycppweb的服务对应的容器的8081端口。总的docker-compose.yml就是启动对应两服务三容器services:web:image:mynginx:v3.0build:context:./nginx ports:-8112:80depends_on:mycppweb:condition:service_started mycppweb:build:context:./cppweb image:mycppweb:v2.0mycppweb2:image:mycppweb:v2.0对于cpp服务的dockerfile的c程序编译及运行还是采取多级构建进行只保留exe的容器j镜像构建# 第一阶段构建阶段编译环境 FROM ubuntu:22.04as buildstage # 配置国内软件源并更新 RUN sed-i s//.*archive.ubuntu.com//mirrors.ustc.edu.cng /etc/apt/sources.list apt update# 安装编译工具链 RUN apt install-y build-essential # 设置工作目录并复制源码 WORKDIR/src COPY./main.cpp.# 编译程序链接pthread库 RUN gmain.cpp-o mycppweb-lpthread # 指定运行时命令s CMD[/src/mycppweb]# 第二阶段运行阶段生产环境 FROM ubuntu:22.04# 从构建阶段仅复制最终可执行文件 COPY--frombuildstage/src/mycppweb/# 指定容器启动命令 CMD[/mycppweb]下面启动下构建对应的镜像。容器构建成功。容器正常启动下面进行访问打开对应的cpp服务日志监控。不停的访问对应网站因为设置的是nginx根据web1:web2发送率是1:2的权重来进行请求分配通过观察日志可以发现。三.docker镜像制作常见问题ADDvsCOPYADD功能更强能复制本地文件和解压远程URL资源COPY更安全透明仅复制本地文件是推荐做法。CMDvsENTRYPOINTENTRYPOINT定义容器启动后固定的执行命令CMD提供默认参数。两者组合使用ENTRYPOINT为命令CMD为参数实现灵活与固定的平衡。多阶段构建 (MultipleFROM)多个FROM指令用于多阶段构建。前期阶段如编译环境用于准备程序后期阶段如运行环境仅复制所需文件最终镜像只保留最后阶段内容极大减小镜像体积。Dockerfile 的优势相比直接制作快照docker commitDockerfile 具备可追溯、可重复、可审计、易于自动化的巨大优势是构建镜像的标准方式比如commit的方法可能会产生一些无关文件包等放入镜像不进行清除dockerfile避免了这点。虚悬镜像 (Dangling Images)指仓库名和标签均为none的镜像主要因镜像更新覆盖旧标签或多次构建同名新镜像导致旧镜像失去名称此类镜像已无用可安全删除。使用docker image ls -f danglingtrue查看并用prune命令清理。中间层镜像 (Intermediate Layer Images)定义是构建其他镜像时所依赖的、无标签的底层镜像也就是本身整个镜像是none none但是其他镜像构建时候使用了它的某一层的缓存避免了冗余操作也就是多个新镜像构建引用了这一层即共用。名称和标签也是none注意与悬空镜像区分。作用用于加速构建过程和复用资源避免重复下载或编译。特性不应手动删除删除会导致上层镜像因依赖丢失而出错。其生命周期自动管理依赖它的顶层镜像被删除后会自动清理。区别特征虚悬镜像 (Dangling Images)中间层镜像 (Intermediate Layer Images)状态已废弃的顶层镜像是构建过程的“废料”无任何镜像依赖它。被依赖的底层镜像是构建过程的“积木”有其他镜像依赖它。成因被新版本覆盖或重建同名镜像后旧镜像失去名称。是构建其他镜像时产生的依赖层本身就没有名字。能否删除可以且应该删除以释放磁盘空间。绝对不能删除删除会导致依赖它的上层镜像损坏。查看命令docker image ls -f danglingtruedocker image ls -a显示所有镜像包括中间层总结“它们的名称和表情都是none但虚悬镜像是没用的垃圾可删中间层镜像是有用的基石不可删。” 所以不能仅凭REPOSITORY和TAG字段为none就删除镜像必须先用docker image ls -f danglingtrue确认它是真正的“虚悬镜像”后再进行清理。四.本篇小结本篇通过完整项目案例掌握Docker生产级应用部署深入理解镜像分层机制与资源优化策略提升容器化开发与运维效率。