优雅的 Dockerfile 是怎样炼成的?
Docker 简介
目前,Docker 主要有两个形态:Docker Desktop 和 Docker Engine。
Docker Desktop 是专门针对个人使用而设计的,支持 Mac(已支持arm架构的M系芯片) 和 Windows 快速安装,具有直观的图形界面,还集成了许多周边工具,甚至可以在docker engine 中直接创建出一个 kubernetes(kubernetes in docker方案,与 kind 类似),极大的方便个人用户使用。
不过,我并不推荐使用Docker Desktop,原因有两个:第一,Docker Desktop是商业化产品,有一些非通用的东西,不利于对容器相关技术的进一步学习。第二,Docker Desktop 只对个人学习免费,受条款限制不能商用,万一工作中使用可能会“踩到雷区”。
Docker Engine 恰恰和 Docker Desktop 相反,它完全免费,但目前还只能在 Linux 运行,只能使用命令行操作,缺乏辅助工具,需要用户自行 DIY 运行环境。实际上,Docker Engine 才是 Docker 真正的形态,也是核心功能,Docker Desktop 只也是在其基础之上封装而来的,以便为非IT用户提供了一个易于使用的界面和工具,以简化 Docker 的安装、配置和管理过程。但 Docker Engine 是现在各个公司在生产环境中实际使用的 Docker 产品,毕竟机房里 99% 的服务器运行的操作系统都是 Linux。
Docker 使用
Docker 安装
因为大多数用户主要使用 Linux 版本 Docker,这里也只列出 Linux 系统下 docker 的安装
目前 Linux 服务器市场主要有两类系统分庭抗礼,请按自己操作系统选择即可
- RHEL系,代表系统有:RHEL(Redhat Enterprise Linux)、Centos、Fedora
- Debian系,代表系统有:Debian、Ubuntu
# RHEL 系
yum install -y docker# Debian 系
apt install -y docker
安装完毕以后执行以下命令确实是否完成
# 输出 Docker 客户端和服务器各自的版本信息
docker version# 显示当前 Docker 系统相关的信息,如 CPU、内存、容器数量、镜像数量、容器运行时、存储文件系统等
docker info
使用 Docker 创建容器
docker run busybox echo hello world
启动 busybox 镜像,使用 echo 命令输出 hell word,这也正是 Solomon Hykes 在大会上所展示的最精彩的那部分。当使用 run 命令启动容器时,Docker Daemon 会先检查本地是存在镜像,若没有则从远端镜像仓库拉取,当然也可以使用 docker pull busybox
命令将用到的镜像提前下载到本地。
Docker 架构
下面的这张图来自 Docker 官网,精准地描述了 Docker Engine 的内部角色和工作流程:
刚才我所使用的 docker 命令实际是一个客户端 client,它的作用是与 Docker Engine 里的后台服务 Docker daemon 通信,并将我发起的各个指令(run、pull、build 等)转换为 Docker daemon 能够识别的命令并由 Docker daemon 真正执行相关任务。此外,镜像是存储在元旦仓库里,客户端无法直接访问镜像仓库,只能有 Docker daemon 访问。
因此,在 Docker Engine 里,真正干活的是默默运行在后台的 Docker daemon(是不是像极了在公司的你)。
容器化应用
在上面的例子中,我们运行的容器,显然不是从零开始的,而是要先拉取一个“镜像”(image),然后从这个“镜像”来启动容器。那么,“镜像”和“容器”到底是什么,两者又有什么关系呢?
在其他场合中我们也遇到过“镜像”一词,比如:光盘镜像、重装电脑时的硬盘镜像、使用 VMWare 时的虚拟机系统镜像。说到这里,有没有发现他们的一些相同点?是的,这些“镜像”都是只读的,不允许修改,它们是以标准格式存储了一系列的文件,只在我们需要的时候从中提取数据运行起来,而运行起来的这个东西可理解为“容器”。
容器是由操作系统动态创建,因此必然可以将它的初始化环境固定下来,保存成一个静态文件,这样就可以方便存放、传输、版本化管理了。
可以将镜像比喻为“样板间”,它把运行进程所需要的文件系统、依赖库、环境变量、启动参数等信息打包整合到一起,之后镜像文件无论放在哪里,操作系统都能根据这个“样板间”快速重建容器。
从功能上来看,镜像和常见的rpm、deb 等安装包一样,都打包了应用程序,但最大的不同点在于它里面不仅有应用程序的可执行文件,还有应用程序运行时的整个系统环境,开发者可以在 CentOS 系统上开发,然后打包成镜像,再去 Ubuntu 系统上运行,完全不需要考虑环境依赖的问题。
此外,镜像的完整名字由两个部分组成,名字和标签,中间用 : 连接起来。名字代表镜像仓库地址,标签也就是 tag 代表版本号。上面我们执行 docker run busybox ...
命令时没有指定标签,则 默认使用 latest
作为标签。
(base) 🐳 ➜ ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest a416a98b71e2 3 months ago 4.26MB
常用容器操作
上面我们只介绍了 docker run
和 docker pull
命令,下面列举几个工作中常用的命令
########### 镜像相关 ###########
# 查看镜像
docker images
# 删除镜像
docker rmi busybox:latest########### 容器相关 ###########
# 运行容器
## -id:后台运行(执行命令后终端依旧是)
## --name:设置容器名
## bash:容器中运行的命令
docker run -id --name busybox busybox:latest bash
## -it:开启一个交互式操作的 Shell,直接进入容器内部
docker run -it --name busybox busybox:latest bash
# 查看容器(正在运行)
docker ps
# 查看容器(全部容器,包含正在运行和已退出)
docker ps -a
# 进容器并执行 bash 命令
docker exec -it busybox bash
# 停止容器
docker stop busybox
# 启动容器
docker start busybox
# 删除容器
docker rm busybox
容器镜像详解
镜像内部并不是一个平坦的结构,而是由许多的镜像层组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,然后多个层像搭积木一样堆叠起来,再使用一种叫“Union FS 联合文件系统”的技术把它们合并在一起,就形成了容器最终看到的文件系统。
我们可以使用 docker inspect busybox:latest
查看 busybox 镜像的分层信息:
通过这张图可以看到,busybox:latest 镜像里一共有 1 个 Layer,这相比其他镜像已经小很多了,在工作中遇到的镜像有5~10个 Layer 是再正常不过的事情了。
如果有体验过前面 docker 命令的话,这里会明白在使用 docker pull、docker rmi 等命令操作镜像的时候,那些“奇怪”的输出信息就是镜像里的各个 Layer。每次镜像操作 Docker 会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会删除,这样可以节约磁盘和网络成本。
Dockerfile 是什么
上面我们将镜像比喻为“样板间”,要造出这个“样板间”,就必然需要一个“施工图纸”,又它来规定怎样建造地基、铺设水电、开窗撘门等动作,而这个“施工图纸”就是 Dockerfile。
与镜像、容器相比而言,Dockerfile 非常普通,它仅仅是一个纯文本,只不过里面记录了一系列的构建指令,比如选择基础镜像、拷贝文件、运行命令等,几乎每个指令都会生成一个 Layer,而 Docker 顺序执行这个文件里的所有步骤,最后就会创建出一个新的镜像出来。
我们可以将上面运行 busybox 镜像输出 hello word 信息的容器封装为一个新的镜像,其 Dockerfile 如下(我这里将文件命名为 Dockerfile)
# 选择基础镜像
FROM busybox
# 启动容器时默认运行的命令
CMD echo "hello world"
接下来执行命令构建新的镜像,注意:docker 版本不同,构建镜像时的输出信息也不一定完全相同,
# -t:指定镜像名字
# -f:指定 Dockerfile 文件名,若为 Dockerfile 也省略该参数
docker build -t busybox:v1 -f Dockerfile .
这里还需要特别注意命令的格式,用 -f 参数指定 Dockerfile 文件名,后面必须跟一个文件路径,叫做“构建上下文”(build’s context),这里只是一个简单的点号,表示当前路径的意思,构建上下文里的信息会被复制到容器中,所以建议这个构建上下文的目录只保留需要拷贝到容器中的内容。
查看新镜像信息
docker inspect busybox:v1
运行容器并查看输出信息
docker run -id --name busybox busybox:v1
如何编写优雅的 Dockerfile
了解 Dockerfile 之后,下面来一起了解 Dockerfile 的一些常用指令和最佳实践,以便在今后的工作中少踩些坑。
Dockerfile 基础
假设当前目录有一个名为 main.go
的 golang 源码文件和 Dockerfile 文件
package mainimport "fmt"func main() {fmt.Println("hello world.")
}
Dockerfile 文件内容如下:
FROM golang:1.21.3
WORKDIR /root/
COPY main.go /root/
RUN go build -o hello-world main.go
CMD ["/root/hello-world"]
构建镜像
docker build -t hello-world:go -f Dockerfile .
启动容器
docker run -id --name hello-world hello-world:go
上面就是一个从开发到构建镜像再到运行服务的完整流程,接下来解释一下之前没有遇到过的 Dockerfile 中的 WORKDIR、COPY 和 RUNUN 命令。
WORKDIR 命令的意思是设置容器内部的默认工作目录(working directory),在指定 WORK /root/
后,之后的 RUN、CMD、ENTRYPOINT 等命令将在该目录下执行。如果指定的目录不存在,Docker 将自动创建它。注意:大多数原生的基础镜像默认工作目录都是 /
。
在本机上开发时会产生源码、配置等文件,需要将它们打包进镜像里,这时就可以使用 COPY 或 ADD 命令,用法和 Linux 的 cp 差不多,不过拷贝的源文件必须是“构建上下文”路径里存在的文件。
RUN 通常会是 Dockerfile 里最复杂的指令,会包含很多的 Shell 命令,但 Dockerfile 里一条指令只能是一行。为了避免使用多个 RUN 指令导致镜像层过多且镜像较大,我们往往会尽量使用一个 RUN 指令,并且在每行的末尾使用续行符 \,命令之间也会用 && 来连接,这样保证在逻辑上是一行,就像下面这样:
RUN apt-get update \&& apt-get install -y \build-essential \tar \vim \wget \binutils \curl \gcc \gcc-c++ \&& apt-get clean all \&& ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
多段构建
在上面的 Dockerfile 示例中,大家是否发现了一个问题:最终运行的程序是 hello-world
二进制文件,并不需要 main.go
文件和 golang 的环境,那这里保留这两类文件岂不是占用了一定的空间,且可能会带来一定的安全隐患。(生产环境容器镜像追求“最小化原则”),那么我们可以怎样解决这个问题呢?
方案一:在本机先通过 go build
命令将源码构建为二进制文件,再通过 Dockerfile 的 COPY 指令将二进制文件拷贝到镜像中:
# 本机编译
go build -o docker/hello-world main.go
FROM debian:12.2
WORKDIR /root/
COPY hello-world /root/
CMD ["/root/hello-world"]
那么问题又来了,这里为了保证构件上下文的“最小化”,我们要单独创建一个目录,这个目录仅存在编译后的二进制文件和 Dockerfile。
(base) 🐳 ➜ example tree .
.
├── docker
│ ├── Dockerfile
│ └── hello-world
└── main.go
此外,在本机编译也需要依赖 golang 环境的,在工作中,构建任务往往运行在流水线中,开发人员仅需编写 Dockerfile 即可,编译、构建均在“构建机”执行。既然依赖构建机,保证构建机的一致性和稳定性又是一个比较重要的工作了,为了减少依赖项,我们可以通过“多段构建”,在一个文件中编写“两个” Dockerfile,第一个执行编译,第二个是构建出我们最终需要的镜像。
# 第一次构建(编译)
FROM golang:1.21.3 AS builder
## 设置工作目录
WORKDIR /root/
## 复制构建上下文的全部文件到指定目录(构建上下文包含源码 main.go)
COPY . /root/
# 执行编译
RUN go build -o hello-world main.go# 第二次构建
FROM debian:12.2
## 设置工作目录
WORKDIR /root/
## 从第一次构建的命令中获取所需文件
COPY --from=builder /root/hello-world /root/
## 设置启动命令
CMD ["/root/hello-world"]
这样一来,既不依赖本机或“构建机”的编译环境,也不会将不需要的文件打包到最终镜像中。
最佳实践
虽然我们已经解决了编译环境和不必要依赖的问题,但依然还有一些没有考虑到的内容,下面我来全方位梳理 Dockerfile 的最佳实践。
基础镜像
构建镜像的第一条指令必须是 FROM,所以基础镜像的选择是非常关键的。基础镜像使用不当会对造成生产环境的诸多风险。如果应用程序可独立运行而不依赖系统库等,那么一般会选择scratch,就像著名的分布式键值存储 etcd 一样;如果关注的是镜像的安全和大小,那么一般会选择 Alpine;如果关注的是应用的稳定性,那么可能会选择 Debian、Ubuntu和CentOS;如果我们需要在镜像里将源码编译,那么可能会选择对应语言的基础镜像,如:golang:1.21.3、openjdk:22等
FROM scratch # 选择 scratch 镜像
FROM debian:12.2 # 选择 Debian 镜像
FROM ubuntu:23.04 # 选择 Ubuntu 镜像
FROM alpine:3.18 # 选择Alpine镜像
FROM centos:8 # 选择 CentOS 镜像
对于以上基础镜像,如果我们程序很简单,不需要系统环境和依赖库等支撑,完全可以选择 scratch 镜像,就比如上面 go 语言版的 hello world 就可以使用该镜像,这样构建出的镜像会很小。结果如下图所示:
(base) 🐳 ➜ ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world debian bb7b64c8c159 2 minutes ago 118MB
hello-world scratch 1a66709e9ad0 2 hours ago 1.8MB
对于需要系统环境和依赖库的应用程序(这也更符合大多数应用的场景),我个人推荐基础镜像的顺序是:debian -> ubuntu -> alpine --> centos,主要原因有以下几点:
- centos 从 RHEL 9 起,其从 RHEL 复刻系统,转变为了 RHEL 前的验证系统(CentOS Stream),其稳定性还需进一步验证,因此个人将其放到了最后;
- alpine 虽然容器符合“最小化”原则,但它是通过使用 musl libc 库而非常用 Linux 发行版用到的 glibc 库,可能存在一些兼容性问题。以 go 语言为例,在我们需要用到 cgo 时,如果使用 alpine 镜像可能会遇到一些奇怪的问题,若不了解 Linux 操作系统的 libc 库不建议轻易尝试;
- 至于 debian 和 ubuntu,ubuntu 是在 debian 基础上进行定制和优化的另一个 Linux 发行版操作系统,而且很多主流应用(如:MySQL)的官方镜像都是使用 debian 作为基础镜像来构建的,因此这里更推荐使用 debian;同时 ubuntu 也因为其长期开源、社区活跃等特性排在前列。
总之,若不熟悉 Linux 底层知识,建议直接选择 debian 镜像。
此外,基础镜像尽量使用具体标签,而不是 latest
,因为每次新版本发布后,latest 标签都会指向新版本镜像。若本次构建和上次构建前,恰好基础镜像有过更新,那两次构建出的镜像并不相同,这对生产环境而言是非常危险的。
层最小化
比如,我有一个 Dockerfile,内容如下:
# 第一次构建
FROM golang:1.21.3 AS builder
WORKDIR /data/workspace/
## 复制代码至相关文件
COPY . /data/workspace/
## 下载依赖
RUN go mod tidy
## 编译(二进制文件统一命名为server, 业务也可自定义)
RUN mkdir cmd && go build -o /data/workspace/cmd/server# 第二次构建
FROM debian:12.2
WORKDIR /data/workspace/
## 下载所需命令
RUN apt-get update
RUN apt-get install -y tar procps vim less which binutils lsof telnet iputils wget net-tools tcpdump nc lrzsz bind-utils
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN apt-get clean all
## 从第一次构建的命令中获取所需文件(二进制名统一定位server, 业务也可自定义)
COPY --from=builder /data/workspace/cmd/server /data/workspace/
## 设置启动命令
CMD ["/data/workspace/server"]
每一个 RUN 指令都是可缓存的执行单元。太多的 RUN 指令会增加镜像的层数,增大镜像体积。当使用包管理器(apt、yum、dnf)安装软件时,一般会先更新软件索引信息,然后再安装软件。个人更推荐将更新索引和安装软件放在同一个 RUN 指令中,这样可以形成一个可缓存的执行单元,修改后的 Dockerfile 内容如下:
# 第一次构建
FROM golang:1.21.3 AS builder
WORKDIR /data/workspace/
## 复制代码至相关文件
COPY . /data/workspace/
## 下载依赖
RUN go mod tidy
## 编译(二进制文件统一命名为server, 业务也可自定义)
RUN mkdir cmd && go build -o /data/workspace/cmd/server# 第二次构建
FROM debian:12.2
WORKDIR /data/workspace/
## 下载所需命令
RUN apt-get update \&& apt-get install -y tar procps vim less which binutils lsof telnet iputils wget net-tools tcpdump nc lrzsz bind-utils \&& ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& apt-get clean all
## 从第一次构建的命令中获取所需文件(二进制名统一定位server, 业务也可自定义)
COPY --from=builder /data/workspace/cmd/server /data/workspace/
## 设置启动命令
CMD ["/data/workspace/server"]
还有,上面 RUN 命令中的最后一步 apt-get clean all
也很关键,该命令会将包管理工具维护的缓存清空,进一步减少镜像体积。而且,我更推荐在每一个 RUN 指令的末尾执行该命令,如果在下一条指令中执行,镜像的体积并不会减少。
此外,在多个 Dockerfile 中,指令相同的 layer 应尽量放在相同的步骤,这样多个 Dockerfile 构建时能够使用构建缓存,减少构建时长。
安全性
容器中的 root 和主机上的 root 相同,但会受到 docker 守护程序配置的限制。无论有什么限制,如果程序突破了容器,他也能够找到一种方法来获取访问主机的完整权限。
这里可以说一下我之前在维护 kubernetes 集群时的一个故事:因为工作交接没做好,同事离职后,集群中的一台服务器密码忘记了,这时我通过亲和性在该服务器启动了一个容器,并将服务器的 /etc 目录挂载到容器中,进入容器执行了修改服务器密码的命令,就成功修改了容器所在服务器的密码。由此可见,安全性是多么的重要。
因此,我们可以指定一个用户而不使用默认 root 用户,可以在一定程度上提升容器的安全性:
# 第一次构建
FROM golang:1.21.3 AS builder
WORKDIR /data/workspace/
## 复制代码至相关文件
COPY . /data/workspace/
## 下载依赖
RUN go mod tidy
## 编译(二进制文件统一命名为server, 业务也可自定义)
RUN mkdir cmd && go build -o /data/workspace/cmd/server# 第二次构建
FROM debian:12.2
USER server
WORKDIR /home/server/
## 从第一次构建的命令中获取所需文件(二进制名统一定位server, 业务也可自定义)
COPY --from=builder /data/workspace/cmd/server /home/server/
## 设置启动命令(第一个server是用户目录,第二个server是二进制文件)
CMD ["/home/server/server"]
相关文章:

优雅的 Dockerfile 是怎样炼成的?
Docker 简介 目前,Docker 主要有两个形态:Docker Desktop 和 Docker Engine。 Docker Desktop 是专门针对个人使用而设计的,支持 Mac(已支持arm架构的M系芯片) 和 Windows 快速安装,具有直观的图形界面&a…...

2023-2024 中国科学引文数据库来源期刊列表(CSCD)
文章目录 CSCD来源期刊遴选报告2023-2024 中国科学引文数据库来源期刊列表(CSCD) CSCD来源期刊遴选报告 2023-2024 中国科学引文数据库来源期刊列表(CSCD)...

【3D图像分割】基于Pytorch的VNet 3D图像分割5(改写数据流篇)
在这篇文章:【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2(基础数据流篇) 的最后,我们提到了: 在采用vent模型进行3d数据的分割训练任务中,输入大小是16*96*96,这个的裁剪是放到Dataset类…...

WebSocket Day02 : 握手连接
前言 握手连接是WebSocket建立通信的第一步,通过客户端和服务器之间的一系列握手操作,确保了双方都支持WebSocket协议,并达成一致的通信参数。握手连接的过程包括客户端发起握手请求、服务器响应握手请求以及双方完成握手连接。完成握手连接后…...

c#的反编译工具ISPY和net reflector 使用比较
我有一份Asp.net程序需要修改,但没有源码,只有dll,需要使用反编译工具回复源码,尝试使用了市面上的两种主流的工具ISPY和net reflector ,最终用ISPY恢复了源码。 比较 ISPY 恢复的代码和实际有差距,但还能…...

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(四)
目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理2. 模型训练及保存3. 接口实现4. 收集数据5. 界面设计 系统测试相关其它博客工程源代码下载其它资料下载 前言 前段时间,博主分享过关于一篇使用协同过滤算法进行智能电影推荐系统的博…...

方阵行列式与转置矩阵
1.转置矩阵:格式规定:如果矩阵A为n阶方阵,那么A的T次方为矩阵A的转置矩阵,即将矩阵A的行与列互换。 2.转置矩阵的运算性质: 1.任何方阵的转置矩阵的转置矩阵为方阵自身。 2.多个矩阵的和的转置矩阵等于多个转置矩阵的…...

【Java 进阶篇】Java Cookie共享:让数据穿越不同应用的时空隧道
在Web开发中,Cookie是一种常见的会话管理技术,用于存储和传递用户相关的信息。通常,每个Web应用都会在用户的浏览器中设置自己的Cookie,以便在用户与应用之间保持状态。然而,有时我们需要在不同的应用之间共享Cookie数…...

甘特图组件DHTMLX Gantt用例 - 如何拆分任务和里程碑项目路线图
创建一致且引人注意的视觉样式是任何项目管理应用程序的重要要求,这就是为什么我们会在这个系列中继续探索DHTMLX Gantt图库的自定义。在本文中我们将考虑一个新的甘特图定制场景,DHTMLX Gantt组件如何创建一个项目路线图。 DHTMLX Gantt正式版下载 用…...

克里金插值matlab代码
% 克里金插值示例 clc; clear; % 生成模拟数据 x linspace(0, 10, 11); y linspace(0, 10, 11); [X, Y] meshgrid(x, y); Z sin(sqrt(X.^2 Y.^2)) 0.1 * randn(size(X)); % 设置克里金参数 nugget 0.1; % 块金值 range 1; % 范围 sill 1; % 基台值 azimuth …...

【LeetCode】23. 合并 K 个升序链表
题目链接:23. 合并 K 个升序链表 题目描述: 数据范围: **思考:**这题实际上就是合并两个有序列表的进阶版,只不过这里变成了合并K个,那么这里我们显然就知道,核心的合并两个有序列表的思路不…...

2023年【熔化焊接与热切割】免费试题及熔化焊接与热切割考试总结
题库来源:安全生产模拟考试一点通公众号小程序 熔化焊接与热切割免费试题参考答案及熔化焊接与热切割考试试题解析是安全生产模拟考试一点通题库老师及熔化焊接与热切割操作证已考过的学员汇总,相对有效帮助熔化焊接与热切割考试总结学员顺利通过考试。…...

为什么要学中文编程?它能有哪些益处?免费版编程工具怎么下载?系统化的编程教程课程怎么学习
一、为什么要学习这个编程工具?能给自己带来什么益处? 1、不论在哪里上班,都不是铁饭碗:现在全球经济低迷,使得很多企业倒闭, 大到知名国企小到私营企业,大量裁员。任何人都无法保证自己现在的…...

数据分析实战 - 2 订单销售数据分析(pandas 进阶)
题目来源:和鲸社区的题目推荐: 刷题源链接(用于直接fork运行 https://www.heywhale.com/mw/project/6527b5560259478972ea87ed 刷题准备 请依次运行这部分的代码(下方4个代码块),完成刷题前的数据准备 …...

测试服务器端口是否开通,计算退休时间
本案例知识点 netstat -tuln | grep 80 nestat 目前主机打开的网络服务端口,-tuln目前主机启动的服务,如图 报错说参数太多,仔细检查发现if后的中括号内,变量少双引号导致,改完之后运行显示22,25端口开放࿰…...

Prometheus接入AlterManager配置企业微信告警(基于K8S环境部署)
文章目录 一、创建企业微信机器人二、配置AlterManager告警发送至企业微信三、Prometheus接入AlterManager配置四、部署PrometheusAlterManager(放到一个Pod中)五、测试告警 注意:请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、创…...

11.1 Linux 设备树
一、什么是设备树? 设备树(Device Tree),描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息: 树的主干就是系统总线, IIC 控制器、 GPIO 控制…...

万宾科技管网水位监测助力智慧城市的排水系统
以往如果要了解城市地下排水管网的水位变化,需要依靠人工巡检或者排查的方式,这不仅加大了人员的工作量,而且也为市政府带来了更多的工作难题。比如人员监管监测不到位或无法远程监控等情况,都会降低市政府对排水管网的管理能力&a…...
Glide transform CircleCrop()圆图,Kotlin
Glide transform CircleCrop()圆图,Kotlin import android.os.Bundle import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import com.bumptech.glide.load.resource.bitmap.CircleCropclass MainActivity : AppCompatActivity() {o…...

从NetSuite Payment Link杂谈财务自动化、数字化转型
最近在进行信息化的理论学习,让我有机会跳开软件功能,用更加宏大的视野,来审视我们在哪里,我们要到哪去。 在过去20多年,我们的财务软件经历了电算化、网络化、目前处于自动化、智能化阶段。从NetSuite这几年的功能发…...

1.UML面向对象类图和关系
文章目录 4种静态结构图类图类的表示类与类之间的关系依赖关系(Dependency)关联关系(Association)聚合(Aggregation)组合(Composition)实现(Realization)继承/泛化(Inheritance/Generalization)常用的UML工具reference欢迎访问个人网络日志🌹🌹知行空间🌹🌹 4种静态结构…...

JAVA小说小程序系统是怎样开发的
随着移动互联网的普及,小说阅读已经成为人们休闲娱乐的重要方式之一。为了满足广大读者的需求,我们开发了一款基于JAVA编程语言的小说小程序系统。本系统旨在提供一种便捷、高效、有趣的阅读体验,让用户能够随时随地阅读最新、最热门的小说。…...

【深度学习】pytorch——Tensor(张量)详解
笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ pytorch——Tensor 简介创建Tensortorch.Tensor( )和torch.tensor( )的区别torch.Tensor( )torch.tensor( ) tensor可以是一个数(标量)、一维数组(向量)、二维数组&…...

装修服务预约小程序的内容如何
大小装修不断,市场中大小品牌也比较多,对需求客户来说,可以线下咨询也可以线上寻找品牌,总是可以找到满意的服务公司,而对装修公司来说如今线下流量匮乏,很多东西也难以通过线下方式承载,更需要…...

easypoi 导出Excel 使用总结
easypoi 导出Excel 导出Excel需要设置标题,且标题是多行,标题下面是列表头 设置表格标题 ExportParams headExportParams new ExportParams();StringBuilder buffer new StringBuilder("");buffer.append("1、课程名称:....…...

MySQL性能优化的最佳20条经验
概述 关于数据库的性能,这并不只是DBA才需要担心的事。当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能。下面讲下MySQL性能优化的一些点。 1. 为查询缓存优化你的查询 大多数的MySQL服务器…...

【Liunx基础】之指令(一)
【Liunx基础】之指令(一) 1.ls指令2.pwd命令3.cd指令4.touch指令5.mkdir指令(重要)6.rmdir指令与rm指令(重要)7.man指令(重要)8.cp指令(重要) 📃博客主页: 小…...

jQuery案例专题
jQuery案例专题 本学期主要担任的课程是js和jQuery,感觉用到的有一些案例挺有意思的,就对其进行了一下整理。 目录: 电影院的幕帘特效 手风琴特效 星光闪烁 网页轮播图 1.电影院的幕帘特效代码如下 html <!DOCTYPE html > <html…...

【Linux】服务器间免登陆访问
准备两台服务器,服务器A,服务器B 在服务器A中实现免登陆服务器B 进入服务器A操作 进入目录/root/.ssh cd /root/.ssh秘钥对使用默认文件名 生成秘钥对,在输入秘钥文件时直接回车则会使用默认文件名:id_rsa ssh-keygen -t rsa…...

【信息安全原理】——IP及路由安全(学习笔记)
目录 🕒 1. IPv4协议及其安全性分析🕒 2. IPsec(IP Security)🕘 2.1 IPsec安全策略🕤 2.1.1 安全关联(Security Association, SA)🕤 2.1.2 安全策略(Security…...