教程:使 .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。

  • Docker 社区版

  • Dockerfile 和 .NET Core 示例应用的临时工作文件夹 。在本教程中,docker-working 用作工作文件夹的名称。

使用 SDK 版本 2.2Use SDK version 2.2

如果使用的是更高版本 SDK(如 3.0),请务必强制应用使用 SDK 2.2。在工作文件夹中创建名为 global.json 的文件,并粘贴以下 json 代码:

  1. {
  2. "sdk": {
  3. "version": "2.2.100"
  4. }
  5. }

保存此文件。如果存在此文件,则强制 .NET Core 对从此文件夹及其子级调用的任何 dotnet 命令使用版本 2.2。

创建 .Net Core 应用Create .NET Core app

需要有可供 Docker 容器运行的 .NET Core 应用。打开终端、创建工作文件夹(如果尚没有),然后进入该文件夹。在工作文件夹中,运行下面的命令,在名为“应用”的子目录中新建一个项目:

  1. dotnet new console -o app -n myapp

文件夹树将如下所示:

  1. docker-working
  2. global.json
  3. └───app
  4. myapp.csproj
  5. Program.cs
  6. └───obj
  7. myapp.csproj.nuget.cache
  8. myapp.csproj.nuget.g.props
  9. myapp.csproj.nuget.g.targets
  10. project.assets.json

dotnet new 命令会新建一个名为“应用”的文件夹,并生成一个“Hello World”应用 。进入“应用”文件夹并运行命令 dotnet run输出如下:

  1. > dotnet run
  2. Hello World!

默认模板创建应用,此应用先打印输出到终端,再退出。本教程将使用无限循环的应用。在文本编辑器中,打开“Program.cs” 文件。它应如以下代码所示:

  1. using System;
  2. namespace myapp
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. Console.WriteLine("Hello World!");
  9. }
  10. }
  11. }

将此文件替换为以下每秒计数一次的代码:

  1. using System;
  2. namespace myapp
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. var counter = 0;
  9. var max = args.Length != 0 ? Convert.ToInt32(args[0]) : -1;
  10. while(max == -1 || counter < max)
  11. {
  12. counter++;
  13. Console.WriteLine($"Counter: {counter}");
  14. System.Threading.Tasks.Task.Delay(1000).Wait();
  15. }
  16. }
  17. }
  18. }

保存此文件,并使用 dotnet run 再次测试程序。请注意,此应用无限期运行。使用取消命令 CTRL + C 可以停止运行。输出如下:

  1. > dotnet run
  2. Counter: 1
  3. Counter: 2
  4. Counter: 3
  5. Counter: 4
  6. ^C

如果你在命令行中向应用传递一个数字,它就只会计数到这个数字,然后退出。试一试用 dotnet run — 5 计数到 5。

备注

之后的参数都不传递到 dotnet run 命令,而是传递到你的应用程序。

发布 .Net Core 应用Publish .NET Core app

请先发布 .NET Core 应用,再将它添加到 Docker 映像。需确保容器在启动时运行应用的发布版本。

在工作文件夹中,进入包含示例源代码的“应用”文件夹,并运行以下命令 :

  1. dotnet publish -c Release

此命令将应用编译到“发布”文件夹中 。从工作文件夹到“发布”文件夹的路径应为 .\app\bin\Release\netcoreapp2.2\publish\

获取“发布”文件夹的目录清单,以验证 myapp.dll 是否已创建。在“应用”文件夹中,运行下列命令之一 :

  1. > dir bin\Release\netcoreapp2.2\publish
  2. Directory of C:\docker-working\app\bin\Release\netcoreapp2.2\publish
  3. 04/05/2019 11:00 AM <DIR> .
  4. 04/05/2019 11:00 AM <DIR> ..
  5. 04/05/2019 11:00 AM 447 myapp.deps.json
  6. 04/05/2019 11:00 AM 4,608 myapp.dll
  7. 04/05/2019 11:00 AM 448 myapp.pdb
  8. 04/05/2019 11:00 AM 154 myapp.runtimeconfig.json
  1. me@DESKTOP:/docker-working/app$ ls bin/Release/netcoreapp2.2/publish
  2. myapp.deps.json myapp.dll myapp.pdb myapp.runtimeconfig.json

创建 DockerfileCreate the Dockerfile

docker build 命令使用 Dockerfile 文件来创建容器映像。此文件是名为“Dockerfile” 的纯文本文件,它没有扩展名。

在终端中,导航到你在启动时创建的工作文件夹的目录。在工作文件夹中创建名为“Dockerfile”的文件,在文本编辑器中打开它 。将以下命令添加为此文件的首行:

  1. 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 文件 。工作文件夹的目录结果应如下所示。为节省本文的空间,删掉了一些更深级别的文件和文件夹:

  1. docker-working
  2. Dockerfile
  3. global.json
  4. └───app
  5. myapp.csproj
  6. Program.cs
  7. ├───bin
  8. └───Release
  9. └───netcoreapp2.2
  10. └───publish
  11. myapp.deps.json
  12. myapp.dll
  13. myapp.pdb
  14. myapp.runtimeconfig.json
  15. └───obj

在终端中运行以下命令:

  1. docker build -t myimage -f Dockerfile .

Docker 会处理 Dockerfile 中的每一行。docker build 命令中的 . 指示 Docker 在当前文件夹中查找 Dockerfile 。此命令生成映像,并创建指向相应映像的本地存储库“myimage” 。在此命令完成后,运行 docker images 以列出已安装的映像:

  1. > docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. mcr.microsoft.com/dotnet/core/runtime 2.2 d51bb4452469 2 days ago 314MB
  4. myimage latest d51bb4452469 2 days ago 314MB

请注意,两个映像共用相同的“IMAGE ID” 值。两个映像使用的 ID 值相同是因为,Dockerfile 中的唯一命令是在现有映像的基础之上生成新映像。接下来,在 Dockerfile 中添加两个命令。两个命令都新建映像层,最后一个命令表示 myimage 存储库将指向的映像。

  1. COPY app/bin/Release/netcoreapp2.2/publish/ app/
  2. ENTRYPOINT ["dotnet", "app/myapp.dll"]

COPY 命令指示 Docker 将计算机上的指定文件夹复制到容器中的文件夹。在此示例中,“发布” 文件夹被复制到容器中的“应用” 文件夹。

下一个命令 ENTRYPOINT 指示 Docker 将容器配置为可执行文件运行。在容器启动时,ENTRYPOINT 命令运行。当此命令结束时,容器也会自动停止。

在终端中,运行 docker build -t myimage -f Dockerfile .;在此命令完成后,运行 docker images

  1. > docker build -t myimage -f Dockerfile .
  2. Sending build context to Docker daemon 819.7kB
  3. Step 1/3 : FROM mcr.microsoft.com/dotnet/core/runtime:2.2
  4. ---> d51bb4452469
  5. Step 2/3 : COPY app/bin/Release/netcoreapp2.2/publish/ app/
  6. ---> a1e98ac62017
  7. Step 3/3 : ENTRYPOINT ["dotnet", "app/myapp.dll"]
  8. ---> Running in f34da5c18e7c
  9. Removing intermediate container f34da5c18e7c
  10. ---> ddcc6646461b
  11. Successfully built ddcc6646461b
  12. Successfully tagged myimage:latest
  13. > docker images
  14. REPOSITORY TAG IMAGE ID CREATED SIZE
  15. myimage latest ddcc6646461b 10 seconds ago 314MB
  16. mcr.microsoft.com/dotnet/core/runtime 2.2 d51bb4452469 2 days ago 314MB

Dockerfile 中的每个命令生成了一个层,并创建了“IMAGE ID” 。最终“IMAGE ID” 是“ddcc6646461b” (你的 ID 会有所不同),接下来在此映像的基础之上创建容器。

创建容器Create a container

至此,已有包含应用的映像,可以创建容器了。可以通过两种方式来创建容器。首先,新建已停止的容器。

  1. > docker create myimage
  2. 0e8f3c2ca32ce773712a5cca38750f41259a4e54e04bdf0946087e230ad7066c

上面的 docker create 命令在 myimage 映像的基础之上创建容器。此命令的输出显示已创建容器的“CONTAINER ID” (你的 ID 会有所不同)。若要查看所有 容器的列表,请使用 docker ps -a 命令:

  1. > docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 0e8f3c2ca32c myimage "dotnet app/myapp.dll" 4 seconds ago Created boring_matsumoto

管理容器Manage the container

每个容器都分配有随机名称,可用来引用相应容器实例。例如,自动创建的容器选择了名称“boring_matsumoto” (你的名称会有所不同),此名称可用于启动容器。可以使用 docker create —name 参数将自动名称替代为特定名称。

下面的示例使用 docker start 命令来启动容器,然后使用 docker ps 命令仅显示正在运行的容器:

  1. > docker start boring_matsumoto
  2. boring_matsumoto
  3. > docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. 0e8f3c2ca32c myimage "dotnet app/myapp.dll" 7 minutes ago Up 8 seconds boring_matsumoto

同样,docker stop 命令会停止容器。下面的示例使用 docker stop 命令来停止容器,然后使用 docker ps 命令来显示没有正在运行的容器。

  1. > docker stop boring_matsumoto
  2. boring_matsumoto
  3. > docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

连接到容器Connect to a container

在容器运行后,可以连接到它来查看输出。使用 docker startdocker attach 命令,启动容器并查看输出流。在此示例中,CTRL + C 命令用于从正在运行的容器中分离出来。这其实是结束容器中的进程,进而停止容器。—sig-proxy=false 参数可确保 Ctrl + C 不停止容器中的进程。

从容器中分离出来后重新连接,以验证它是否仍在运行和计数。

  1. > docker start boring_matsumoto
  2. boring_matsumoto
  3. > docker attach --sig-proxy=false boring_matsumoto
  4. Counter: 7
  5. Counter: 8
  6. Counter: 9
  7. ^C
  8. > docker attach --sig-proxy=false boring_matsumoto
  9. Counter: 17
  10. Counter: 18
  11. Counter: 19
  12. ^C

删除容器Delete a container

就本文而言,你不希望存在不执行任何操作的容器。删除前面创建的容器。如果容器正在运行,停止容器。

  1. > docker stop boring_matsumoto

下面的示例列出了所有容器。然后,它使用 docker rm 命令来删除容器,并再次检查是否有任何正在运行的容器。

  1. > docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 0e8f3c2ca32c myimage "dotnet app/myapp.dll" 19 minutes ago Exited boring_matsumoto
  4. > docker rm boring_matsumoto
  5. boring_matsumoto
  6. > docker ps -a
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

单次运行Single run

Docker 提供了 docker run 命令,用于将容器作为单一命令进行创建和运行。使用此命令,无需依次运行 docker createdocker start另外,还可以将此命令设置为,在容器停止时自动删除容器。例如,使用 docker run -it —rm 可以执行两项操作,先自动使用当前终端连接到容器,再在容器完成时删除容器:

  1. > docker run -it --rm myimage
  2. Counter: 1
  3. Counter: 2
  4. Counter: 3
  5. Counter: 4
  6. Counter: 5
  7. ^C

使用 docker run -itCTRL + C 命令会停止在容器中运行的进程,进而停止容器。由于提供了 —rm 参数,因此在进程停止时自动删除容器。验证它是否不存在:

  1. > docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

更改 ENTRYPOINTChange the ENTRYPOINT

使用 docker run 命令,还可以修改 Dockerfile 中的 ENTRYPOINT 命令,并运行其他内容,但只能针对相应容器。例如,使用以下命令来运行 bashcmd.exe根据需要,编辑此命令。

WindowsWindows

在本例中,ENTRYPOINT 更改为 cmd.exe通过按下 CTRL + C 来结束进程并停止容器。

  1. > docker run -it --rm --entrypoint "cmd.exe" myimage
  2. Microsoft Windows [Version 10.0.17763.379]
  3. (c) 2018 Microsoft Corporation. All rights reserved.
  4. C:\>dir
  5. Volume in drive C has no label.
  6. Volume Serial Number is 3005-1E84
  7. Directory of C:\
  8. 04/09/2019 08:46 AM <DIR> app
  9. 03/07/2019 10:25 AM 5,510 License.txt
  10. 04/02/2019 01:35 PM <DIR> Program Files
  11. 04/09/2019 01:06 PM <DIR> Users
  12. 04/02/2019 01:35 PM <DIR> Windows
  13. 1 File(s) 5,510 bytes
  14. 4 Dir(s) 21,246,517,248 bytes free
  15. C:\>^C

LinuxLinux

在本例中,ENTRYPOINT 更改为 bash通过运行 quit 命令来结束进程并停止容器。

  1. root@user:~# docker run -it --rm --entrypoint "bash" myimage
  2. root@8515e897c893:/# ls app
  3. myapp.deps.json myapp.dll myapp.pdb myapp.runtimeconfig.json
  4. root@8515e897c893:/# exit
  5. exit

重要命令Essential commands

Docker 有许多不同的命令,可用于执行你要对容器和映像执行的操作。下面这些 Docker 命令对于管理容器来说至关重要:

清理资源Clean up resources

在本教程中,你创建了容器和映像。如果需要,请删除这些资源。以下命令可用于

  • 列出所有容器
  1. > docker ps -a
  • 停止正在运行的容器。CONTAINER_NAME 表示自动分配给容器的名称。
  1. > docker stop CONTAINER_NAME
  • 删除容器
  1. > docker rm CONTAINER_NAME

接下来,删除计算机上不再需要的任何映像。依次删除 Dockerfile 创建的映像,以及 Dockerfile 所依据的 .NET Core 映像。可以使用 IMAGE ID 或 REPOSITORY:TAG 格式字符串。

  1. docker rmi myimage:latest
  2. docker rmi mcr.microsoft.com/dotnet/core/runtime:2.2

使用 docker images 命令来列出已安装的映像。

备注

映像文件可能很大。通常情况下,需要删除在测试和开发应用期间创建的临时容器。如果计划在相应运行时的基础之上生成其他映像,通常会将基础映像与运行时一同安装。

后续步骤Next steps