教程:使 .NET Core 应用程序容器化Tutorial: Containerize a .NET Core app
本文内容
本教程介绍如何生成包含 .NET Core 应用程序的 Docker 映像。此映像可用于为本地开发环境、私有云或公有云创建容器。
你将了解如何:
- 创建并发布简单的 .NET Core 应用
- 创建并配置用于 .NET Core 的 Dockerfile
- 生成 Docker 映像
- 创建并运行 Docker 容器
你将了解用于 .NET Core 应用的 Docker 容器生成和部署任务。Docker 平台 使用 Docker 引擎 快速生成应用,并将应用打包为 Docker 映像 。这些映像采用 Dockerfile 格式编写,以供在分层容器中部署和运行。
系统必备Prerequisites
安装以下必备组件:
.NET Core 2.2 SDK如果已安装 .NET Core,请使用
dotnet —info
命令来确定使用的是哪个 SDK。Dockerfile 和 .NET Core 示例应用的临时工作文件夹 。在本教程中,
docker-working
用作工作文件夹的名称。
使用 SDK 版本 2.2Use SDK version 2.2
如果使用的是更高版本 SDK(如 3.0),请务必强制应用使用 SDK 2.2。在工作文件夹中创建名为 global.json
的文件,并粘贴以下 json 代码:
{
"sdk": {
"version": "2.2.100"
}
}
保存此文件。如果存在此文件,则强制 .NET Core 对从此文件夹及其子级调用的任何 dotnet
命令使用版本 2.2。
创建 .Net Core 应用Create .NET Core app
需要有可供 Docker 容器运行的 .NET Core 应用。打开终端、创建工作文件夹(如果尚没有),然后进入该文件夹。在工作文件夹中,运行下面的命令,在名为“应用”的子目录中新建一个项目:
dotnet new console -o app -n myapp
文件夹树将如下所示:
docker-working
│ global.json
│
└───app
│ myapp.csproj
│ Program.cs
│
└───obj
myapp.csproj.nuget.cache
myapp.csproj.nuget.g.props
myapp.csproj.nuget.g.targets
project.assets.json
dotnet new
命令会新建一个名为“应用”的文件夹,并生成一个“Hello World”应用 。进入“应用”文件夹并运行命令 dotnet run
。输出如下:
> dotnet run
Hello World!
默认模板创建应用,此应用先打印输出到终端,再退出。本教程将使用无限循环的应用。在文本编辑器中,打开“Program.cs” 文件。它应如以下代码所示:
using System;
namespace myapp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
将此文件替换为以下每秒计数一次的代码:
using System;
namespace myapp
{
class Program
{
static void Main(string[] args)
{
var counter = 0;
var max = args.Length != 0 ? Convert.ToInt32(args[0]) : -1;
while(max == -1 || counter < max)
{
counter++;
Console.WriteLine($"Counter: {counter}");
System.Threading.Tasks.Task.Delay(1000).Wait();
}
}
}
}
保存此文件,并使用 dotnet run
再次测试程序。请注意,此应用无限期运行。使用取消命令 CTRL + C 可以停止运行。输出如下:
> dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C
如果你在命令行中向应用传递一个数字,它就只会计数到这个数字,然后退出。试一试用 dotnet run — 5
计数到 5。
备注
—
之后的参数都不传递到 dotnet run
命令,而是传递到你的应用程序。
发布 .Net Core 应用Publish .NET Core app
请先发布 .NET Core 应用,再将它添加到 Docker 映像。需确保容器在启动时运行应用的发布版本。
在工作文件夹中,进入包含示例源代码的“应用”文件夹,并运行以下命令 :
dotnet publish -c Release
此命令将应用编译到“发布”文件夹中 。从工作文件夹到“发布”文件夹的路径应为 .\app\bin\Release\netcoreapp2.2\publish\
获取“发布”文件夹的目录清单,以验证 myapp.dll 是否已创建。在“应用”文件夹中,运行下列命令之一 :
> dir bin\Release\netcoreapp2.2\publish
Directory of C:\docker-working\app\bin\Release\netcoreapp2.2\publish
04/05/2019 11:00 AM <DIR> .
04/05/2019 11:00 AM <DIR> ..
04/05/2019 11:00 AM 447 myapp.deps.json
04/05/2019 11:00 AM 4,608 myapp.dll
04/05/2019 11:00 AM 448 myapp.pdb
04/05/2019 11:00 AM 154 myapp.runtimeconfig.json
me@DESKTOP:/docker-working/app$ ls bin/Release/netcoreapp2.2/publish
myapp.deps.json myapp.dll myapp.pdb myapp.runtimeconfig.json
创建 DockerfileCreate the Dockerfile
docker build
命令使用 Dockerfile 文件来创建容器映像。此文件是名为“Dockerfile” 的纯文本文件,它没有扩展名。
在终端中,导航到你在启动时创建的工作文件夹的目录。在工作文件夹中创建名为“Dockerfile”的文件,在文本编辑器中打开它 。将以下命令添加为此文件的首行:
FROM mcr.microsoft.com/dotnet/core/runtime:2.2
FROM
命令指示 Docker 从 mcr.microsoft.com/dotnet/core/runtime 存储库拉取标记为“2.2” 的映像。请确保拉取的 .NET Core 运行时与 SDK 定目标到的运行时一致。例如,上一部分中创建的应用定目标到 .Net Core 2.2,且使用 .Net Core 2.2 SDK。因此,Dockerfile 中引用的基础映像被标记为“2.2” 。
保存 Dockerfile 文件 。工作文件夹的目录结果应如下所示。为节省本文的空间,删掉了一些更深级别的文件和文件夹:
docker-working
│ Dockerfile
│ global.json
│
└───app
│ myapp.csproj
│ Program.cs
│
├───bin
│ └───Release
│ └───netcoreapp2.2
│ └───publish
│ myapp.deps.json
│ myapp.dll
│ myapp.pdb
│ myapp.runtimeconfig.json
│
└───obj
在终端中运行以下命令:
docker build -t myimage -f Dockerfile .
Docker 会处理 Dockerfile 中的每一行。docker build
命令中的 .
指示 Docker 在当前文件夹中查找 Dockerfile 。此命令生成映像,并创建指向相应映像的本地存储库“myimage” 。在此命令完成后,运行 docker images
以列出已安装的映像:
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mcr.microsoft.com/dotnet/core/runtime 2.2 d51bb4452469 2 days ago 314MB
myimage latest d51bb4452469 2 days ago 314MB
请注意,两个映像共用相同的“IMAGE ID” 值。两个映像使用的 ID 值相同是因为,Dockerfile 中的唯一命令是在现有映像的基础之上生成新映像。接下来,在 Dockerfile 中添加两个命令。两个命令都新建映像层,最后一个命令表示 myimage 存储库将指向的映像。
COPY app/bin/Release/netcoreapp2.2/publish/ app/
ENTRYPOINT ["dotnet", "app/myapp.dll"]
COPY
命令指示 Docker 将计算机上的指定文件夹复制到容器中的文件夹。在此示例中,“发布” 文件夹被复制到容器中的“应用” 文件夹。
下一个命令 ENTRYPOINT
指示 Docker 将容器配置为可执行文件运行。在容器启动时,ENTRYPOINT
命令运行。当此命令结束时,容器也会自动停止。
在终端中,运行 docker build -t myimage -f Dockerfile .
;在此命令完成后,运行 docker images
。
> docker build -t myimage -f Dockerfile .
Sending build context to Docker daemon 819.7kB
Step 1/3 : FROM mcr.microsoft.com/dotnet/core/runtime:2.2
---> d51bb4452469
Step 2/3 : COPY app/bin/Release/netcoreapp2.2/publish/ app/
---> a1e98ac62017
Step 3/3 : ENTRYPOINT ["dotnet", "app/myapp.dll"]
---> Running in f34da5c18e7c
Removing intermediate container f34da5c18e7c
---> ddcc6646461b
Successfully built ddcc6646461b
Successfully tagged myimage:latest
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myimage latest ddcc6646461b 10 seconds ago 314MB
mcr.microsoft.com/dotnet/core/runtime 2.2 d51bb4452469 2 days ago 314MB
Dockerfile 中的每个命令生成了一个层,并创建了“IMAGE ID” 。最终“IMAGE ID” 是“ddcc6646461b” (你的 ID 会有所不同),接下来在此映像的基础之上创建容器。
创建容器Create a container
至此,已有包含应用的映像,可以创建容器了。可以通过两种方式来创建容器。首先,新建已停止的容器。
> docker create myimage
0e8f3c2ca32ce773712a5cca38750f41259a4e54e04bdf0946087e230ad7066c
上面的 docker create
命令在 myimage 映像的基础之上创建容器。此命令的输出显示已创建容器的“CONTAINER ID” (你的 ID 会有所不同)。若要查看所有 容器的列表,请使用 docker ps -a
命令:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0e8f3c2ca32c myimage "dotnet app/myapp.dll" 4 seconds ago Created boring_matsumoto
管理容器Manage the container
每个容器都分配有随机名称,可用来引用相应容器实例。例如,自动创建的容器选择了名称“boring_matsumoto” (你的名称会有所不同),此名称可用于启动容器。可以使用 docker create —name
参数将自动名称替代为特定名称。
下面的示例使用 docker start
命令来启动容器,然后使用 docker ps
命令仅显示正在运行的容器:
> docker start boring_matsumoto
boring_matsumoto
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0e8f3c2ca32c myimage "dotnet app/myapp.dll" 7 minutes ago Up 8 seconds boring_matsumoto
同样,docker stop
命令会停止容器。下面的示例使用 docker stop
命令来停止容器,然后使用 docker ps
命令来显示没有正在运行的容器。
> docker stop boring_matsumoto
boring_matsumoto
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
连接到容器Connect to a container
在容器运行后,可以连接到它来查看输出。使用 docker start
和 docker attach
命令,启动容器并查看输出流。在此示例中,CTRL + C 命令用于从正在运行的容器中分离出来。这其实是结束容器中的进程,进而停止容器。—sig-proxy=false
参数可确保 Ctrl + C 不停止容器中的进程。
从容器中分离出来后重新连接,以验证它是否仍在运行和计数。
> docker start boring_matsumoto
boring_matsumoto
> docker attach --sig-proxy=false boring_matsumoto
Counter: 7
Counter: 8
Counter: 9
^C
> docker attach --sig-proxy=false boring_matsumoto
Counter: 17
Counter: 18
Counter: 19
^C
删除容器Delete a container
就本文而言,你不希望存在不执行任何操作的容器。删除前面创建的容器。如果容器正在运行,停止容器。
> docker stop boring_matsumoto
下面的示例列出了所有容器。然后,它使用 docker rm
命令来删除容器,并再次检查是否有任何正在运行的容器。
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0e8f3c2ca32c myimage "dotnet app/myapp.dll" 19 minutes ago Exited boring_matsumoto
> docker rm boring_matsumoto
boring_matsumoto
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
单次运行Single run
Docker 提供了 docker run
命令,用于将容器作为单一命令进行创建和运行。使用此命令,无需依次运行 docker create
和 docker start
。另外,还可以将此命令设置为,在容器停止时自动删除容器。例如,使用 docker run -it —rm
可以执行两项操作,先自动使用当前终端连接到容器,再在容器完成时删除容器:
> docker run -it --rm myimage
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
^C
使用 docker run -it
,CTRL + C 命令会停止在容器中运行的进程,进而停止容器。由于提供了 —rm
参数,因此在进程停止时自动删除容器。验证它是否不存在:
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
更改 ENTRYPOINTChange the ENTRYPOINT
使用 docker run
命令,还可以修改 Dockerfile 中的 ENTRYPOINT
命令,并运行其他内容,但只能针对相应容器。例如,使用以下命令来运行 bash
或 cmd.exe
。根据需要,编辑此命令。
WindowsWindows
在本例中,ENTRYPOINT
更改为 cmd.exe
。通过按下 CTRL + C 来结束进程并停止容器。
> docker run -it --rm --entrypoint "cmd.exe" myimage
Microsoft Windows [Version 10.0.17763.379]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\>dir
Volume in drive C has no label.
Volume Serial Number is 3005-1E84
Directory of C:\
04/09/2019 08:46 AM <DIR> app
03/07/2019 10:25 AM 5,510 License.txt
04/02/2019 01:35 PM <DIR> Program Files
04/09/2019 01:06 PM <DIR> Users
04/02/2019 01:35 PM <DIR> Windows
1 File(s) 5,510 bytes
4 Dir(s) 21,246,517,248 bytes free
C:\>^C
LinuxLinux
在本例中,ENTRYPOINT
更改为 bash
。通过运行 quit
命令来结束进程并停止容器。
root@user:~# docker run -it --rm --entrypoint "bash" myimage
root@8515e897c893:/# ls app
myapp.deps.json myapp.dll myapp.pdb myapp.runtimeconfig.json
root@8515e897c893:/# exit
exit
重要命令Essential commands
Docker 有许多不同的命令,可用于执行你要对容器和映像执行的操作。下面这些 Docker 命令对于管理容器来说至关重要:
清理资源Clean up resources
在本教程中,你创建了容器和映像。如果需要,请删除这些资源。以下命令可用于
- 列出所有容器
> docker ps -a
- 停止正在运行的容器。
CONTAINER_NAME
表示自动分配给容器的名称。
> docker stop CONTAINER_NAME
- 删除容器
> docker rm CONTAINER_NAME
接下来,删除计算机上不再需要的任何映像。依次删除 Dockerfile 创建的映像,以及 Dockerfile 所依据的 .NET Core 映像。可以使用 IMAGE ID 或 REPOSITORY:TAG 格式字符串。
docker rmi myimage:latest
docker rmi mcr.microsoft.com/dotnet/core/runtime:2.2
使用 docker images
命令来列出已安装的映像。
备注
映像文件可能很大。通常情况下,需要删除在测试和开发应用期间创建的临时容器。如果计划在相应运行时的基础之上生成其他映像,通常会将基础映像与运行时一同安装。