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

文章目录

本篇摘要

本文基于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
启动集群
测试功能-清理资源

首先下载对应源码:

Redis源码包

然后本地解压拿到对应的conf文件:

  • 修改成集群相关配置。

修改如下:

  1. 安全认证设置

    • requirepass 123456 :设置客户端连接密码为 123456。
    • masterauth 123456:设置主从节点间同步数据的认证密码,与连接密码一致,确保集群内部通信安全。
  2. 网络与连接设置

    • bind * -::* :允许所有 IPv4 和 IPv6 地址连接,这是实现远程访问 和集群节点间通信的关键。
    • port 6379:保持默认服务端口。
  3. 集群模式配置

    • cluster-enabled yes:启用集群模式,这是核心开关。
    • cluster-config-file nodes.conf:指定集群节点配置文件,由 Redis 自动生成和维护,记录集群状态。
    • cluster-node-timeout 5000:设置节点超时时间(5秒),用于判断节点是否故障。
  4. 数据与运行设置

    • 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镜像制作常见问题

  1. ADD vs COPY:

    ADD 功能更强,能复制本地文件和解压远程URL资源;COPY 更安全透明,仅复制本地文件,是推荐做法。

  2. CMD vs ENTRYPOINT:

    ENTRYPOINT 定义容器启动后固定的执行命令,CMD 提供默认参数。两者组合使用,ENTRYPOINT 为命令,CMD 为参数,实现灵活与固定的平衡。

  3. 多阶段构建 (Multiple FROM):

    多个 FROM 指令用于多阶段构建。前期阶段(如编译环境)用于准备程序,后期阶段(如运行环境)仅复制所需文件,最终镜像只保留最后阶段内容,极大减小镜像体积。

  4. Dockerfile 的优势:

    相比直接制作快照(docker commit),Dockerfile 具备可追溯、可重复、可审计、易于自动化的巨大优势,是构建镜像的标准方式(比如commit的方法可能会产生一些无关文件包等放入镜像不进行清除;dockerfile避免了这点)。

  5. 虚悬镜像 (Dangling Images):

    指仓库名和标签均为 <none> 的镜像;主要因镜像更新覆盖旧标签或多次构建同名新镜像导致旧镜像失去名称:

  • 此类镜像已无用,可安全删除。使用 docker image ls -f dangling=true 查看,并用 prune 命令清理。
  1. 中间层镜像 (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生产级应用部署,深入理解镜像分层机制与资源优化策略,提升容器化开发与运维效率。

相关推荐
别动我齐刘海1 小时前
ROS2 Jazzy + C++ 时间系统——Time / Clock / Duration
linux·c++·人工智能·学习·机器学习·机器人·自动驾驶
你不是我我1 小时前
【AI 测评】群晖NAS部署CloudSaver:Docker安装、多源搜索、网盘转存与cpolar远程访问
运维·docker·容器
重生之小比特1 小时前
【C++进阶】unordered_set 和unordered_map 深度解析
c++·哈希算法
踏雪Vernon1 小时前
[OpenHarmony7.0][环境][教程]OpenHarmony 7.0源码在Docker Ubuntu22.04编译环境搭建与踩坑记录
docker·容器·eureka
chenbingjie_c9 小时前
C++ 进阶核心知识点总结:模板、内存分区、new/delete、内存池
开发语言·c++
程序猿乐锅10 小时前
【黑马点评 | 第八篇】Redisson分布式锁
java·数据库·spring boot·redis·分布式·spring·缓存
Frank_refuel11 小时前
【C++】Json库介绍和使用
c++
重生之小比特11 小时前
【C++进阶】map和set
java·开发语言·c++
阿狗童鞋11 小时前
Redis实战指南
数据库·redis·缓存