我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统。陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户,在这个过程中,我也积累了不少如何开发运营一款独立产品的经验。

在这几年时间中,有一些客户始终在用我的客服系统,昨天给一位客户更新系统,看到更新记录,第一次给他版本是 2021 年 9 月,一转眼,快 4 年了……

78019-20250212114120578-1932829074.png


最初我开发的在线客服系统版本,只能运行在 Windows + SQL Server 上,后来在用户的建议和帮助下,逐渐拓展到了 Linux + MySQL、宝塔面板,最后拓展到了 Docker,在这篇文章中,我主要讲 Docker 打包发布的过程和一些注意事项。

可能有些朋友会疑问,打包 Docker 镜像应该很容器吧?其实这说的也没错,如何仅仅只是打包一个能跑的镜像,那几行命令就可以,但是如果你要提供一个商业级的产品,那方方面面的细节都需要考虑周全。

我记得最初我做的镜像,docker run 之后,还得 exec 到容器内部去修改配置文件,再重启服务才能使用,这个使用门槛就一下变得很高,用户要用也很麻烦。

现在我通过环境变量,直接在 docker run 的时候把参数带进去,完全实现了一键运行,一分钟系统上线,有兴趣的朋友可以看我的 Docker Hub 主页:升讯威在线客服与营销系统


.NET 程序打包到docker 镜像的注意事项

1. 选择合适的基础镜像

  • .NET Core / .NET 5+:如果你使用的是 .NET Core 或 .NET 5+,你应该选择官方的 .NET 镜像作为基础镜像。例如,mcr.microsoft.com/dotnet/aspnet:5.0 适用于 ASP.NET Core 应用,mcr.microsoft.com/dotnet/runtime:5.0 适用于运行时。
  • 分层镜像:为了减小镜像体积,可以使用分层镜像(multi-stage build)。例如,首先使用包含 SDK 的镜像进行构建,最后只复制构建好的产物到运行时镜像。
# 使用 .NET SDK 镜像进行构建
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /app
COPY . .
RUN dotnet publish -c Release -o out

# 使用 .NET Runtime 镜像运行
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "MyApp.dll"]

2. 优化 Dockerfile

  • COPY 指令:尽量减少 Docker 镜像层数。将 .csproj 和其他依赖文件单独复制,然后运行 dotnet restore,而不是将整个项目一起复制。这可以避免每次构建时重新下载依赖。
  • 构建缓存:利用 Docker 构建缓存,使得在代码不发生改变时,Docker 可以复用先前的层,减少构建时间。
# 先复制项目文件并还原依赖
COPY *.csproj ./
RUN dotnet restore

# 然后复制所有源代码
COPY . ./

3. 确保运行时环境的正确性

  • 环境变量:如果应用程序依赖于环境变量(如数据库连接字符串、API 密钥等),可以在 Dockerfile 或 Docker 运行命令中设置这些环境变量。
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ConnectionStrings__MyDb="Server=db;Database=mydb;User=sa;Password=Password123"
  • 文件权限:确保复制到 Docker 镜像中的文件有正确的权限。例如,使用 RUN chmod 调整权限,避免容器内的文件权限问题。

4. 配置文件

  • appsettings.json:如果你的应用使用配置文件,可以选择将配置文件放入 Docker 镜像中,或者将配置文件放在外部,并通过 Docker 挂载来管理配置。

5. 网络和数据库连接

  • 连接字符串:如果应用连接到数据库或其他外部服务,最好避免将敏感数据(如连接字符串)硬编码在代码中。可以使用环境变量或 Docker secrets 进行管理。

6. 日志记录

  • Docker 容器中通常没有 UI,所以要确保将日志输出到标准输出(stdout)或标准错误(stderr)。这能确保日志能被 Docker 捕获并显示出来,便于调试。
// 在 .NET 中,通常会配置日志输出
builder.Logging.AddConsole();

7. 容器内存限制

  • 如果你的应用程序需要较多内存,考虑在 Docker 容器中设置合适的内存限制,避免应用因资源不足而崩溃。
docker run -m 512m myapp

8. 清理无用文件

  • 在 Dockerfile 中使用 dotnet publish 生成发布文件时,可以去除不必要的文件,如测试、源码文件,减少镜像大小。
  • 使用 dotnet publish --self-contained 创建自包含的发布包,以便不依赖 Docker 中的 .NET SDK。

9. 安全性

  • 使用 Docker 多阶段构建时,确保最终镜像只包含运行时所需的文件。
  • 不要在镜像中包含开发工具、调试符号、源码或其他非生产所需的内容。

10. 测试镜像

  • 打包完 Docker 镜像后,确保在本地或 staging 环境进行全面的测试,检查应用在容器中的运行情况,包括性能、连接、日志和异常处理。

11. 推送和部署

  • 在推送镜像到 Docker 仓库时,考虑使用合适的标签(例如,latest, v1.0.0, production)来管理版本。
  • 使用 Kubernetes 或 Docker Swarm 等容器编排工具来管理和部署你的容器。

示例 Dockerfile

# 1. 基础镜像
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build

# 2. 设置工作目录并复制文件
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore

# 3. 复制源代码并构建发布包
COPY . ./
RUN dotnet publish -c Release -o out

# 4. 使用运行时镜像
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
WORKDIR /app
COPY --from=build /app/out ./

# 5. 设置启动命令
ENTRYPOINT ["dotnet", "MyApp.dll"]

如何让你的独立产品用上 Docker 环境变量

Docker环境变量是 Docker 容器化应用中一种重要的配置方式。它们可以帮助在容器启动时为应用提供必要的参数或配置,而不需要修改容器内部的代码或配置文件。Docker 环境变量使得容器可以更加灵活和动态化,尤其在不同的部署环境中尤为重要。

1. 什么是 Docker 环境变量?

Docker 环境变量(Environment Variables)是操作系统层面上用于存储配置信息的变量。在 Docker 容器中,环境变量通常用于存储应用的配置信息、凭证、API 密钥、数据库连接字符串等。它们可以在容器启动时传递,并可以在运行时访问和修改。

2. 为什么使用环境变量?

  • 灵活性:可以根据不同的运行环境提供不同的值,无需修改容器镜像。
  • 安全性:可以避免硬编码敏感信息(如密码、API 密钥等),提升安全性。
  • 配置管理:通过环境变量可以在不修改容器内容的情况下,动态调整容器行为。

3. 如何在 Docker 中使用环境变量?

Docker 提供了几种设置和使用环境变量的方式:

3.1 使用 -e--env 参数设置环境变量

在使用 docker run 启动容器时,可以通过 -e 参数来设置环境变量。例如:

docker run -e MY_ENV_VAR=value my_image

这样会在容器中设置一个名为 MY_ENV_VAR 的环境变量,值为 value

3.2 使用 .env 文件

为了方便管理多个环境变量,可以使用 .env 文件。在 .env 文件中,每一行定义一个环境变量的键值对:

MY_ENV_VAR=value
ANOTHER_ENV_VAR=another_value

然后通过 --env-file 参数将该文件传递给 Docker 容器:

docker run --env-file .env my_image

3.3 在 Dockerfile 中使用 ENV 指令

在构建镜像时,可以在 Dockerfile 中使用 ENV 指令来设置环境变量:

FROM ubuntu:latest
ENV MY_ENV_VAR=value

这将创建一个在容器运行时可用的环境变量 MY_ENV_VAR

3.4 使用 docker-compose 中的环境变量

在使用 docker-compose 管理多个容器时,可以在 docker-compose.yml 文件中定义环境变量:

version: '3'
services:
  webapp:
    image: my_image
    environment:
      - MY_ENV_VAR=value
      - ANOTHER_VAR=another_value

此外,也可以从 .env 文件加载环境变量:

version: '3'
services:
  webapp:
    image: my_image
    env_file:
      - .env

3.5 通过 docker exec 查看环境变量

可以通过 docker exec 进入容器内部,使用 envprintenv 命令查看容器中的环境变量:

docker exec -it container_id env

4. 环境变量的作用与实践

环境变量通常用于以下几种场景:

4.1 数据库连接信息

在多环境部署中,数据库连接信息可以通过环境变量配置,以避免在源代码中硬编码这些信息。例如:

docker run -e DB_HOST=localhost -e DB_USER=root -e DB_PASS=secret my_image

4.2 API 密钥

很多应用依赖于外部服务的 API 密钥,这些密钥可以通过环境变量来传递,以避免泄露。例如:

docker run -e API_KEY=your_api_key my_image

4.3 配置不同的运行环境

可以根据不同的环境传递不同的环境变量值,如开发、测试和生产环境。例如,在生产环境中你可能需要开启调试日志,但在开发环境中关闭它。

docker run -e ENV=production -e LOG_LEVEL=error my_image

5. 容器间共享环境变量

在多容器的场景中,如果需要多个容器共享环境变量,可以通过 Docker 网络和服务间的环境变量传递来实现。例如,使用 Docker Compose 启动多个服务时,web 服务可以访问 db 服务的环境变量。

6. 限制和注意事项

  • 敏感信息:环境变量可以在 Docker 容器启动时传递,但它们也有泄露的风险。例如,容器日志或操作系统的某些工具可能会暴露这些变量。
  • 变量覆盖:在容器运行时,环境变量的值可能会被外部传递的变量覆盖。例如,在 docker-compose.yml 中设置的环境变量可以在 docker run 时通过 -e 参数覆盖。
  • 共享变量的作用域:在 Docker Compose 或多容器应用中,环境变量的作用域仅限于指定的容器,跨容器传递时需要显式声明。

简介下这个 .net 开发的小系统

https://kf.shengxunwei.com/

升讯威在线客服与营销系统是一款客服软件,但更重要的是一款营销利器。

  • 可以追踪正在访问网站或使用 APP 的所有访客,收集他们的浏览情况,使客服能够主动出击,施展话术,促进成单。
  • 可嵌入网站、手机 APP、公众号、或者通过 URL 地址直接联系客服。
  • 支持访客信息互通,可传输访客标识、名称和其它任意信息到客服系统,与您的业务系统对接。
  • 可全天候 7 × 24 小时挂机运行,网络中断,拔掉网线,手机飞行模式,不掉线不丢消息,欢迎实测。

image.png

image.png

希望能够打造: 开放、开源、共享。努力打造 .net 社区的一款优秀开源产品。

钟意的话请给个赞支持一下吧,谢谢~


曹旭升
1 声望0 粉丝