制作一个能构建 dotnet AOT 的 gitlab ruuner 的 Debian docker 镜像

news/发布时间2024/5/17 1:59:51

我的需求是需要有一个能够构建出 dotnet 的 AOT 包的环境,要求这个环境能解决 glibc 兼容依赖的问题,能打出来 x64 和 arm64 的 AOT 的包,且能够运行 gitlab runner 对接自动构建

需求

以下是我列举的需求

  • 支持制作能在 UOS 系统和麒麟系统上运行的包
  • 支持制作出来的包是 AOT 版本的
  • 可以使用 gitlab runner 对接自动构建

开始之前必须说明的是,对于 dotnet 应用来说,如果不需要 AOT 的话,完全可以在 Windows 上构建出其他 Linux 系统和其他平台适用的应用。仅仅只是在 AOT 下,强依赖平台构建时,才有需要在对应的系统平台构建

制作方法

我制作的 docker 的 Dockerfile 是基于 debian:buster-slim 打上负载的

FROM debian:buster-slim

为了提升一点拉取速度,我换成国内的源,使用的是阿里的源

RUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/sources.list
RUN apt-get update

这里的 sources.list 的代码是从 debian镜像_debian下载地址_debian安装教程-阿里巴巴开源镜像站 抄的,代码如下

deb http://mirrors.aliyun.com/debian/ buster main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib
deb http://mirrors.aliyun.com/debian-security buster/updates main
deb-src http://mirrors.aliyun.com/debian-security buster/updates main
deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib

为了交叉构建,同时构建出 ARM64 的 AOT 的 dotnet 应用,我根据 Cross-compilation - .NET - Microsoft Learn 的文档安装上必要的负载

RUN dpkg --add-architecture arm64
RUN apt updateRUN apt-get install libicu-dev -y
RUN apt-get install libssl-dev -y
RUN apt-get install wget -y
RUN apt-get install clang llvm -y
RUN apt-get install gcc-aarch64-linux-gnu -y
RUN apt-get install binutils-aarch64-linux-gnu -y
RUN apt-get install zlib1g-dev -y
RUN apt-get install zlib1g-dev:arm64 -y

为了方便调试和对接 gitlab runner 我还加上了 git 和 vim 工具

RUN apt-get install vim -y
RUN apt-get install git -yRUN apt-get clean

到这一步,就完成了 docker image 里面的基础部分了,现在的 Dockerfile 的代码如下

FROM debian:buster-slim
WORKDIR /rootRUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/sources.list
RUN apt-get updateRUN dpkg --add-architecture arm64
RUN apt updateRUN apt-get install libicu-dev -y
RUN apt-get install libssl-dev -y
RUN apt-get install wget -y
RUN apt-get install clang llvm -y
RUN apt-get install gcc-aarch64-linux-gnu -y
RUN apt-get install binutils-aarch64-linux-gnu -y
RUN apt-get install zlib1g-dev -y
RUN apt-get install zlib1g-dev:arm64 -yRUN apt-get install vim -y
RUN apt-get install git -yRUN apt-get clean

接着到 dotnet 官网 下载 dotnet 8 和 dotnet 6 的 sdk 压缩包,本文这里使用的是自己解压缩的方式。换成命令方式安装也可以,只是命令方式拉取的速度可能不如先下载压缩包的方式,且下载压缩包可以方便多次重新构建,在 Dockerfile 不断需要修改时,使用压缩包可以省去多次修改之后的重新构建时的拉取时间

本文这里采用的是下载压缩包的方式,下载到 dotnet-sdk-6.0.421-linux-x64.tar.gz 和 dotnet-sdk-8.0.204-linux-x64.tar.gz 这两个压缩包。如果大家下载失败,或者没有网速的话,可以邮件给我,让我用网盘发给你。一般情况下在国内都能拉取成功,因为微软帮忙提供了全球 CDN 了,下载速度在我这里还是很快的。下载 dotnet 6 版本仅仅只是为了让我的构建工具正常工作而已,属于可选项

下载完成 dotnet 的压缩包,即可使用 Dockerfile 的 ADD 命令将压缩包解压缩到 docker image 里的某个文件夹里面,如下面代码

WORKDIR /root
ADD dotnet-sdk-6.0.421-linux-x64.tar.gz ./dotnet
ADD dotnet-sdk-8.0.204-linux-x64.tar.gz ./dotnet

解压缩完成之后,配置环境变量等,让全局可以使用 dotnet 命令

ENV DOTNET_ROOT="/root/dotnet"
ENV PATH="${PATH}:${DOTNET_ROOT}:${DOTNET_ROOT}/tools"
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
RUN ln -s /root/dotnet/dotnet /usr/bin/dotnet

完成以上步骤之后,一个特别简单的 dotnet 构建 Dockerfile 已经完成了,接下来一步则是配置 gitlab runner 的步骤。我将参考 gitlab runner 官方安装文档 进行配置,只是过程稍微取巧

先根据 Install GitLab Runner manually on GNU/Linux - GitLab 提供的方法,拉取 GitLab Runner 的二进制压缩包,本文这里是需要下载 Linux x86-64 版本,当前的下载链接是 https://s3.dualstack.us-east-1.amazonaws.com/gitlab-runner-downloads/latest/binaries/gitlab-runner-linux-amd64

下载链接可能会有变更,还请大家重新参考 Install GitLab Runner manually on GNU/Linux - GitLab 文档,找到更新的下载 Linux x86-64 版本的下载地址

完成下载之后,通过 COPY 命令拷贝到 docker image 里

COPY gitlab-runner-linux-amd64 /usr/share/gitlab/gitlab-runnerRUN chmod +x /usr/share/gitlab/gitlab-runner

完成以上步骤之后需要对 GitLab Runner 进行配置。本文这里采用取巧的方式,即先将 GitLab Runner 运行起来,配置完成之后,存放配置文件,再将配置文件打入到 docker image 里面,后续就只需启动 docker image 即可

具体的步骤是先将当前的 Dockerfile 构建且运行。我这里使用的是 podman 工具,如果大家使用的是 docker desktop 的话,只需将 podman 命令换成 docker 命令即可,其他参数相同

// 先 cd 到 Dockerfile 所在的文件夹,再执行以下命令
podman build -t t1 .

当前的 Dockerfile 文件的代码如下

FROM debian:buster-slim
WORKDIR /rootRUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/sources.list
RUN apt-get updateRUN dpkg --add-architecture arm64
RUN apt updateRUN apt-get install libicu-dev -y
RUN apt-get install libssl-dev -y
RUN apt-get install wget -y
RUN apt-get install clang llvm -y
RUN apt-get install gcc-aarch64-linux-gnu -y
RUN apt-get install binutils-aarch64-linux-gnu -y
RUN apt-get install zlib1g-dev -y
RUN apt-get install zlib1g-dev:arm64 -yRUN apt-get install vim -y
RUN apt-get install git -yRUN apt-get cleanADD dotnet-sdk-6.0.421-linux-x64.tar.gz ./dotnet
ADD dotnet-sdk-8.0.204-linux-x64.tar.gz ./dotnet
ENV DOTNET_ROOT="/root/dotnet"
ENV PATH="${PATH}:${DOTNET_ROOT}:${DOTNET_ROOT}/tools"
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
RUN ln -s /root/dotnet/dotnet /usr/bin/dotnetCOPY gitlab-runner-linux-amd64 /usr/share/gitlab/gitlab-runnerRUN chmod +x /usr/share/gitlab/gitlab-runner

再将打包好的 docker image 运行,运行时记得挂载上文件夹,用于将 docker 里面的文件传输到主机

// 提前创建好 C 盘的 lindexi 的 wsl 文件夹,你换成自己的文件夹也可以
podman run -i -t -v /mnt/c/lindexi/wsl:/etc/gitlab-runner t1

以上代码的 /mnt/c/lindexi/wsl 是我自己的 C:\lindexi\wsl 文件夹,这是我提前建立好的空文件夹。大家换成自己的文件夹也可以,如果用 docker desktop 的话,需要看一下是否运行在 wsl 上,如果不在的话,也许需要换成 Windows 下的路径表示方法,相信这一步难不倒大家的

进入之后,即可使用 /usr/share/gitlab/gitlab-runner 命令进行注册,具体的注册步骤如下

本文使用注册 GitLab 组 作为例子,注册单个项目的步骤也类似,详细请参阅 https://docs.gitlab.com/runner/configuration/ 文档。先进入到 GitLab 的 组 的 Runner 配置界面里面,点击 New group runner 按钮

点击之后,进入配置界面。由于这是一个特殊的构建方式,我推荐写上 gitlab 的 runner tag 项,我这里写的是 debian-dotnet-docker 标记。这里的标记需要和 git 的 tag 区分哦,这是两个完全不相同的东西

点击 Create runner 按钮,即可进入到创建配置命令界面,拷贝其配置命令参数,如我这里的是

gitlab-runner register--url https://gitlab.lindexi.com--token glrt-HbCpfssbPSFqR_xVtxLX

于是我在运行起来的 docker 命令行里面输入以下命令用于注册

/usr/share/gitlab/gitlab-runner register --url https://gitlab.lindexi.com --token glrt-HbCpfssbPSFqR_xVtxLX

输入之后,一路都是回车下一步,除了执行命令部分可选使用 shell 之外。完成之后再使用 /usr/share/gitlab/gitlab-runner run 命令运行起来试试,如果能够运行成功,且在 gitlab 的 runner 页面里面能够看到运行起来的 runner 则证明成功。否则还请自行调试哈,我也不熟悉

完成之后即可愉快退出 docker 环境,此时即可在挂载到 /etc/gitlab-runner 的文件夹里面,即本文的 C:\lindexi\wsl 文件夹里面看到配置文件,一般是 config.toml 文件

在上述步骤完成之后,咱可以取出来挂载的文件夹,如我这里的 C:\lindexi\wsl 文件夹,将其拷贝到 Dockerfile 文件所在的文件夹里面,用于编写 Dockerfile 拷贝到 /etc/gitlab-runner 文件夹里面,如此制作出来的 docker image 将会带上已经注册的 gitlab runner 信息

COPY wsl /etc/gitlab-runner

接着再执行安装命令,以及设置入口为 gitlab-runner run 即可

RUN /usr/share/gitlab/gitlab-runner install --user=root --working-directory=/root/.local/share/gitlabENTRYPOINT ["/usr/share/gitlab/gitlab-runner", "run"]

完成以后的 Dockerfile 文件如下

FROM debian:buster-slim
WORKDIR /rootRUN rm /etc/apt/sources.list
COPY sources.list /etc/apt/sources.list
RUN apt-get updateRUN dpkg --add-architecture arm64
RUN apt updateRUN apt-get install libicu-dev -y
RUN apt-get install libssl-dev -y
RUN apt-get install wget -y
RUN apt-get install clang llvm -y
RUN apt-get install gcc-aarch64-linux-gnu -y
RUN apt-get install binutils-aarch64-linux-gnu -y
RUN apt-get install zlib1g-dev -y
RUN apt-get install zlib1g-dev:arm64 -yRUN apt-get install vim -y
RUN apt-get install git -yRUN apt-get cleanADD dotnet-sdk-6.0.421-linux-x64.tar.gz ./dotnet
ADD dotnet-sdk-8.0.204-linux-x64.tar.gz ./dotnet
ENV DOTNET_ROOT="/root/dotnet"
ENV PATH="${PATH}:${DOTNET_ROOT}:${DOTNET_ROOT}/tools"
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
RUN ln -s /root/dotnet/dotnet /usr/bin/dotnetCOPY gitlab-runner-linux-amd64 /usr/share/gitlab/gitlab-runnerRUN chmod +x /usr/share/gitlab/gitlab-runnerCOPY wsl /etc/gitlab-runnerRUN /usr/share/gitlab/gitlab-runner install --user=root --working-directory=/root/.local/share/gitlabENTRYPOINT ["/usr/share/gitlab/gitlab-runner", "run"]RUN mkdir /root/build
WORKDIR /root/build

尝试构建此 Dockerfile 文件

// 先 cd 到 Dockerfile 所在的文件夹,再执行以下命令
podman build -t t1 .

接着挂载到后台运行

podman container run -v nuget_global:/root/.nuget/packages -v nuget_cache:/root/.local/share/NuGet -v gitlabrunner:/root/.local/share/gitlab -d t1

以上命令的 -v nuget_global:/root/.nuget/packages -v nuget_cache:/root/.local/share/NuGet -v gitlabrunner:/root/.local/share/gitlab 属于可选的参数,用来挂载 nuget 缓存等内容,解决 docker 每次重启都会丢失缓存文件,提升重启 docker 之后的构建速度,减少重复拉取 nuget 包

完成以上步骤之后,就已经完成了制作一个能构建 dotnet AOT 的 gitlab ruuner 的 Debian docker 镜像

可以尝试在自己的项目里面,编写 .gitlab-ci.yml 文件,指定到这个运行起来的 docker image 上运行,以下是我的测试使用的 .gitlab-ci.yml 文件代码

stages:- buildBuildLinuxX64InDocker:stage: buildscript:- 'dotnet run publish -p:PublishAot=true -c Release -r linux-x64'tags:- docker-uosBuildLinuxArm64InDocker:stage: buildscript:- 'dotnet run publish -p:PublishAot=true -c Release -r linux-arm64'tags:- docker-uos 

如果能够构建成功,且构建出 linux-x64 和 linux-arm64 的 dotnet 可执行文件,则表示成功。否则还请自行根据输出的错误信息修复

踩坑记录

为什么不在 WSL 里面构建

核心原因是 WSL 里面的 glibc 版本过于新,使用 ldd --version 命令可以看到的输出如下

ldd (GNU libc) 2.36

而麒麟的 Desktop-V10-SP1 版本的 glibc 是 2.31 版本,更惨的 UOS 20.1050.11068.102 版本的 glibc 是 2.28 版本,都低于 WSL 里面的版本

这就意味着在 WSL 里面构建出来的应用将无法在以上的两个系统上运行

这就是为什么使用 debian:buster-slim 的原因。当前我拉取的 debian:buster-slim 的 docker image id 是 6d0d34a48ee1 的版本。通过 cat /etc/debian_version 可以看到在此版本里面带的是 debian 10.13 版本

再通过 ldd --version 命令行获取的 glibc 版本信息,可以看到带的是 2.28 版本,刚好与 UOS 20.1050.11068.102 版本的 glibc 版本相同,低于麒麟的 Desktop-V10-SP1 的 glibc 版本

因此在此 debian:buster-slim 里面 AOT 构建出来的包可以同时在 UOS 20.1050.11068.102 和麒麟的 Desktop-V10-SP1 版本运行

debian buster-backports Release does not have a Release file

开始国内源使用了阿里的,结果遇到以下错误内容

E: The repository 'http://mirrors.aliyun.com/debian buster-backports Release' does not have a Release file.
Error: building at STEP "RUN apt update": while running runtime: exit status 100

重新参考了 替换docker容器默认的debian镜像 - OrcHome 博客,结果依然配置失败。核心原因是配置的版本不正确

我当前使用的是 debian 是 10.13 版本,需要根据 debian镜像_debian下载地址_debian安装教程-阿里巴巴开源镜像站 教程文档,更新对应的 debian 10.x (buster) 的配置

我是如何知道 debian 版本的,我通过运行镜像,输入 cat /etc/debian_version 命令获取到版本

No system certificates available

完成配置阿里的源,遇到以下的错误内容

W: https://mirrors.aliyun.com/debian/dists/buster/InRelease: No system certificates available. Try installing ca-certificates.

原因是 ca-certificates 没有提前安装,可以在切换为国内源之前,安装好。安装方法可参阅 修复 Debian 安装 dotnet 失败 depends on ca-certificates

由于我这里不需要关注安全性问题,更简单的方法是将 https 全部更换为 http 即可

安装 dotnet tool 失败

执行任何的 dotnet tool install 都会提示如下错误

Unhandled exception: System.IO.FileNotFoundException: Unable to find the specified file.at Interop.Sys.GetCwdHelper(Byte* ptr, Int32 bufferSize)at Interop.Sys.GetCwd()at Microsoft.DotNet.Cli.ToolPackage.ToolPackageDownloader..ctor(IToolPackageStore store, String runtimeJsonPathForTests)at Microsoft.DotNet.ToolPackage.ToolPackageFactory.CreateToolPackageStoresAndDownloader(Nullable`1 nonGlobalLocation, IEnumerable`1 additionalRestoreArguments)at Microsoft.DotNet.Tools.Tool.Update.ToolUpdateLocalCommand..ctor(ParseResult parseResult, IToolPackageDownloader toolPackageDownloader, IToolManifestFinder toolManifestFinder, IToolManifestEditor toolManifestEditor, ILocalToolsResolverCache localToolsResolverCache, IReporter reporter)at Microsoft.DotNet.Tools.Tool.Update.ToolUpdateCommand..ctor(ParseResult result, IReporter reporter, ToolUpdateGlobalOrToolPathCommand toolUpdateGlobalOrToolPathCommand, ToolUpdateLocalCommand toolUpdateLocalCommand)at Microsoft.DotNet.Cli.ToolUpdateCommandParser.<>c.<ConstructCommand>b__14_0(ParseResult parseResult)at System.CommandLine.Invocation.InvocationPipeline.Invoke(ParseResult parseResult)at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient)

暂时没有找到可用方法,只能绕路

我在 windows 下将所需工具下载下来,然后通过拷贝进入的方式即可完全安装

当然,在本文例子里面,我没有加上我所使用的工具

在 gitlab 构建脚本找不到 dotnet 命令

在命令行里面,可以使用 dotnet 命令,但是在 .gitlab-ci.yml 文件里面编写的脚本找不到 dotnet 命令

加上如下配置到 Dockerfile 即可

RUN ln -s /root/dotnet/dotnet /usr/bin/dotnet

以上命令是对 dotnet 建立链接,如此即可让全局可以使用 dotnet 命令

为什么使用 podman 工具

原因是在 windows 下的 docker desktop 是收费的,于是我用平替的 podman 工具

还原速度过慢

由于 docker 本身是不带持久化存储文件,只有通过挂载本机存储的方式,才能让 docker 里面的文件持久化存放

还原速度过慢的问题,是因为初始化时没有任何的 NuGet 缓存,导致需要大量拉取,从而导致拉取过慢

根据 How to manage the global packages, cache, temp folders in NuGet - Microsoft Learn 官方文档说明,获取到默认的缓存路径,使用如下命令将缓存路径挂载到本机

-v nuget_global:/root/.nuget/packages -v nuget_cache:/root/.local/share/NuGet

我这里挂载写的是相对路径,如 nuget_global 等路径,相对路径在 podman 下将会存放到 wsl 里面,详细请看 在 windows 上运行的 podman 默认的挂载相对路径是什么

为什么代码仓库路径不挂载

如上述还原速度过慢原因,由于 docker 本身是不带持久化存储文件,只有通过挂载本机存储的方式,才能让 docker 里面的文件持久化存放。有些伙伴认为将代码仓库路径也进行本机挂载,可以减少拉取代码仓库的时间。实际上这么做可能带来的后果是开启多 docker 容器时,出现构建过程中的相互影响问题

拉取代码仓库时,大部分时间都是拉取内网的,且只影响容器的重启后的首次拉取。因此挂在代码仓库不是必要的

挂载代码仓库可能受到 Windows 自带杀毒影响,导致 llvm-objcopy 这一步失败,大概的错误信息如下

llvm-objcopy: failed to open xx.dbg Input/output error./root/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.4/build/Microsoft.NETCore.Native.targets(379,5): error MSB3073: The command ""llvm-objcopy" --only-keep-debug xx xx exited with code 1.

解决方法是要么不挂载,要么在 Windows 自带杀毒加白名单

如何使用交叉编译

由于我缺少 ARM64 的机器,或者准确来说我缺少一台可以撑住构建的有性能的 ARM64 的机器,我期望能够在原有的 linux-x64 机器上构建出 ARM64 的应用。于是我就需要使用到交叉编译技术,通过此技术我就可以在 linux-x64 的机器上构建出 linux-arm64 的应用

参考 Cross-compilation - .NET - Microsoft Learn 的文档安装上必要的负载,如下面的 docker 代码,即可在 debian 的 x64 系统上构建出 ARM64 的 dotnet 的 AOT 应用

RUN dpkg --add-architecture arm64
RUN apt updateRUN apt-get install libicu-dev -y
RUN apt-get install libssl-dev -y
RUN apt-get install wget -y
RUN apt-get install clang llvm -y
RUN apt-get install gcc-aarch64-linux-gnu -y
RUN apt-get install binutils-aarch64-linux-gnu -y
RUN apt-get install zlib1g-dev -y
RUN apt-get install zlib1g-dev:arm64 -y

在进行 dotnet 发布时,将在 dotnet 里面自动根据 -r 参数自动执行交叉编译,如下面命令

dotnet publish -p:PublishAot=true -c Release -r linux-arm64

相关文档请看 runtime/docs/workflow/building/libraries/cross-building.md at main · dotnet/runtime 和 runtime/docs/workflow/building/coreclr/cross-building.md at main · dotnet/runtime

为什么不使用 gitlab-runner start 命令

因为实际测试 gitlab-runner start 之后没有真的执行,如下面代码

RUN /usr/share/gitlab/gitlab-runner start

添加之后运行 docker image 也不会有 gitlab runner 上线

如果换成下面的代码,则启动 docker image 之后立刻退出

ENTRYPOINT ["/usr/share/gitlab/gitlab-runner", "start"]

实际测试只有以下代码符合预期

ENTRYPOINT ["/usr/share/gitlab/gitlab-runner", "run"]

找不到 runner 机器或找错

先调查是否 dotnet 配置 Gitlab 的 CI 找不到 Runner 或找错的可能原因 提及的问题

排除之后,记得查看是否带上了 tags 和 runner 在 gitlab 上配置正确且相同的

参考文档

  • https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/compiling.md#cross-architecture-compilation
  • dotnet publish command - .NET CLI - Microsoft Learn
  • .NET Runtime Identifier (RID) catalog - .NET - Microsoft Learn
  • Cross-compilation - .NET - Microsoft Learn
  • Prepare .NET libraries for trimming - .NET - Microsoft Learn
  • How to manage the global packages, cache, temp folders in NuGet - Microsoft Learn
  • https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0
  • Troubleshooting GitLab Runner - GitLab
  • docker Debian Buster 国内常用镜像源
  • docker - What is the difference between CMD and ENTRYPOINT in a Dockerfile? - Stack Overflow
  • How to fix 'buster-backports' no longer has a Release file - nixCraft
  • 替换docker容器默认的debian镜像 - OrcHome
  • debian镜像_debian下载地址_debian安装教程-阿里巴巴开源镜像站
  • Configure GitLab Runner - GitLab
  • Install GitLab Runner - GitLab
  • Gitlab runner setup with podman - GitLab CI/CD - GitLab Forum
  • Registering runners - GitLab
  • Install GitLab Runner manually on GNU/Linux - GitLab
  • GitLab Runner bleeding edge releases - GitLab
  • NuGet pack and restore as MSBuild targets - Microsoft Learn
  • /usr/bin/dotnet: 没有那个文件或目录-CSDN博客
  • 在 Linux ARM 系统上安装 .Net - 快乐就在你的心 的个人博客
  • dotnet 配置 Gitlab 的 Runner 做 CI 自动构建
  • dotnet 配置 Gitlab 的 CI 找不到 Runner 或找错的可能原因

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ulsteruni.cn/article/17185348.html

如若内容造成侵权/违法违规/事实不符,请联系编程大学网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

Python高阶--闭包

闭包作用:可以用来在一个函数与一组私有变量之间创建关联关系,在给定函数被多次调用的过程中,这些私有变量能够保持其持久性(保存运行环境与变量的状态) 闭包的特征:1.必须要有函数的嵌套,而且外层函数必须返回内层函数。外层函数相当于给内层函数提供了一个包装起来的运…

U9网页提示Base-64字符数组或字符串的长度无效

GoogleChrome打开U9网页突然出现错误 Base-64 字符数组或字符串的长度无效。 说明:Base-64 字符数组或字符串的长度无效。Page URL: /U9/mvc/login/index?ReturnUrl=%2fU9%2fmvc%2fmain%2findex 堆栈信息在 System.Convert.FromBase64_Decode(Char* startInputPtr, Int32 inp…

leetcode(力扣) 2866. 美丽塔 II

原题链接 暴力做法 (时间复杂度 O(n^2)) 每次选取下标 i 为峰值, 进行 n 次,对每次取max就可以找打答案对于 i 左边的序列: 需要满足序列是非递减的, 同时每个值尽可能大 所以满足: 下标为 j 的位置上的数 <= 下标是 (j, i] 的最小的值 (等于时取得最大值) , 同时需要保证…

日志分析-apache日志分析

简介 账号密码 root apacherizhi ssh root@IP 1、提交当天访问次数最多的IP,即黑客IP: 2、黑客使用的浏览器指纹是什么,提交指纹的md5: 3、查看index.php页面被访问的次数,提交次数: 4、查看黑客IP访问了多少次,提交次数: 5、查看2023年8月03日8时这一个小时内有多少IP…

Python多线程编程深度探索:从入门到实战

title: Python多线程编程深度探索:从入门到实战 date: 2024/4/28 18:57:17 updated: 2024/4/28 18:57:17 categories:后端开发tags:多线程 并发编程 线程安全 Python 异步IO 性能优化 实战项目第1章:Python基础知识与多线程概念 Python简介: Python是一种高级、通用、解释型…

U盘、硬盘泄密无处不在,如何锁紧企业数据大门?

在当今信息化的时代,数据泄露的问题尤为严重。特别是U盘、硬盘等移动储存设备,更是数据泄露的重灾区。那么,如何锁紧企业的数据大门呢?我们需要认识到信息安全就是一种生产要素,没有安全就没有生产。企业数据的安全性直接关系到企业的稳定和发展。也就是说,没有安全事故并…