一、 先准备好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的时候有数据备份的功能。实现了定时自动备份数据的功能。
六、更新
可以安装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)