容器技术—.net core on docker

1. 开发环境使用docker调试

.net core应用想要启用docker支持非常简单,vs能够已经做了很好的支持。我们可以在创建.net core应用时,选择启用docker
在这里插入图片描述
也可以对已经创建完成的应用启用docker
在这里插入图片描述
上面的两个操作都会在应用根目录生产一个Dockerfile文件,关于Dockerfile下面再细讲。

启用了docker的应用在vs上除了IIS和控制台模式启动之外,还会多出一种docker启动的模式。
在这里插入图片描述
但是在使用docker模式进行启动时,可能会遇到问题,如果你遇到以下的情况,长期卡在容器预热这一步
在这里插入图片描述
查看容器工具有以下的信息
在这里插入图片描述
可以这样解决:
手工下载 https://vsdebugger.azureedge.net/vsdbg-17-2-10518-1/vsdbg-linux-x64.zip 文件,Url中的版本号需要与上面的提示一致。下载后解压到提示的文件夹中,然后创建文件success_rid.txt,内容为linux-x64,创建文件success_version.txt,内容为提示中的版本号。
再下载 https://vsdebugger.azureedge.net/vsdbg-17-2-10518-1/vsdbg-linux-musl-x64.zip 将这个文件解压到vsdbg\vs2017u5\linux-musl-x64中,同样新建success_rid.txt,内容为linux-musl-x64,success_version.txt,内容为提示是中的版本号。重新启动Visual Stuido就可以了。

之后就可以在vs通过docker容器运行我们的应用了。运行起来之后,可以在容器窗口看到以下内容:
在这里插入图片描述
通过vs以容器的方式启动我们的应用,需要依赖于windows版本的的docker应用 docker desktop。之后通过cmd命令也可以看到正在运行的容器和生成的镜像。
在这里插入图片描述

2. Dockerfile

从上面的例子可以看到,添加了启用了docker支持之后,我们工程的文件结构较之前多了一个Dockerfile,Dockerfile是用来构建镜像的关键文件,包含了一条条构建镜像所需的指令和说明。

Dockerfile常用的指令如下:

2. 1. FROM: 指定基础镜像

定制镜像,需要先有一个基础镜像,FROM 就是指定基础镜像,此指令必需放在有效指令的第一行。

格式:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
说明:
--platform 指定镜像的平台,比如:linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest

2.2 RUN: 执行shell命令

RUN 指令是用来执行命令的,有以下俩种格式:

#shell 格式: 
RUN <命令> # <命令行命令> 等同于,在终端操作的 shell 命令。
#exec 格式: 
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

注意:run可以写多个,每一个run指令都会建立一层,所以尽可能合并成一条指令

范例:
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
RUN ["/bin/bash", "-c", "echo hello world"]
RUN yum -y install epel-release \
      && yum -y install nginx \
      && rm -rf /usr/share/nginx/html/*
      && echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html

2.3 COPY:复制文本

复制本地主机的 (为 Dockerfile 所在目录的相对路径)到容器中的 。

COPY <src>... <dest>
COPY ["<src1>",... "<目标路径>"]

说明:

  • 可以是多个、以及使用通配符,通配符规则满足Go的filepath.Match 规则
  • 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
  • 如果是目录,只复制目录内容,而非目录本身
范例:
COPY hom* /mydir/    COPY hom?.txt /mydir/

2.4 ADD:复制和解包文件

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY),功能也类似。

ADD [--chown=<user>:<group>] <src>... <dest> 
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] 
  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

2.5 CMD:容器启动命令

类似于 RUN 指令,用于指定启动容器时默认执行的命令,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么就会使用执行CMD指定的默认的命令。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。如:docker run xxx /bin/bash,则/bin/bash 会覆盖 CMD 指定的命令。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

# 使用 exec 执行,推荐方式,第一个参数必须是命令的全路径
CMD ["executable","param1","param2"] 
# 在 /bin/sh 中执行,提供给需要交互的应用;
CMD command param1 param2 
# 提供给 ENTRYPOINT 的默认参数;
CMD ["param1","param2"] 
范例:
CMD ["nginx", "-g", "daemon off;"]

使用CMD作为容器启动的默认执行命令时,要注意一点:

如果业务进程可以在CMD中启动,则使用executable模式;如果必须放在shell脚本中启动,则在业务进程的启动命令前加上exec关键字使其pid保持为1

这是因为在容器中,如果pid=1的进程退出了,那么容器就会退出。所以要保证容器持久运行,就要保证pid=1的进程能够持久运行,我们应该一个容器尽量只跑一个业务进程,并且让业务进程的pid保持为1,这样才能利用好容器的重启策略,在容器挂掉之后自动重启,而不会出现容器内部的应用进程挂了,但是容器还在正常运行,导致难以排除问题

2.6 ENTRYPOINT:入口点

功能类似于CMD,配置容器启动后执行的命令及参数,并且不可被 docker run 提供的参数覆盖,而是追加

如果docker run命令有参数,那么参数全部都会作为ENTRYPOINT的参数。如果docker run后面没有额外参数,但是dockerfile中的CMD里有(即上面CMD的第三种用法),那么CMD的全部内容会作为ENTRYPOINT的参数

但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

使用CMD要在运行时重新写命令才能追加运行参数,ENTRYPOINT则可以执行docker run时接受新参数

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效

# 使用 exec 执行
ENTRYPOINT ["executable", "param1", "param2"]
# shell中执行
ENTRYPOINT command param1 param2

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例:
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参 
1、不传参运行
	$ docker run  nginx:test
	容器内会默认运行以下命令,启动主进程。
	nginx -c /etc/nginx/nginx.conf
2、传参运行
	$ docker run  nginx:test -c /etc/nginx/new.conf
	容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
	nginx -c /etc/nginx/new.conf

2.7 ENV:设置环境变量

指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
范例:
ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

2.8 ARG:构建参数

指定变量

ARG <name>[=<default value>]

如果和ENV同名,ENV覆盖ARG变量
和ENV不同的是,ARG 只有 docker build 的过程中有效,容器运行时不会存在这些环境变量
可以用 docker build –build-arg <参数名>=<值> 来覆盖

范例:
FROM busybox
ARG user1=someuser
ARG buildno=1
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

2.9 VOLUME:挂载点

在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,一般会将宿主机上的目录挂载至VOLUME 指令指定的容器目录。即使容器后期删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。在启动容器时没有指定挂载数据卷,会自动挂载到匿名卷。
作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。
VOLUME ["<容器内路径1>", "<容器内路径2>"...]
VOLUME <路径>
范例:在容器创建一个/data/ 的挂载点
VOLUME [ "/data","/data2" ]  

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

2.10 XPOSE:暴露端口

告诉 Docker 服务端容器暴露的端口号,以方便配置映射。

EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射

因此,在启动容器时需要通过 -P 或-p进行端口映射 才可以使用,如果使用-P命令,Docker 主机会随机分配一个端口转发到指定暴露的端口

EXPOSE <端口1> [<端口2>...] 

2.11 WORKDIR:指定工作目录

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,也是当容器运行后,进入容器内的默认目录。

用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在(WORKDIR 指定的工作目录,必须是提前创建好的)。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

WORKDIR <工作目录>
范例:
#两次run不在一个环境内,可以使用WORKDIR
RUN cd /app
RUN echo "hello" > world.txt

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c

以上为Dockerfile常用命令,其他的一些命令可查看网上的文章。看完这些Dokerfile指令,再回过头来看vs为我们生成的Dockerfile文件就能知道是什么意思了。

# 拉取.net 6 aspnet镜像,并重命名为base
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
# 设置工作目录为/app,app目录已存在
WORKDIR /app
# 暴露80端口
EXPOSE 80
# 拉取.net 6 sdk镜像,重命名为build
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
# 设置工作目录为/src
WORKDIR /src
# 复制类库文件到/src/dockerSample/文件夹
# 这里比较奇特,vs在构建镜像的时候宿主机的当前目录并不在Dockerfile的目录
# 而是在解决方案所在的目录
# 如果我们在当前Dockerfile的目录用docker build命令构建镜像是不成功的
COPY ["dockerSample/dockerSample.csproj", "dockerSample/"]
# 还原依赖包
RUN dotnet restore "dockerSample/dockerSample.csproj"
# 复制当前目录到/src
COPY . .
# 设置工作目录为/scr/dockerSample
WORKDIR "/src/dockerSample"
# 生成当前类库
RUN dotnet build "dockerSample.csproj" -c Release -o /app/build
# 使用.net 6 sdk镜像
FROM build AS publish
# 发布当前类库
RUN dotnet publish "dockerSample.csproj" -c Release -o /app/publish
# 使用.net 6 aspnet镜像
FROM base AS final
# 设置工作目录
WORKDIR /app
# 从publish层的/app/publish复制文件到当前目录,这里是因为/app/publish没有被设置成工作目录,所以不会在所有层到存在,只能这样指定从那一层复制,容器启动后进行容器内部也看不到/app/publish文件夹的
COPY --from=publish /app/publish .
# 执行以下脚本启动.net 应用
ENTRYPOINT ["dotnet", "dockerSample.dll"]

3. docker发布

对基于docker的.net core应用进行发布很简单,有多种方式可以实现。

3.1 镜像移植

如果你的开发机上已经安装了docker,并且像上面讲到的一样已经在开发环境利用docker进行运行调试了,可以通过vs直接生成docker镜像,根据上一节讲到的docker的基本操作,可以直接将本地的docker镜像导出,这也适用于某些只能离线部署,无法联网的情况。

步骤如下:

(1) 修改一下swagger配置,去掉开发环境判断,方便测试,生产环境发布根据自己的情况来。
在这里插入图片描述
(2)右击Dockerfile,使用VS容器工具生成映像
在这里插入图片描述
生成完镜像之后,通过vs的容器窗口或者在cmd中通过docker命令可以看到新生成的镜像。
在这里插入图片描述
这里可以看到,发布时vs已经通过环境变量设置了我们的应用在docker启动的端口了。这里还可以看到有两个镜像,标签分别是dev和latest,这两个镜像是不同的,latest标签的镜像才是我们可以导出使用的镜像,通过查看容器,可以看到这两个镜像的启动脚本是不同的。
在这里插入图片描述
(3) 导出镜像
在这里插入图片描述
(4) 将镜像上传到服务器,并重新导入
导入的镜像是没有名称和标签的,这里对它进行了以下重命名
在这里插入图片描述
(5) 启动容器

docker run -d --name dockersample -p 8005:80 dockersample:v1.0

在这里插入图片描述
通过浏览器也可以看到特意留出来的swagger了
在这里插入图片描述

3.2 release发布

如果按照我们以往的发布方式,都是通过vs将应用发布到一个文件夹中,再将文件夹拷贝到服务器上,通过dotnet xxx.dll的方式运行我们的应用,这种方式也可以结合docker来发布运行。这也是网上开源项目提供release版本支持docker的方式。

下面是通过release发布文件结合docker的方式,步骤如下:

(1) 发布应用
在这里插入图片描述
(2) 添加Dockerfile
在这里插入图片描述
如果本地不安装docker,不想利用vs进行docker调试,也就没法利用vs一键构建镜像的功能了,那样可以直接修改vs生成的Dockerfile文件,并将文件设置为较新则复制,这样在发布时Dockerfile文件就会发布到相应的文件夹,无需手动创建,这里手动添加只是做一个示例。

新的Dockerfile文件内容如下:
在这里插入图片描述
(3) 将文件夹打包,传输到服务器
在这里插入图片描述
(4) 通过docker build命令 + Dockerfile构建镜像

docker build -t dockersample:v2.0 .

在这里插入图片描述
值得一提的是,docker build 命令最后有一个点,.是指当前目录,用于指定Dockerfile所在的目录,所以使用点时需要进入Dockerfile所在的目录,也可以使用相对路径或者绝对路径。

从这里也可以看到通过FROM依赖于一个镜像构建我们自己的docker镜像是需要将依赖的镜像下载下来的,在某些特殊的项目中,如果是完全离线断网的环境下是无法这样部署的。

(5) 启动容器

docker run -d --name dockersample -p 8005:80 dockersample:v2.0

在这里插入图片描述
启动容器之后,通过浏览器可以访问到我们的应用
在这里插入图片描述

3.3 发布到docker仓库

除了以上两种发布发布之外,我们还可以将应用镜像发布到docker仓库,再在服务器上从仓库拉取进行安装部署,就像我们使用第三方的docker镜像一样。我们可以将镜像发布到Docker Hub等公开的docker仓库中,但是在项目中我们往往需要使用私有仓库。

(1) 私有仓库部署
开源的私有化docker仓库有很多,如Registry、Harbor、nexus3等,我习惯使用nexus3,工作中的nuget包、npm包等也是用nexus3来管理的。这里就不详细讲解nexus3的安装部署了,只是用官方docker镜像快速启动一个nexus3的应用作为演示,版本为 v3.39.0,nexus3的详细部署文档请参考网上教程或官方文档

#因为官方容器运行用户有所指定,挂载目录必须授权
mkdir /home/yyl/nexus/nexus-data && chown -R 200 /home/yyl/nexus/nexus-data
docker run -d -p 8081:8081-p 8082:8082 --name nexus -v /home/yyl/nexus/nexus-data:/nexus-data sonatype/nexus3

持久目录/nexus-data用于配置、日志和存储。此目录需要可由 Nexus 进程写入,该进程以 UID 200 运行。

nexus运行只需要一个8081端口就可以了,这里增加一个8082端口是为了给后面docker仓库的登录端口使用。这里很关键,不加这个端口映射,后续新增了docker仓库之后外部是登录不上的。

启动成功之后就可以通过浏览器访问到nexus管理页面
在这里插入图片描述
登录默认管理员admin,密码在 /home/yyl/nexus/nexus-data/admin.password,初次登录之后需要修改下密码。
在这里插入图片描述
(2) 创建docker 仓库
nexus部署之后,nuget和maven仓库是默认就有的,但是docker仓库需要我们自己配置,点击设置进行管理页面。

在这里插入图片描述
选择仓储,点击创建新仓储
在这里插入图片描述
选择docker(hosted),可以看到nexus中的管理一种包的仓储有三种类型,如上面的nuget、maven,这里的docker。

  • (hosted)为本地仓库,我们上传自己的镜像就上传到这个仓库
  • (proxy)为代理仓库,通过url地址代理到线上的仓库,如Docker Hub,当我们使用到第三方的镜像时,这个仓库会从线上参考拉取并存储到本地,下次再使用这个镜像就是直接使用这个仓库的
  • (group)为聚合仓库,将host、proxy两个仓库聚合,我们一般最终使用的就是这个仓库
    在这里插入图片描述在这里插入图片描述
    (3) 设置权限
    点击菜单Security->Realms,把 Docker Bearer Token Realm 移到右边的框中保存。
    在这里插入图片描述
    这里在工作中根据实际需要可以针对Role和User进行新增设置,通过专门的用户角色权限来控制对docker仓库操作的权限,这里就直接使用admin用户了。

(4) 镜像上传私有仓库
要像上传镜像到私有仓库,需要先登录。由于我们上面设置的登录方式是http协议,而docker默认是https链接,创建仓库为http判定不安全链接会被拒绝。我们需要修改daemon.json文件内容,将仓库链接手动添加为信任链接。linux下daemon.json文件位于/etc/docker下,如果没有此文件,可以自己创建此文件。
文件内容为:

{
    "insecure-registries": ["ip:port" ]
}

之后重启docker daemon

systemctl daemon-reload
systemctl restart docker

如果是windows下,直接在doker desktop 的setting下进行配置
在这里插入图片描述
之后通过以下命令登录:

docker login ip:port

在这里插入图片描述
之后我们就通过命令可以将本地的镜像推送到私有仓库了,本地镜像推送到私有仓库前需要先修改镜像标签,在镜像repository上指定仓库,否则会推送到默认仓库

docker tag dockersample:dev 192.168.137.200:8082/dockersample:v1.0
docker push 192.168.137.200:8082/dockersample:v1.0

在这里插入图片描述
在这里插入图片描述
(5) .net 应用发布
对于我们的.net 应用来说,通过命令推送本地镜像始终不方便,vs发布的时候就可以选择直接将应用发布到docker仓库。
在这里插入图片描述
这里看到的latest版本就是我们发布到私有仓库中的镜像了。
在这里插入图片描述
(6) 服务器下载镜像部署
服务器上通过以下命令拉取私有仓库的镜像,也是需要指定镜像仓库:

docker pull 192.168.137.200:8082/dockersample

在这里插入图片描述

docker run -d --name dockersample -p 8005:80 192.168.137.200:8082/dockersample

之后就是正常的启动容器了。

一篇博文下来1万来字,有些长了。剩下的一些内容放到下一篇了。

参考文档:
Visual Studio 准备容器时,卡在 vsbdg\vs2017u5 exists deleting
docker-dockerfile指令详细介绍
Docker Dockerfile

微服务系列文章:
上一篇:容器技术—docker基础
下一篇:容器技术—docker compose

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值