Asp.Net Core使用System.Drawing.Common部署到docker报错问题

原文:Asp.Net Core使用System.Drawing.Common部署到docker报错问题

Asp.Net Core 2.1发布后,正式支持System.Drawing.Common绘图了,可以用来做一些图片验证码之类的功能。但是把网站部署到docker容器里运行会遇到很多问题,也是非常闹心的,本文记录这些问题,希望帮到有需要的人。

创建网站

前提条件:安装最新版VS2017和Net Core SDK 2.1。

首先新建网站,选择Asp.Net Core 2.1 Web应用程序(模型视图控制器),不勾选docker,我习惯自行编写Dockerfile。

指定网站访问端口5000。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                //必须指定端口,否则在Win10命令行运行端口是5000,在CentOS docker运行端口是80
                .UseUrls("http://*:5000")
                .UseStartup<Startup>();

  

为了调试方便,把隐私要求和https要求先屏蔽。

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                //options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                //app.UseHsts();
            }

            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

  

修改appsettings.json输出全部调试信息,便于查找错误。

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug"
    }
  },
  "AllowedHosts": "*"
}

  

调试运行一下,确认网站没问题。

把Home控制器的Index页面改一下,简单粗暴一点,就显示一个图片好了。

@{
    ViewData["Title"] = "Home Page";
}

<h4>System.Drawing.Common绘图</h4>
<img src="Home/GetImg" alt="pic" class="img-thumbnail" width="400" height="200" />

  

NuGet安装System.Drawing.Common。

给Home控制器增加一个绘图函数GetImg。

public IActionResult GetImg()
        {
            _logger.LogInformation($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:fff}, start create image");

            string msg = $"{DateTime.Now:HH:mm:ss}, 您好 drawing from .NET Core";

            Image image = new Bitmap(400, 200);
            Graphics graph = Graphics.FromImage(image);
            graph.Clear(Color.Azure);
            Pen pen = new Pen(Brushes.Black);
            graph.DrawLines(pen, new Point[] { new Point(10, 10), new Point(380, 180) });
            graph.DrawString(msg, new Font(new FontFamily("微软雅黑"), 12, FontStyle.Bold), Brushes.Blue, new PointF(10, 90));

            //把图片保存到内存文件流
            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Png); ;
            byte[] buf = ms.GetBuffer();

            _logger.LogInformation($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:fff}, finish create image");

            return File(buf, "image/png");
        }

  

调试运行一下,是没问题的。

部署网站到docker

编写Dockerfile,注意把文件属性“复制到输出目录”设置为“如果较新则复制”。

FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
COPY . /app
EXPOSE 5000
ENTRYPOINT ["dotnet", "NetCoreDraw.dll"]

  

重新编译网站,以文件夹方式发布到默认的bin\Release\PublishOutput。在Windows控制台进入发布目录,输入dotnet NetCoreDraw.dll运行网站,浏览器访问http://localhost:5000/,确认没问题。

编写docker-compose.yml

version: ‘3‘

services:

  myweb:
    container_name: myweb
    image: mywebimage
    build:
      context: ./PublishOutput
      dockerfile: Dockerfile
    ports:
      - "5000:5000"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - TZ=Asia/Shanghai
    restart: always

  

我用CentOS 7.3虚拟机做试验,安装好docker环境,用Xshell访问虚拟机,用Xftp把PublishOutput文件夹、docker-compose.yml拖到Home下面。

进入Home目录,把容器跑起来。

[[email protected] home]# docker-compose up
Creating network "home_default" with the default driver
Building myweb
Step 1/5 : FROM microsoft/dotnet:2.1-aspnetcore-runtime
2.1-aspnetcore-runtime: Pulling from microsoft/dotnet
be8881be8156: Pull complete
f854db899319: Pull complete
4591fd524b8e: Pull complete
65f224da8749: Pull complete
Digest: sha256:a43b729b84f918615d4cdce92a8bf59e3e4fb2773b8491a7cf4a0d728886eeba
Status: Downloaded newer image for microsoft/dotnet:2.1-aspnetcore-runtime
 ---> fcc3887985bb
Step 2/5 : WORKDIR /app
Removing intermediate container aba36715acfc
 ---> 25bc5bb6871f
Step 3/5 : COPY . /app
 ---> 9baaa790a82f
Step 4/5 : EXPOSE 5000
 ---> Running in 269408c67989
Removing intermediate container 269408c67989
 ---> fbd444c44d20
Step 5/5 : ENTRYPOINT ["dotnet", "NetCoreDraw.dll"]
 ---> Running in 2a9ba559b137
Removing intermediate container 2a9ba559b137
 ---> b1bb1dccd49a
Successfully built b1bb1dccd49a
Successfully tagged mywebimage:latest
WARNING: Image for service myweb was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating myweb ... done
Attaching to myweb
myweb    | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
myweb    |       No XML encryptor configured. Key {1f87d27e-c6b1-435f-bab6-aebacd6d6817} may be persisted to storage in unencrypted form.
myweb    | Hosting environment: Production
myweb    | Content root path: /app
myweb    | Now listening on: http://[::]:5000
myweb    | Application started. Press Ctrl+C to shut down.

  

在浏览器访问我的虚拟机里的网站http://192.168.41.129:5000/,图片显示不出来。

在Xshell可以看到容器调试信息,发生了错误。

myweb    |       2018-07-29 09:50:04:753, start create image
myweb    | info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
myweb    |       Executed action NetCoreDraw.Controllers.HomeController.GetImg (NetCoreDraw) in 18.5988ms
myweb    | fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
myweb    |       An unhandled exception has occurred while executing the request.
myweb    | System.TypeInitializationException: The type initializer for ‘Gdip‘ threw an exception. ---> System.DllNotFoundException: Unable to load shared library ‘libdl‘ or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibdl: cannot open shared object file: No such file or directory
myweb    |    at Interop.Libdl.dlopen(String fileName, Int32 flag)
myweb    |    at System.Drawing.SafeNativeMethods.Gdip.LoadNativeLibrary()
myweb    |    at System.Drawing.SafeNativeMethods.Gdip..cctor()
myweb    |    --- End of inner exception stack trace ---

  

找不到库文件libdl,网上有类似的问题和解决方案,就是建立一个文件连接。

https://q.cnblogs.com/q/107946/

但是aspnetcore容器中的库文件位置,跟正式发行版本的CentOS和Ubuntu不太一样,得进入容器去找。在容器运行的时候,通过Xshell的复制功能,再开一个终端窗口,进入容器。

docker exec -it myweb /bin/bash

aspnetcore容器不支持locate命令,我对Linux系统文件位置不熟,只好硬着头皮挨个看了一遍,还好目录不多,最后确定在这里。

[email protected]:/# ls lib/x86_64-linux-gnu/libdl*

lib/x86_64-linux-gnu/libdl-2.24.so  lib/x86_64-linux-gnu/libdl.so.2

所以修改Dockerfile为

FROM microsoft/dotnet:2.1-aspnetcore-runtime

RUN ln -s /lib/x86_64-linux-gnu/libdl-2.24.so /lib/x86_64-linux-gnu/libdl.so

WORKDIR /app
COPY . /app
EXPOSE 5000
ENTRYPOINT ["dotnet", "NetCoreDraw.dll"]

  

重新编译、发布网站到虚拟机,重新运行编译运行容器。

[[email protected] home]# docker-compose up --build

浏览网站,仍然无法显示图片,但是错误内容变了。

myweb    | fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
myweb    |       An unhandled exception has occurred while executing the request.
myweb    | System.TypeInitializationException: The type initializer for ‘Gdip‘ threw an exception. ---> System.DllNotFoundException: Unable to load DLL ‘libgdiplus‘: The specified module could not be found.
myweb    |    at System.Runtime.InteropServices.FunctionWrapper`1.get_Delegate()
myweb    |    at System.Drawing.SafeNativeMethods.Gdip.GdiplusStartup(IntPtr& token, StartupInput& input, StartupOutput& output)
myweb    |    at System.Drawing.SafeNativeMethods.Gdip..cctor()
myweb    |    --- End of inner exception stack trace ---

  

这次是找不到libgdiplus,网上也有关于这个问题的解决方案。

https://q.cnblogs.com/q/103863/

因为需要在容器跑起来的时候安装一些东西,所以要更换为国内的源,提高速度。参考

https://www.cnblogs.com/OMango/p/8519980.html

把Dockerfile改为这样了,注意更新源之后要apt-get update一下,否则会报错Unable to locate package libgdiplus。

FROM microsoft/dotnet:2.1-aspnetcore-runtime

RUN ln -s /lib/x86_64-linux-gnu/libdl-2.24.so /lib/x86_64-linux-gnu/libdl.so

RUN echo "deb http://mirrors.aliyun.com/debian wheezy main contrib non-free deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free" > /etc/apt/sources.list

RUN apt-get update
RUN apt-get install libgdiplus -y && ln -s libgdiplus.so gdiplus.dll

WORKDIR /app
COPY . /app
EXPOSE 5000
ENTRYPOINT ["dotnet", "NetCoreDraw.dll"]

  

再次运行,终于看到图片了,但是汉字没有显示出来。

参考上面的文章,把字体文件复制到容器中,再安装字体相关的功能即可。最终Dockerfile长这样。

FROM microsoft/dotnet:2.1-aspnetcore-runtime

RUN ln -s /lib/x86_64-linux-gnu/libdl-2.24.so /lib/x86_64-linux-gnu/libdl.so

RUN echo "deb http://mirrors.aliyun.com/debian wheezy main contrib non-free deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free" > /etc/apt/sources.list

RUN apt-get update
RUN apt-get install libfontconfig1 -y
RUN apt-get install libgdiplus -y && ln -s libgdiplus.so gdiplus.dll

COPY ./fonts/msyh.ttc /usr/share/fonts/dejavu

WORKDIR /app
COPY . /app
EXPOSE 5000
ENTRYPOINT ["dotnet", "NetCoreDraw.dll"]

  

把Win10的微软雅黑字体文件msyh.ttc放到项目的fonts目录下,设置文件属性“复制到输出目录”设置为“如果较新则复制”。

再次运行容器,搞定了。

Asp.Net Core网站跨平台部署到Linux容器运行是一个飞跃性的技术进步,但是时不时会碰到一些跟Linux系统相关的问题,总感觉是又爱又恨,心太累。

源代码

https://github.com/woodsun2018/NetCoreDraw

原文地址:https://www.cnblogs.com/lonelyxmas/p/10836029.html

时间: 2024-08-01 19:49:40

Asp.Net Core使用System.Drawing.Common部署到docker报错问题的相关文章

.Net Core 使用 System.Drawing.Common 部署到CentOS上遇到的问题

一开始报这个错误:Unable to load shared library 'libdl' 找到libdl安装位置是/usr/lib64: #locate libdl /usr/lib64/libdl-2.17.so /usr/lib64/libdl.so.2 创建连接: #cd /usr/lib64 #ln -s libdl-2.17.so libdl.so 现在又报这个错误:Unable to load DLL 'libgdiplus' yum install libgdiplus-dev

ASP.NET Core使用Docker-Swarm集群部署实现负载均衡实战演练

一.需求背景 人生苦短,我用.NET Core!阿笨对Docker是这样评价的:Docker在手,环境我有!Docker出手,集群我有!前面的Doc基础课程我们学习了如何使用Docker来部署搭建单机容器应用.当一台服务器的处理能力.存储空间不足时,不要企图去换更强大的服务器,对大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业务需求.实践中会发现,生产环境中使用单个 Docker 节点是远远不够的,搭建 Docker 集群势在必行.然而,面对 Kubernetes, Mesos等众

利用Tomcat部署Web项目报错

1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help | start | stop } 八月 18, 2014 7:35:40 下午 org.apache.catalina.core.AprLifecycleListener init 信息: Loaded APR based Apache Tomcat Native library 1.1.24 u

12C -- ORA-65048 ORA-65048 创建common user的时候报错:

转载至:https://www.cnblogs.com/abclife/p/6726909.html 创建common user的时候报错: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 $ sqlplus '/as sysdba' SQL*Plus: Release 12.2.0.1.0 Production on Tu

Nginx部署Django项目报错 KeyError: &#39;REQUEST_METHOD&#39;

这个问题是应为Nginx的配置文件有问题: 要使用uwsgi启动Django的话要有以下配置: upstream djangos14{ # nginx负载均衡配置: server 10.0.0.10:9999; #server 10.0.0.11:80; } server { listen 80; server_name www.s14hanju.com; location / { # 要使用uwsgi,代理就不要用proxy_pass了,要使用uwsgi_pass: uwsgi_pass dj

Asp.net Core 初探(发布和部署Linux)

前言 俗话说三天不学习,赶不上刘少奇.Asp.net Core更新这么长时间一直观望,周末帝都小雨,宅在家看了下Core Web App,顺便搭建了个HelloWorld环境来尝尝鲜,第一次看到.Net Web运行在Linux上还是有点小激动(只可惜微软走这一步路走的太晚,要不然屌丝们也不会每每遇见Java VS .Net就想辩论个你死我活). 开发环境和部署环境 Windows 10.VS2015 Update3.安装.Net Core SDK.DotNetCore.1.0.1-VS2015T

[Error]分布式部署SCOM安装报错——Report Services实例无效

一般实验环境中,把SQL服务器和SCOM服务器安装在一台机器上的话,只要注意一下细节,安装的过程是不会有什么问题的.但是如果我不是在同一台机器上安装呢?我现有的环境里就有一个数据库,我就想用这个数据库来做SCOM的报表数据库,但我又不想在这台服务器上安装SCOM角色,那么这就可以分布式部署,就是数据库与SCOM分离. 过程也是差不多,只不过不用再在SCOM服务器上安装SQL Server,只需要安装两个组件: Microsoft Report Viewer 2012(点击下载) Microsof

ASP.NET MVC 3 入门级常用设置、技巧和报错

1.ASP.NET MVC 3 如何去除默认验证 这个默认验证是在web.config配置文件中设置的    <add key="ClientValidationEnabled" value="true"/>设置为false就行了,    这个是去掉所有model的默认验证,然后你可以在model中加上自己的验证,这样哪些属性需要验证,哪些不需要 就看你自己了 示例代码: publicclass Movie{publicint ID { get; set

项目部署到Tomat报错:jar not loaded.See Servlet Spec 2.3, section 9.7.2. Offending

项目部署到Tomcat报这样的异常: Java代码   jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class 造成这种情况的原因很可能是: 一.项目的WEB-INF/lib下有servlet-api.jar. 二.项目的WEB-INF/lib下 有包含javax.servlet包的JAR包(如WebLogic 8.1的weblogic.jar),造成与To