k8s

webhook开发 侧车容器

axing
2023-09-01 / 0 评论 / 7 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年09月29日,已超过115天没有更新,若内容或图片失效,请留言反馈。

一、 先准备好webhook环境

之前博客有写

二、 准备业务镜像

1. 下载镜像
docker pull centos:7

2.配置yum源
rm -f /etc/yum.repos.d/CentOS-Base.repo
cat > /etc/yum.repos.d/CentOS-Base.repo <<EOF
[base]
name=CentOS-\$releasever - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/\$releasever/os/\$basearch/
        http://mirrors.aliyuncs.com/centos/\$releasever/os/\$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/\$releasever/os/\$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

#released updates 
[updates]
name=CentOS-\$releasever - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/\$releasever/updates/\$basearch/
        http://mirrors.aliyuncs.com/centos/\$releasever/updates/\$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/\$releasever/updates/\$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

#additional packages that may be useful
[extras]
name=CentOS-\$releasever - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/\$releasever/extras/\$basearch/
        http://mirrors.aliyuncs.com/centos/\$releasever/extras/\$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/\$releasever/extras/\$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-\$releasever - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/\$releasever/centosplus/\$basearch/
        http://mirrors.aliyuncs.com/centos/\$releasever/centosplus/\$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/\$releasever/centosplus/\$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

#contrib - packages by Centos Users
[contrib]
name=CentOS-\$releasever - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/\$releasever/contrib/\$basearch/
        http://mirrors.aliyuncs.com/centos/\$releasever/contrib/\$basearch/
        http://mirrors.cloud.aliyuncs.com/centos/\$releasever/contrib/\$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
EOF

3.安装需要的应用
yum install -y cronie   openssh-server  ssh  wget

4.安装mysql5.7
wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
rpm -ivh mysql57-community-release-el7-11.noarch.rpm
yum repolist enabled | grep "mysql.*-community.*"
yum install mysql-community-server

5.创建用户mysql并且给权限
chown -R mysql:mysql /var/lib/mysql
chmod -R 755 /var/lib/mysql

6.后台启动数据库
/usr/sbin/mysqld --user=mysql &

7.初始化数据库
/usr/sbin/mysqld --initialize

8.查看初始化的密码
grep 'temporary password' /var/log/mysqld.log
mysql -u root -p

9.把初始化的密码输入上
ALTER USER 'root'@'localhost' IDENTIFIED BY 'newpassword';

10.测试创建一个账号还有一个库和一个表
-- 创建用户 'axing' 并设置密码
CREATE USER 'axing'@'localhost' IDENTIFIED BY 'Egon@123';
-- 授予所有权限给用户 'axing' 在所有数据库中
GRANT ALL PRIVILEGES ON *.* TO 'axing'@'localhost' WITH GRANT OPTION;
-- 刷新权限表以使更改生效
FLUSH PRIVILEGES;
--创建库
CREATE DATABASE my_database;
USE my_database;
--创建表
CREATE TABLE my_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

三、准备测车镜像
(1)准备数据库备份的脚本
cat jiaoben.py
脚本里面有些地方还可以优化,嫌麻烦没完善了。(按这个是可以跑起来的修改好对应的账号密码还有IP地址)

#!/bin/bash
/usr/sbin/mysqld --user=mysql &
/usr/sbin/sshd

# 配置
#!/bin/bash

# 启动 MySQL 和 SSH 服务
/usr/sbin/mysqld --user=mysql &   # 启动 MySQL 数据库服务
/usr/sbin/sshd                    # 启动 SSH 服务

# 配置变量
MYSQL_HOST="127.0.0.1"            # MySQL 数据库主机地址
MYSQL_USER="root"                 # MySQL 数据库用户名
MYSQL_PASSWORD="Egon@123"         # MySQL 数据库密码
BACKUP_DIR="/backup"              # 本地备份文件存储目录
REMOTE_HOST="192.168.110.110"     # 远程主机地址
REMOTE_USER="root"                # 远程主机用户名
REMOTE_DIR="/remote/backup/dir"   # 远程备份文件存储目录

host_info="root:1"                # 存储用户名和密码的变量
target_ip="192.168.110.110"       # 目标主机 IP 地址

# 提取用户名和密码
user=$(echo $host_info | awk -F: '{print $1}')  # 从 host_info 中提取用户名
pass=$(echo $host_info | awk -F: '{print $2}')  # 从 host_info 中提取密码

# SSH 密钥路径
key_path="/root/.ssh/id_rsa"           # SSH 私钥路径
pub_key_path="/root/.ssh/id_rsa.pub"   # SSH 公钥路径

# 检查并生成 SSH 密钥对
if [ ! -f "$pub_key_path" ]; then
    echo "SSH 公钥文件不存在,生成新的密钥对..."   # 如果公钥文件不存在,则生成新的密钥对
    ssh-keygen -t rsa -b 4096 -f "$key_path" -N ""   # 生成新的 SSH 密钥对
else
    echo "SSH 公钥文件已存在。"  # 如果公钥文件已存在,则不进行生成
fi

# 使用 expect 自动化 ssh-copy-id 过程
expect << EOF
    spawn ssh-copy-id -i $pub_key_path $user@$target_ip
    expect {
        "yes/no" {send "yes\n"; exp_continue}
        "password:" {send "$pass\n"}
    }
    expect eof
EOF

# 检查 expect 命令的退出状态
if [ $? -eq 0 ]; then
    echo "公钥已成功复制到目标主机。"  # 如果 expect 命令成功执行,打印成功消息
else
    echo "公钥复制过程失败。"  # 如果 expect 命令失败,打印失败消息
fi

# 创建备份目录
mkdir -p $BACKUP_DIR  # 创建备份存储目录(如果不存在)

# 获取当前时间
TIMESTAMP=$(date +"%F-%H-%M-%S")  # 获取当前时间,格式为 YYYY-MM-DD-HH-MM-SS

# 备份数据库
mysqldump -h $MYSQL_HOST --all-databases > $BACKUP_DIR/db_backup_$TIMESTAMP.sql  # 使用 mysqldump 工具备份所有数据库到本地文件

# 创建远程备份目录(如果不存在)
ssh root@$REMOTE_HOST "mkdir -p $REMOTE_DIR"  # 在远程主机上创建备份存储目录(如果不存在)

# 上传备份到远程主机
scp $BACKUP_DIR/db_backup_$TIMESTAMP.sql $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR  # 使用 scp 工具将备份文件上传到远程主机

# 删除本地备份文件
rm $BACKUP_DIR/db_backup_$TIMESTAMP.sql  # 删除本地备份文件,以节省磁盘空间

(2)准备定时任务的文件
cat crontab

0 2 * * * /usr/local/bin/jiaoben.py

(3)构建镜像
cat dockerfile

可以偷懒直接用上面业务镜像
# 使用 CentOS 作为基础镜像
FROM registry.cn-guangzhou.aliyuncs.com/xingcangku/axingcangku:latest

# 复制备份脚本
COPY jiaoben.sh /usr/local/bin/jiaoben.sh
RUN chmod +x /usr/local/bin/jiaoben.sh

# 复制 crontab 文件
COPY crontab /etc/cron.d/backup-cron
RUN chmod 0644 /etc/cron.d/backup-cron

# 确保 cron 服务在容器中运行
RUN crontab /etc/cron.d/backup-cron

# 创建 MySQL 配置文件 .my.cnf
RUN echo "[client]" > /root/.my.cnf && \
    echo "user=root" >> /root/.my.cnf && \
    echo "password=Egon@123" >> /root/.my.cnf && \
    chmod 600 /root/.my.cnf

# 启动 cron 服务,并保持容器运行
CMD ["crond", "-n"]

四、准备webhook.py文件
这个是webhook pod里面运行的python文件
有两个功能
1.检测到带有app:mysql标签的pod的时候,会自动创建一个侧车容器,会定时备份数据给远程主机。
2.如果创建的pod没有带有标签会自动添加标签
3.labels 中存在 'app: test' 标签,则添加 annotation reloader.stakater.com/auto: "true"

# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
import base64
import json
import ssl
import kubernetes.client
from kubernetes.client.rest import ApiException
from kubernetes import config
import os
import threading
import time

# 创建 Flask 应用实例
app = Flask(__name__)

# 加载 Kubernetes 集群内的配置
config.load_incluster_config()

@app.route('/mutate', methods=['POST'])
def mutate_pod():
    try:
        # 从 AdmissionReview 请求中解析 JSON 数据
        admission_review = request.get_json()
        pod = admission_review['request']['object']
        patch = []

        # 初始化 metadata, labels, 和 annotations 如果它们不存在
        # 检查 pod 是否有 metadata 部分,如果没有则添加
        if 'metadata' not in pod:
            patch.append({
                "op": "add",
                "path": "/metadata",
                "value": {}
            })
        
        # 检查 metadata 是否有 labels 部分,如果没有则添加
        if 'labels' not in pod.get('metadata', {}):
            patch.append({
                "op": "add",
                "path": "/metadata/labels",
                "value": {}
            })

        # 检查 metadata 是否有 annotations 部分,如果没有则添加
        if 'annotations' not in pod.get('metadata', {}):
            patch.append({
                "op": "add",
                "path": "/metadata/annotations",
                "value": {}
            })

        # 获取现有的 labels 和 annotations
        labels = pod.get('metadata', {}).get('labels', {})
        annotations = pod.get('metadata', {}).get('annotations', {})

        # 如果 labels 中不存在 'environment' 标签,则添加
        if 'environment' not in labels:
            patch.append({
                "op": "add",
                "path": "/metadata/labels/environment",
                "value": "production"
            })

        # 如果 labels 中存在 'app: test' 标签,则添加 annotation reloader.stakater.com/auto: "true"
        if labels.get('app') == 'test':
            if 'reloader.stakater.com/auto' not in annotations:
                patch.append({
                    "op": "add",
                    "path": "/metadata/annotations/reloader.stakater.com~1auto",
                    "value": "true"
                })

        # 如果 labels 中存在 'app: mysql' 标签,则在 Pod 中添加 sidecar 容器
        if labels.get('app') == 'mysql':
            container = {
                "name": "sidecar-container",
                "image": "registry.cn-guangzhou.aliyuncs.com/xingcangku/axingcangku:v1.1",
                "ports": [{"containerPort": 8080}]
            }
            
            patch.append({
                "op": "add",
                "path": "/spec/containers/-",
                "value": container
            })

        # 构造 AdmissionReview 响应
        admission_response = {
            "apiVersion": "admission.k8s.io/v1",
            "kind": "AdmissionReview",
            "response": {
                "uid": admission_review['request']['uid'],
                "allowed": True,
                "patchType": "JSONPatch",
                "patch": base64.b64encode(json.dumps(patch).encode()).decode()  # 对 patch 进行 Base64 编码
            }
        }

        # 返回 AdmissionReview 响应
        return jsonify(admission_response)
    except Exception as e:
        # 如果发生异常,返回包含错误消息的响应
        return jsonify({
            "apiVersion": "admission.k8s.io/v1",
            "kind": "AdmissionReview",
            "response": {
                "uid": admission_review['request']['uid'],
                "allowed": False,
                "status": {
                    "message": str(e)  # 将异常消息作为响应的一部分
                }
            }
        }), 500

def keep_alive():
    while True:
        time.sleep(3600)  # 每小时休眠一次,保持进程活跃

if __name__ == '__main__':
    # 创建 SSL 上下文并加载证书
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain('/certs/tls.crt', '/certs/tls.key')  # 加载 SSL 证书和私钥

    # 使用 SSL 上下文启动 Flask 应用,监听所有网络接口的 8080 端口
    app.run(host='0.0.0.0', port=8080, ssl_context=context)

五、总结:

这个测车容器主要实现了,在使用mysql为业务pod的时候有数据备份的功能。实现了定时自动备份数据的功能。

m0jizsl0.png

六、更新

可以安装ansible直接执行下面的命令部署,但是因为镜像里面的webhook.py文件的定制。
如果需要自定义功能需要按上面的步骤创建镜像然后在下面的代码中更新镜像地址即可。
---
- name: Deploy Kubernetes Webhook
  hosts: localhost
  connection: local
  become: yes
  tasks:
    # 1. 生成 CA 私钥
    - name: Generate CA private key
      command: openssl genrsa -out ca.key 2048
      args:
        creates: ca.key

    # 2. 生成自签名 CA 证书
    - name: Generate self-signed CA certificate
      command: >
        openssl req -x509 -new -nodes -key ca.key -subj "/CN=webhook-service.default.svc" -days 36500 -out ca.crt
      args:
        creates: ca.crt

    # 3. 创建 OpenSSL 配置文件
    - name: Create OpenSSL configuration file
      copy:
        dest: webhook-openssl.cnf
        content: |
          [req]
          default_bits = 2048
          prompt = no
          default_md = sha256
          req_extensions = req_ext
          distinguished_name = dn

          [dn]
          C = CN
          ST = Shanghai
          L = Shanghai
          O = egonlin
          OU = egonlin
          CN = webhook-service.default.svc

          [req_ext]
          subjectAltName = @alt_names

          [alt_names]
          DNS.1 = webhook-service
          DNS.2 = webhook-service.default
          DNS.3 = webhook-service.default.svc
          DNS.4 = webhook-service.default.svc.cluster.local

          [req_distinguished_name]
          CN = webhook-service.default.svc

          [v3_req]
          keyUsage = critical, digitalSignature, keyEncipherment
          extendedKeyUsage = serverAuth
          subjectAltName = @alt_names

          [v3_ext]
          authorityKeyIdentifier=keyid,issuer:always
          basicConstraints=CA:FALSE
          keyUsage=keyEncipherment,dataEncipherment
          extendedKeyUsage=serverAuth,clientAuth
          subjectAltName=@alt_names

    # 4. 生成 Webhook 服务的私钥
    - name: Generate webhook service private key
      command: openssl genrsa -out webhook.key 2048
      args:
        creates: webhook.key

    # 5. 使用配置文件生成 CSR
    - name: Generate CSR
      command: openssl req -new -key webhook.key -out webhook.csr -config webhook-openssl.cnf
      args:
        creates: webhook.csr

    # 6. 生成 webhook 证书
    - name: Generate webhook certificate
      command: >
        openssl x509 -req -in webhook.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out webhook.crt -days 36500 -extensions v3_ext -extfile webhook-openssl.cnf
      args:
        creates: webhook.crt

    # 7. 删除旧的 Kubernetes Secret
    - name: Delete existing Kubernetes Secret
      command: kubectl delete secret webhook-certs --namespace=default
      ignore_errors: true

    # 8. 创建 Kubernetes Secret
    - name: Create Kubernetes Secret for webhook certificates
      command: >
        kubectl create secret tls webhook-certs --cert=webhook.crt --key=webhook.key --namespace=default --dry-run=client -o yaml
      register: secret_yaml

    - name: Apply Kubernetes Secret
      command: kubectl apply -f -
      args:
        stdin: "{{ secret_yaml.stdout }}"

    # 9. 创建 webhook Deployment 和 Service
    - name: Create webhook deployment YAML
      copy:
        dest: webhook-deployment.yaml
        content: |
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: webhook-deployment
            namespace: default
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: webhook
            template:
              metadata:
                labels:
                  app: webhook
              spec:
                containers:
                - name: webhook
                  image: registry.cn-guangzhou.aliyuncs.com/xingcangku/webhook:v1.0
                  command: [ "python", "/app/webhook.py" ]
                  volumeMounts:
                  - name: webhook-certs
                    mountPath: /certs
                    readOnly: true
                volumes:
                - name: webhook-certs
                  secret:
                    secretName: webhook-certs

    - name: Apply webhook deployment
      command: kubectl apply -f webhook-deployment.yaml

    - name: Create webhook service YAML
      copy:
        dest: webhook-service.yaml
        content: |
          apiVersion: v1
          kind: Service
          metadata:
            name: webhook-service
            namespace: default
          spec:
            ports:
            - port: 443
              targetPort: 443
            selector:
              app: webhook

    - name: Apply webhook service
      command: kubectl apply -f webhook-service.yaml

    # 10. 生成 base64 编码的 CA 证书
    - name: Create base64 encoded CA certificate
      shell: base64 -w 0 ca.crt > ca.crt.base64
      args:
        creates: ca.crt.base64

    # 11. 读取 base64 内容并生成 MutatingWebhookConfiguration YAML
    - name: Read CA base64 content
      slurp:
        src: ca.crt.base64
      register: ca_base64

    - name: Generate MutatingWebhookConfiguration YAML
      copy:
        dest: m-w-c.yaml
        content: |
          apiVersion: admissionregistration.k8s.io/v1
          kind: MutatingWebhookConfiguration
          metadata:
            name: example-mutating-webhook
          webhooks:
            - name: example.webhook.com
              clientConfig:
                service:
                  name: webhook-service
                  namespace: default
                  path: "/mutate"
                caBundle: "{{ ca_base64.content | b64decode }}"
              rules:
                - operations: ["CREATE"]
                  apiGroups: [""]
                  apiVersions: ["v1"]
                  resources: ["pods"]
              admissionReviewVersions: ["v1"]
              sideEffects: None

    - name: Apply MutatingWebhookConfiguration
      command: kubectl apply -f m-w-c.yaml
0

评论 (0)

取消