目录

文章目录

前言

一、运行时

1.1OCI 运行时(OCI Runtime):

1.2容器运行时(Container Runtime):

1.2.1主流容器运行时核心定位与设计背景

1.2.2容器运行时(Container Runtime) 直接交互的工具

1.2.3 容器运行时(Container Runtime)交互工具的使用

(1)crictl

(2)podman(工具也是服务)

二、容器服务调用链

2.1 k8s+docker+containerd

2.2 k8s + cri-o

2.3 docker+containerd

2.4 podman

三、镜像仓库(容器的家)

3.1国内容器镜像站点

3.2docker registry

3.2.1 部署方式

(1)制作证书

(2)部署仓储服务

(3)部署仓储Web服务

(4)添加podman信任

3.3Harbor

3.3.1 概述

3.3.2 核心组件及作用

3.3.3 部署方式

(1)下载harborGithb项目

(2)制作证书(标准法)

(3)docker compose部署

下载工具镜像prepare

执行install导入组件镜像并运行

优化目录结构(可以不做)

(4)添加docker和podman证书信任

四、容器统一平台(可视化管理)

4.1 Dpanel

4.1.1Dpanel核心功能

4.1.2部署Dpanel

4.2 Portainer

4.2.1Portainer核心功能

4.2.2部署Portainer

4.3 1Panel

4.3.1 1Panel核心功能

4.3.2 部署1Panel

五、docker服务(安装相关问题)

5.1docker、docker-compose安装教程

5.1.1 卸载旧版

5.1.2 docker安装

5.1.2.1安装方式一:dnf/yum方式

5.1.2.2安装方式二:二进制包方式

总结


文章目录


前言

在数字化转型的浪潮中,容器技术已经成为现代应用开发和部署的事实标准。但很多人对容器的理解仍停留在"Docker"层面,而忽视了支撑容器运行的底层引擎——容器运行时,以及构建在其上的完整技术体系——云原生。本文将深入探讨这三者之间的关联,为您揭开现代应用架构的神秘面纱。

云原生定义
云原生是一套构建和运行应用程序的方法论,它充分利用云计算的优势,实现应用的弹性、可观测性和韧性。

云原生技术栈
应用定义层:    Helm、Kustomize
编排调度层:    Kubernetes、Nomad
服务网格:      Istio、Linkerd
可观测性:      Prometheus、Grafana、Jaeger
CI/CD:        Jenkins、GitLab CI、ArgoCD
运行时层:      容器运行时
基础设施层:     云平台、物理服务器

一、运行时

1.1OCI 运行时(OCI Runtime):

是符合 OCI(Open Container Initiative,开放容器倡议)标准 的底层工具,负责直接与操作系统内核交互,创建并运行容器进程。它是容器化的 “执行引擎”,仅关注容器的初始化(如创建 namespace、cgroup、挂载文件系统等),完成后即退出,不负责长期管理。典型代表:runc(Go 语言实现,应用最广)、crun(C 语言实现,更轻量)、youki(Rust 语言实现,安全性更强)

1.2容器运行时(Container Runtime):

是完整的容器生命周期管理系统,负责镜像管理、容器创建 / 启动 / 停止 / 删除、网络配置、存储挂载等全流程操作,通常包含对 OCI 运行时的调用。它是上层工具(如 docker、podman 命令,或 K8s 的 kubelet)与底层 OCI 运行时之间的中间层。典型代表:containerd、CRI-O、Docker Engine(底层依赖 containerd)、Podman(无守护进程的运行时)

1.2.1主流容器运行时核心定位与设计背景

运行时核心定位开发 / 维护方设计初衷
containerd通用容器运行时,专注于容器生命周期管理,原生支持 CRI 标准Docker 公司(后捐给 CNCF)从 Docker Engine 中剥离的核心组件,追求轻量、稳定,成为容器运行时的事实标准
cri-o专为 Kubernetes 设计的轻量容器运行时,完全遵循 CRI 标准,无 Docker 依赖Red Hat 主导,CNCF 孵化项目提供 K8s 专属的极简运行时,移除 Docker 相关冗余组件,优化 K8s 集成体验

1.2.2容器运行时(Container Runtime) 直接交互的工具

工具名称核心定位支持的容器运行时遵循标准主要功能适用场景特点总结
crictlK8s 生态的 CRI 接口客户端containerd、CRI-O、cri-dockerd(Docker)CRI(K8s 定义)管理容器(启停、查询)、镜像(拉取、删除)、Pod 沙箱K8s 节点上的容器调试与管理轻量、专注 CRI 接口,与 K8s 生态深度契合,(无构建、编排功能
nerdctlcontainerd 的 Docker 风格命令行工具containerd(原生)OCI、CRI(可选)完全模拟 docker 命令(run/ps/build 等),支持 Compose单机或 K8s 环境中用 Docker 习惯操作 containerd兼容 Docker 命令, 支持镜像构建(build)、Compose 编排、网络 / 存储管理等高级功能
dockerDocker 引擎的原生命令行工具Docker Engine(依赖 containerd)Docker API、OCI全功能容器 / 镜像管理,支持构建、网络、卷、Compose单机 Docker 环境,开发者日常使用 支持镜像构建(build)、Compose 编排、网络 / 存储管理等高级功能,但依赖 Docker 引擎
podman无守护进程的 Docker 兼容工具自身集成(依赖 runc/crun)OCI、CRI(可选)命令与 docker 完全兼容,支持 rootless 容器追求安全(rootless)、无守护进程的单机环境无 daemon 设计,安全性高,兼容 Docker 命令
ctrcontainerd 的原生调试工具containerd(原生)containerd 私有接口直接操作 containerd 内部组件(命名空间、快照等)containerd 底层调试与高级配置功能基础,偏底层,适合调试 containerd 本身
crioctlCRI-O 的专用命令行工具CRI-OCRI管理 CRI-O 运行时的容器、镜像、Pod 沙箱纯 CRI-O 环境的容器管理专为 CRI-O 设计,功能类似 crictl 但更专用,(无构建、编排功能

1.2.3 容器运行时(Container Runtime)交互工具的使用

备注:由于docker工具安装即用,在此不做介绍,主要介绍主流且功能强大的下述两者,使用工具前需要知道自己采用的容器时是哪一种,然后进行配置。

(1)crictl

如果你的节点已安装 Docker,旧版本支持docker-shim的docker不要额外配置,新版本docker需通过 cri-dockerd 适配 CRI 接口,centos 7 需要安装下载对应 el7 版本的 cri-dockerd 包(而非 el8),注:尽量后续使用rock-linux替代centos

wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.1/cri-dockerd-0.3.1-3.el7.x86_64.rpm

# 重新安装
sudo rpm -ivh cri-dockerd-0.3.1-3.el7.x86_64.rpm

#手动创建 cri-dockerd 的 systemd 服务文件
vim /usr/lib/systemd/system/cri-dockerd.service

[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd \
  --container-runtime-endpoint  unix:///var/run/cri-dockerd.sock\
  --network-plugin=cni \
  --cni-conf-dir=/etc/cni/net.d \
  --cni-bin-dir=/opt/cni/bin
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process

[Install]
WantedBy=multi-user.target

启动cri-dockerd服务并配置crictl工具配置,指定sock:

systemctl start cri-dockerd

#创建 / 修改 crictl 配置文件
vim /etc/crictl.yaml

# 对接 Docker 内置 containerd 的套接字
runtime-endpoint: unix:////run/cri-dockerd.sock
image-endpoint: unix:////run/cri-dockerd.sock
timeout: 10  # 超时时间,单位秒
debug: false  # 关闭调试模式(如需排查问题可设为 true)

crictl使用:

[root@k8s-master ~]# ./crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD                 NAMESPACE
[root@k8s-master ~]# ./crictl images
IMAGE                                                TAG                 IMAGE ID            SIZE
docker.1ms.run/registryk8s/descheduler-descheduler   v0.19.0             71eea72eaa850       43MB
socp.io/zb/descheduler                               v0.19.0             71eea72eaa850       43MB
docker.1ms.run/registryk8s/descheduler-descheduler   v0.21.1             ae9a3bdbaf6a5       58.8MB
socp.io/zb/descheduler                               v0.21.1             ae9a3bdbaf6a5       58.8MB
docker.1ms.run/registryk8s/descheduler-descheduler   v0.22.1             8591902620c10       61MB
socp.io/zb/descheduler                               v0.22.1             8591902620c10       61MB

备注:如果使用的是containerd容器运行时,containerd 默认未启用 CRI 插件,需手动生成配置并修改,/etc/crictl.yaml中的sock不一样!!!:

# 1. 生成默认配置文件(默认路径 /etc/containerd/config.toml)
containerd config default > /etc/containerd/config.toml

# 2. 编辑配置文件,确保 CRI 插件启用(关键步骤!)
vim /etc/containerd/config.toml

#验证 containerd 套接字是否存在
ls -l /run/containerd/containerd.sock  # 应输出类似 "srw-rw---- 1 root root 0 ... /run/containerd/containerd.sock"

#修改crictl 配置文件
vim /etc/crictl.yaml

runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false

#如果是CRI-O运行时:
runtime-endpoint: unix:///run/crio/crio.sock
image-endpoint: unix:///run/crio/crio.sock
timeout: 10
debug: false
(2)podman(工具也是服务)

如果是使用的centos7系统,安装过高版本docker环境的,需要卸载containerd.io,它是centos8的默认运行时,centos8和7已经不再维护。通过 yum install 指定 runc 版本,并通过 --setopt 选项禁止 yum 用 containerd.io 取代 runc,podman默认用runc。注:尽量后续使用rock-linux替代centos

sudo yum install -y --setopt=obsoletes=0 runc-1.0.0-70.rc10.el7_9.x86_64
[root@k8s-master ~]# podman load -i deschedulerimage19.tar.gz
Getting image source signatures
Copying blob bfd14c139152 done  
Copying config 71eea72eaa done  
Writing manifest to image destination
Storing signatures
Loaded image(s): socp.io/zb/descheduler:v0.19.0
[root@k8s-master ~]# 
[root@k8s-master ~]# 
[root@k8s-master ~]# podman images
REPOSITORY               TAG       IMAGE ID       CREATED       SIZE
socp.io/zb/descheduler   v0.19.0   71eea72eaa85   5 years ago   43 MB

二、容器服务调用链

备注:container-shim 的设计目的是解耦高层运行时(containerd)与底层容器进程

docker-shim:早期 Docker 引擎中使用的垫片,随 Docker 一起发布,用于衔接 dockerd 和 runc。

container-shim:containerd 独立后推出的通用垫片,功能与 docker-shim 类似,但更标准化,目前已替代 docker-shim 成为主流。

2.1 k8s+docker+containerd

K8s中采用docker的containerd作为容器运行时:其中cri-dockerd是 “翻译器”,让不支持 CRI 的 Docker 能与遵循 CRI 标准的工具(如 crictl、k8s)通信。

用户/K8s → crictl/kubelet(CRI 请求)→ cri-dockerd → dockerd → containerd → container-shim → OCI 运行时(runc/crun)→ 容器进程

2.2 k8s + cri-o

K8s 控制平面 → kubelet → CRI 接口 → CRI-O → container-shim(crio-shim)→ OCI 运行时(runc/crun)→ 容器进程

2.3 docker+containerd

docker 命令 → dockerd → containerd → containerd-shim-runc-v2 → OCI 运行时(runc/crun) → 容器进程

2.4 podman

用户 → podman 命令 → 容器运行时(runc/crun)→ 容器进程
        ↓
    (可选)OCI 钩子 / 垫片```
例如:podman 命令 → conmon → runc/crun → 容器进程
               ↑                ↓
               └────────────────┘(conmon 接管为容器父进程)

具体流程解析:
1. **用户层**:通过 `podman run`、`podman start` 等命令发起操作。
2. **Podman 引擎层**:
   - 直接解析命令并处理镜像拉取、容器配置(如网络、存储、资源限制)等逻辑。
   - 生成符合 OCI 标准的容器配置文件(`config.json`)。
3. **运行时层**:
   - 调用底层 OCI 运行时(默认 `crun` 或 `runc`),将配置文件传递给运行时。
   - 运行时(如 `crun`)负责与 Linux 内核交互,创建 namespace、cgroup 等隔离环境,最终启动容器进程。
4. **容器进程**:在隔离环境中运行用户指定的应用程序。


### 二、与 Docker 调用链的核心差异
| 维度                | Podman 调用链                          | Docker 调用链                          |
|---------------------|----------------------------------------|----------------------------------------|
| 守护进程            | 无(`podman` 是单机命令,直接调用运行时) | 依赖 `dockerd` 守护进程作为中间层       |
| 中间组件            | 无额外垫片(运行时直接作为容器父进程)   | 需 `containerd` + `containerd-shim` 垫片 |
| 权限处理            | 直接以用户身份(或 root)执行操作        | 所有操作通过 `dockerd` 权限代理         |


### 三、特殊场景的调用链扩展
1. **使用 Pod 功能时**:  
   当创建 Pod(`podman pod create`)时,Podman 会启动一个“基础设施容器(infra container)”作为 Pod 内所有容器的共享网络命名空间,调用链变为:

三、镜像仓库(容器的家)

备注:安装docker等软件yum源小窍门,强制替换/etc/yum.repos.d路径下repo文件里$releasever和$basearch版本变量,$releasever是软件包对应操作系统版本,$basearch是x86_64,cpu架构。

3.1国内容器镜像站点

毫秒镜像:Docker镜像极速下载服务 - 毫秒镜像

度度鸟镜像:渡渡鸟镜像同步站

3.2docker registry

Docker Registry用于创建私服版本个人的docker hub,相比Harbor更加轻量,部署更快更容易,便于快速构建临时镜像仓储服务。在上述镜像站点下载Docker Registry:

注意:此处我采取的是registry+ssl自签证书+hosts域名映射+docker-registry-browser页管理

参考优质博客:https://blog.csdn.net/securitit/article/details/109668824Dockerfile Registry 配置HTTPS服务https://blog.csdn.net/securitit/article/details/109668824

参考优质博客:Dockerfile Registry WebUI 之 docker-registry-frontend 高级应用

https://blog.csdn.net/securitit/article/details/109671914

3.2.1 部署方式

(1)制作证书

#证书文件,采取证书config定义文件比命令行生成更规范,对podman使用该镜仓踩坑少

[root@cloudbobo tool]# cat openssl.cnf 
[req]
default_bits = 4096  # 与你现有证书的4096位一致
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca  # 启用扩展字段

[dn]
C = CN
ST = YourProvince  # 与现有证书一致
L = YourCity      # 与现有证书一致
O = YourOrg       # 与现有证书一致
OU = YourDept     # 与现有证书一致
CN = cloudbobo.com  # 主域名

[v3_ca]
subjectAltName = DNS:cloudbobo.com  # 必须添加这行,明确域名
keyUsage = digitalSignature, keyEncipherment  # 证书用途(服务器端)
extendedKeyUsage = serverAuth  # 限定用于服务器认证
basicConstraints = CA:FALSE  # 注意:设为FALSE(你的现有证书误设为CA:TRUE,非根证书无需CA权限,用CA也可,或许更好,更容易通过“严格的,不那么友好的podman"的信任,要不podman不承认该镜仓,无法推拉镜像
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

证书生成:

#推荐,基于上述配置定义文件生成
openssl req -new -x509 -days 3650 -key /data/my_registry/certs/registry.key   -out /data/my_registry/certs/registry.crt -config openssl.cnf
#纯命令行,生成证书
openssl req -newkey rsa:4096 -nodes -sha256 -keyout /data/my_registry/certs/registry.key  -x509 -days 36500 -out /data/my_registry/certs/registry.crt -subj "/C=CN/ST=YourProvince/L=YourCity/O=YourOrg/OU=YourDept/CN=cloudbobo.com"
(2)部署仓储服务
docker run -d \
--name registry \
-p 443:5000 \
--restart=always \
-v /data/my_registry/certs/:/securitit/registry/certs \
-v /data/my_registry/data/:/var/lib/registry
-e REGISTRY_HTTP_TLS_CERTIFICATE=/securitit/registry/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/securitit/registry/certs/registry.key \
docker.bobo.com/registry:v2

#查看容器运行是否成功
[root@cloudbobo tool]# docker ps | grep registry2
30c067109458   cloudbobo.com/registry/registry:v2         "/entrypoint.sh /etc…"   11 hours ago   Up 10 hours   0.0.0.0:443->5000/tcp, [::]:443->5000/tcp                                                            registry2

#修改/etc/hots域名映射,方便直接通过域名拉取镜像,不再需要指定端口
[root@cloudbobo tool]# tail -1 /etc/hosts 
192.168.44.142 cloudbobo.com

#修改docker指定信任私有仓库地址域名
[root@cloudbobo tool]# cat /etc/docker/daemon.json 
{ 
  "live-restore": true,
  "insecure-registries" : [
    "cloudbobo.com"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "10"
  },
  "data-root": "/data/docker"
}

#检查registry仓库端口,是否有返回
[root@cloudbobo tool]# curl https://cloudbobo.com/v2/_catalog -k
{"repositories":["dpanel/dpanel","moelin/1panel","portainer/portainer","registry/registry"]}
(3)部署仓储Web服务
docker run -d \
  --name registryBrowser \
  --restart=always \
  -p 9091:8080 \
  -e DOCKER_REGISTRY_URL=https://192.168.44.142 \
  -e NO_SSL_VERIFICATION=true \
  -e SECRET_KEY_BASE=miaomiao \
  cloudbobo.com/docker-registry-browser:v3
(4)添加podman信任

如果需要使用registry作为docker和podman之间的桥梁,进行镜像推拉,项目建设测试开发等,需要给podman配置无条件信任私有仓库

#将registry的ssl证书复制到podman的证书信任路径中,需要创建域名目录
[root@cloudbobo tool]# ll /etc/containers/certs.d/cloudbobo.com/
总计 4
-rw-r--r--. 1 root root 2346  9月24日 22:48 registry.crt

3.3Harbor

3.3.1 概述

        Harbor是一个开源的企业级容器镜像仓库,提供镜像的存储、分发、安全扫描、漏洞分析等功能。它支持多租户管理、基于角色的访问控制(RBAC)、镜像复制、与CI/CD工具集成等特性,适合企业级容器化场景。

3.3.2 核心组件及作用

  • Core:核心服务(默认启用),负责处理API请求、UI交互、权限控制等。主要功能包括用户管理、项目管理、镜像仓库管理,需配置harbor.yml(域名、证书、存储路径)。
  • PostgreSQL:元数据库(默认启用,v2.0+,默认使用PostgreSQL存储元数据存储如用户信息、项目配置、镜像标签等信息),内嵌于容器,无需额外配置。
  • Registry:镜像存储库(默认启用),依赖registry容器,需配置存储后端(本地/S3/OSS等)。
  • Nginx:反向代理(默认启用),自动生成配置,需绑定HTTPS证书。
  • Job Service:任务管理(默认启用,处理复制和扫描任务)。
  • Redis:缓存和消息队列服务(默认启用),用于临时存储会话数据、加速UI响应,以及协调Job Service的异步任务。

可选组件(需手动启用):

  • Trivy/Clair:漏洞扫描(需在harbor.yml中启用,下载Trivy离线数据库),Trivy(默认):轻量级扫描工具,集成在Harbor中,支持快速检测镜像漏洞,Clair(旧版):通过独立服务分析镜像层漏洞,需额外部署。
  • Notary:镜像签名(需独立配置证书),镜像签名验证组件,提供内容信任(Content Trust)功能,确保镜像来源可信。

部署架构:

  • 单节点部署:docker-compose法,所有组件运行在同一主机,适合测试或小规模环境。
  • 高可用部署:支持多副本Core、独立数据库集群、外部存储(如S3)等,确保服务可靠性,kubernetes部署等。

3.3.3 部署方式

官方参考:Harbor 文档 | Harbor 2.13 文档 - Harbor 中文 (goharbor.cn)

(1)下载harborGithb项目

Github项目加速下载站点(用于快速下载release包):Github Proxy 文件代理加速 (akams.cn)

(2)制作证书(标准法)

步骤 1:准备 openssl.cnf 配置文件
创建配置文件(如 ./openssl.cnf),定义 CA 根证书和服务器证书的生成规则

[req]
# 1. 基础配置段(控制证书请求和生成的全局规则)
default_bits       = 2048            # 生成私钥的默认长度(2048位,平衡安全性和性能)
default_md         = sha256          # 签名时默认使用的哈希算法(SHA-256,现代安全标准)
distinguished_name = req_distinguished_name  # 引用证书持有者身份信息的配置段
x509_extensions    = v3_ca           # 生成自签名证书(如CA根证书)时使用的扩展配置段
string_mask        = utf8only        # 限制证书中字符串字段仅使用UTF-8编码(避免乱码)
prompt             = no              # 关闭交互式提示,直接使用配置文件或命令行参数中的信息

# 2. 证书持有者身份信息(可根据实际场景修改)
[req_distinguished_name]
C  = CN            # Country(国家代码,如CN=中国)
ST = Beijing       # State or Province(省份/地区)
L  = Beijing       # Locality(城市)
O  = MyCompany     # Organization(组织/公司名称)
OU = IT            # Organizational Unit(部门,如IT部)
CN = MyCA          # Common Name(通用名称,CA根证书此处为CA机构名称)

# 3. CA根证书扩展配置(标记为CA证书,定义其权限)
[v3_ca]
subjectKeyIdentifier = hash                  # 生成证书持有者公钥的哈希标识(用于证书链验证)
authorityKeyIdentifier = keyid:always,issuer # 生成签发者(CA)的公钥哈希和 issuer 信息(用于追溯信任链)
basicConstraints = critical, CA:true         # 标记为CA证书(critical表示该字段不可忽略),允许签发其他证书
keyUsage = critical, digitalSignature, keyCertSign  # 允许的密钥用途:数字签名、证书签发(CA核心权限)
extendedKeyUsage = serverAuth, clientAuth    # 扩展用途(可选):允许用于服务器和客户端认证

# 4. 服务器证书扩展配置(限制为服务器用途,禁止作为CA)
[server_cert]
subjectKeyIdentifier = hash                  # 同CA配置,用于标识服务器证书公钥
authorityKeyIdentifier = keyid:always,issuer # 关联签发者(CA)信息,用于客户端验证证书合法性
basicConstraints = critical, CA:false        # 禁止作为CA证书(核心安全约束,防止证书被滥用签发其他证书)
keyUsage = critical, digitalSignature, keyEncipherment,dataEncipherment  # 允许的密钥用途:数字签名(验证身份)、密钥加密(加密通信)
extendedKeyUsage = critical, serverAuth      # 扩展用途:仅允许用于服务器认证(明确证书用途)
subjectAltName = DNS:cloudbobo.io, DNS:*.cloudbobo.io  # 服务器域名列表(支持主域名和通配符,比CN更灵活)

步骤 2:生成 CA 根证书(自签名)
CA 根证书是信任链的起点,用于签发服务器 / 客户端证书,操作如下
说明:
ca.key 是 CA 体系的核心机密,需离线存储(如加密 U 盘),泄露会导致整个信任链失效;
ca.crt 是公钥证书,需分发给所有客户端(如 Docker、浏览器),用于验证由该 CA 签发的证书。

# 1. 创建工作目录(集中管理证书文件)
mkdir -p certs && cd certs

# 2. 生成 CA 根证书私钥(ca.key)
# - genrsa:生成RSA私钥
# - out ca.key:输出私钥文件
# - 4096:私钥长度(比默认2048位更高,适合CA根证书长期使用)
openssl genrsa -out ca.key 4096

# 3. 限制私钥权限(仅所有者可读写,防止泄露)
chmod 600 ca.key

# 4. 生成 CA 根证书(自签名,ca.crt)
# - req:生成证书请求或自签名证书
# - x509:直接生成自签名证书(跳过CSR步骤,CA根证书专用)
# - new:生成新的证书请求(结合x509则为自签名)
# - nodes:不加密私钥(避免每次使用时输入密码,生产环境可移除该参数并设置密码)
# - key ca.key:使用指定的私钥签名
# - days 3650:证书有效期(10年,CA根证书通常长期有效)
# - out ca.crt:输出公钥证书文件
# - config ../openssl.cnf:引用配置文件,应用[v3_ca]扩展
openssl req -x509 -new -nodes \
  -key ca.key \
  -days 3650 \
  -out ca.crt \
  -config ./openssl.cnf

步骤 3:生成服务器证书请求(CSR)
服务器证书需先通过 CSR(证书请求文件)向 CA 申请签名,操作如下:
生成服务器证书请求(CSR)的命令

说明:
CN=harbor.example.com 需与服务器实际域名一致,若需多域名,优先通过配置文件 [server_cert] 的 subjectAltName 定义;server.csr 不含私钥,可安全提交给 CA 用于签发证书。

# 1. 生成服务器证书私钥(server.key)
# - genrsa:生成RSA私钥
# - out server.key:输出服务器私钥文件
# - 2048:私钥长度(符合配置文件default_bits,平衡性能)
openssl genrsa -out server.key 2048

# 2. 限制私钥权限(仅所有者可读写)
chmod 600 server.key

# 3. 生成服务器证书请求(server.csr)
# - req:生成证书请求
# - new:创建新的请求
# - key server.key:使用服务器私钥关联请求
# - out server.csr:输出CSR文件(包含公钥和服务器身份信息)
# - subj:覆盖配置文件中的身份信息(重点修改CN为服务器域名)
# - config ../openssl.cnf:引用配置文件,确保请求格式合规
openssl req -new \
  -key server.key \
  -out server.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=cloudbobo.io" \
  -config ./openssl.cnf

步骤 4:用 CA 根证书签发服务器证书
通过 CA 私钥签名服务器 CSR,生成最终可用的服务器证书:
用 CA 签发服务器证书的命令

# 用 CA 根证书签发服务器证书(生成 server.crt)
# - x509:生成X.509格式证书
# - req:处理证书请求
# - in server.csr:输入服务器证书请求文件
# - CA ca.crt:指定签发者(CA)的公钥证书
# - CAkey ca.key:指定CA的私钥(用于签名)
# - CAcreateserial:首次签发时生成序列号文件(ca.srl),确保每个证书序号唯一
# - out server.crt:输出签发生成的服务器证书
# - days 365:证书有效期(1年,服务器证书建议定期轮换)
# - extfile ../openssl.cnf:指定扩展配置文件
# - extensions server_cert:应用[server_cert]扩展(限制证书用途为服务器认证)
openssl x509 -req \
  -in server.csr \
  -CA ca.crt \
  -CAkey ca.key \
  -CAcreateserial \
  -out server.crt \
  -days 3650 \
  -extfile ./openssl.cnf \
  -extensions server_cert

说明:
核心是通过 -extensions server_cert 强制服务器证书仅用于 serverAuth(服务器认证),且不可作为 CA 证书(CA:false),符合安全规范;生成的 server.crt(公钥)和 server.key(私钥)需部署到服务器(如 Harbor 的 ssl 目录),用于 HTTPS 通信。

(3)docker compose部署
  • 修改harbor配置文件,harbor.yml cp harbor.yml.tmpl harbor.yml

特别注意要将hostname 设置为域名,和证书一致,要不最后证书在docker客户端验证不通过

  • 下载工具镜像prepare

需要先去渡渡鸟或者毫秒镜像拉取goharbor/prepare:对应harbor版本的工具镜像,并重新打包为:docker.io/goharbor/prepare:vxx版本后才能执行./prepare生成配置文件脚本

  • 执行install导入组件镜像并运行

利用install脚本解压缩harbor.v2.13.2.tar.gz镜像集合,该压缩包是所有组件的镜像层集合,执行过程中会边解压边自己docker-load最后会根据./prepare生成的docker-compose.yaml文件run起来

  • 优化目录结构(可以不做)

原来的结构config是在执行prepare的目录的common/comfig路径下,去掉common层实现下述效果

/data/harbor/
├── crt/                  # 证书目录(手动创建,存 server.crt、server.key)
│   ├── server.crt
│   └── server.key
├── data/                 # 所有组件的持久化数据(自动按组件分文件夹)
│   ├── registry/         # Registry 镜像数据
│   ├── database/         # PostgreSQL 数据库数据
│   ├── redis/            # Redis 缓存数据
│   └── jobservice/       # Jobservice 任务数据
├── logs/                 # 所有组件的运行日志(自动按组件分文件夹)
│   ├── core/             # Core 服务日志
│   ├── nginx/            # Nginx 访问日志
│   ├── jobservice/       # Jobservice 任务日志
│   └── registry/         # Registry 镜像服务日志
├── config/               # 所有组件的核心配置(自动按组件分文件夹)
│   ├── nginx/            # Nginx 配置
│   ├── core/             # Core 服务配置
│   └── registry/         # Registry 配置
├── harbor.yml            # 你的核心配置文件(修改后)
├── docker-compose.yml    # 重新生成的 Compose 文件
└── 其他安装文件(install.sh、prepare 等)

先暂停harbor,修改docker-compose.yaml中的volum映射

再将./prepare生成的common里面的配置文件归档复制到/data/harbor/config下,最后再启动服务

(4)添加docker和podman证书信任

[root@cloudbobo harbor]# mkdir /etc/docker/certs.d/cloudbobo.io
[root@cloudbobo harbor]# cp /data/harbor/crt/
ca/         server.crt  server.key  
[root@cloudbobo harbor]# cp /data/harbor/crt/ca/
ca.crt       ca.key       ca.srl       openssl.cnf  server.csr   
[root@cloudbobo harbor]# cp /data/harbor/crt/ca/ca.crt /etc/docker/certs.d/cloudbobo.io/
(5)给harbor项目db增加可视化工具pgadmin

毫秒镜像:docker.1ms.run/dpage/pgadmin4

编辑docker-compose.yaml,增加以下内容:

#添加pgadmin后,直接在docker的harbor内网环境下安全连接pgsql  
pgadmin:
    image: cloudbobo.io/library/pgadmin4:9  # 官方pgAdmin镜像重打包
    container_name: harbor-pgadmin
    restart: always
    environment:
      - PGADMIN_DEFAULT_EMAIL=pg@admin.com  # 登录pgAdmin的邮箱
      - PGADMIN_DEFAULT_PASSWORD=Harbor12345  # 登录pgAdmin的密码(可自定义)
    volumes:
      - /data/harbor/data/pgadmin:/var/lib/pgadmin  # 持久化pgAdmin数据
    networks:
      - harbor  # 加入Harbor默认网络,与postgres容器互通
    ports:
      - 5050:80
    depends_on:
      - postgresql  # 确保数据库启动后再启动pgAdmin
    healthcheck:
      test: ["CMD", "sh", "-c", "nc -z localhost 80 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 90s

执行:docker compose stop ;  docker compose start重启所有组件,同时将新加入的pgadmin启动

配置:

四、容器统一平台(可视化管理)

4.1 Dpanel

Dpanel 是一个多功能的面板工具,通常用于服务器管理、网站托管或自动化任务。

4.1.1Dpanel核心功能

  • 服务器管理:支持 Linux 和 Windows 服务器的监控、配置及维护。
  • 网站托管:提供一键部署网站、SSL 证书管理及域名绑定功能。
  • 数据库管理:集成 MySQL、PostgreSQL 等数据库的可视化管理工具。
  • 自动化任务:支持定时任务、脚本执行和日志分析。

4.1.2部署Dpanel

docker run \
  -p 8807:8080 \
  --name dpanel \
  --restart=always
  -e APP_NAME=dpanel \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /data/dpanel:/dpanel \
  -d cloudbobo.comdpanel/dpanel:v2

4.2 Portainer

Portainer 是一个轻量级的容器管理工具,提供基于 Web 的用户界面,用于管理 Docker 和 Kubernetes 环境。它简化了容器化应用的部署、监控和维护,适合开发者和运维人员使用。

4.2.1Portainer核心功能

  • 容器管理:查看、启动、停止、删除容器。
  • 镜像管理:拉取、构建、删除镜像。
  • 网络与存储:配置 Docker 网络和卷。
  • 用户权限:支持多用户和角色权限控制。
  • 集群支持:兼容 Docker Swarm 和 Kubernetes。

4.2.2部署Portainer

docker run -d \
  -p 9000:9000 \
  --name portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /data/portainer:/data \
  cloudbobo.com/outlovecn/portainer-cn:latest

4.3 1Panel

1Panel 是一个现代化的开源 Linux 服务器运维管理面板,提供可视化操作界面,支持 Web 端管理服务器、容器、数据库、文件等。其定位类似于宝塔面板,但更注重轻量化和容器化部署,适合开发者及运维人员快速搭建和管理服务。

4.3.1 1Panel核心功能

  • 服务器监控:实时查看 CPU、内存、磁盘等资源使用情况。
  • 容器管理:集成 Docker,支持容器及镜像的创建、启动、停止等操作。
  • 网站管理:支持 Nginx、PHP、MySQL 等环境的快速部署。
  • 文件管理:内置文件浏览器,支持上传、下载、编辑等操作。
  • 数据库管理:可视化操作 MySQL、PostgreSQL 等数据库。
  • 计划任务:通过界面配置定时任务(如备份、脚本执行)。

    4.3.2 部署1Panel

    docker run -d \
      --name 1panel \
      -p 8888:10086 \
      -v /data/1panel:/data/ \
      -v /var/run/docker.sock:/var/run/docker.sock \
      cloudbobo.com/moelin/1panel

    五、docker服务(安装相关问题)

    5.1docker、docker-compose安装教程

    5.1.1 卸载旧版

    #查看当前旧版本安装情况
    dnf list installed | grep docker
    #删除安装过docker的相关包
    dnf -y remove containerd.io.x86_64 \ docker-buildx-plugin.x86_64 \ docker-ce.x86_64 \ docker-ce-c
    dnf  remove -y docker*
    #删除docker相关的镜像和容器,进入 /var/lib 目录,删除 docker 目录,这是存放容器和镜像的目录
    rm -rf docker
    

    5.1.2 docker安装

    5.1.2.1安装方式一:dnf/yum方式
    yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    yum install -y yum-utils
    
    yum list docker-ce --showduplicates | sort -r
    
    

    备注:安装最新版本直接执行:dnf -y install docker-ce

    如果想要安装旧版本或者历史版本:版本号只要“:”后面的那部分

    yum install docker-ce-23.0.3-1.el7 docker-ce-23.0.3-1.el7 containerd.io

    5.1.2.2安装方式二:二进制包方式

    1.下载docker二进制包

    arm:官方下载地址:https://download.docker.com/linux/static/stable/aarch64/

    x86:官方下载地址:  https://download.docker.com/linux/static/stable/x86_64/
    2.使用wget下载或者直接打开网站下载对应版本到服务器,如:19.03.8

    wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.8.tgz
    或者:

    3.解压下载好的docker包
    
    tar zxf docker-19.03.8.tgz
    4.将docker目录下的二进制文件移动到/usr/bin/
    
    mv docker/* /usr/bin/
    5.测试docker
    
    docker
    
    6.编写docker守护进程daemon.json(insecure-registries:[私有镜像仓库地址])
    
    [root@localhost sophon]# cat /etc/docker/daemon.json 
    {
    "exec-opts": ["native.cgroupdriver=systemd"],
    "graph": "/data/docker_storage",
    "log-driver": "json-file",
    "log-opts": {
    "max-size": "100m"
    },
    "storage-driver": "overlay2",
    "storage-opts": [
    "overlay2.override_kernel_check=true"
    ],
    "insecure-registries" : ["192.168.4.17:8090","111.136.254.111:8090"],
    "registry-mirrors": ["https://g427vmjy.mirror.aliyuncs.com"],
    "live-restore": true
    }
    
    7.编写docker.server,配置开机启动
    
    [root@localhost sophon]# cat /lib/systemd/system/docker.service 
    [Unit]
    Description=Docker Application Container Engine
    Documentation=https://docs.docker.com
    After=network-online.target firewalld.service
    Wants=network-online.target
    
    [Service]
    Type=notify
    EnvironmentFile=-/etc/sysconfig/docker
    EnvironmentFile=-/etc/sysconfig/docker-storage
    EnvironmentFile=-/etc/sysconfig/docker-network
    Environment=GOTRACEBACK=crash
    
    ExecStart=/usr/bin/dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock
    ExecReload=/bin/kill -s HUP $MAINPID
    LimitNOFILE=1048576
    LimitNPROC=1048576
    LimitCORE=infinity
    # set delegate yes so that systemd does not reset the cgroups of docker containers
    Delegate=yes
    # kill only the docker process, not all processes in the cgroup
    KillMode=process
    
    [Install]
    WantedBy=multi-user.target
    
    8.启动docker服务
    
    systemctl daemon-reload
    systemctl restart docker
    systemctl enable docker

    六、docker容器(宿主机:容器目录持久化常见问题)

    Docker 容器内的进程默认使用非 root 用户(如 alertmanager 容器用 nobody 或专用用户,UID 通常为 65534 或 1000 等),而宿主机目录默认权限可能为 root:root(UID=0)或其他用户,导致容器内用户无读写权限,容器进程没有宿主机持久化目录的写入权限,日志信息反复提示:映射的目录读写permission denied导致容器报错:

    解决方案:

    1. 提前创建宿主机目录并匹配 UID
    核心思路:在宿主机手动创建挂载目录,修改其所有者 UID 与容器内进程的 UID 一致(无需知道用户名,只需 UID 匹配)。
    步骤:
    ① 查看容器内进程的 UID(以 alertmanager 为例):
    bash
    # 临时启动容器查看用户 UID
    docker run --rm --entrypoint id prom/alertmanager:v0.28.0
    # 输出示例:uid=1000(alertmanager) gid=1000(alertmanager) ...
    # 可知容器内用户 UID=1000
    ② 在宿主机创建目录并修改权限:
    bash
    # 创建持久化目录(配置、数据分开更清晰)
    mkdir -p /data/alertmanager/{config,data}

    # 修改目录所有者 UID 为容器内 UID(1000)
    sudo chown -R 1000:1000 /data/alertmanager

    # 赋予读写权限(无需 777,644 或 755 即可)
    sudo chmod -R 755 /data/alertmanager

    七、打包docker容器镜像(基于alert的webhook服务器)

    1. 配置文件 (config.yaml)

    # 数据库配置
    database:
      host: "localhost"
      port: 5432
      name: "alertmanager"
      user: "postgres"
      password: "password"
      table_name: "alerts"
    
    # Webhook 服务器配置
    server:
      host: "0.0.0.0"
      port: 5000
      debug: false
    
    # 日志配置
    logging:
      level: "INFO"

    2. 数据库初始化脚本 (init_db.sql)

    -- 创建告警表
    CREATE TABLE IF NOT EXISTS alerts (
        id SERIAL PRIMARY KEY,
        fingerprint VARCHAR(64) NOT NULL,
        alertname VARCHAR(256) NOT NULL,
        instance VARCHAR(256),
        severity VARCHAR(64),
        status VARCHAR(32) NOT NULL,
        summary TEXT,
        description TEXT,
        starts_at TIMESTAMP NOT NULL,
        ends_at TIMESTAMP,
        generator_url TEXT,
        labels JSONB,
        annotations JSONB,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        
        -- 唯一约束,确保 fingerprint + status 组合唯一
        UNIQUE(fingerprint, status)
    );

    3. Python Webhook 程序 (webhook_app.py)

    #!/usr/bin/env python3
    import logging
    import psycopg2
    import yaml
    from flask import Flask, request, jsonify
    from datetime import datetime
    from contextlib import contextmanager
    import sys
    import os
    
    # 设置日志格式
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            logging.StreamHandler(sys.stdout)
        ]
    )
    
    logger = logging.getLogger(__name__)
    
    def load_config():
        """加载配置文件"""
        try:
            config_path = os.getenv('CONFIG_PATH', 'config.yaml')
            logger.info(f"Loading configuration from: {config_path}")
            with open(config_path, 'r') as f:
                config = yaml.safe_load(f)
            logger.info("Configuration loaded successfully")
            return config
        except Exception as e:
            logger.error(f"Failed to load configuration: {e}")
            sys.exit(1)
    
    # 加载配置
    config = load_config()
    app = Flask(__name__)
    
    @contextmanager
    def get_db_connection():
        """数据库连接上下文管理器"""
        conn = None
        db_config = config['database']
        logger.debug(f"Attempting to connect to database: {db_config['host']}:{db_config['port']}")
        
        try:
            conn = psycopg2.connect(
                host=db_config['host'],
                port=db_config['port'],
                dbname=db_config['name'],
                user=db_config['user'],
                password=db_config['password']
            )
            logger.debug("Database connection established successfully")
            yield conn
        except Exception as e:
            logger.error(f"Database connection failed: {e}")
            raise
        finally:
            if conn:
                conn.close()
                logger.debug("Database connection closed")
    
    def init_database():
        """初始化数据库表"""
        logger.info("Starting database initialization")
        try:
            with get_db_connection() as conn:
                with conn.cursor() as cur:
                    logger.info("Creating database table from init_db.sql")
                    with open('init_db.sql', 'r') as f:
                        sql_content = f.read()
                        logger.debug(f"Executing SQL: {sql_content}")
                        cur.execute(sql_content)
                    conn.commit()
                    logger.info("Database table created successfully")
        except Exception as e:
            logger.error(f"Database initialization failed: {e}")
            raise
    
    def save_alert_to_db(alert_data):
        """保存告警到数据库,依赖数据库唯一约束去重"""
        insert_sql = f"""
        INSERT INTO {config['database']['table_name']} (
            fingerprint, alertname, instance, severity, status, summary,
            description, starts_at, ends_at, generator_url, labels, annotations
        ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
        
        logger.debug(f"Preparing to save alert: {alert_data['alertname']} with fingerprint: {alert_data['fingerprint']}")
        
        try:
            with get_db_connection() as conn:
                with conn.cursor() as cur:
                    logger.debug(f"Executing SQL insert for alert: {alert_data['alertname']}")
                    cur.execute(insert_sql, (
                        alert_data['fingerprint'],
                        alert_data['alertname'],
                        alert_data.get('instance'),
                        alert_data.get('severity'),
                        alert_data['status'],
                        alert_data.get('summary'),
                        alert_data.get('description'),
                        alert_data['starts_at'],
                        alert_data.get('ends_at'),
                        alert_data.get('generator_url'),
                        alert_data.get('labels', {}),
                        alert_data.get('annotations', {})
                    ))
                    conn.commit()
                    logger.info(f"Alert saved successfully: {alert_data['alertname']} - {alert_data['status']} - {alert_data['fingerprint']}")
                    return True
        except psycopg2.IntegrityError:
            logger.info(f"Alert already exists, skipping insert: {alert_data['fingerprint']} - {alert_data['status']}")
            return False
        except Exception as e:
            logger.error(f"Failed to save alert {alert_data['alertname']}: {e}")
            return False
    
    def parse_alert_data(alert):
        """解析告警数据"""
        logger.debug(f"Parsing alert data: {alert.get('fingerprint', 'unknown')}")
        
        labels = alert.get('labels', {})
        annotations = alert.get('annotations', {})
        
        alert_data = {
            'fingerprint': alert.get('fingerprint', ''),
            'alertname': labels.get('alertname', 'unknown'),
            'instance': labels.get('instance'),
            'severity': labels.get('severity'),
            'status': alert.get('status', 'unknown'),
            'summary': annotations.get('summary'),
            'description': annotations.get('description'),
            'starts_at': datetime.fromisoformat(alert['startsAt'].replace('Z', '+00:00')),
            'ends_at': datetime.fromisoformat(alert['endsAt'].replace('Z', '+00:00')) if alert.get('endsAt') else None,
            'generator_url': alert.get('generatorURL'),
            'labels': labels,
            'annotations': annotations
        }
        
        logger.debug(f"Parsed alert: {alert_data['alertname']} - {alert_data['status']}")
        return alert_data
    
    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        """处理 Alertmanager Webhook 请求"""
        logger.info("Received webhook request")
        
        try:
            data = request.get_json()
            if not data:
                logger.warning("Webhook request contains no JSON data")
                return jsonify({"error": "No JSON data received"}), 400
            
            alerts = data.get('alerts', [])
            logger.info(f"Processing {len(alerts)} alerts from webhook")
            
            saved_count = 0
            for i, alert in enumerate(alerts):
                logger.debug(f"Processing alert {i+1}/{len(alerts)}")
                alert_data = parse_alert_data(alert)
                if save_alert_to_db(alert_data):
                    saved_count += 1
                    logger.debug(f"Alert {i+1} saved successfully")
                else:
                    logger.debug(f"Alert {i+1} was duplicate or failed to save")
            
            logger.info(f"Webhook processing completed: {saved_count}/{len(alerts)} alerts saved")
            return jsonify({"status": "success", "saved": saved_count}), 200
            
        except Exception as e:
            logger.error(f"Webhook processing error: {e}", exc_info=True)
            return jsonify({"error": "Internal server error"}), 500
    
    @app.route('/health', methods=['GET'])
    def health_check():
        """健康检查端点"""
        logger.debug("Health check requested")
        try:
            with get_db_connection() as conn:
                with conn.cursor() as cur:
                    cur.execute("SELECT 1")
            logger.debug("Health check passed")
            return jsonify({"status": "healthy"}), 200
        except Exception as e:
            logger.error(f"Health check failed: {e}")
            return jsonify({"status": "unhealthy"}), 503
    
    @app.route('/ready', methods=['GET'])
    def ready_check():
        """就绪检查端点"""
        logger.debug("Ready check requested")
        try:
            with get_db_connection() as conn:
                with conn.cursor() as cur:
                    cur.execute(f"SELECT COUNT(*) FROM {config['database']['table_name']}")
                    count = cur.fetchone()[0]
            logger.debug(f"Ready check passed, table contains {count} alerts")
            return jsonify({"status": "ready", "alert_count": count}), 200
        except Exception as e:
            logger.error(f"Ready check failed: {e}")
            return jsonify({"status": "not ready"}), 503
    
    if __name__ == '__main__':
        logger.info("Starting Alertmanager Webhook Service")
        
        # 初始化数据库
        try:
            init_database()
        except Exception as e:
            logger.error(f"Failed to initialize database: {e}")
            sys.exit(1)
        
        # 启动 Webhook 服务器
        server_config = config['server']
        logger.info(f"Starting webhook server on {server_config['host']}:{server_config['port']}")
        
        app.run(
            host=server_config['host'],
            port=server_config['port'],
            debug=server_config['debug']
        )

    4. Dockerfile

    FROM python:3.9-slim
    
    WORKDIR /app
    
    # 安装系统依赖
    RUN apt-get update && apt-get install -y \
        postgresql-client \
        && rm -rf /var/lib/apt/lists/*
    
    # 复制应用文件
    COPY webhook_app.py .
    COPY config.yaml .
    COPY init_db.sql .
    
    # 安装 Python 依赖
    RUN pip install --no-cache-dir \
        flask==2.3.3 \
        psycopg2-binary==2.9.7 \
        pyyaml==6.0.1
    
    # 创建非root用户
    RUN useradd -m -u 1000 webhookuser && chown -R webhookuser:webhookuser /app
    USER webhookuser
    
    # 暴露端口
    EXPOSE 5000
    
    # 健康检查
    HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
        CMD curl -f http://localhost:5000/health || exit 1
    
    # 启动命令
    CMD ["python", "webhook_app.py"]

    5. Docker Compose 文件 (docker-compose.yml)

    version: '3.8'
    
    services:
      webhook:
        build: .
        ports:
          - "5000:5000"
        environment:
          - CONFIG_PATH=/app/config.yaml
        depends_on:
          - postgres
        restart: unless-stopped
    
      postgres:
        image: postgres:13
        environment:
          - POSTGRES_DB=alertmanager
          - POSTGRES_USER=postgres
          - POSTGRES_PASSWORD=password
        ports:
          - "5432:5432"
        volumes:
          - postgres_data:/var/lib/postgresql/data
        restart: unless-stopped
    
    volumes:
      postgres_data:

    6. 构建和运行脚本 (build_and_run.sh)

    #!/bin/bash
    
    # 构建镜像
    echo "Building Docker image..."
    docker build -t alertmanager-webhook .
    
    # 运行服务
    echo "Starting services..."
    docker-compose up -d
    
    # 检查服务状态
    echo "Checking services..."
    docker-compose ps
    
    # 显示日志
    echo "Showing logs..."
    docker-compose logs -f webhook

    7. 测试脚本 (test_webhook.py)

    #!/usr/bin/env python3
    import requests
    import json
    import logging
    
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    def test_webhook():
        url = "http://localhost:5000/webhook"
        
        test_data = {
            "receiver": "webhook",
            "status": "firing",
            "alerts": [
                {
                    "status": "firing",
                    "labels": {
                        "alertname": "HighMemory",
                        "instance": "server1",
                        "severity": "warning"
                    },
                    "annotations": {
                        "summary": "High memory usage",
                        "description": "Memory usage is above 80%"
                    },
                    "startsAt": "2023-10-01T10:00:00Z",
                    "endsAt": "0001-01-01T00:00:00Z",
                    "generatorURL": "http://prometheus:9090",
                    "fingerprint": "test_fingerprint_123"
                }
            ]
        }
        
        logger.info("Sending test webhook request")
        response = requests.post(url, json=test_data)
        logger.info(f"Response Status: {response.status_code}")
        logger.info(f"Response Body: {response.json()}")
    
    if __name__ == "__main__":
        test_webhook()

    构建和运行:

    # 方式1: 使用 Docker Compose (推荐)
    docker-compose up -d
    
    # 方式2: 手动构建和运行
    docker build -t alertmanager-webhook .
    docker run -p 5000:5000 alertmanager-webhook
    
    # 方式3: 使用构建脚本
    chmod +x build_and_run.sh
    ./build_and_run.sh
    
    测试服务:
    bash
    # 健康检查
    curl http://localhost:5000/health
    
    # 就绪检查
    curl http://localhost:5000/ready
    
    # 测试 webhook
    python test_webhook.py

    总结

    数字化转型背景下,容器技术已成为现代应用开发的核心标准。云原生作为方法论,通过弹性、可观测性和韧性设计,充分发挥云计算潜力。其技术栈覆盖应用定义、编排调度、服务网格、可观测性及CI/CD,而容器运行时是底层关键组件。生产环境中,K8s集群推荐containerd+CRI-O组合,平衡性能与生态;开发测试可采用nerdctl保持Docker兼容性,podman的安全性更高但是生态不成熟,部署服务会受到大量权限问题限制,谨慎考虑。

    Logo

    码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

    更多推荐