Dockerfile编写实践篇
Docker通过一种打包和分发的软件,完成传统容器的封装。这个用来充当容器分发角色的组件被称为镜像。Docker镜像是一个容器中运行程序的所有文件的捆绑快照。当使用Docker分发软件,其实就是分发这些镜像,并在接收的机器上创建容器。镜像在Docker生态系统中是可交付的基本单位。
镜像的创建方式有很多种,如使用docker commit命令根据当前容器的更改创建一个新的镜像,也可以使用docker save保存镜像或使用docker export导出镜像。但是,最常用、最推荐的方式,还是使用Dockerfile定义镜像,然后使用docker build命令创建镜像。
Dockerfile使用基本的基于DSL语法的指令来定义一个Docker镜像,之后就可以使用docker build命令基于该Dockerfile中的指令构建一个新的镜像。Dockerfile具有信息表达性,且易于理解,这些要归功于Dockerfile支持注释的简洁语法。Dockerfile构建程序自身使用缓存技术来解决快速开发和迭代带来的问题。这个构建过程可追踪且可重用。它们能够很简单地和现有的构建系统、持续构建和集成工具一起工作。
Dockerfile编写及使用
Dockerfile是一个文件,它由构建镜像的指令组成,用户可以使用Dockerfile来快速创建自定义的镜像。指令由Docker镜像构建者自上而下排列,能够被用来修改镜像的任何信息。
Dockerfile具有信息表达性,且易于理解,这些都要归功于Dockerfile支持注释的简洁语法。开发者可以使用任何版本控制工具来跟踪Dockerfile文件的变动。维护多个版本的镜像就和管理多个Dockerfile一样简单。
基本结构
Dockerfile由一系列指令和其参数组成,并且支持以#开头的注释行。每条指令都建议使用大写字母,且后面跟随一个参数。Dockerfile中指令的基本使用可以参考笔者Dockerfile指令大全一文,更详细的指令使用说明建议参考官网Dockerfile reference一文。
Dockerfile中的指令会按顺序从上到下执行,所以应根据需要合理安排指令的顺序。Docker大体上按照如下流程执行Dockerfile中的指令:
(1) Docker从基础镜像运行一个容器。
(2) 执行一条指令,对容器做出修改。
(3) 执行类似docker commit的操作指令,提交一个新的镜像层。
(4) Docker再基于刚提交的镜像运行一个新容器。
(5) 执行Dockerfile中的下一条指令,直到所有指令都执行完毕。
如果Dockerfile由于某些原因(如指令执行失败)没有正常结束,那么仍将得到一个可用的镜像,只是最后一条指令执行失败,这对日常的开发和调试是有帮助的。
一般而言, Dockerfile 主体内容分为四部分:基础镜像信息、制作者信息、镜像操作指令和容器启动时执行指令。但是,考虑到容器安全,还有必要对其进行安全加固,如配置用户或用户组等。此外,构建基础镜像时,有时还需要考虑继承的镜像需要执行的一些默认操作,如创建一组默认的用户和用户组供子镜像使用。
基础镜像信息
在编写Dockerfile文件时,第一个要考虑的事情就是创建一个基础镜像供他人使用,或是基于基础镜像去构建一个新的镜像。这些诉求都可以通过FROM指令实现。一般情况下,FROM指令是一个Dockerfile的第一条指令。
FROM指令用来指定一个父镜像,以开始新的构建阶段。Dockerfile支持在一个文件中使用多个FROM指令,以创建多个镜像。FROM的指令格式如下:
FROM [–platform=] [:|@|-] [AS ]
其中,–platform参数用来指定镜像应用的平台,主要应用于多平台场景,如linux/amd64或linux/arm64等;tag和digest用来指定需要引用的镜像的tag,如果不指定,则使用latest。如果一个Dockerfile中需要创建多个镜像,会使用到多个FROM。如果下一个FROM指令中需要使用上一个FROM指令构建的镜像,可以现在上一个FROM指令中定义别名,也即使用AS 。
某些场景下,不需要父镜像,如构建一个操作系统镜像,这时可以使用"FROM scratch"这个指令来表示不需要父镜像。
制作者信息
对于一个镜像,无论是镜像开发者还是镜像的继承者,有时需要直到这个镜像的作者相关的信息。对于这个需求,可以通过MAINTAINER指令或LABEL指令来指定镜像作者信息。这里不推荐使用MAINTAINER指令,更推荐LABEL指令。相比MAINTAINER指令,LABEL指令会将这部分信息保存到元数据,这个就可以通过docker image inspect命令方便的查询该部分信息。示例如下:
$ docker image inspect --format='{{json .Config.Labels}}' target-image-name-or-id
使用LABEL记录镜像制作者信息的示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# Maintainer: docker user <docker_user@email.com>
LABEL maintainer docker_user<docker_user@email.com>...
上述示例中,首先使用FROM指令指明所基础镜像的名称,接下来就是使用LABEL指令说明制作者信息。这里,制作者信息并不是一个必须的信息,但建议都添加上。注意,这个Dockerfile只是编写了一部分,还不算一个完整的Dockerfile。
镜像操作指令
Dockerfile文件的主体部分就是基于基础镜像的进一步操作,如使用RUN指令执行特定的命令,使用ADD/COPY指令将特定的产物复制到容器文件系统,等等。如这里使用RUN指令下载软件包,然后将构建上下文中的基于Java源码生成的JAR包复制到容器文件系统的示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# Maintainer: docker user <docker_user@email.com>
LABEL maintainer docker_user<docker_user@email.com>RUN apt-get update \
&& apt-get install -y libsnappy-dev \
&& rm -rf /var/cache/apt...
上述示例中,使用RUN指令下载特定的软件包,并在下载完毕后,删除了遗留的缓存。每运行一条RUN指令,镜像添加新的一层,所以这里是将在多个RUN指令执行的命令合并成了一行。同样的,到这个阶段,Dockerfile也只是编写了一部分,还不算一个完整的Dockerfile。
容器启动时执行指令
容器启动时,可以指定默认执行的命令或指定默认的可执行文件。这对于提供后端服务的应用来说很有必要。如需要在容器中运行一个Java Web应用,可以在容器启动时指定可执行的shell脚本,以运行一个Java应用。对于这个需求,可以通过CMD指令或ENTRYPOINT指令来实现。
CMD指令和ENTRYPOINT指令均用来定义启动容器时需要执行的命令,推荐优先使用ENTRYPOINT指令。对于同时出现CMD指令和ENTRYPOINT指令的场景,遵循如何规则:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
上表中,对同时存在CMD指令和ENTRYPOINT指令的场景,要根据指令使用的exec模式或shell模式,进行如下划分:如果ENTRYPOINT指令是shell模式,则会忽略CMD指令;如果ENTRYPOINT指令是exec模式,CMD指令是exec模式,则CMD指令的命令失效,参数会追加到ENTRYPOINT指令上;如果ENTRYPOINT指令是exec模式,CMD指令是shell模式,则先执行ENTRYPOINT指令,再执行CMD指令。
使用示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# Maintainer: docker user <docker_user@email.com>
LABEL maintainer docker_user<docker_user@email.com># COPY and rename application jar file to the container's filesystem
COPY app-*.jar /app.jar# EXECUTE jar file
ENTRYPOINT ["java", "-jar", "/app.jar"]
上述示例中,使用COPY指令将构建上下文中的软件包复制到容器的文件系统。然后,使用ENTRYPOINT声明容器启动时需要执行的命令。这样当容器启动时,就可以运行JAR包,从而启动这个应用。到这个阶段,一个基本的Dockerfile文件就编写完了。
用户权限加固
默认情况下,容器以root用户运行,root用户权限太高,对于业务应用来说,存在安全风险,如常见的容器逃离手段,都是依赖于获得容器的root权限。对于用户权限分配来说,最佳实践就是尽可能地消减用户的特权,也即遵循最小权限原则。当一个Docker用户创建容器时,是能够覆盖镜像的默认配置的。因此,并不存在某个方法来完全防止容器以root用户运行。但是,镜像的制作者可以创建非root用户,并以非root用户来执行应用。
Dockerfile通过提供USER指令、docker run指令或docker create指令来设置用户和用户组,从而限制用户的访问。推荐使用USER指令设置用户和用户组,使用docker run命令后docker create指令来更改设置用户和用户组。这里重点介绍下使用USER指令设置用户和用户组。示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# Maintainer: docker user <docker_user@email.com>
LABEL maintainer docker_user<docker_user@email.com># Creat application directory
RUN mkdir -p /opt/app# COPY and rename application jar file and set new owner and mode to the container's filesystem
COPY --chown=1000:1000 --chmod=644 app-*.jar /opt/app/app.jar# SET new user id and group id
USER 1000:1000# EXECUTE jar file
ENTRYPOINT ["java", "-jar", "/opt/app/app.jar"]
这里,要注意把握设置用户UID和GID的时机。如果过早地设置,可能导致当前用户没有权限完成Dockerfile中的其他指令。如需要执行一个root目录下的文件,但是先使用USER指令设置了用户UID和GID,则会导致当前用户权限过低,导致root目录下的文件执行失败。示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# SET new user id and group id
USER 1000:1000# Creat application directory
RUN mkdir -p /opt/app
因为/opt目录属于root用户,所以不能在UID是1000的用户中在/opt目录创建新目录。
注意,如果是编写一个基础镜像的Dockerfile文件,则不建议遵循最小权限原则,而是尽量提供root权限,让业务镜像去考虑最小权限的问题。
注入下游镜像在构建时发生的操作
在构建基础镜像时,有时需要考虑继承的镜像需要执行的一些默认操作,如创建一组默认的用户和用户组供子镜像使用。对于这个需求,可以通过ONBUILD指令实现。ONBUILD指令不会在包含它们的Dockerfile被构建时执行。这些指令会被记录在生成镜像的元数据ContainerConfig.OnBuild下。这个元数据会一直被保留,直到生成的镜像被另外的Dockerfile作为基础镜像。这样,当子镜像构建时,ONBUILD后跟随的指令将会在FROM指令后,下一条指令前被执行。示例如下:
# Base image to use, this must be set as the first line
FROM ubuntu:18.04# Maintainer: docker user <docker_user@email.com>
LABEL maintainer docker_user<docker_user@email.com># ONBUILD command
ONBUILD RUN echo "This is an ONBUILD trigger." # 其他的设置或命令
...
编写Dockerfile文件
介绍完了编写Dockerfile的基本结构,接下来就根据真实的业务场景,介绍下如何编写特定于业务场景的Dockerfile文件。由于笔者目前主要参与的是Java Web后端应用的开发,所以这里重点介绍下一个商用Java Web后端应用的Dockerfile的编写示例,其他场景的Dockerfile编写示例还请自行学习。
首先是基础镜像的选择。对于一个业务服务来说,为了保证业务服务不依赖内核的操作系统,在基础镜像的选择上,可以使用一个简化版本的操作系统。这个操作系统是一个精简版本,只包含操作系统自带的一些功能。一些软件包的安装,如python、jdk等,均留给业务镜像去按需补充。接着是JRE的版本选择。注意,在Java应用运行时,只有JRE即可。目前,主流的Java版本还是Java 8和Java 11。不同公司根据业务需要选择合适的JDK版本。对于基于Java的业务服务来说,可以使用精简操作系统+JRE构成的基础镜像。
Java Web后端应用依赖的基础镜像已经选择完毕,接下来就是把Java Web后端应用依赖的工具安装到操作系统。对于不同的Java应用,可能会使用到一些实用工具。如对网络服务来说,可能使用nmap来进行网络发现。
考虑完Java Web后端应用依赖的工具,下面要考虑的就是Java Web后端应用的lib的依赖。使用Maven进行打包时,默认情况下不会将依赖包打入jar中。但是,可以通过配置Maven插件来将依赖包和项目本身打包到一个jar文件中。这样,无论是二方库,还是三方库,这部分依赖都会作为Java Web后端应用构建产物的一部分。对于基于Spring Boot构建的Java Web后端应用来说,其依赖会自动打包到jar文件中,无需特殊处理。
对于Java Web后端应用来说,容器启动后,需要启动一个进程在运行Java Web后端应用。这个应用会一直活跃,直到容器意外中止或容器正常终止为止。也即是说,容器启动后,要立即执行Java Web后端应用。为了聚合启动Java Web后端应用相关的操作,使用shell脚本来编写启动相关的命令。这样,在容器启动时,只需要执行下shell脚本即可。
编写一个Java Web后端应用的主要事项都已考虑完毕,接下来简单描述下构建上下文的目录结构:
--docker # docker目录,存储docker镜像制作相关资源
----package # package目录,存储业务应用打包相关资源
------start.sh # start.sh,用来启动业务服务
------service.jar # service.jar,业务服务代码构建的jar
-- app.dockerfile # app.dockerfile,业务服务的Dockerfile,描述构建镜像的过程
介绍完构建上下文的目录结构,接下来说明下app.dockerfile文件中的内容:
# 使用centos操作系统 + jre(Java 1.8)的基础镜像,镜像仓库并不包含该镜像,可以基于centos镜像制作一个包含jre的镜像
FROM centos_jre_1.8:centos8_jar8# 记录镜像的制作者,方便后期的运维
LABEL maintainer custom_java_web<custom_java_web@email.com># jar包和运行jar包的shell脚本等复制到容器中
## 将构建上下文的package目录下的内容拷贝到/opt/app目录下,注意这里并没有预先在容器的文件系统创建/opt/app/目录
COPY package /opt/app/
## 对于可执行文件,在使用前,赋予可执行权限
RUN chmod 544 /opt/app/start.sh \
&& chmod 544 /opt/app/start.jar# 设置ENTRYPOINT来直接执行脚本
ENTRYPOINT ["/opt/app/start.sh"]
这样,一个Java Web应用的Dockerfile文件就编写完毕了。考虑到安全问题,还有必要调整下用户,使用USER指令改造后的Dockerfile文件如下:
# 使用centos操作系统 + jre(Java 1.8)的基础镜像,镜像仓库并不包含该镜像,可以基于centos镜像制作一个包含jre的镜像
FROM centos_jre_1.8:centos8_jar8# 记录镜像的制作者,方便后期的运维
LABEL maintainer custom_java_web<custom_java_web@email.com># jar包和运行jar包的shell脚本等复制到容器中
## 将构建上下文的package目录下的内容拷贝到/opt/app目录下,注意这里并没有预先在容器的文件系统创建/opt/app/目录
COPY package /opt/app/
## 对于可执行文件,在使用前,赋予可执行权限
RUN chown -R 1000:1000 /opt/app \
&& chmod 644 /opt/app \
&& chmod 544 /opt/app/start.sh \
&& chmod 544 /opt/app/start.jar# 设置用户及用户组
USER 1000:1000# 设置ENTRYPOINT来直接执行脚本
ENTRYPOINT ["/opt/app/start.sh"]
在ENTRYPOINT指令中,使用exec格式执行可执行文件时,不会启动一个shell进程,docker会直接运行可执行文件,这里是start.sh。需要注意的是,这里的start.sh脚本的第一行应该是#!/bin/sh或者其它希望用来执行脚本的解释器路径。start.sh脚本的示例如下:
#!/bin/bash# 指定JAR包的路径
JAR_PATH="/opt/app/service.jar"# 使用nohup在后台启动JAR包,并将输出重定向到日志文件
nohup java -jar "$JAR_PATH" &
使用docker build命令生成镜像
编写完Dockerfile文件后,接下来就是使用docker build命令构建镜像。docker build命令的基本格式如下:
docker image build [OPTIONS] PATH | URL | -
该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有数据作为构建上下文(Context)发送给 Docker服务器端。Docker服务端在校验Dockerfile格式通过后,逐条执行其中定义的指令,如果碰到ADD、COPY和RUN指令则会生成一层新的镜像。最终如果创建镜像成功,会返回最终镜像的ID。
在构建过程中会有新层被加入到要产生的镜像中。这不仅意味着开发者能够从任意一步开始创建分支,更重要的是构建过程能够缓存每一步的结果,当运行完几个指令后,如果下一条指令出现问题,构建过程能够在问题被修复后,从同一步重新启动。如果构建过程需要下载资源或包含某些需要消耗大量时间的任务,那么缓存可以起到节省时间的效果。如果需要从零开始构建,可以使用–no-cache选项来禁止缓存的使用。
将执行目录切换到上述目录结构的docker目录下,上述示例的dockerfile文件,就可以使用如下的docker build命令生成镜像:
$ docker build -f app.docerfile -t service-image:202403102100 .
在docker build命令执行完毕后,如果镜像制作者可以看见"Successfully built XXX"字样,则说明镜像构建成功。且会生成一个service-image的镜像,镜像的版本是202403102100。可以使用docker images查看该镜像,或使用docker start命令启动该镜像。
Dockerfile最佳实践
所谓Dockerfile最佳实践,就是从需求出发,来定制适合当前业务场景、高效方便的镜像。首先,要尽量吃透每个指令的含义和执行效果,多编写一些简单的例子进行测试,弄清楚了再撰写正式的Dockerfile文件。此外,Docker Hub官方仓库中提供了大量的优秀镜像和对应的Dockefile,可以通过阅读它们来学习如何撰写高效的Dockerfile。
这里梳理一些实践经验。建议读者在生成镜像过程中,尝试从如下角度进行思考,完善所生成镜像:
(1) 精简镜像用途:尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能的镜像。
(2) 选用合适的基础镜像:容器的核心是应用。选择过大的父镜像(如Ubuntu系统镜像)会造成最终生成应用镜像的膝肿,推荐选用瘦身过的应用镜像(如node:slim),或者较为小巧的系统镜像(如alpine、busybox或debian)。
(3) 提供必要的注释和维护者信息: Dockerfile也是一种代码,需要考虑方便后续的扩展和他人的使用。
(4) 正确使用版本号:使用明确的版本号信息,如1.0,2.0,而非依赖于默认的latest。通过版本号可以避免环境不一致导致的问题。
(5) 减少镜像层数:如果希望所生成镜像的层数尽量少,则要尽量合并RUN、ADD和COPY指令。通常情况下,多个RUN指令可以合并为一条RUN指令。
(6) 恰当使用多步骤创建:通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但这种方式需要维护多个Dockerfile。
(7) 使用.dockerignore文件:使用该文件可以标记在执行docker build时忽略的路径和文件,避免发送不必要的数据内容,从而加快整个镜像创建过程。
(8) 及时删除临时文件和缓存文件:特别是在执行 apt-get 指令后,/var/cache/apt下面会缓存了一些安装包。
(9) 提高生成速度:如合理使用 cache, 减少内容目录下的文件,或使用.dockerignore 文件指定等。
(10) 调整合理的指令顺序:在开启cache的情况下,内容不变的指令尽量放在前面,这样可以尽量复用。
(11) 减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复用而不出错。
参考
《Docker技术入门与实战》 杨保华 戴王剑 曹亚仑 著
《Docker实战》 Jeff Nickoloff 著, 胡震,杨润青 黄帅 译
https://yiyan.baidu.com/ 文心一言
https://docs.docker.com/reference/dockerfile/ Dockerfile reference
http://www.dockerinfo.net/3328.html 7 步精简 Docker 镜像
https://www.runoob.com/docker/docker-install-ubuntu.html Docker 安装 Ubuntu
https://blog.csdn.net/u014163312/article/details/127330574 Maven打包所有依赖到一个可执行jar中
相关文章:
Dockerfile编写实践篇
Docker通过一种打包和分发的软件,完成传统容器的封装。这个用来充当容器分发角色的组件被称为镜像。Docker镜像是一个容器中运行程序的所有文件的捆绑快照。当使用Docker分发软件,其实就是分发这些镜像,并在接收的机器上创建容器。镜像在Dock…...
BJFU|计算机网络缩写对照表
之前有过这个题型,但23年没考,所以按需准备 A ACK (ACKnowledgement) 确认 ADSL (Asymmetric Digital Subscriber Line) 非对称数字用户线 API (Applicatin Programming Interface) 应用编程接口 ARP (Address Resolution Protocol) 地址解析协议 ARQ (…...

Grafana dashboards as ConfigMaps
文章目录 1. 简介2. 创建 configmaps3. grafana 界面查看 1. 简介 将 Grafana 仪表板存储为 Kubernetes ConfigMap 相比传统的通过 Grafana 界面导入仪表板有以下一些主要优点: 版本控制: ConfigMap 可以存储在版本控制系统(如Git)中,便于跟踪和管理仪表板的变更历…...

【QA-SYSTEMS】CANTATA-解决Jenkins中build Cantata报错
【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 解决Jenkins中build Cantata测试项目报找不到license server的错误。 2、 问题场景 在Jenkins中build Cantata测试项目,报错“Failed to figure out the license server correctly”。 3、软硬件环…...

个人网站展示(静态)
大学期间做了一个个人博客网站,纯H5编码的网站,利用php搭建了一个留言模块。 有需要源码的同学,可以联系我~ 首页: IT杂记模块 文人墨客模块 劳有所获模块 生活日志模块 关于我 一个推崇全栈开发的前端开发人员 微信: itrzzh …...
C++——内存管理、模板
一、C内存管理 在C语言中我们曾学习过动态内存管理的相关知识,通过malloc、calloc、realloc和free等对堆上的空间进行申请和释放。在C中我们同样会面临类似的需求,因此C对动态开辟内存的方式进行了一些调整,我们可以使用new和delete操作符来对…...
商品上传上货搬家使用1688商品采集api接口
1688.item_get 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称(包括在请求地址中)[item_search,item_get,item_search_shop等]cacheString否[yes,no…...

redisson解决redis服务器的主从一致性问题
redisson解决redis的主节点和从节点一致性的问题。从而解决锁被错误获取的情况。 实际开发中我们会搭建多台redis服务器,但这些服务器分主次,主服务器负责处理写的操作(增删改),从服务器负责处理读的操作,…...
Vue-router
router的使用(52) 5个基础步骤: 1.在终端执行yarn add vue-router3.6.5,安装router插件 yarn add vue-router3.6.5 2.在文件的main.js中引入router插件 import VueRouter from vue-router 3.在main.js中安装注册Vue.use(Vue…...

白皮书发布|超融合运行 K8s 的场景、功能与优势
目前,不少企业都使用虚拟化/超融合运行 Kubernetes 和容器化应用。一些用户可能会有疑惑:既然 Kubernetes 可以部署在裸金属上,使用虚拟化不是“多此一举”吗? 在电子书《IT 基础架构团队的 Kubernetes 管理:从入门到…...
全局Ceph节点宕机处理
在极端情况下,如数据中心断电,造成 Ceph 存储集群全局宕机,可以按照本节所示流程进行 Ceph 集群上电恢复操作。 4.1 手动上电执行步骤 如为 Ceph 集群上电,monitor server 应最先上电;集群上电前确认使用 Ceph 之前端…...

电脑中缺失EMP.dll文件怎么办,解决EMP.dll丢失问题的有效方法分享
当你的电脑出现由于找不到emp.dll无法继续执行代码的提示,那你要怎么办呢?其实解决方法还是挺多的,今天就来给大家详细的说说emp.dll这方面的信息吧。 一、电脑为什么会出现emp.dll丢失 不完全卸载软件:在卸载程序时,…...

Linux 进程程序替换
💓博主CSDN主页:麻辣韭菜-CSDN博客💓 ⏩专栏分类:http://t.csdnimg.cn/G90eI⏪ 🚚代码仓库:Linux: Linux日常代码练习🚚 🌹关注我🫵带你学习更多Linux知识 🔝ǵ…...

系统分析与设计(一)
我们有这么多各式各样的工具,互联网给我们带来了这么多用户和数据,这是好事也有副作用。 世界上能访问用户数据,并根据数据做分析和改进的公司,大概Google是其中翘楚,这种 data-centric 的做法做过了头,也有悲剧发生: Douglas Bowman 曾经是Google 的视觉设计主管,2009年的一天…...

【QT】自定义控件的示例
自定义控件(很重要) 什么是自定义控件? 顾名思义就是创建一个窗口,放入多个控件,拼接起来,一起使用。 为什么需要它? 需求,假设有100个窗口,那如果有两个控件同时被使…...

Rust入门:Rust如何调用C静态库的函数
关于Rust调用C,因为接口比较复杂,貌似Rust不打算支持。而对于C函数,则相对支持较好。 如果要研究C/Rust相互关系的话,可以参考: https://docs.rs/cxx/latest/cxx/ Rust ❤️ C 这里只对调用C静态库做一个最简短的介…...

阿里云Linux系统MySQL8忘记密码修改密码
相关版本 操作系统:Alibaba Cloud Linux 3.2104 LTS 64位MySQL:mysql Ver 8.0.34 for Linux on x86_64 (Source distribution) MySQL版本可通过下方命令查询 mysql --version一、修改my.cnf文件 文件位置:etc/my.cnf进入远程连接后可以打…...
初识C语言—字符串、转义字符、注释
字符串 字符串就是一串字符 用英文双引号括起来的字符 int main() {"dasgfhjkasg\n""hello world!"return 0; } 字符串的结束标志是\0这个转义字符 如何证明呢? int main() {//字符数组 - 数组是一组相同类型的元素char arr[] "hel…...

洛谷 P1731 [NOI1999] 生日蛋糕
题目 题目链接 自己没看题解写的,摸石头过河,解释一下 首先,输入输出都是正整数。先搞定输入,再判断条件,如果无解,输出0,否则输出蛋糕外表面面积Q(这里用全局变量,开l…...

操作教程|使用MeterSphere对恒生UFX系统进行压力测试
恒生UFX(United Finance Exchange,统一金融交换)系统(以下简称为“UFX系统”),是一款帮助证券公司统一管理外部接入客户的系统,该系统整体上覆盖了期货、证券、基金、银行、信托、海外业务等各类…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...