文章目录

本篇摘要
本文基于Dockerfile实战搭建Redis集群与C++微服务,详解多阶段构建、镜像优化及Nginx负载均衡配置,并解析虚悬镜像与中间层镜像的核心区别与处理方法。
一.基于dockerfile构建redis集群
在这里插入图片描述

- 下面通过容器化技术搭建6个redis的集群;然后第七个redis把他们够成集群后退出。
搭建过程结构:
#mermaid-svg-egQFtxlb4p43OyTD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-egQFtxlb4p43OyTD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-egQFtxlb4p43OyTD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-egQFtxlb4p43OyTD .error-icon{fill:#552222;}#mermaid-svg-egQFtxlb4p43OyTD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-egQFtxlb4p43OyTD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-egQFtxlb4p43OyTD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-egQFtxlb4p43OyTD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-egQFtxlb4p43OyTD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-egQFtxlb4p43OyTD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-egQFtxlb4p43OyTD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-egQFtxlb4p43OyTD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-egQFtxlb4p43OyTD .marker.cross{stroke:#333333;}#mermaid-svg-egQFtxlb4p43OyTD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-egQFtxlb4p43OyTD p{margin:0;}#mermaid-svg-egQFtxlb4p43OyTD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-egQFtxlb4p43OyTD .cluster-label text{fill:#333;}#mermaid-svg-egQFtxlb4p43OyTD .cluster-label span{color:#333;}#mermaid-svg-egQFtxlb4p43OyTD .cluster-label span p{background-color:transparent;}#mermaid-svg-egQFtxlb4p43OyTD .label text,#mermaid-svg-egQFtxlb4p43OyTD span{fill:#333;color:#333;}#mermaid-svg-egQFtxlb4p43OyTD .node rect,#mermaid-svg-egQFtxlb4p43OyTD .node circle,#mermaid-svg-egQFtxlb4p43OyTD .node ellipse,#mermaid-svg-egQFtxlb4p43OyTD .node polygon,#mermaid-svg-egQFtxlb4p43OyTD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-egQFtxlb4p43OyTD .rough-node .label text,#mermaid-svg-egQFtxlb4p43OyTD .node .label text,#mermaid-svg-egQFtxlb4p43OyTD .image-shape .label,#mermaid-svg-egQFtxlb4p43OyTD .icon-shape .label{text-anchor:middle;}#mermaid-svg-egQFtxlb4p43OyTD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-egQFtxlb4p43OyTD .rough-node .label,#mermaid-svg-egQFtxlb4p43OyTD .node .label,#mermaid-svg-egQFtxlb4p43OyTD .image-shape .label,#mermaid-svg-egQFtxlb4p43OyTD .icon-shape .label{text-align:center;}#mermaid-svg-egQFtxlb4p43OyTD .node.clickable{cursor:pointer;}#mermaid-svg-egQFtxlb4p43OyTD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-egQFtxlb4p43OyTD .arrowheadPath{fill:#333333;}#mermaid-svg-egQFtxlb4p43OyTD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-egQFtxlb4p43OyTD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-egQFtxlb4p43OyTD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-egQFtxlb4p43OyTD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-egQFtxlb4p43OyTD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-egQFtxlb4p43OyTD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-egQFtxlb4p43OyTD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-egQFtxlb4p43OyTD .cluster text{fill:#333;}#mermaid-svg-egQFtxlb4p43OyTD .cluster span{color:#333;}#mermaid-svg-egQFtxlb4p43OyTD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-egQFtxlb4p43OyTD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-egQFtxlb4p43OyTD rect.text{fill:none;stroke-width:0;}#mermaid-svg-egQFtxlb4p43OyTD .icon-shape,#mermaid-svg-egQFtxlb4p43OyTD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-egQFtxlb4p43OyTD .icon-shape p,#mermaid-svg-egQFtxlb4p43OyTD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-egQFtxlb4p43OyTD .icon-shape .label rect,#mermaid-svg-egQFtxlb4p43OyTD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-egQFtxlb4p43OyTD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-egQFtxlb4p43OyTD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-egQFtxlb4p43OyTD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 下载配置redis集群
编写dockerfile制作镜像
启动容器测试镜像能否正常运行
编写集群docker-compose.yml
启动集群
测试功能-清理资源
首先下载对应源码:
然后本地解压拿到对应的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 容器的最佳实践。容器需要一个前台进程来保持运行,否则会立即退出。

- 对应文件拖转进来。
对应目录结构:
bash
root@iv-ye1i2elts0wh2yp1ahah:/home/youxing/docker-learn/master_slave/rediscluster# tree
.
├── docker-compose.yml
└── redis
├── dockerfile
├── redis-7.0.11.tar.gz
└── redis.conf
1 directory, 4 files
下面看下对应的dockerfile文件:
cpp
# 构建阶段:编译Redis
FROM ubuntu:22.04 as buildstage
# 替换阿里云源并更新
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.aliyun.com@g' /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.11
RUN 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 --from=buildstage /redis /redis
# 暴露端口
EXPOSE 6379
# 设置启动命令
ENTRYPOINT ["/redis/redis-server", "/redis/redis.conf"]
- 这里在第一个ubuntu环境进行解压下载编译等;得到可执行程序然后搞到第二个ubuntu里面(由于原先包解压自带对应运行环境,因此第二个容器还需要下对应环境);实现了多级构建效果;大大减少了对应容器占用空间情况。
docker-compose.yml:
cpp
services:
redis01:
image: myredis:v1.0
build: ./redis
container_name: redis01
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis02:
image: myredis:v1.0
container_name: redis02
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis03:
image: myredis:v1.0
container_name: redis03
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis04:
image: myredis:v1.0
container_name: redis04
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis05:
image: myredis:v1.0
container_name: redis05
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis06:
image: myredis:v1.0
container_name: redis06
healthcheck:
test: ["CMD", "/redis/redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
redis07:
image: myredis:v1.0
container_name: redis07
entrypoint: >
/redis/redis-cli --cluster create
redis01:6379 redis02:6379 redis03:6379
redis04:6379 redis05:6379 redis06:6379
--cluster-replicas 1 -a 123456 --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端口;最后通过映射发给对应浏览器。
步骤如下:
#mermaid-svg-yu3TZDzZ5b3qyLlG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yu3TZDzZ5b3qyLlG .error-icon{fill:#552222;}#mermaid-svg-yu3TZDzZ5b3qyLlG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yu3TZDzZ5b3qyLlG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .marker.cross{stroke:#333333;}#mermaid-svg-yu3TZDzZ5b3qyLlG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yu3TZDzZ5b3qyLlG p{margin:0;}#mermaid-svg-yu3TZDzZ5b3qyLlG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster-label text{fill:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster-label span{color:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster-label span p{background-color:transparent;}#mermaid-svg-yu3TZDzZ5b3qyLlG .label text,#mermaid-svg-yu3TZDzZ5b3qyLlG span{fill:#333;color:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .node rect,#mermaid-svg-yu3TZDzZ5b3qyLlG .node circle,#mermaid-svg-yu3TZDzZ5b3qyLlG .node ellipse,#mermaid-svg-yu3TZDzZ5b3qyLlG .node polygon,#mermaid-svg-yu3TZDzZ5b3qyLlG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .rough-node .label text,#mermaid-svg-yu3TZDzZ5b3qyLlG .node .label text,#mermaid-svg-yu3TZDzZ5b3qyLlG .image-shape .label,#mermaid-svg-yu3TZDzZ5b3qyLlG .icon-shape .label{text-anchor:middle;}#mermaid-svg-yu3TZDzZ5b3qyLlG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .rough-node .label,#mermaid-svg-yu3TZDzZ5b3qyLlG .node .label,#mermaid-svg-yu3TZDzZ5b3qyLlG .image-shape .label,#mermaid-svg-yu3TZDzZ5b3qyLlG .icon-shape .label{text-align:center;}#mermaid-svg-yu3TZDzZ5b3qyLlG .node.clickable{cursor:pointer;}#mermaid-svg-yu3TZDzZ5b3qyLlG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .arrowheadPath{fill:#333333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yu3TZDzZ5b3qyLlG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yu3TZDzZ5b3qyLlG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yu3TZDzZ5b3qyLlG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster text{fill:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG .cluster span{color:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yu3TZDzZ5b3qyLlG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yu3TZDzZ5b3qyLlG rect.text{fill:none;stroke-width:0;}#mermaid-svg-yu3TZDzZ5b3qyLlG .icon-shape,#mermaid-svg-yu3TZDzZ5b3qyLlG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yu3TZDzZ5b3qyLlG .icon-shape p,#mermaid-svg-yu3TZDzZ5b3qyLlG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yu3TZDzZ5b3qyLlG .icon-shape .label rect,#mermaid-svg-yu3TZDzZ5b3qyLlG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yu3TZDzZ5b3qyLlG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yu3TZDzZ5b3qyLlG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yu3TZDzZ5b3qyLlG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 编写代码
2. 编写微服务Dockerfile
3. 配置Nginx负载均衡
编写Nginx Dockerfile
4. 编写docker-compose.yml
构建镜像
5. 启动服务
6. 测试功能
对应工程目录:
bash
root@iv-ye1i2elts0wh2yp1ahah:/home/youxing/docker-learn/C++_micro_server# tree
.
├── cppweb
│?? ├── dockerfile
│?? ├── main.cpp
│?? └── mycppweb
├── docker-compose.yml
└── nginx
├── default.conf
└── dockerfile
2 directories, 6 files
c++代码:
cpp
#include <iostream>
#include <netinet/in.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;
struct pthread_data {
struct sockaddr_in client_addr;
int sock_fd;
};
void *serverForClient(void *arg);
int main() {
int socket_fd;
int conn_fd;
int res;
socklen_t len = sizeof(sockaddr_in);
struct sockaddr_in sever_add;
memset(&sever_add, 0, sizeof(sever_add));
sever_add.sin_family = AF_INET;
sever_add.sin_port = htons(8081);
sever_add.sin_addr.s_addr = htonl(INADDR_ANY);
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
assert(socket_fd > 0);
int option = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
res = bind(socket_fd, (struct sockaddr*)&sever_add, len);
assert(res != -1);
res = listen(socket_fd, 5);
assert(res != -1);
cout << "server init" << endl;
while(1) {
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
conn_fd = accept(socket_fd, (struct sockaddr *)&client, &client_len);
if (conn_fd < 0) {
perror("accept");
continue;
}
pthread_data *pdata = new pthread_data;
pdata->client_addr = client;
pdata->sock_fd = conn_fd;
std::cout<<"in "<<conn_fd<<endl;
pthread_t pt;
pthread_create(&pt, NULL, serverForClient, (void*)pdata);
pthread_detach(pt);
}
close(socket_fd);
return 0;
}
void *serverForClient(void *arg) {
pthread_data *pdata = (pthread_data*)arg;
int conn_fd = pdata->sock_fd;
std::cout<<"process "<<conn_fd<<endl;
char request[1024];
int len = recv(conn_fd, request, sizeof(request)-1, 0);
if (len <= 0) {
close(conn_fd);
delete pdata;
return nullptr;
}
request[len] = '\0';
const char* http_header = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n";
send(conn_fd, http_header, strlen(http_header), 0);
const char* html_content = "<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<title>Welcome to C++ web server!</title>\n"
"<style>\n"
" body { width: 35em; margin: 0 auto;\n"
" font-family: Tahoma, Verdana, Arial, sans-serif; }\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<h1>Welcome to C++ web server!</h1>\n"
"<p>If 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);
delete pdata;
return nullptr;
}
- 这里绑定默认8081端口(如果容器里进行就绑定容器的8081端口);然后接受到请求就发送对应html。
默认nginx的conf:
cpp
# Nginx 负载均衡配置
upstream backend {
server mycppweb:8081 weight=1;
server mycppweb2:8081 weight=2;
}
server {
listen 80;
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就是启动对应两服务三容器:
cpp
services:
web:
image: mynginx:v3.0
build:
context: ./nginx
ports:
- "8112:80"
depends_on:
mycppweb:
condition: service_started
mycppweb:
build:
context: ./cppweb
image: mycppweb:v2.0
mycppweb2:
image: mycppweb:v2.0
对于cpp服务的dockerfile的c++程序编译及运行还是采取多级构建进行只保留exe的容器j镜像构建:
cpp
# 第一阶段:构建阶段(编译环境)
FROM ubuntu:22.04 as buildstage
# 配置国内软件源并更新
RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list && apt update
# 安装编译工具链
RUN apt install -y build-essential
# 设置工作目录并复制源码
WORKDIR /src
COPY ./main.cpp .
# 编译程序(链接pthread库)
RUN g++ main.cpp -o mycppweb -lpthread
# 指定运行时命令s
CMD ["/src/mycppweb"]
# 第二阶段:运行阶段(生产环境)
FROM ubuntu:22.04
# 从构建阶段仅复制最终可执行文件
COPY --from=buildstage /src/mycppweb /
# 指定容器启动命令
CMD ["/mycppweb"]
下面启动下:

- 构建对应的镜像。

- 容器构建成功。

- 容器正常启动,下面进行访问(打开对应的cpp服务日志监控)。



- 不停的访问对应网站;因为设置的是nginx根据web1:web2发送率是1:2的权重来进行请求分配;通过观察日志可以发现。
三.docker镜像制作常见问题
-
ADDvsCOPY:ADD功能更强,能复制本地文件和解压远程URL资源;COPY更安全透明,仅复制本地文件,是推荐做法。 -
CMDvsENTRYPOINT:ENTRYPOINT定义容器启动后固定的执行命令,CMD提供默认参数。两者组合使用,ENTRYPOINT为命令,CMD为参数,实现灵活与固定的平衡。 -
多阶段构建 (Multiple
FROM):多个
FROM指令用于多阶段构建。前期阶段(如编译环境)用于准备程序,后期阶段(如运行环境)仅复制所需文件,最终镜像只保留最后阶段内容,极大减小镜像体积。 -
Dockerfile 的优势:
相比直接制作快照(
docker commit),Dockerfile 具备可追溯、可重复、可审计、易于自动化的巨大优势,是构建镜像的标准方式(比如commit的方法可能会产生一些无关文件包等放入镜像不进行清除;dockerfile避免了这点)。 -
虚悬镜像 (Dangling Images):
指仓库名和标签均为
<none>的镜像;主要因镜像更新覆盖旧标签或多次构建同名新镜像导致旧镜像失去名称:

- 此类镜像已无用,可安全删除。使用
docker image ls -f dangling=true查看,并用prune命令清理。
- 中间层镜像 (Intermediate Layer Images):
- 定义 :是构建其他镜像时所依赖的、无标签的底层镜像;也就是本身整个镜像是
none none;但是其他镜像构建时候使用了它的某一层的缓存;避免了冗余操作;也就是多个新镜像构建引用了这一层即共用。(名称和标签也是none;注意与悬空镜像区分)。 - 作用:用于加速构建过程和复用资源,避免重复下载或编译。
- 特性:不应手动删除,删除会导致上层镜像因依赖丢失而出错。其生命周期自动管理,依赖它的顶层镜像被删除后会自动清理。
- 定义 :是构建其他镜像时所依赖的、无标签的底层镜像;也就是本身整个镜像是
区别:
| 特征 | 虚悬镜像 (Dangling Images) | 中间层镜像 (Intermediate Layer Images) |
|---|---|---|
| 状态 | 已废弃的顶层镜像,是构建过程的"废料",无任何镜像依赖它。 | 被依赖的底层镜像,是构建过程的"积木",有其他镜像依赖它。 |
| 成因 | 被新版本覆盖或重建同名镜像后,旧镜像失去名称。 | 是构建其他镜像时产生的依赖层,本身就没有名字。 |
| 能否删除 | 可以且应该删除,以释放磁盘空间。 | 绝对不能删除,删除会导致依赖它的上层镜像损坏。 |
| 查看命令 | docker image ls -f dangling=true |
docker image ls -a (显示所有镜像,包括中间层) |
总结:
"它们的名称和表情都是<none>,但虚悬镜像是没用的垃圾(可删),中间层镜像是有用的基石(不可删)。" 所以,不能仅凭 REPOSITORY 和 TAG 字段为 <none> 就删除镜像,必须先用 docker image ls -f dangling=true 确认它是真正的"虚悬镜像"后再进行清理。
四.本篇小结
本篇通过完整项目案例掌握Docker生产级应用部署,深入理解镜像分层机制与资源优化策略,提升容器化开发与运维效率。