zoukankan      html  css  js  c++  java
  • 我的Docker之路(三):容器化APS.NET Core应用

    Docker容器说到底还是为应用服务的,其最根本的作用还是用来部署我们开发的应用。在开发容器化的应用时,通常都会要创建自定义的镜像。目前在Docker中创建镜像最常用的方法应该就是使用Dockerfile了。Dockerfile是文本文件,一种类似脚本的描述文件,来定义镜像的每层是如何构建,正文部分会详细介绍。
    简单概括,开发基于Docker容器的应用程序的常用工作流程:

    1. 开发环境中完成应用的开发
    2. 编写Dockerfile文件
    3. 使用Dockerfile创建自己的镜像
    4. 使用镜像实例化容器并正常运行
    5. 测试与修正
    6. 可以将镜像Push到Hub

    本次的学习任务是构建自定义镜像,并将一个ASP.NET Core应用程序使用容器化的方式部署到Linux服务器中。

    Dockerfile指令

    Docker可以通过定义好的Dockerfile中的指令自动构建映像。虽然现在通过一些高级的IDE(比如visual studio或者visual studio code)可以自动根据当前项目生成Dockerfile,但是了解Dockerfile仍是很有必要的。

    为了对Dockerfile有个整体印象,先预览一份官方文档中Dockefile的示例。可以看出来这里就是按步骤定义指令,大小写不敏感但建议全部大写,“#”标识注释文本。

    FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
    WORKDIR /app
    
    # Copy csproj and restore as distinct layers
    COPY *.csproj ./
    RUN dotnet restore
    
    # Copy everything else and build
    COPY . ./
    RUN dotnet publish -c Release -o out
    
    # Build runtime image
    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
    WORKDIR /app
    COPY --from=build-env /app/out .
    ENTRYPOINT ["dotnet", "aspnetapp.dll"]

    接下来对Dockerfile中的常用指令做一个总结。

    FROM <image>[:<Tag>] [AS <name>]

    1. 指明本镜像是基于哪个基础镜像来创建的
    2. 必须放在Dockerfile第一行
    # 以官方 ASP.NET Core镜像(Tag是3.1)作为基础镜像
    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS build-env
    
    

    RUN <command>

    RUN ["executable","arg1","arg2"]

    1. 运行shell命令的两种写法,第一种可当作Windows上的 cmd /S /C 或者Linux上的 /bin/sh -c ,第二种是exec form,要注意的是参数会被传递成Json,所以不要忘记双引号。
    2. 注意的是每运行一个RUN命令都会在当前镜像的顶层生成一个新层,并将结果提交。
    RUN dotnet restore
    RUN ["dotnet", "restore"]

    CMD ["executable","param1","param2"]

    CMD ["param1","param2"]

    CMD command param1 param2

    1.  定义镜像创建的容器默认执行的命令,理论上一个Dockerfile中只有一个CMD指令,如果存在多个只执行最后一条。
    2. CMD指令有3中写法,第二种是最为ENTRYPOINT的参数
    3. 要注意区别RUN和CMD,CMD指令在创建镜像的过程中不会执行,是在容器创建好后执行的。
    4. 当Docker执行run命令并附加了CMD时,我们在Dockerfile中指定的CMD将会被重写。
    CMD echo "This is a test container."

    LABEL <key>=<value> <key>=<value> <key>=<value> ...

    1. LABEL指令为镜像添加键值对形式的元数据
    2. 如果在键值对中需要写入空格,要使用双引号或者反斜杠
    3. 若想查看镜像中定义的label,可使用 docker image inspect --format='' <image> 命令
    LABEL version="1.0"
    LABEL maintainer="xuhy"

     EXPOSE <port> [<port>/<protocol>...]

    1. 用来声明容器运行时监听的网络端口
    2. 并且可以指定监听的协议时TCP或是UDP,默认时采用TCP
    3. 注意当使用EXPOSE指定指定端口后其实并不会在容器运行时就自动开启这个端口,主要是针对镜像使用者作为一个声明,在启动容器还是需要使用 -p 标识来映射端口。
    EXPOSE 80/tcp
    EXPOSE 80/udp
    docker run -p 80:80/tcp -p 80:80/udp ...

    ENV <key> <value>
    ENV <key>=<value> ...

    1. 设置环境变量,参数使用键值对的形式增加
    2. 后续的所有指令包括RUN都可以使用它所创建的环境变量
    3. 使用该镜像创建的容器中也可以使用该环境变量,可以使用 docker inspect 命令来查看环境变量,并可以使用 docker run --env <key>=<value> 来修改这些环境变量。
    ENV myName John Doe
    ENV myDog Rex The Dog
    ENV myCat fluffy

    ADD [--chown=<user>:<group>] <src>... <dest>
    ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

    1. 将 <src> 中指定的路径/文件(也可以是远端文件的URL)拷贝到镜像中的文件系统的 <dest> 路径。
    2.  <src> 中指定的路径或者文件可以使用通配符
    3.  --chown 特性只在创建Linux容器时起作用
    # 添加所有以 "hom" 开头的文件
    ADD hom* /mydir/   
    # ? 替换任何单个的字符, e.g., "home.txt"
    ADD hom?.txt /mydir/  
    # 添加 "test" 到 `WORKDIR`/relativeDir/
    ADD test relativeDir/   
    # 添加 "test" 到 /absoluteDir/
    ADD test /absoluteDir/
    # 当添加特殊字符如“[”时,需要遵循Go语言的规则对其转移,如拷贝文件“ arr[0].txt”时使用
    ADD arr[[]0].txt /mydir/

    COPY [--chown=<user>:<group>] <src>... <dest>
    COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

    1. COPY指令与上面的ADD指令基本功能一样
    COPY hom* /mydir/
    COPY hom?.txt /mydir/
    COPY test.txt relativeDir/
    COPY test.txt /absoluteDir/
    ADD arr[[]0].txt /mydir/

    ENTRYPOINT command param1 param2

    ENTRYPOINT ["executable", "param1", "param2"]

    1. 使用ENTRYPOINT指令可以将容器配置成一个可执行文件,这样就不能通过docker run中的命令行所覆盖,但是可以通过参数传递的方式影响到容器的执行
    2. 当定义了ENTRYPOINT后,CMD只能够作为参数进行传递
    3. 每个Dockerfile理论上只有一个ENTRYPOINT,若定义了多个ENTRYPOINT只有最后一个起作用
    ENTRYPOINT ["dotnet", " MyWebApi.dll "]

    VOLUME ["/data"]

    1. 为镜像创建一个挂载点供创建容器时用卷来挂载
    VOLUME ["/usr/data/mysql"]
    VOLUME ["/var/data1","/var/data2"]

    USER <user>[:<group>]
    USER <UID>[:<GID>]

    1. 设定运行RUN或者CMD的用户名或用户ID
    # Set it for subsequent commands
    USER xuhy

    WORKDIR /path/to/workdir

    1. 后续的 RUN , CMD , ENTRYPOINT  COPY 指令设定工作目录
    2. 如果指定的路径不存在会自动创建
    3.  WORKDIR 在Dockerfile中可以多次使用,如果提供的是一个相对路径,那么这个相对路径会被指定为前一个 WORKDIR 指令的相对路径下
    WORKDIR /a
    WORKDIR b
    WORKDIR c
    
    # pwd命令的工作目录在“/a/b/c”下
    RUN pwd

    ARG <name>[=<default value>]

    1. 该指令用来定义参数变量,定义了参数后,可以在创建镜像是使用 docker build 命令时使用 --build-arg <varname>=<value> 标识来为参数赋值。
    2. 在定义参数时也可为其设定默认值 ARG user1=someusr 
    3. 一个 ARG 变量是在Dockerfile中定义的行之后才开始生效
    4. 如果在同个Dockerfile中使用 ARG 和 ENV 定义了同名变量,那么使用 ENV 定义的环境变量会覆盖 ARG 定义的参数
    5.  ARG 定义的变量的作用域只在当前构建层中有效,也就是说在使用 RUN 命令之后便超出了其作用域
    ARG user1="abc"
    ARG CONT_IMG_VER = "v1.0"
    ENV CONT_IMG_VER v1.0.0        #同名变量,ENV会覆盖ARG的变量 
    USER $user1
    #...
    
    $ docker build --build-arg user=what_user,CONT_IMG_VER=v2.0.1

    构建ASP.NET Core项目镜像

    接下来我们尝试定制一个ASP.NET Core应用的镜像,当前版本的Visual Studio和Visual Studio Code都已经集成了Docker插件,真不愧时宇宙级IDE。使用VS创建一个测试项目“MyWebApp.MVC”,可以看到在创建项目时便可以同时创建Dockerfile,只需勾选“启动Docker”支持即可。如下图所示。

    等待VS生成要默认脚手架代码后,试运行没问题后在项目上右键 → 添加 → Docker支持,随后目标OS选择Linux。随后VS会帮我们生成一份Dockerfile文件,并且会自动执行镜像的构建和容器的创建执行,全自动挡。

    既然已经学习了Dockerfile的命令,让我们把VS生成的Dockerfile过目一遍。

     1 # 拉取微软官方提供的asp.net core运行时镜像
     2 FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
     3 WORKDIR /app    #指定当前工作目录
     4 EXPOSE 5000     #声明暴露5000端口
     5 
     6 # 拉取微软官方提供的.NET Core SDK 镜像,用于编译和发布项目
     7 FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
     8 WORKDIR /src    #指定当前工作目录
     9 COPY ["MyWebApp.MVC/MyWebApp.MVC.csproj", "MyWebApp.MVC/"]   #项目文件拷贝到镜像中的“/src/MyWebApp.MVC/”目录中
    10 RUN dotnet restore "MyWebApp.MVC/MyWebApp.MVC.csproj"        #执行命令来恢复项目的依赖项
    11 COPY . .        #把当前目录(windows主机)的所有内容拷贝到镜像的当前目录“/src/”
    12 WORKDIR "/src/MyWebApp.MVC"        #切换当前工作目录
    13 RUN dotnet build "MyWebApp.MVC.csproj" -c Release -o /app/build    #使用.net core编译器执行“生成”
    14 
    15 # 基于上面生成项目用的镜像“build”继续搭建本层,工作目录为“/src/MyWebApp.MVC/”
    16 FROM build AS publish
    17 RUN dotnet publish "MyWebApp.MVC.csproj" -c Release -o /app/publish    
    18 # 至此项目的编译与发布已完成,就是说现在生成了“MyWebApp.MVC.dll”文件了
    19 
    20 FROM base AS final
    21 WORKDIR /app
    22 COPY --from=publish /app/publish .        #将刚刚发布项目的publish文件夹下的所有文件拷贝到本镜像的当前目录下(/app),注意结尾的"."代表当前目录
    23 ENTRYPOINT ["dotnet", "MyWebApp.MVC.dll"] #启动网站

    通过powershell来执行镜像的命令,先进入项目文件夹的目录,执行命令。

    PS D:workspaceMyWebApp.MVC > docker build -f "${pwd}MyWebApp.MVCDockerfile" --force-rm -t mywebappmvc:test .

    其中 -f 后跟dockerfile的路径, --force-rm 表示删除中间层镜像, -t 后跟“镜像名:Tag”,最后跟“.”表示当前工作路径或上下文(context)为当前路径。

    等待16个Step(Dockerfile中每一句命令是一个Step)执行完成,可以检查下当前镜像列表中多出刚刚构建的镜像。

    Successfully built 9ae226b0a4e9
    Successfully tagged mywebappmvc:test
    
    PS D:workspaceMyWebApp.MVC> docker image ls                                                              
    REPOSITORY TAG IMAGE ID CREATED SIZE mywebappmvc test 9ae226b0a4e9
    22 seconds ago 212MB

    在本地试执行,将容器的5000端口映射到本地的8080端口。

    docker run -dt -p 8080:5000 --name aspnetcore.test  mywebappmvc:test

    执行成功后在浏览器中访问8080端口,不出意外便可以看到asp.net core mvc的默认页面。

    可执行 docker logs aspnetcore.test 查看容器执行的日志。

    我们可以将创建好的Docker镜像上传到DockerHub上,首先需要我们在DockerHub上建立自己的账户,在本地Docker上登录 docker login <username> 。登录成功之后便可以使用 docker push <image> 命令上传镜像。注意镜像名称的书写有格式要求,要按照 docker push username/image:tag 的。本地可以使用 docker tag  命令来标记本地镜像。

    docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

    在Linux服务器上运行容器

     1. 在Linux上安装Docker

    Linux上安装Docker也很简单,直接参照官方文档一步步执行即可,没啥坑。由于是在一台全新的Ubuntu系统上安装,先更新 apt ,下载和更新必要的包

    ~$ sudo apt-get update
    
    ~$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

    搭建本地Repository

    ~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    
    ~$ sudo apt-key fingerprint 0EBFCD88
    
    ~$ sudo add-apt-repository 
       "deb [arch=amd64] https://download.docker.com/linux/ubuntu 
       $(lsb_release -cs) 
       stable"

    安装Docker Engine,执行下面命令安装最新版Docker Engine

    $ sudo apt-get update
    $ sudo apt-get install docker-ce docker-ce-cli containerd.io

    安装完成之后试一下命令

    ~$ docker --version
    Docker version 19.03.8, build afacb8b7f0
    
    ~$ sudo docker run --rm hello-world

    2.拉取镜像并运行容器

    首先拉取刚刚上传到DockerHub上的镜像

    ~$ sudo docker pull mark826/mywebappmvc:test20515

    查看下本地Docker镜像可以看到拉取成功

    ~$ sudo docker images
    REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
    mark826/mywebappmvc   test20515           9ae226b0a4e9        4 hours ago         212MB

    接着和在Windows下一样运行容器即可。

    ~$ sudo docker run -dt -p 8080:5000 --name aspnetcore.test  mark826/mywebappmvc:test20515

    现在访问服务器的8080端口便可以访问asp.net core的页面了。

    参考文献

    [1] 官文:https://docs.docker.com/engine/reference/builder/

    [2] 微软文档:https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/docker-application-development-process/docker-app-development-workflow

    [3] 介绍Dockerfile的博文:https://www.cnblogs.com/jsonhc/p/7766841.html

    [4] 博文:https://www.cnblogs.com/edisonchou/p/dockerfile_inside_introduction.html

    [5] Ubuntu安装Docker:https://docs.docker.com/engine/install/ubuntu/

    [6] 官文介绍Docker容器化ASP.NET Core应用:https://docs.docker.com/engine/examples/dotnetcore/

  • 相关阅读:
    设计模式(三)原型模式
    PageHelper在Mybatis中的使用
    设计模式(二) 单例模式
    设计模式(一)简单工厂、工厂方法和抽象工厂
    Java网络编程
    循环控制语句if 、for、case、while
    处理海量数据的grep、cut、awk、sed 命令
    shell脚本的输入以及脚本拥有特效地输出
    shell的变量以及常见符号
    shell的使用技巧
  • 原文地址:https://www.cnblogs.com/xhy0826/p/ASPNETCore_Docker.html
Copyright © 2011-2022 走看看