尧图精选

麒麟V10服务器上安装.NET Core并部署Web应用的完整指南

🕒 发布时间:2026/9/18 17:50:25 📁 来源:尧图网络
干这行久了就会碰到各种“页面看起来正常但一部署就翻车”的场景。最近在帮客户做国产化环境迁移目标机器是一台银河麒麟V10服务器任务是在上面把.NET Core环境装好再把Web网站跑起来。听起来很简单等真正动起手来你会发现“麒麟”并不是某一个单一的发行版它既有基于Debian体系的也有基于CentOS体系的CPU架构从x86到飞腾、鲲鹏都有稍不留神就会在第一步选错安装包。这篇文章就把我这次在麒麟服务器上安装.NET Core环境并发布Web网站的完整过程写下来。内容覆盖场景选型、离线安装、发布部署、systemd托管、反向代理以及我在现场遇到的一堆坑。如果你也在做国产化交付、内网部署或者只是想把.NET Web应用挪到Linux服务器上这份记录应该能帮你少走很多弯路。1. 动手前先把底层情况摸清楚1.1 为什么要在麒麟服务器上跑.NET Core首先要搞清楚一个现实很多政企单位、传统行业客户的服务器环境已经切换到了国产操作系统。而麒麟银河麒麟、中标麒麟是目前最常见的国产服务器系统之一。可问题是业务系统不可能一夜之间全部重写很多存量应用是用C#、.NET技术栈开发的Web系统这时候就必须在麒麟上把.NET Core运行时跑起来。另外.NET Core从3.1开始就是跨平台的不只是可以在Windows上跑Linux、macOS都可以。我们平时开发用的ASP.NET Core Web应用发布之后就是一堆dll文件加上一个可执行文件只要目标Linux服务器上有正确的.NET运行时就能像跑Java的jar包一样把它跑起来。这个特性在国产化迁移里特别管用不用重写业务只需要把发布的文件装到新环境就能用。1.2 先查版本和CPU架构这一步错后面全错我见过太多人一上来就直接下载安装包结果解压出来执行时报“Exec format error”搞了半天才发现是CPU架构不匹配。麒麟V10服务器版在真实环境里跑得非常杂x86的Intel、AMD还有海光、兆芯ARM的飞腾、鲲鹏也都有。不同CPU对应不同的.NET运行时包一旦下错连启动都启动不了。上服务器第一件事就是执行这两条命令hostnamectl uname -mhostnamectl能看到操作系统版本uname -m看CPU架构x86_64就是x86 64位aarch64就是ARM 64位。拿我这次部署的机器来说系统显示是“Kylin V10”架构是aarch64那就意味着所有安装包都要选linux-arm64版本而不是linux-x64版本。还要顺便确认一下系统默认的软件包管理工具。麒麟V10有两大分支基于Ubuntu/Debian的用apt基于CentOS/RHEL的用yum或dnf。这个会影响到后面安装依赖库时用什么命令也影响到能不能直接用yum装一些基础组件。1.3 服务器端装SDK还是装Runtime别搞混了很多刚接触Linux部署的同学会把Windows上的经验带进来在服务器上把完整的.NET SDK一装甚至去找“Hosting Bundle”。这个习惯在Windows上问题不大但在Linux上建议分清场景如果服务器负责跑应用不需要在本机编译代码那么装Runtime就够了如果需要在服务器上执行dotnet build、发布操作或者跑一些需要SDK的命令那就装完整SDK“Hosting Bundle”是Windows上ASP.NET Core托管IIS用的概念Linux上对应的其实是ASP.NET Core Runtime。我这次因为有现场调试需求也需要偶尔在服务器上重新发布一下所以选了完整SDK。如果你们公司的发布流程是“开发机上编译好再传包”生产服务器完全可以只装ASP.NET Core Runtime体积小很多占用更少安全面也更小。安装包类型用途适用场景.NET SDK包含编译器、CLI、运行时需要在本机编译、发布、调试.NET Runtime仅运行命令行的dll只跑控制台程序或后台任务ASP.NET Core Runtime运行Web应用所需服务器跑Web网站时选它2. 在麒麟服务器上安装.NET Core运行时2.1 离线环境怎么准备安装包客户的内网服务器通常没有外网或者外网卡在安全策略后面基本没法直接使用微软的在线安装脚本。最稳妥的办法是提前在一台能访问外网的机器上把安装包下载好用U盘或内网传输工具拷到服务器上。下载安装包时认准两个地方微软官方 .NET 下载页面dotnet.microsoft.com 上对应的发行版本页面。文件命名里会区分版本、架构、运行时类型。比如我这次下载的文件名类似dotnet-sdk-6.0.428-linux-arm64.tar.gz如果你只要运行时对应的就是aspnetcore-runtime-6.0.36-linux-arm64.tar.gz注意这里有个比较容易踩的坑ASP.NET Core Runtime和.NET Runtime是两回事。如果你发布的网站是ASP.NET Core MVC或者Web API只装一个纯.NET Runtime是不够的启动时会直接报缺少程序集。所以生产服务器上至少装ASP.NET Core Runtime或者干脆像我一样装完整SDK一步到位省心。2.2 解压安装和环境变量配置麒麟系统上没什么特殊的安装器微软官方提供的tar.gz包就是绿色版解压后就能用。我习惯把全部.NET相关文件放到一个统一目录下比如/usr/local/dotnet方便以后升级和管理。把安装包上传到服务器后执行以下几步sudo mkdir -p /usr/local/dotnet sudo tar zxf dotnet-sdk-6.0.428-linux-arm64.tar.gz -C /usr/local/dotnet sudo ln -s /usr/local/dotnet/dotnet /usr/bin/dotnet最后一步做软链接是为了任何用户状态下都能直接敲dotnet命令不需要每次手动添加PATH。如果你不想做软链接也可以在 /etc/profile 里追加环境变量export PATH$PATH:/usr/local/dotnet改完记得执行 source /etc/profile 让配置生效。2.3 验证安装是否成功命里安装完第一时间验证dotnet --info dotnet --list-runtimesdotnet --info会输出完整的运行时和SDK信息。如果能看到类似.NET SDKs installed: 6.0.428 [/usr/local/dotnet/sdk] .NET runtimes installed: Microsoft.AspNetCore.App 6.0.36 [/usr/local/dotnet/shared] Microsoft.NETCore.App 6.0.36 [/usr/local/dotnet/shared]说明环境基本就绪。这里也要提醒一下有些麒麟服务器为了精简可能缺少一些底层依赖库比如ICU库、OpenSSL、libstdc等。如果启动时报找不到libicu或libssl就需要用apt或yum把依赖补上# Debian/Ubuntu系的麒麟 sudo apt-get update sudo apt-get install -y libicu-dev libssl-dev # CentOS系的麒麟 sudo yum install -y icu openssl2.4 一个容易被忽略的坑权限和目录规划我曾经在一台服务器上遇到dotnet命令可以执行但发布的应用目录没有读权限结果systemd里启动一直失败的现象。排查了很久最后才发现是目录属主不对。建议把网站目录规划成独立用户例如创建一个www用户专门用于运行Web应用。目录结构可以参考/var/www/mycms/ # 存放发布文件 /var/log/mycms/ # 存放日志创建用户和授权sudo useradd -r -s /sbin/nologin www sudo mkdir -p /var/www/mycms sudo chown -R www:www /var/www/mycms这么做的好处是应用以最小权限运行即便被攻破也不能直接影响系统其他目录。很多老手部署时经常跳过这步直接拿root用户跑应用现场确实方便了但安全上非常不值得提倡。3. 发布Web网站并把文件部署到服务器3.1 开发机上执行发布命令如果是开发机发布我建议用命令行发布而不是直接复制项目文件夹。VS的“发布”功能本质和命令行一样但命令行更透明能看到每个参数。在项目根目录执行dotnet publish -c Release -o /tmp/publish如果项目引用了运行时标识可以在命令里指定dotnet publish -c Release -r linux-arm64 --self-contained false -o /tmp/publish这里有两个参数需要注意-r 指定目标运行时如果服务器是ARM就写linux-arm64是x86就写linux-x64--self-contained 为true时会把整套.NET运行时一起打进去服务器上不需要预装Runtime为false时服务器上必须手动装对应的运行时。依赖框架和自带运行时没有绝对好坏。自带运行时部署更简单但发布体积大几十MB每次更新都要重新上传整个包依赖框架则要求服务器环境保持一致。3.2 上传发布文件到麒麟服务器发布完成之后/tmp/publish下面就是整个Web应用的“成品”一堆dll、json配置文件、wwwroot静态资源可能还有一个可执行文件。把这些文件整体传到服务器上scp -r /tmp/publish userserver-ip:/var/www/mycms/在内网环境没有scp可用时也可以打包后通过U盘拷贝tar czf myapp.tar.gz -C /tmp publish到服务器端解压sudo mkdir -p /var/www/mycms sudo tar zxf myapp.tar.gz -C /var/www/mycms --strip-components1 sudo chown -R www:www /var/www/mycms3.3 先手动运行一次排查问题再托管很多新手喜欢直接把服务配好systemd再启动结果一启动就失败日志也看不出所以然。我的习惯是先手动跑一下确认应用本身没问题再交给systemd托管。先看一下程序集名称。通常发布目录下的dll名称和项目名称一致。sudo -u www ASPNETCORE_ENVIRONMENTProduction dotnet /var/www/mycms/MyCms.dll如果看到类似info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5000 info: Microsoft.Hosting.Lifetime[0] Application started. Press CtrlC to shut down.说明应用起来没问题。这时在本地执行curl http://localhost:5000能看到页面代码就表示Web服务已经正常响应。如果没反应先检查端口占用情况ss -tlnp | grep 5000也可能是应用启动后崩了需要看控制台输出的异常信息。这一步手动运行的价值就在这里错误信息直接打印在终端上不用去翻日志。3.4 用systemd把网站托管起来开机自启手动运行只能证明应用能跑但服务器一重启或者SSH一断开网站就没了。生产环境肯定要用systemd托管。编辑服务文件sudo vim /etc/systemd/system/mycms.service内容如下[Unit] DescriptionMy .NET Core Web Application Afternetwork.target [Service] Typesimple Userwww Groupwww WorkingDirectory/var/www/mycms ExecStart/usr/local/dotnet/dotnet /var/www/mycms/MyCms.dll Restartalways RestartSec10 KillSignalSIGINT SyslogIdentifiermycms EnvironmentASPNETCORE_ENVIRONMENTProduction EnvironmentASPNETCORE_URLShttp://0.0.0.0:5000 [Install] WantedBymulti-user.target这里重点解释几个关键参数User/Group以www身份运行不是rootWorkingDirectory工作目录必须指向发布目录很多配置读取、日志写入都以这个目录为基准Restartalways进程崩溃后自动重启这个是生产环境必须的ASPNETCORE_URLS默认Kestrel监听的是localhost:5000外网没法直接访问所以这里设置成0.0.0.0:5000让所有网卡都能访问KillSignalSIGINTASP.NET Core有优雅停机处理收到SIGINT会先处理完正在进行的请求再退出比直接SIGKILL安全。配置好之后执行sudo systemctl daemon-reload sudo systemctl enable mycms.service sudo systemctl start mycms.service sudo systemctl status mycms.service看到Active: active (running)就说明托管成功。以后查看日志用journalctl -u mycms.service -f3.5 配置Nginx反向代理一个服务器部署多个Web项目如果直接让Kestrel监听80端口也不是不能用但Kestrel对静态文件、压缩、并发连接的优化不如成熟Web服务器而且一个服务器上往往要跑多个Web项目全都占着80端口显然不行。常规做法是Nginx监听80端口按域名把请求转发给不同的Kestrel端口。比如项目A跑在5000项目B跑在5001。安装Nginx# Debian系 sudo apt-get install -y nginx # CentOS系 sudo yum install -y nginx编辑站点配置sudo vim /etc/nginx/conf.d/mycms.conf配置内容server { listen 80; server_name your.domain.com; location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }改完后重载Nginxsudo nginx -t sudo systemctl reload nginx如果同一台服务器部署多个Web项目就复制这个配置文件改server_name和proxy_pass端口即可。这种方式比直接给每个端口开放防火墙更安全外部用户只看到80端口不会暴露内部端口。4. 现场排障记录这些问题最容易踩4.1 下载错架构的安装包服务启动不了有一次我帮同事排查服务器是飞腾ARM架构但同事下载的是x64安装包。解压、配置PATH都正常执行dotnet --list-runtimes也能看到输出但一运行Web应用就报Exec format error后来用 file 检查了dotnet二进制文件file /usr/local/dotnet/dotnet果然显示是x86-64的ELF文件而系统内核却是aarch64。解决办法也很粗暴重新下载linux-arm64版本即可。所以前面反复强调uname -m检查架构是有原因的。4.2 应用能启动但外网访问不了这种情况八成是Kestrel只绑定了localhost。手动运行时如果没设置ASPNETCORE_URLS默认的监听地址就是localhost:5000外部访问自然被拒绝。解决方式有两个启动命令里带环境变量ASPNETCORE_URLShttp://0.0.0.0:5000 dotnet xxx.dll在appsettings.json里添加Urls: http://0.0.0.0:5000另外还要检查防火墙。麒麟V10很多自带firewalldsudo firewall-cmd --list-all sudo firewall-cmd --add-port5000/tcp --permanent sudo firewall-cmd --reload如果是内部部署也可以临时关闭防火墙验证一下是不是这个原因但生产环境不建议直接关闭防火墙。4.3 提示缺少ICU或OpenSSL依赖在精简安装的麒麟服务器上安装完.NET之后执行dotnet命令可能直接报找不到libicu或libssl。这类报错信息比较直接比如error while loading shared libraries: libicuuc.so.66可以通过下面的命令确认哪些库缺失ldd /usr/local/dotnet/dotnet | grep not found然后根据缺什么补什么sudo apt-get install -y libicu66 # 或 sudo yum install -y libicu有些新版本的系统里包名不同可以用install命令后再用ldd确认一遍直到没有not found为止。4.4 systemd启动失败但手动运行正常这个现象特别迷惑人。手动执行dotnet xxx.dll时一切正常但一配好systemd启动就失败journalctl里显示Permission denied。我遇到这种情况几乎都是因为目录权限。比如/var/www/mycms目录的所有者是root而Systemd里指定的Userwww没有读取权限。马上用chown授权sudo chown -R www:www /var/www/mycms还有一种是site目录挂载在特殊文件系统上比如NFSsystemd默认启动顺序在挂载之前导致启动时找不到目录。排查方法是在systemd服务文件里加上Afterremote-fs.target或者直接把挂载写到fstab里确保开机先挂载。这类问题在现场非常典型。4.5 80端口被占用或者权限不够很多人都想在80端口直接跑Kestrel但有两个现实问题80端口可能被Nginx或者其他Web服务占用了Linux下1024以下端口需要root权限用www用户跑就会报权限不足。最快确认端口占用情况ss -tlnp | grep :80如果确认被Nginx占用那刚才配置反向代理的方案就是正解让Nginx去监听80端口Kestrel继续跑在高位端口上。这样既有性能又绕开了权限问题还方便多站点共用一个公网入口。4.6 打开页面出现乱码或者静态资源404乱码问题大概率是系统没有对应的中文字体网页上的中文内容显示成方块。安装字体包可以解决sudo apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei静态资源404则要检查发布目录下的wwwroot文件夹是否完整。有时候用scp或U盘拷贝发布包时漏掉了wwwroot页面HTML能返回但CSS、JS全部加载不了。直接看一下ls -l /var/www/mycms/wwwroot如果这个目录不存在就得从开发机重新发布一份完整的包传上来。5. 可能遇到的其他奇怪问题实际部署不总是一帆风顺这类奇怪问题也值得记录一下。有些Web应用在页面里注册Service Worker时会看到类似加载web视图时出错: error: could not register service worker: invalidstateerror这个和服务器环境关系不大通常是浏览器安全策略或HTTPS证书的问题。检查一下站点是否启用了HTTPSService Worker要求在安全上下文下注册如果只是http且没有证书部分浏览器就会报这个。要么配好HTTPS证书要么在开发环境临时把Service Worker注册逻辑屏蔽掉。还有一次部署debug版的Web应用页面提示需要认证认证地址后面带了一个奇怪的token。后来发现是应用自身有一套Dsh认证机制和.NET环境没关系但第一次遇到确实容易让人误以为是环境装错了。遇到认证相关提示时先看看是不是应用代码或配置里包含token认证逻辑别盲目去重装环境。另外提醒一下有的麒麟系统默认开启了SELinux。如果Nginx反代之后始终504或者连不上后端可以临时执行getenforce如果返回Enforcing可以先用setenforce 0临时关闭测试。确认是SELinux拦截之后再根据服务类型配置对应的布尔值或上下文策略。不要为了图省事长期关闭SELinux否则系统安全性会明显下降。还有的时候服务器本身时间不对会导致HTTPS证书校验失败。用date看一下系统时间如果偏差太大安装一下chrony或者ntp并同步时间sudo apt-get install -y chrony sudo systemctl enable --now chrony这种情况多见于刚装好的银河麒麟服务器时间还停留在初始安装时的时间。最后说一件容易被忽视的事不要在晚上加班部署时才想起来新建用户和授权。整个过程中最花时间的是应用内部的配置和依赖问题而不是.NET环境本身。提前规划目录、用户、端口和数据库连接串现场真正安装的时间其实只要十几分钟。我在同型号的多台麒麟服务器上做过批量部署流程固定之后每台机器从解压到测试通过基本在15分钟以内。平常做完一个项目我会顺手把用到的安装包版本、依赖库清单、发布配置、systemd服务文件一起存到本地一个脚本仓库里。下次遇到相似项目只需要改项目名和端口就能直接复用省下来的时间非常可观。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →