让我的 .NET Core 博客系统支持 Docker

点击上方蓝字关注“汪宇杰博客”

导语

我的博客(https://edi.wang)所使用的博客系统 Moonglade 开源已经一年多了。目前已有至少4位社区朋友使用此系统在 Azure、阿里云上部署了自己的博客。可惜长久以来该系统一直缺乏 Docker 支持,而 .NET Core 必须结合 Docker 才是当今世界的政治正确。我作为一名20年的老软粉,虽然嘴上说着很不情愿用 Linux、Docker这种非微软的东西,但也只能假装抱着批判的态度,向 Linux 和 Docker 伸出了魔爪,让我的博客系统能够容器化运行。

Docker 环境安装

我作为一个20多年的老软粉,怎么可以在自己纯洁的 Windows 电脑上装 Docker 呢?装完以后:Docker 真香。

为了最大限度的避免 Windows 被污染(尽管它已经是咖喱拌饭了),我的 Docker 编译和发布环境都配置在云端,采用 Azure DevOps + Docker Hub + Azure App Service Linux Plan 的方式去编译运行。

Dockerfile

Visual Studio 可以直接右键一个 ASP.NET Core 项目添加 Docker 支持,这种方式可以让你很方便的在本地调试 Docker 中的 ASP.NET Core 程序。VS除了向工程目录添加一个 Dockerfile 以外,还会修改你的 csproj 工程文件,好让工具链整合你的容器。而其实对于单纯编译和运行 ASP.NET Core 网站而言,单独一个 Dockerfile 就够了,Docker 会根据这个 Dockerfile 编译出应用的容器镜像。

最初我博客的 Dockerfile 内容如下:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

WORKDIR /app

EXPOSE 80

EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build

WORKDIR /src

COPY ["Moonglade.Web/Moonglade.Web.csproj", "Moonglade.Web/"]

# COPY 其余的工程文件,篇幅关系省略

RUN dotnet restore "Moonglade.Web/Moonglade.Web.csproj"

COPY . .

WORKDIR "/src/Moonglade.Web"

RUN dotnet build "Moonglade.Web.csproj" -p:Version=10.2.0-docker -c Release -o /app/build

FROM build AS publish

RUN dotnet publish "Moonglade.Web.csproj" -p:Version=10.2.0-docker -c Release -o /app/publish

FROM base AS final

WORKDIR /app

COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "Moonglade.Web.dll"]

这份 Dockerfile 和 Visual Studio 自动生成的没太大区别。其中,我指定编译版本号参数为 -p:Version=10.2.0-docker,以便于直接从博客网站的界面分辨部署类型是 Docker 还是传统的 Code。

YAML

在 Azure DevOps 上,我使用 YAML 方式编译和部署我的博客项目,其中 Docker 的编译步骤定义如下:

- job: Docker

  pool:

    vmImage: 'ubuntu-latest'

  steps:

  - task: Docker@2

    displayName: 'Build and Push'

    inputs:

      containerRegistry: 'ediwang_dockerhub'

      repository: ediwang/moonglade

      tags: latest

由于VM镜像选的是 Ubuntu,因此这个 Docker 镜像编译出来为 Linux/x64 架构。如果你需要其他架构,可以自行添加其他类型的VM镜像。

ediwang_dockerhub 是预先在 Azure DevOps 授权配置好的针对 Docker Hub 的连接名称。编译完成后,Azure DevOps 会使用其中的授权向 Docker Hub 发布镜像。

福报#1:路径问题

当我兴高采烈的测试我的 Docker 容器时,我惊喜的发现,博客的博主头像、RSS订阅、OPML等全部都404了。根据之前我修过的Linux福报,我立即明白这是路径写法的问题。

在 Windows 系统中,表示一个文件或文件夹的路径通常用反斜杠分割目录,如:

C:\Fubao\996.icu

而 Linux 系统中,路径得用斜杠来分割目录,如:

/use/dotnet/work/955

像我这样的老牌软狗,很容易按照习惯把代码写成 Windows 的形式,毕竟微软曾经说好的 Linux 是毒瘤, .NET 只能在 Windows 上跑:

var fallbackImageFile = $@"{AppDomain.CurrentDomain.GetData(Constants.AppBaseDirectory)}\wwwroot\images\default-avatar.png";

其实 .NET 自古以来都有个API:Path.Combine(),用来拼路径,它在 .NET Core 里遇到 Linux 环境可以正确使用斜杠,于是软狗以为这样写就没事了:

var cssPath = Path.Combine(webRootPath, "css", "theme", currentTheme);

大部分情况确实是好的,然而我们来看个会爆的例子:

var p1 = "/dotnet";

var p2 = "/fubao/996.icu";

Path.Combine(p1, p2);

猜猜结果变成什么?

/dotnet 被丢掉了,只能996,进ICU。

好在微软为了不让我们进ICU,在.NET Standard 2.1里引入了 Path.Join() 方法,可以输出我们想要的结果:

因此,我把博客代码里用到路径的地方全部都用 Path.Join() 改了一遍,终于恢复了博主头像、RSS等资源的正常访问。

Path.Join() 参考文档:https://docs.microsoft.com/en-us/dotnet/api/system.io.path.join?view=netcore-3.1

福报#2:libgdiplus

博客程序运行期间,还报了另一个错,日志如下:

2020-03-31T12:02:53.405115468Z System.TypeInitializationException: The type initializer for 'Gdip' threw an exception.
2020-03-31T12:02:53.405359877Z  ---> System.DllNotFoundException: Unable to load shared library 'libgdiplus' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibgdiplus: cannot open shared object file: No such file or directory
2020-03-31T12:02:53.405375177Z    at System.Drawing.SafeNativeMethods.Gdip.GdiplusStartup(IntPtr& token, StartupInput& input, StartupOutput& output)
2020-03-31T12:02:53.405390778Z    at System.Drawing.SafeNativeMethods.Gdip..cctor()
2020-03-31T12:02:53.405397878Z    --- End of inner exception stack trace ---

这是由于博客代码用到了一些 .NET Core 的绘图 API,而这些 API 的底层需要 Linux 系统上装一个叫做 libgdiplus 的库。可是 Azure App Service 的 Linux 容器主机对用户来说无法直接操作,不可能 SSH 进去给它装个库,怎么办呢?

Bing 了一番之后发现,Dockerfile 里面居然可以直接定义 Linux 安装包的命令,把依赖性搞定。直接加入一条RUN命令的步骤即可:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

RUN apt-get update && apt-get install -y libgdiplus

WORKDIR /app

EXPOSE 80

EXPOSE 443

...

配置默认值

使用 Docker 容器部署应用的体验我希望是一键部署以后啥都不用干,直接能跑。而以前版本的博客系统,必须要求用户先手工配置一堆环境变量或是配置文件才能跑,非常996。

这个问题非常好办,只要在 appsettings.json 中留配置的默认值,保证程序能先跑起来即可。至于自定义的配置,可以让用户通过环境变量传给 Docker 容器。即保证了一键部署的方便性,又保留了自定义配置的灵活性。

小结

让 .NET Core 程序支持 Docker 并不麻烦。麻烦的是老一代 .NET 程序员会被根深蒂固的 Windows 设计所牵绊。在新的时代,我们必须学习新的实践,不要想着吃老本。.NET Framework 已经日薄西山,及时删库跑路,上 .NET Core + Docker 的船,才能保证在新的时代还能继续用 C# 释放生产力!我的 Docker 之旅刚刚起步,肯定还有很多我没遇到过的情况。欢迎读者在留言中补充和建议!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 实现带权有向图的单源点最短路径Dijkstra算法(完整代码)

首先,引进一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v0到每个终点vi的最短路径的长度。 它的初态为:若从v0到vi有弧,则D[i]为弧上的权值;否则,置D[i]为∞。 显然,长度为 D[j]Min{…

[ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

昨天有个朋友在微信上问我一个问题:他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展,比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中,问我是否有好解决方案。这是一个挺有意思的问题,我们…

C++ 实现带权有向图的每对顶点之间的最短路径Floyd算法(完整代码)

基本思想是: 假设求从顶点vi到vj的最短路径。 如果从vi到vj有弧,则从vi到vj存在一条长度为arcs[i][j]的路径,该路径不一定是最短路径,尚需进行n次试探。 首先考虑路径(vi, v0, vj)是否存在(判别…

等额本息,等额本金区别

等额本金,等额本息区别 买房银行贷款分为两种: 等额本金和等额本息 等额本息 等额本息定义:还款本金占比逐月递增,利息占比逐月递减,月还款数不变由于每月的还款额度是一样的,其中每个月的还款包括了根…

【视频回放与课件】Build your AI solution with MLOps

4月8日在Global AI Community on Vitural Tour与大家分享了Build your AI solution with MLOps的专题,本课程主要介绍了微软Azure Machine Learning如何使用 , 以及如何通过Azure Machine Learning 结合 MLOps的概念完成机器学习项目的工作。本次Global AI Communit…

C++ 实现分块查找(顺序存储结构)(完整代码)

代码如下: #include <iostream> using namespace std; const int Maxsize 1000; const int MINNUM -999999; class Index_table {friend class SeqList; private:int key;int address; };class SeqList {//该顺序表从下标为0开始 public:~SeqList(){delete[] elem;del…

《五分钟商学院》管理篇学习笔记

【商业知识】| 作者 / Edison Zhou这是恰童鞋骚年的第213篇原创文章在商业篇中&#xff0c;主要探讨的是企业如何处理与外部&#xff08;比如客户&#xff09;的关系。而在管理篇中&#xff0c;关注的重点则是企业如何处理与内部的关系。外部的世界很精彩&#xff0c;而内部的世…

如何在.NET应用程序中分析CPU使用率过高的问题

原文来自互联网&#xff0c;由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权&#xff0c;请联系小编&#xff0c;小编将在24小时内删除。限于译者的能力有限&#xff0c;个别语句翻译略显生硬&#xff0c;还请见谅。作者:胡安帕勃罗希达&#xff0c;JUAN PABLO SCIDA是一…

HashMap实现原理

HashMap HashMap基础数据结构&#xff1a; 如上结构课看出&#xff0c;HashMap主要是有一个链表的形式来存储数据 &#xff0c;上面Node类和C语言中的结构体很像&#xff0c;如上可以看出HashMap底层由是一个数组结构&#xff0c;数组中的每一项又是一个链表&#xff0c;新建一…

做好技术管理,你必须要跨越的4道槛

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份读者群里有不少刚开始做管理的技术人&#xff0c;很多都和我谈过他们的困惑。总结下来主要是不知道继续晋升需要培养哪方面的能力。技术经理其实是技术人最难做好的管理岗&#xff0c;原因主要有两方面&#…

图的最小生成树和最短路径算法思路总结(Prim,Kruskal,Dijkstra,Floyd)

带权无向图—>最小生成树算法—>Prim算法: 思路: 首先&#xff0c;我们先设置两个集合&#xff0c;U_{}&#xff1a;一个用来放最小生成树的顶点&#xff0c;T_{}&#xff1a;一个用来放最小生成树的边。选取最开始的点V_0&#xff0c;将V_0放入U_{}中&#xff0c;得到U_…

玩转控件:对Dev的GridControl控件扩展

缘由一切实现来源于需求&#xff0c;目的在于不盲目造轮子&#xff0c;有小伙伴儿在看了《玩转控件:对Dev中GridControl控件的封装和扩展》文章后&#xff0c;私信作者说&#xff0c;因公司业务逻辑比较复杂&#xff0c;展示字段比较多&#xff0c;尤其网格列表控件展示数据太多…

二叉排序树(搜索树BST)-详解结点的删除

在二叉排序树中删除一个结点时&#xff0c;需保证删除后的二叉树仍然是二叉排序树。为讨论方便&#xff0c;假定被删除结点为p&#xff0c;其双亲结点为f。删除的过程可按下述的两种情况分别处理。 在这里我们用红色三角形表示我们要删除的结点&#xff0c;蓝色表示我们要改变指…

java调优方法,jvm监控工具

graph LR A-->B性能概述 程序性能表现形式 执行速度&#xff1a;程序响应速度&#xff0c;总耗时是否足够短内存分配&#xff1a;内存分配是否合理&#xff0c;是否过多消耗内存或者存在泄漏启动时间&#xff1a;程序运行到可以正常处理业务需要的时间负载承受能力 性能测…

那位五十多岁的创业者给我的启示!

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区特邀嘉宾&#xff01;一我曾经提到过最终改行从事美缝行业的老w&#xff0c;他靠自己的“不够努力”&#xff0c;最终离开了行业。但是&#xff0c;这个世界其实有点讽刺。在沉迷于安逸小日子的老…

平衡二叉树(AVL树)-详解平衡调整

平衡调整: (注意&#xff1a;平衡调整只是平衡调整&#xff0c;没有进行结点的插入) LL型调整: (带阴影的小框表示插入的结点) 代码如下: AVLNode *AVLTree::LL_Rotate(AVLNode *a) {AVLNode *b;b a->lchild;a->lchild b->rchild;b->rchild a;a->bf b-&g…

初识消息队列/RabbitMQ详解

欢迎大家阅读《朝夕Net社区技术专刊》我们致力于.NetCore的推广和落地&#xff0c;为更好的帮助大家学习&#xff0c;方便分享干货&#xff0c;特创此刊&#xff01;很高兴你能成为忠实读者&#xff0c;文末福利不要错过哦&#xff01;今天来给大家分享关于消息队列的内容&…

zookeeper理解

Zookeeper简介 Zookeeper的数据模型 层次化的目录结构&#xff0c;命名符合常规文件系统规范每个节点在zookeeper中叫做znode&#xff0c;并且有一个唯一的路径标识节点znode可以包含数据和子节点&#xff0c;但是EPHEMERAL类型的节点不能有子节点Znode中的互刷可以有多个版本…

那些年,在MSRA实习过的女孩,现在都怎么样了?

编者按&#xff1a;我们用两周时间回访了五位在 MSRA 实习过的女孩&#xff0c;她们也都是当年“实习派”的主人公。我们本想在更长的时间维度下&#xff0c;看一看 MSRA 给予她们的改变。然而超乎预期的是&#xff0c;在她们身上&#xff0c;坚持的力量比改变更强。变与不变&a…

Zookeeper理解---ZAB协议

ZAB协议 Zookeeper并不是完全采用Paxos算法&#xff0c;而是使用了一种称为Zookeeper Atomic Broadcast&#xff08;ZAB&#xff0c;Zookeeper原子消息广播协议&#xff09;作为数据一致性的核心算法&#xff0c;依据此算法来实现分布式数据一致性的解决。他是一种特别为Zooke…