
正文
ASP.NET Core & Docker 实战经验分享
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
必读
本文总结了博主在使用持续集成 ASP.NET Core & Docker & Jenkins 的经验,里面有些章节讲的是 Docker Compose,这是由于早期版本的Visual Studio 2017添加的Docker支持采用的Docker Compose方式,15.8 版本以后,就放弃了Docker Compose方式,直接采用Dockerfile,简单、方面。比如第二小节中出现的 docker-compose.override.yml 这个就是以前版本的VS2017自动生成的Docker Compose 文件,特此说明。持续集成教程链接:《ASP.NET Core & Docker 零基础持续集成 》
一.前言
最近一直在研究和实践ASP.NET Core、Docker、持续集成。在ASP.NET Core 和 Dcoker结合下遇到了一些坑,在此记录和分享,希望对大家有一些帮助。
二.中间镜像多阶段构建产生的中间镜像,镜像缓存层等
多阶段构建产生的中间镜像,镜像缓存层等
我前面写过一个 《ASP.NET Core & Docker 零基础持续集成 》的教程。里面我们通过持续集成工具Jenkins构建Docker镜像并运行容器,采用的是Docker Compose来进行编排构建运行的(Visual Studio 2017添加Docker支持是采用的Docker Compose)。细心的朋友可能会发现,每次构建完毕以后通过docker images命令查询,可以发现多了许多没有名称(<none>)的镜像。这些都是构建过程中的中间镜像,我们可以在构建完成以后 进行统一删除。
删除所有无名称镜像:
docker rmi $(docker images -f "dangling=true" -q)
此命令应当加在构建的最后一步,示例:
echo ---------------Remove-Orphans------------------
docker-compose -f src/docker-compose.yml -f src/docker-compose.override.yml -p alipaydemo down --rmi local --remove-orphans
echo ---------------Publishing...------------------
docker-compose -f "src/docker-compose.yml" -f "src/docker-compose.override.yml" -p alipaydemo up -d --buildecho ---------------Clear-Images...------------------
docker rmi $(docker images -f "dangling=true" -q)
执行之后会看到以下效果:

这是非常有必要的,因为如果每次构建都残留一些中间镜像,会额外消耗我们的磁盘空间的。
三.固定容器外部端口 (Docker Compose)这里主要讲的是在自动化构建的过程中,通过docker compose来运行容器的外部端口,而不是直接通过docker run命令来指定。
这里主要讲的是在自动化构建的过程中,通过docker compose来运行容器的外部端口,而不是直接通过docker run命令来指定。
我们通过Visual Studio 2017添加Docker支持(Docker Compose),通过Docker Compose编排构建运行容器,我们会发现每次构建以后,运行的容器的外部端口都不是固定的,比如32774、32775、32776等等。这对于我们设置了Nginx反向代理和API Gateway等配置的肯定是十分不方便的,我们每次构建完毕以后还要去改这些配置,不是扯淡吗。所以我们需要固定我们容器运行的外部端口,我们可以通过改变docker compose的yml文件来固定容器的外部端口。
Visual Studio 2017 添加的Docker支持所生成的文件有如下结构:

我是用的版本为VS2017 15.6.5。如果是更早的版本添加Docker支持可能会多出一个
docker-compose.ci.build.yml文件,其实这一步没必要,目前的最新的VS2017已经移除了该文件。
我们固定容器外部端口需要修改的是docker-compose.override.yml文件,我们需要修改的是ports。默认为:
ports:
- "80"
这个80端口只是容器的内部端口,我们进行如下修改来知道容器运行时映射到服务器的端口也就是外部端口:
ports:
- "32775:80"
通过上面的设置,我们将容器的外部端口指定为32775,这样我们在构建完成以后,容器运行以后的外部端口都将会为32775,无需再次修改Nginx反向代理等配置。
四.设置镜像版本 (Docker Compose)
我们的应用程序具有不同的版本号,我们不同版本的应用程序构建出来的镜像应该也是具有不同的版本的,我们可以通过设置镜像的Tag来表示不同的版本:

我们同样可以在docker compose的yml里面进行设置,这次修改的是docker-compose.yml文件,我们直接在镜像的名称后面设置Tag,语法为:
image: <镜像名称>:<Tag>
比如我设置一个名为alipaydemo的镜像Tag为V1:
image: alipaydemo:v1
完整的配置分享:
version: '3'services:
alipay.demo.pcpayment:
image: alipaydemopcpayment:v1
container_name: alipaydemocontainer
build:
context: .
dockerfile: Alipay.Demo.PCPayment/Dockerfile
五.设置容器名称 (Docker Compose)
我们在通过docker compose运行的容器将会被指定一个默认的容器名称,如果是第四节的配置,那么容器的默认名称为alipaydemopcpayment.alipay.demo.pcpayment.build_1,具有非常一长串,此时我们可以自己来指定这个容器的名称,同样我们需要修改docker-compose.yml文件,设置容器名称的命令格式为:
container_name: <容器名称>
完整的配置分享:
version: '3'services:
alipay.demo.pcpayment:
image: alipaydemopcpayment:v1
container_name: alipaydemocontainer
build:
context: .
dockerfile: Alipay.Demo.PCPayment/Dockerfile
通过上面的配置我们将容器名称设置为了alipaydemocontainer

六.设置容器重启策略 (Docker Compose)
某一天我对服务器进行了重启,重启以后发现通过Docker运行的服务无法访问了,然后查看原因,发现Docker服务没有开机自启,启动了Docker以后发现容器又没有自动启动。
1.设置Docker开机自启
我们可以直接通过chkconfig命令来设置Docker开启自启:
chkconfig docker
执行成功如下:

2.设置容器随Docker启动
我们要让容器随Docker启动,就必须设置容器的重启策略为always,我们通过docker compose来运行容器时可以在yml里面指定。打开docker-compose.override.yml文件,添加配置:
restart: always
完整的配置示例:
version: '3'services:
alipay.demo.pcpayment:
restart: always
environment:
- ASPNETCORE_ENVIRONMENT=Production
ports:
- "32775:80"
七.Docker的重启策略1.设置容器重启策略
这里讲的设置容器重启策略主要是通过命令来进行交互,并非第六节讲的通过docker compose来设置重启策略,是对第六节的一个扩展阅读。
这里讲的设置容器重启策略主要是通过命令来进行交互,并非第六节讲的通过docker compose来设置重启策略,是对第六节的一个扩展阅读。
我们可以在使用docker run命令时通过--restart参数来设置重启策略:
docker run -d --restart=always alipaydemo
docker run -d --restart=on-failure:10 alipaydemo
第一条命令代表容器退出时总是重启容器,第二条代表在容器非正常退出时重启容器,最多重启10次。
对于已经运行的容器可以通过docker update命令来指定:
docker update --restart=always alipaydemo
2.Docker容器的重启策略
Docker容器的重启策略是面向生产环境的一个启动策略,在开发过程中可以忽略该策略。
Docker容器的重启都是由Docker守护进程完成的,因此与守护进程息息相关。
Docker容器的重启策略如下:
- no,默认策略,在容器退出时不重启容器
- on-failure,在容器非正常退出时(退出状态非0),才会重启容器
- on-failure:3,在容器非正常退出时重启容器,最多重启3次
- always,在容器退出时总是重启容器
- unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
3.Docker容器的退出状态码
docker run的退出状态码如下:
0,表示正常退出
非0,表示异常退出(退出状态码采用chroot标准)
- 125,Docker守护进程本身的错误
- 126,容器启动后,要执行的默认命令无法调用
- 127,容器启动后,要执行的默认命令不存在
其他命令状态码,容器启动后正常执行命令,退出命令时该命令的返回状态码作为容器的退出状态码
参考资料:https://blog.csdn.net/taiyangdao/article/details/73076019
八.设置自动交互
我们在编写Dockerfile时,可以通过RUN命令来运行一些命令,由此我们可以通过运行apt-get等命令,将一些必要的组件安装到我们的镜像之中,比如lsof等。我们有如下配置:
RUN apt-get install lsof
但是我们在安装一个组件时,非常有可能遇到交互操作,比如“Dou you want to xxx?[y/n]”等,遇到这种将会中断我们的Docker镜像构建过程,那么如何解决呢?我们可以给命令指定--assume-yes来实现自动交互:
RUN apt-get install lsof --assume-yes
持续集成自动化构建过程中,如若不设置自动交互将会被中断:

使用apt-get install命令以前,最好使用apt-get update更新一下,避免出现问题。
九.ASP.NET Core 生成图片问题
大家应该知道目前.NET Core(2.0)还是没有System.Drawing程序集,如果我们要使用Image等对象来完成生成图片验证码、图片二维码等操作只有通过第三方编写的组件,ZKWeb.System.Drawing便是其中一个,我们使用它以后,我们在windows上运行良好,无需其他额外的操作。但是我们一到Linux运行或者使用Docker(dotnet镜像使用的是ubantu环境)运行时,会发现程序无法正常生成图片,会出现异常,这是因为我们的zk在Linux/Docker下运行需要安装一个名为 libgdiplus 的组件,我们在构建Docker镜像的时候可以通过RUN命令使用apt-get命令进行安装:
RUN apt-get update
RUN apt-get install libgdiplus --assume-yes
RUN cd /usr/lib
RUN ln -s libgdiplus.so gdiplus.dll
apt-get update是非常有必要的
完整的Dockerfile配置提供给大家参考:
FROM microsoft/aspnetcore:2.0 AS base
WORKDIR /app
EXPOSE 80FROM microsoft/aspnetcore-build:2.0 AS build
WORKDIR /src
COPY Alipay.Demo.PCPayment.sln ./
COPY Alipay.Demo.PCPayment/Alipay.Demo.PCPayment.csproj Alipay.Demo.PCPayment/
RUN dotnet restore -nowarn:msb3202,nu1503
COPY . .
WORKDIR /src/Alipay.Demo.PCPayment
RUN dotnet build -c Release -o /appFROM build AS publish
RUN dotnet publish -c Release -o /appFROM base AS final
WORKDIR /app
COPY --from=publish /app .
RUN apt-get update
RUN apt-get install libgdiplus --assume-yes
RUN cd /usr/lib
RUN ln -s libgdiplus.so gdiplus.dll
ENTRYPOINT ["dotnet", "Alipay.Demo.PCPayment.dll"]
十.写在最后
本文乃是我在实践ASP.NET Core & Docker & 持续集成过程中遇到的问题和解决之道,完完全全的实战经验、总结,希望能帮助到大家。关于实践ASP.NET Core & Docker 构建持续集成大家可以看我的这一系列文章:《ASP.NET Core & Docker 零基础持续集成》。本文所说的实战是我在实践我的开源项目:
https://github.com/dotnetcore/Alipay.AopSdk.Core
此项目及其演示项目通过持续集成自动化发布到Nuget和通过Docker运行。这个项目是一个支持.NET Core的支付宝的服务端SDK项目,不仅仅提供支付能力,还提供支付宝生活号、服务窗、行业合作等开发。觉得好的希望能给一个Star支持(手动滑稽)。
最后分享给大家一个高效写博客的方法,本文亦是用此来进行编写和发布:http://www.cnblogs.com/stulzq/p/9043632.html
ASP.NET Core & Docker 实战经验分享的更多相关文章- 【无私分享:ASP.NET CORE 项目实战(第十三章)】Asp.net Core 使用MyCat分布式数据库,实现读写分离
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 MyCat2.0版本很快就发布了,关于MyCat的动态和一些问题,大家可以加一下MyCat的官方QQ群:106088787.我 ...
- ASP.NET Core &; Docker &; Jenkins 零基础持续集成实战
原文:ASP.NET Core & Docker & Jenkins 零基础持续集成实战 一.本系列教程说明 源代码管理工具:Gogs 持续集成工具:Jenkins 容器:Docker ...
- 【无私分享:ASP.NET CORE 项目实战】目录索引
简介 首先,我们的 [无私分享:从入门到精通ASP.NET MVC] 系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...
- 【无私分享:ASP.NET CORE 项目实战(第十四章)】图形验证码的实现
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 很长时间没有来更新博客了,一是,最近有些忙,二是,Core也是一直在摸索中,其实已经完成了一个框架了,并且正在准备在生产环境中 ...
- 【无私分享:ASP.NET CORE 项目实战(第十二章)】添加对SqlServer、MySql、Oracle的支持
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 增加对多数据库的支持,并不是意味着同时对多种数据库操作,当然,后面,我们会尝试同时对多种数据库操作,这可能需要多个上下文,暂且 ...
- 【无私分享:ASP.NET CORE 项目实战(第十一章)】Asp.net Core 缓存 MemoryCache 和 Redis
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 ...
- 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...
- 【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右击添加视图,转到试图页等功能图不见了,虽然我 ...
- 【无私分享:ASP.NET CORE 项目实战(第八章)】读取配置文件(二) 读取自定义配置文件
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 我们在 读取配置文件(一) appsettings.json 中介绍了,如何读取appsettings.json. 但随之产生 ...
随机推荐- python flask (一)
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World ...
- css sprite css雪碧图生成工具
最新地址:http://www.cnblogs.com/wang4517/p/4476758.html
- vector中的元素删除
删除vector中的元素,最容易的方法就是使用vector的erase()函数. vector vec;for ( vector::iterator iter = vec.begin(); iter! ...
- POJ 2531 Network Saboteur
http://poj.org/problem?id=2531 题意 :有N台电脑,每两台电脑之间都有个通信量C[i][j]; 问如何将其分成两个子网,能使得子网之间的通信量最大. 也就是说将所有节点分 ...
- fckeditor的用法
1.下载解压文件 2.将解压后的文件放入webroot下 3.将js引入要使用fckeditor的页面 4.用ckeditor替换textarea <@e.textarea id="t ...
- gulp分享文档
Grunt--I/O操作: 读取A → A.a() → 写出A → 读取A → A.b() → 写出A; gulp--数据流:读取A → A.a() → A.b() → 写出A. Part① 构建gu ...
- 说服式设计(persuasive design)的行为模型
转自:http://www.sharetk.com/html/ued/User-Research/1404.html 一 模型简介 BJ Fogg提出了一个新的理解人类行为的模型,他称之为Fogg b ...
- Java远程调用邮件服务器,实现邮件发送
写这篇文章的背景是公司Android客户端需要实现一个功能,实现类似于密码找回或者用户注册完发送一个邮件给用户的功能,当然这些逻辑客户端只负责请求自己的服务端,自己的服务端再去请求邮件服务器. 邮件服 ...
- jQuery 事件绑定 和 JavaScript 原生事件绑定
总结一下:jQuery 事件绑定 和 JavaScript 原生事件绑定 及 区别 jQuery 事件绑定 jQuery 中提供了四种事件监听绑定方式,分别是 bind.live.delegate.o ...
- 01LaTeX学习系列之---TeX的介绍与认识
目录 01TeX的介绍与认识 目录 前言 (一)TeX 的宣传 TeX - Beauty and Fun 1. TeX 是什么? 2. TeX 是哪家公司生产的? 3. 我们今天主角的名字怎么念啊?& ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 MyCat2.0版本很快就发布了,关于MyCat的动态和一些问题,大家可以加一下MyCat的官方QQ群:106088787.我 ...
原文:ASP.NET Core & Docker & Jenkins 零基础持续集成实战 一.本系列教程说明 源代码管理工具:Gogs 持续集成工具:Jenkins 容器:Docker ...
简介 首先,我们的 [无私分享:从入门到精通ASP.NET MVC] 系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 很长时间没有来更新博客了,一是,最近有些忙,二是,Core也是一直在摸索中,其实已经完成了一个框架了,并且正在准备在生产环境中 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 增加对多数据库的支持,并不是意味着同时对多种数据库操作,当然,后面,我们会尝试同时对多种数据库操作,这可能需要多个上下文,暂且 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右击添加视图,转到试图页等功能图不见了,虽然我 ...
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 我们在 读取配置文件(一) appsettings.json 中介绍了,如何读取appsettings.json. 但随之产生 ...
- python flask (一)
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World ...
- css sprite css雪碧图生成工具
最新地址:http://www.cnblogs.com/wang4517/p/4476758.html
- vector中的元素删除
删除vector中的元素,最容易的方法就是使用vector的erase()函数. vector vec;for ( vector::iterator iter = vec.begin(); iter! ...
- POJ 2531 Network Saboteur
http://poj.org/problem?id=2531 题意 :有N台电脑,每两台电脑之间都有个通信量C[i][j]; 问如何将其分成两个子网,能使得子网之间的通信量最大. 也就是说将所有节点分 ...
- fckeditor的用法
1.下载解压文件 2.将解压后的文件放入webroot下 3.将js引入要使用fckeditor的页面 4.用ckeditor替换textarea <@e.textarea id="t ...
- gulp分享文档
Grunt--I/O操作: 读取A → A.a() → 写出A → 读取A → A.b() → 写出A; gulp--数据流:读取A → A.a() → A.b() → 写出A. Part① 构建gu ...
- 说服式设计(persuasive design)的行为模型
转自:http://www.sharetk.com/html/ued/User-Research/1404.html 一 模型简介 BJ Fogg提出了一个新的理解人类行为的模型,他称之为Fogg b ...
- Java远程调用邮件服务器,实现邮件发送
写这篇文章的背景是公司Android客户端需要实现一个功能,实现类似于密码找回或者用户注册完发送一个邮件给用户的功能,当然这些逻辑客户端只负责请求自己的服务端,自己的服务端再去请求邮件服务器. 邮件服 ...
- jQuery 事件绑定 和 JavaScript 原生事件绑定
总结一下:jQuery 事件绑定 和 JavaScript 原生事件绑定 及 区别 jQuery 事件绑定 jQuery 中提供了四种事件监听绑定方式,分别是 bind.live.delegate.o ...
- 01LaTeX学习系列之---TeX的介绍与认识
目录 01TeX的介绍与认识 目录 前言 (一)TeX 的宣传 TeX - Beauty and Fun 1. TeX 是什么? 2. TeX 是哪家公司生产的? 3. 我们今天主角的名字怎么念啊?& ...






