多种方案图文并茂分分钟教你解决Kubernetes(k8s)容器安全问题(不断更新中)

news/2024/10/3 14:29:39

Kubernetes(k8s)Secrets in clear text environment variables 明文中的秘密环境变量

问题详解

  • 在Kubernetes(k8s)中,Secrets用于管理诸如密码、令牌和密钥等敏感信息。安全地存储这些密钥至关重要。然而,将密钥直接注入到明文环境变量中可能会使它们面临潜在的安全风险。
  • 在 Kubernetes 集群中,容器能够通过环境变量查询到其他 Service的信息,是因为Kubernetes 在调度Pod时,会自动为每个Service创建一组环境变量。这些环境变量包含了Service的名称、Cluster IP 地址(内部虚拟 IP)、以及 Service 监听的端口等信息。这是 Kubernetes 内置的服务发现机制的一部分,旨在帮助集群内的 Pod 之间进行通信。
  • Kubernetes通过环境变量为每个Service创建一个服务发现记录。这些记录允许Pod通过Service 名称来发现和连接到其他 Service,而无需知道它们的IP 地址或端口。

解决方案一

STEP1: 创建Kubernetes Secret - 编写一个yaml文件

注意,这里的值需要进行base64编码

apiVersion: v1
kind: Secret
metadata:name: 我的密钥
type: Opaque
data:PASSWORD: <base64编码的密码>API_KEY: <base64编码的API密钥>

STEP2: 部署执行上述yaml

STEP3: 将Secret作为卷挂载到Pod中-在项目部署K8S.yaml中挂载Secrets

相较于直接注入环境变量,更安全的做法是将Secret作为卷挂载到Pod中。这样,密钥数据将以文件形式存在于容器的文件系统中。

apiVersion: v1
kind: Pod
metadata:name: 我的Pod
spec:containers:- name: 我的容器image: 我的镜像volumeMounts:- name: 密钥卷mountPath: /var/密钥readOnly: truevolumes:- name: 密钥卷secret:secretName: 我的密钥

代码实战案例

下面是我写过的一个的sqlserver创建yaml

apiVersion: apps/v1
kind: Deployment
metadata:name: xiaojin-sqlserver-dev-deployment
spec:replicas: 1selector:matchLabels:app: xiaojin-sqlserver-devtemplate:metadata:labels:app: xiaojin-sqlserver-devspec:terminationGracePeriodSeconds: 30hostname: mssqlinstsecurityContext:fsGroup: 10001containers:- name: xiaojin-sqlserver-devimage: mcr.microsoft.com/mssql/server:2022-latestports:- containerPort: 1433env:- name: MSSQL_PIDvalue: "Developer"- name: ACCEPT_EULAvalue: "Y"- name: MSSQL_SA_PASSWORDvalueFrom:secretKeyRef:name: xiaojin-sqlserver-devkey: MSSQL_SA_PASSWORD
---
apiVersion: v1
data:MSSQL_SA_PASSWORD: WGlhb2ppbkAxMjNoYWhh   # 等同于密码Xiaojin@123haha
kind: Secret
metadata:creationTimestamp: nullname: xiaojin-sqlserver-devnamespace: xiaojin---
apiVersion: v1
kind: Service
metadata:name: xiaojin-sqlserver-devnamespace: xiaojin
spec:type: NodePortselector:app: xiaojin-sqlserver-devports:- name: xiaojin-sqlserver-devprotocol: TCPport: 1433targetPort: 1433nodePort: 30034type: NodePort

解决方案二

STEP1: 创建Kubernetes Secret - 编写一个yaml文件

注意,这里的值需要进行base64编码

apiVersion: v1
kind: Secret
metadata:name: 我的密钥
type: Opaque
data:PASSWORD: <base64编码的密码>API_KEY: <base64编码的API密钥>

STEP2: 部署执行上述yaml

STEP3: 将密钥作为环境变量使用-在项目部署K8S.yaml中直接配置env

如果你非要将密钥作为环境变量使用,这种方式比直接文件挂载安全性稍低,写法如下(下面代码是复制到你的k8s.yaml部署文件里哦)

env:
- name: PASSWORDvalueFrom:secretKeyRef:name: 我的密钥key: PASSWORD

解决方案三(超级推荐)

在Kubernetes中,挂载Secret以便容器能够安全地访问敏感信息(如密码、API密钥等)是一种常见做法。

创建Secret

同上,可以参考上面方案中的创建步骤

挂载Secret

apiVersion: v1
kind: Pod
metadata:name: my-app-secret
spec:containers:- name: my-app-containerimage: my-app-imagevolumeMounts:- name: secret-volume # 与volumes中定义的卷名称对应mountPath: "/app/secrets" # 容器内挂载路径readOnly: true # 为了安全,通常设置为只读volumes:- name: secret-volume # Secret卷定义secret:secretName: my-secret-name # Secret的名称

案例截图

Container is running as root 容器以root 身份运行

问题详解

当容器以root身份运行的情况下,具有包括但不限于以下几点影响:

  • 最高权限访问:容器内的进程可以不受限制地访问宿主机的资源,包括文件系统、网络接口和其他运行中的进程。
  • 修改系统配置:能够修改系统级别的配置文件,安装系统包,甚至修改内核参数。
  • 安全风险:如果容器内的服务或应用程序存在安全漏洞,攻击者可以利用root权限对整个系统造成严重影响,包括数据窃取、植入恶意软件或完全控制宿主机。

解决方案一:制作镜像的时候就使用非root用户运行(较为麻烦,需要重新打包镜像,建议先尝试方案二,如果不行再参考这个)

Kubernetes推荐使用非root用户运行容器,以遵循最小权限原则。通过指定容器以普通用户身份运行,可以显著降低潜在的安全风险。

  • (CIS_Docker_v1.2.0 - 4.1) Image should be created with a non-root user 或者遇到 /var/run/nginx.pid" failed (13: Permission denied)也一样可以采用此方案解决

解决思路:修改Dockerfile或容器镜像,确保应用可以在非root用户下正常运行,使用非root用户运行容器

dockerfile加入下面代码

RUN adduser -D xiaojin
USER xiaojin

在这里插入图片描述

k8s yaml 加入下面代码

 securityContext:runAsUser: 0

在这里插入图片描述

且修改以后,如果是nginx,可能还会遇到其他问题,我把你可能遇到的问题处理方案放在后面啦

bind() to 0.0.0.0:80 failed (13: Permission denied)

XXXXXXXX pm2023/06/05 08:04:39 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
XXXXXXXX pmnginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
Mon, Jun 5 2023 4:04:41 pm2023/06/05 08:04:39 [emerg] 1#1: bind() to 0.0.0.0:80 failed (13: Permission denied)
Mon, Jun 5 2023 4:04:41 pmnginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)

问题分析:

某些情况下,非Root用户不能绑定1024以下端口,否则会报错:没有权限绑定该端口

问题解决:

  • 修改Dockerfile中的端口配置
  • nginx.conf中的端口监听
  • k8s.yml中的服务端口暴露配置
  • containerPort配置
    所有你项目中nginx绑定的都要修改一遍哦~~

代码举例:



解决方案二:使用Security Context(设置简单方便,推荐这种方案)

Kubernetes提供了securityContext字段,允许用户在Pod或容器级别设置安全策略,比如指定运行用户(runAsUser)和组(runAsGroup),以及是否允许容器以特权模式运行(privileged)。

问题示例

  • 运行脚本无权限/bin/sh: ./bxxx.sh: Permission denied 可以采用此方案解决
StartError (failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/var/lib/kubelet/pods/149aaaaaa/volume-xxx/xxx/0" to rootfs at "/app/logs": mount /var/lib/kubelet/pods/149xxx/0:/app/logs (via /proc/self/fd/6), flags: 0x5001: not a directory: unknown)/bin/sh: ./xxx.sh: Permission denied

解决方案

  apiVersion: v1kind: Podmetadata:name: my-podspec:securityContext:runAsUser: 1000containers:- name: my-containerimage: my-imagesecurityContext:runAsNonRoot: true # 重点代码runAsUser: 1000 # 重点代码

代码实战案例

Do not disable default seccomp profile 不禁用默认的seccomp配置文件

问题详解

在Kubernetes(k8s)中,Seccomp(Secure Computing Mode)是一种Linux内核安全特性,用于限制容器内进程可以执行的系统调用,以此增强容器的安全性并降低潜在的攻击面。Kubernetes默认为Pod提供了一个基本的Seccomp配置文件(默认配置文件),这个配置文件定义了一系列允许或拒绝的系统调用规则,旨在阻止不安全或不必要的系统调用行为。不要禁用默认Seccomp配置文件的原因包括:

  • 减少攻击面:默认的Seccomp配置有助于防止容器执行危险的系统调用,如那些可以修改系统时间、重启系统或执行其他对宿主机有潜在危害的操作。这减少了容器逃逸攻击的可能性。
  • 安全最佳实践:遵循Kubernetes社区推荐的安全最佳实践,其中就包括保留默认Seccomp配置。这有助于维护一个统一的安全标准,使得集群管理更加规范和安全。
  • 细粒度控制:虽然默认配置已经足够安全,但Kubernetes还允许自定义Seccomp配置以达到更细粒度的控制。禁用默认配置意味着放弃这种细粒度控制的机会,除非有明确且必要的理由去定制化配置。
  • 性能影响小:启用Seccomp对容器的性能影响微乎其微,尤其是在使用默认配置时。因此,保持默认配置开启不会对应用的运行效率产生显著负面影响。
  • 易于升级和维护:随着Kubernetes和Seccomp规则的发展,默认配置会不断更新以应对新的安全威胁。禁用默认配置可能意味着错过这些自动更新,增加手动维护成本和安全风险

解决方案(超级简单)

 containers:- image: myImage:v1imagePullPolicy: AlwayssecurityContext:                         seccompProfile:   # 重点代码                      type: RuntimeDefault  # 重点代码 

代码实战案例

Restrict container from acquiring additional privileges 限制容器获得额外特权

问题详解

  • 在 Kubernetes (k8s) 中,"Restrict container from acquiring additional privileges" 是一项安全措施,旨在限制容器运行时获得超出其初始分配权限的能力。这主要是通过 Pod 的安全上下文中的 securityContext 配置来实现的,特别是利用 runAsUser、runAsGroup、fsGroup 以及 allowPrivilegeEscalation 等字段。
  • allowPrivilegeEscalation: 这是一个布尔值,当设置为 false 时,会阻止容器内的进程提升其自身的权限(例如,从非 root 用户变为 root 用户)。这是直接限制容器获取额外权限的关键设置。

解决方案

 securityContext:                          allowPrivilegeEscalation: false  # 重点代码

Mount container's root filesystem as read only 将容器的根文件系统装载为只读

问题详解

  • 在 Kubernetes (k8s) 中,将容器的根文件系统挂载为只读是一种提高容器安全性和稳定性的做法。
  • 这意味着容器内的进程不能修改其根 (/) 目录下的任何文件或目录。这对于运行无状态服务或者防止恶意修改系统文件特别有用。

解决方案

在 Pod 的 YAML 配置中的容器 securityContext 字段下设置 readOnlyRootFilesystem 为 true

apiVersion: v1
kind: Pod
metadata:name: example-pod
spec:containers:- name: example-containerimage: your-container-imagesecurityContext:readOnlyRootFilesystem: true # 这里设置了根文件系统为只读# 其他容器配置...# 其他 Pod 配置...

注意,如果你的容器需要写入文件(比如日志、临时文件或应用数据),你做了上面的配置以后,会遇到无法写入文件的问题,例如会遇到项目启动报错:Read-only file system,可参考下面的解决方案

Read-only file system 问题解决

此问题分为两种,单个文件需要权限写入以及多个文件(建议使用文件夹来管理)需要获取写入权限,下面会根据不同来进行分析解决

文件夹只读问题解决:如果你要在readOnlyRootFilesystem: true状态下,读写一个文件夹

方案一:EmptyDir Volume - 动态创建一个临时存储卷,该卷的生命周期与 Pod 相同,但不持久化数据。

   volumeMounts:- mountPath: /var/log   # 需要获取读写权限的文件夹name: temp-log-volume   # 自己命名就好volumes:- name: temp-log-volume   # 自己命名就好emptyDir: {}   # 不需要改动,你就这么抄就行了

代码实战案例

方案二:HostPath Volume - 将主机上的目录挂载到容器内,适用于单节点测试或有特定需求的场景。(很少用这个,一般我们通常都是多节点,所以都不用这个挂载哦~~)

   volumeMounts:- mountPath: /var/dataname: host-data-volumevolumes:- name: host-data-volumehostPath:path: /data/on/hosttype: Directory

方案三:PersistentVolumeClaim (PVC)-对于需要持久化存储的数据,使用 PVC 绑定到 PersistentVolume (PV),实现数据的持久化存储。(持久或者重要的数据,一定要使用这个)

创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: my-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1Gi
挂载
   volumeMounts:- mountPath: /var/persistent-dataname: persistent-data-volumevolumes:- name: persistent-data-volumepersistentVolumeClaim:claimName: my-pvc

代码实战案例

因此步骤可以展开的有点多,所以可以参考我的其他文章:如何在 Kubernetes (k8s)创建并使用PersistentVolumeClaim (PVC)

单个文件只读问题解决:如果你要在readOnlyRootFilesystem: true状态下,读写单个文件

方案一:使用 EmptyDir 实现单个文件可写

  • 如果是已经存在的文件,需要设置为可写,我们可以使用Init Containers(初始化容器)把资料搞进去,下面有Init Containers用法=
  • 尽管 EmptyDir 提供的是临时存储,但它是可写的,适合存放那些不需持久保存的数据。如果确实只需要单个文件可写,可以通过在 Pod 定义中挂载 EmptyDir 卷并使用 subPath 技巧来模拟单文件写入。

代码

这个我还没试过,最近太忙啦,小伙伴帮我试试看

apiVersion: v1
kind: Pod
metadata:name: my-pod
spec:securityContext:readOnlyRootFilesystem: truecontainers:- name: my-containerimage: my-imagevolumeMounts:- name: temp-file-volumemountPath: /path/to/writable/file # 期望文件可写的位置subPath: writable.txt # 指定文件名volumes:- name: temp-file-volumeemptyDir: {}
  • 此种类型使用起来比较麻烦,大家酌情选择
  • 在容器启动脚本中创建一个从挂载的目录到所需文件的软链接。这要求你的应用能够接受通过软链接访问配置文件。但这种方法增加了容器配置的复杂度,并且不如直接使用subPath优雅。
  • 在readOnlyRootFilesystem: true的约束下,直接在容器的只读文件系统中创建软链接至可写位置(如外部卷或宿主机目录)是不可行的,因为创建软链接本身也算作是对文件系统的写操作。但是,你可以在构建镜像时预先创建软链接至一个预期会在运行时挂载为可写卷的目录。

代码示例1:在dockerfile中使用软链接,然后打包镜像,再使用新镜像运行yaml

下面是一个nginx镜像部署普通前端代码的dockerfile案例,代码中两个RUN指令向/entrypoint.sh文件末尾追加内容

  • 第一条指令将/dev/stdout链接到/opt/nginx/log/nginx/access.log
  • 第二条指令将/dev/stderr链接到/opt/nginx/log/nginx/error.log
FROM node:16.14.2-alpine as build
WORKDIR /app
COPY package.json .npmrc ./
RUN npm config set strict-ssl false
RUN npm config set registry https://registry.npmmirror.com && npm installCOPY . ./
RUN npm run lint:fix
RUN npm run buildFROM docker/nginx:1.25.4-bookworm
COPY --from=build /app/dist/ /usr/share/nginx/html
COPY nginx-uat.conf /etc/nginx/conf.d/default.confRUN useradd -m xiaojin
RUN sed -i 's|/var/|/opt/nginx/|' /etc/nginx/nginx.conf  
RUN echo '#!/bin/sh' > /entrypoint.sh    
RUN echo 'mkdir -p /opt/nginx/log/nginx' >> /entrypoint.sh 
RUN echo 'mkdir -p /opt/nginx/run' >> /entrypoint.sh 
RUN echo 'touch /opt/nginx/log/nginx/error.log' >> /entrypoint.sh   
RUN echo 'touch /opt/nginx/log/nginx/access.log' >> /entrypoint.sh   
RUN echo 'ln -sf /dev/stdout /opt/nginx/log/nginx/access.log' >> /entrypoint.sh  # 关键代码
RUN echo 'ln -sf /dev/stderr /opt/nginx/log/nginx/error.log' >> /entrypoint.sh  # 关键代码
RUN echo 'touch /opt/nginx/run/nginx.pid' >> /entrypoint.sh      
RUN echo 'chown -R xiaojin:xiaojin /opt/nginx' >> /entrypoint.sh   
RUN echo 'exec nginx -g "daemon off;"' >> /entrypoint.sh          
RUN chmod +x /entrypoint.sh                                    CMD ["/entrypoint.sh"]    USER xiaojin

代码示例2:在dockerfile中使用软链接,然后打包镜像,再使用新镜像运行yaml

假设现在项目中有个文件是:/app/readonly/xiaojin.json,但是目前它是只读的,我想要将它改为可写,怎么办呢?我们可以在运行时挂载一个卷到/var/data,并且应用需要写入xiaojin.json,你可以在Dockerfile中这样写代码:

# 在构建镜像时创建软链接
RUN mkdir -p /var/data && \ln -s /var/data/xiaojin.json /app/readonly/xiaojin.json

尽管容器的根文件系统是只读的,但通过软链接 /app/readonly/xiaojin.json 写入的数据实际上会存储在可写的/var/data/xiaojin.json中

注意事项
  • 这种方法要求在构建镜像时明确知道运行时卷的挂载点
  • 软链接本身及其目标目录(在本例中为/var/data)必须在容器启动前就存在或被正确挂载,否则软链接将无效或指向不存在的位置
  • 安全性考虑:确保软链接不会意外地指向容器中敏感或重要的文件系统部分,特别是当容器的其他部分可能对这些链接有写权限时

方案三:使用 PersistentVolumeClaim (PVC) 实现持久化文件写入

对于需要持久化的文件,应该创建一个 PersistentVolumeClaim (PVC),然后在 Pod 中挂载该 PVC。

代码:创建 PersistentVolumeClaim (PVC) yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: my-pvc
spec:accessModes:- ReadWriteOnce # 可选模式有:ReadWriteOnce、ReadOnlyMany、ReadWriteManyresources:requests:storage: 1Gi # 请求1GiB的存储空间storageClassName: standard # 如果使用动态卷,则需指定存储类名称
  • accessModes: 指定如何访问存储卷,常见的有只读多路(ReadOnlyMany)、读写一次(ReadWriteOnce)、读写多路(ReadWriteMany)。
  • resources.requests.storage: 请求的存储空间大小。
  • storageClassName: 如果希望 Kubernetes 自动为你创建 PV(使用动态存储供给),则需要指定一个存在的存储类

代码:部署yaml,此处以Pod为例吧~


apiVersion: v1
kind: Pod
metadata:name: my-pod
spec:securityContext:readOnlyRootFilesystem: truecontainers:- name: my-containerimage: my-imagevolumeMounts:- name: persistent-file-volumemountPath: /path/to/persistent/filevolumes:- name: persistent-file-volumepersistentVolumeClaim:claimName: my-pvc

[spec.jobTemplate.spec.template.spec.volumes[3].configMap.defaultMode: Invalid value: 755: must be a number between 0 and 0777 (octal), both inclusive

解决方案

这个一般出现在修改configMap.defaultMode的时候,解决它非常简单,yaml文件使用命令行执行,不要使用网页端修改和保存就OK了

使用Init Containers初始化你的文件夹或者文件~

为什么要考虑到使用Init Containers?

  • 当挂载点目录与已存在的目录重名时,挂载的卷(不论是EmptyDir、PersistentVolumeClaim还是ConfigMap等)会覆盖原有的目录内容。这意味着原有的目录及其内容在Pod运行期间将不可见,取而代之的是挂载卷提供的内容。

Kubernetes不可不知的服务执行顺序

在Kubernetes中,Init Containers 会比 应用容器(主容器)中的目录挂载先执行。Init Containers的设计目的就是在任何应用容器启动之前执行初始化任务,包括但不限于预填充数据、设置配置、甚至等待依赖服务就绪。具体流程如下所示:

  • Pod调度:Kubernetes调度器找到合适的节点来运行Pod。
  • 创建网络接口和存储卷:为Pod创建必要的网络接口和挂载存储卷。
  • Init Container执行:按顺序执行Init Containers,每个Init Container必须成功结束,下一个才能开始。在此阶段,应用容器尚未启动,但存储卷(包括EmptyDir)已经挂载。
  • 主容器启动:所有Init Containers成功执行完毕后,应用容器开始启动。此时,应用容器会挂载相同的存储卷,可以看到由Init Containers创建或修改的任何文件和目录。

如果我配置了/app/xiaojin目录为emptyDir实现可写,如何初始化这个文件夹的内容呢

apiVersion: v1
kind: Pod
metadata:name: my-app-init-xiaojin
spec:initContainers: # 初始化容器部分- name: init-xiaojin-directoryimage: busybox:latest # 使用包含基础命令的镜像,如mkdir、touch等command: ['sh', '-c', "mkdir -p /mnt/xiaojin && echo 'Initial content' > /mnt/xiaojin/initial_file.txt"] # 初始化命令volumeMounts: # 挂载卷- name: xiaojin-emptydir # 与下面volumes中定义的卷名称对应mountPath: /mnt/xiaojin # Init Container中挂载的路径containers: # 应用容器部分- name: my-app-containerimage: my-app-imagevolumeMounts:- name: xiaojin-emptydirmountPath: /app/xiaojin # 应用容器中挂载的路径volumes: # 卷定义- name: xiaojin-emptydiremptyDir: {} # 使用emptyDir卷

尽管Init Container直接操作的是/mnt/xiaojin,但所有在/mnt/xiaojin下的更改都会反映到与之共享相同emptyDir卷的/app/xiaojin目录中,通过共享emptyDir卷,Init Container对/mnt/xiaojin的初始化操作等同于间接初始化了/app/xiaojin目录的内容。
上面这个我是查资料写的,没试过,等我忙完了贴initContainer的实战截图

结语

  • 今天就写到这里啦~
  • 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~
  • 大家要天天开心哦

欢迎大家指出文章需要改正之处~
学无止境,合作共赢

在这里插入图片描述

欢迎路过的小哥哥小姐姐们提出更好的意见哇~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

团队总结 - Beta版总结会议

在过去的几周中,我们团队共同努力完成了我们的项目。在这次会议中,我们将简要总结我们的成果以及我们仍需要改进的方面。 设想和目标: 解决问题:我们的软件旨在解决已发生或可能发生校园霸凌的问题,我们将基于AI大模型通过和用户的聊天,定点解决用户得心理痛点以及用户学…

Current without potential difference(没有电位差为什么还有会电流?)

Reference:Current without potential difference电路短路时,电池正极和负极之间的电位差虽然在理想情况下应该接近于0,但在实际情况下仍然会有一些电位差存在。这些电位差和电流的产生有几个主要原因: 1. 电池内阻 电池并不是一个理想的电源,它具有内阻。当电池短路时,电…

如何更改.NET中的默认时区?

除了"在操作系统中修改时区信息,然后重启.NET应用程序,使其生效"之外。如何在不修改操作系统时区的前提下,修改.NET中的默认时区呢? 这是一位 同学兼同事 于5月21日在技术群里问的问题,我当时简单地研究了一下,就写出来了。 现在写文章分享给大家,虽然我觉得这…

vulnhub - Geisha

目录扫描时要注意端口vulnhub - Geisha 描述 Machine Name: Geisha Author : SunCSR Goal: Get the root flag of the target. Difficulty: Medium/Intermediate Level DHCP Enabled Tested: VMware Workstation 15.x Pro & VirtualBox 6.x (This works better with VMware…

centos7的虚拟机只显示本地回环

问题:安装好的centos7的虚拟机只显示本地回环,没有其它网卡信息虚拟机 : centps7.9centos安装好后,登录成功后,输入ip a只显示本地127.0.0.1,而不显示ens33的网卡和ip信息,具体详情如下图所示:输入命令systemctl status network.service 不管得到的结果是下面的任意一个或…

纯碱

之前走出了5波驱动 阶段性反弹后 中期要跌的

【知识点】图与图论入门

两三个星期没有发布新文章了,今天再来讲一个新的数据结构:图。何为图论 见名知意,图论 (Graph Theory) 就是研究 图 (Graph) 的数学理论和方法。图是一种抽象的数据结构,由 节点 (Node) 和 连接这些节点的 边 (Edge) 组成。图论在计算机科学、网络分析、物流、社会网络分析…

ASP.NET Core之Serilog、SEQ、ILogger构建可视化日志

一、前言在上一章节中使用NLog+SEQ+ILogger构建可视化日志,本章基于Serilog来构建可视化日志。Serilog是.NET中一款非常出色的结构化日志的日志库,其与NLog对比在前一章节进行了说明。Serilog项目不仅仅包含核心项目Seirlog,而且还提供了很多接收器Sinks(超过100个),这些接…