velero
This commit is contained in:
@@ -1,10 +0,0 @@
|
|||||||
kubeadm join 192.168.0.20:6443 --token rvz86n.c8rdb9ygtikrwnub --discovery-token-ca-cert-hash sha256:f925653dfb3d2b4697395a272e0b07cf4eb16b7ae5a2cc3b33aeab1f36fe7d13 --control-plane --certificate-key eeab98651b2f07f6ce53649b2cca1bf3c449d4fe6270ec0645219cd8c6795ca7 --apiserver-advertise-address=192.168.4.2
|
|
||||||
|
|
||||||
|
|
||||||
sudo rm -rf /var/lib/kubelet/
|
|
||||||
sudo rm -rf /etc/kubernetes
|
|
||||||
sudo rm -rf /etc/cni/
|
|
||||||
sudo rm -rf /var/lib/etcd
|
|
||||||
sudo systemctl stop kubelet
|
|
||||||
sudo systemctl status kubelet
|
|
||||||
sudo kubeadm join 192.168.0.20:6443 --token rvz86n.c8rdb9ygtikrwnub --discovery-token-ca-cert-hash sha256:f925653dfb3d2b4697395a272e0b07cf4eb16b7ae5a2cc3b33aeab1f36fe7d13 --control-plane --certificate-key eeab98651b2f07f6ce53649b2cca1bf3c449d4fe6270ec0645219cd8c6795ca7 --apiserver-advertise-address=192.168.4.2
|
|
||||||
16
minio-velero/backupstoragelocations/bsl-a.yaml
Normal file
16
minio-velero/backupstoragelocations/bsl-a.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: velero.io/v1
|
||||||
|
kind: BackupStorageLocation
|
||||||
|
metadata:
|
||||||
|
name: bsl-incluster-a
|
||||||
|
spec:
|
||||||
|
provider: aws
|
||||||
|
objectStorage:
|
||||||
|
bucket: velero-backups
|
||||||
|
config:
|
||||||
|
s3Url: http://minio-a.minio-velero.svc.cluster.local:9000
|
||||||
|
s3ForcePathStyle: "true"
|
||||||
|
region: site-a
|
||||||
|
accessMode: ReadWrite
|
||||||
|
credential:
|
||||||
|
name: cloud-credentials
|
||||||
|
key: cloud
|
||||||
16
minio-velero/backupstoragelocations/bsl-b.yaml
Normal file
16
minio-velero/backupstoragelocations/bsl-b.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: velero.io/v1
|
||||||
|
kind: BackupStorageLocation
|
||||||
|
metadata:
|
||||||
|
name: bsl-incluster-b
|
||||||
|
spec:
|
||||||
|
provider: aws
|
||||||
|
objectStorage:
|
||||||
|
bucket: velero-backups
|
||||||
|
config:
|
||||||
|
s3Url: http://minio-b.minio-velero.svc.cluster.local:9000
|
||||||
|
s3ForcePathStyle: "true"
|
||||||
|
region: site-b
|
||||||
|
accessMode: ReadWrite
|
||||||
|
credential:
|
||||||
|
name: cloud-credentials
|
||||||
|
key: cloud
|
||||||
47
minio-velero/deployments/minio-a.yaml
Normal file
47
minio-velero/deployments/minio-a.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: minio-a
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-a
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-a
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-a
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- name: minio
|
||||||
|
image: quay.io/minio/minio:latest
|
||||||
|
args: ["server", "/data", "--console-address", ":9001"]
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: minio-root
|
||||||
|
ports:
|
||||||
|
- name: s3
|
||||||
|
containerPort: 9000
|
||||||
|
- name: console
|
||||||
|
containerPort: 9001
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "250m"
|
||||||
|
memory: "512Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "2"
|
||||||
|
memory: "4Gi"
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: minio-a-data
|
||||||
47
minio-velero/deployments/minio-b.yaml
Normal file
47
minio-velero/deployments/minio-b.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: minio-b
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-b
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-b
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-b
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- name: minio
|
||||||
|
image: quay.io/minio/minio:latest
|
||||||
|
args: ["server", "/data", "--console-address", ":9001"]
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: minio-root
|
||||||
|
ports:
|
||||||
|
- name: s3
|
||||||
|
containerPort: 9000
|
||||||
|
- name: console
|
||||||
|
containerPort: 9001
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "250m"
|
||||||
|
memory: "512Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "2"
|
||||||
|
memory: "4Gi"
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: minio-b-data
|
||||||
50
minio-velero/jobs/minio-a-init.yaml
Normal file
50
minio-velero/jobs/minio-a-init.yaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: minio-a-init
|
||||||
|
namespace: minio-velero
|
||||||
|
spec:
|
||||||
|
backoffLimit: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
containers:
|
||||||
|
- name: init
|
||||||
|
image: bitnami/minio-client:latest
|
||||||
|
command: ["/bin/sh","-lc"]
|
||||||
|
env:
|
||||||
|
- name: MINIO_ENDPOINT
|
||||||
|
value: "http://minio-a.minio-velero.svc.cluster.local:9000"
|
||||||
|
- name: MINIO_ROOT_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-root
|
||||||
|
key: MINIO_ROOT_USER
|
||||||
|
- name: MINIO_ROOT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-root
|
||||||
|
key: MINIO_ROOT_PASSWORD
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
echo "[init-a] waiting for $MINIO_ENDPOINT ..."
|
||||||
|
until curl -sf "$MINIO_ENDPOINT/minio/health/ready" >/dev/null; do sleep 2; done
|
||||||
|
echo "[init-a] configuring bucket/user/policy"
|
||||||
|
mc alias set minioA "$MINIO_ENDPOINT" "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
|
||||||
|
mc mb minioA/velero-backups || true
|
||||||
|
mc version enable minioA/velero-backups || true
|
||||||
|
mc admin user add minioA velero Velero12345 || true
|
||||||
|
cat > /tmp/policy.json <<'EOF'
|
||||||
|
{ "Version":"2012-10-17",
|
||||||
|
"Statement":[
|
||||||
|
{"Effect":"Allow","Action":["s3:*"],
|
||||||
|
"Resource":["arn:aws:s3:::velero-backups","arn:aws:s3:::velero-backups/*"]}
|
||||||
|
]}
|
||||||
|
EOF
|
||||||
|
mc admin policy create minioA velero-rw /tmp/policy.json || true
|
||||||
|
mc admin policy attach minioA velero-rw --user velero || true
|
||||||
|
echo "[init-a] verifying with velero creds"
|
||||||
|
mc alias set a-vel "$MINIO_ENDPOINT" velero Velero12345
|
||||||
|
mc ls a-vel/velero-backups >/dev/null
|
||||||
|
echo "[init-a] done"
|
||||||
50
minio-velero/jobs/minio-b-init.yaml
Normal file
50
minio-velero/jobs/minio-b-init.yaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: minio-b-init
|
||||||
|
namespace: minio-velero
|
||||||
|
spec:
|
||||||
|
backoffLimit: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
containers:
|
||||||
|
- name: init
|
||||||
|
image: bitnami/minio-client:latest
|
||||||
|
command: ["/bin/sh","-lc"]
|
||||||
|
env:
|
||||||
|
- name: MINIO_ENDPOINT
|
||||||
|
value: "http://minio-b.minio-velero.svc.cluster.local:9000"
|
||||||
|
- name: MINIO_ROOT_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-root
|
||||||
|
key: MINIO_ROOT_USER
|
||||||
|
- name: MINIO_ROOT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: minio-root
|
||||||
|
key: MINIO_ROOT_PASSWORD
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
echo "[init-b] waiting for $MINIO_ENDPOINT ..."
|
||||||
|
until curl -sf "$MINIO_ENDPOINT/minio/health/ready" >/dev/null; do sleep 2; done
|
||||||
|
echo "[init-b] configuring bucket/user/policy"
|
||||||
|
mc alias set minioB "$MINIO_ENDPOINT" "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
|
||||||
|
mc mb minioB/velero-backups || true
|
||||||
|
mc version enable minioB/velero-backups || true
|
||||||
|
mc admin user add minioB velero Velero12345 || true
|
||||||
|
cat > /tmp/policy.json <<'EOF'
|
||||||
|
{ "Version":"2012-10-17",
|
||||||
|
"Statement":[
|
||||||
|
{"Effect":"Allow","Action":["s3:*"],
|
||||||
|
"Resource":["arn:aws:s3:::velero-backups","arn:aws:s3:::velero-backups/*"]}
|
||||||
|
]}
|
||||||
|
EOF
|
||||||
|
mc admin policy create minioB velero-rw /tmp/policy.json || true
|
||||||
|
mc admin policy attach minioB velero-rw --user velero || true
|
||||||
|
echo "[init-b] verifying with velero creds"
|
||||||
|
mc alias set b-vel "$MINIO_ENDPOINT" velero Velero12345
|
||||||
|
mc ls b-vel/velero-backups >/dev/null
|
||||||
|
echo "[init-b] done"
|
||||||
21
minio-velero/kustomization.yaml
Normal file
21
minio-velero/kustomization.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
namespace: minio-velero
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- secrets/minio-root.yaml
|
||||||
|
- secrets/cloud-credentials.yaml
|
||||||
|
- networkpolicies/default-deny-ingress.yaml
|
||||||
|
- networkpolicies/allow-self-to-minio.yaml
|
||||||
|
- pvcs/minio-a-pvc.yaml
|
||||||
|
- deployments/minio-a.yaml
|
||||||
|
- services/minio-a.yaml
|
||||||
|
- jobs/minio-a-init.yaml
|
||||||
|
- pvcs/minio-b-pvc.yaml
|
||||||
|
- deployments/minio-b.yaml
|
||||||
|
- services/minio-b.yaml
|
||||||
|
- jobs/minio-b-init.yaml
|
||||||
|
- backupstoragelocations/bsl-a.yaml
|
||||||
|
- backupstoragelocations/bsl-b.yaml
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
metadata:
|
metadata:
|
||||||
name: velero
|
name: minio-velero
|
||||||
19
minio-velero/networkpolicies/allow-self-to-minio.yaml
Normal file
19
minio-velero/networkpolicies/allow-self-to-minio.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-self-to-minio
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
policyTypes: ["Ingress"]
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
kubernetes.io/metadata.name: minio-velero
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 9000
|
||||||
|
- protocol: TCP
|
||||||
|
port: 9001
|
||||||
7
minio-velero/networkpolicies/default-deny-ingress.yaml
Normal file
7
minio-velero/networkpolicies/default-deny-ingress.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: default-deny-ingress
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes: ["Ingress"]
|
||||||
10
minio-velero/pvcs/minio-a-pvc.yaml
Normal file
10
minio-velero/pvcs/minio-a-pvc.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: minio-a-data
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
storageClassName: sc-me5-site-a
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Ti
|
||||||
10
minio-velero/pvcs/minio-b-pvc.yaml
Normal file
10
minio-velero/pvcs/minio-b-pvc.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: minio-b-data
|
||||||
|
spec:
|
||||||
|
accessModes: ["ReadWriteOnce"]
|
||||||
|
storageClassName: sc-me5-site-b
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Ti
|
||||||
@@ -1,28 +1,33 @@
|
|||||||
# Velero + MinIO (c2et.net)
|
# Velero + MinIO (c2et.net)
|
||||||
|
|
||||||
Este paquete contiene:
|
Este paquete contiene:
|
||||||
- `namespace.yaml`
|
|
||||||
- Secrets de credenciales (`cloud-credentials-site-a`, `cloud-credentials-site-b`)
|
|
||||||
- BackupStorageLocation (BSL) por YAML: `default` (site-a) y `site-b`
|
|
||||||
- Ejemplo de `Schedule` (nightly a las 02:00 y 02:30)
|
|
||||||
- `helm/values-approach-b.yaml`: despliegue de Velero sin BSL/Secret (GitOps)
|
|
||||||
- `ServiceMonitor` (si usas Prometheus Operator)
|
|
||||||
- Dashboard de Grafana (JSON)
|
|
||||||
|
|
||||||
## Flujo recomendado (GitOps, Approach B)
|
* `namespace.yaml`
|
||||||
|
* Secrets de credenciales (`cloud-credentials.yaml`, `minio-root.yaml`)
|
||||||
|
* Despliegues de MinIO (`minio-a`, `minio-b`) con sus `PVC` y `Service`
|
||||||
|
* Jobs de inicialización (`minio-a-init`, `minio-b-init`)
|
||||||
|
* `BackupStorageLocation` (BSL) por YAML: `bsl-incluster-a` y `bsl-incluster-b`
|
||||||
|
* Ejemplo de `Schedule` (nightly a las 02:00 y 02:30)
|
||||||
|
* `values-velero.yaml`: despliegue de Velero sin BSL/Secret (GitOps)
|
||||||
|
* `ServiceMonitor` (si usas Prometheus Operator)
|
||||||
|
* Dashboard de Grafana (JSON)
|
||||||
|
|
||||||
|
## Flujo recomendado (GitOps)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1) Instala Velero por Helm sin BSL ni secrets
|
# 1) Instalar Velero por Helm sin BSL ni secrets
|
||||||
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
|
||||||
helm upgrade --install velero vmware-tanzu/velero -n velero --create-namespace -f helm/values-approach-b.yaml
|
helm upgrade --install velero vmware-tanzu/velero -n minio-velero -f values-velero.yaml
|
||||||
|
|
||||||
# 2) Aplica Secrets, BSLs y Schedules
|
# 2) Crear namespace y desplegar MinIO con sus Secrets, PVCs, Services y Jobs de init
|
||||||
kubectl apply -f namespace.yaml
|
kubectl apply -k ./
|
||||||
kubectl apply -f secrets/secret-site-a.yaml -f secrets/secret-site-b.yaml
|
|
||||||
kubectl apply -f bsl/bsl-default-site-a.yaml -f bsl/bsl-site-b.yaml
|
# 3) (Opcional) Aplicar Schedules
|
||||||
kubectl apply -f schedules/schedules.yaml
|
kubectl apply -f schedules/schedules.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cliente Velero
|
## Cliente Velero
|
||||||
|
|
||||||
Para interactuar con Velero necesitas el binario en tu máquina de administración.
|
Para interactuar con Velero necesitas el binario en tu máquina de administración.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -30,32 +35,33 @@ Para interactuar con Velero necesitas el binario en tu máquina de administraci
|
|||||||
wget https://github.com/vmware-tanzu/velero/releases/download/v1.16.2/velero-v1.16.2-linux-amd64.tar.gz
|
wget https://github.com/vmware-tanzu/velero/releases/download/v1.16.2/velero-v1.16.2-linux-amd64.tar.gz
|
||||||
tar -xvf velero-v1.16.2-linux-amd64.tar.gz
|
tar -xvf velero-v1.16.2-linux-amd64.tar.gz
|
||||||
sudo mv velero-v1.16.2-linux-amd64/velero /usr/local/bin/
|
sudo mv velero-v1.16.2-linux-amd64/velero /usr/local/bin/
|
||||||
|
|
||||||
# MacOS Intel
|
|
||||||
wget https://github.com/vmware-tanzu/velero/releases/download/v1.16.2/velero-v1.16.2-darwin-amd64.tar.gz
|
|
||||||
tar -xvf velero-v1.16.2-darwin-amd64.tar.gz
|
|
||||||
sudo mv velero-v1.16.2-darwin-amd64/velero /usr/local/bin/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Verifica la instalación:
|
Verifica la instalación:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
velero version
|
velero version
|
||||||
```
|
```
|
||||||
|
|
||||||
## Hacer un backup manual
|
## Hacer un backup manual
|
||||||
|
|
||||||
Ejemplo: respaldar el namespace `wireguard`.
|
Ejemplo: respaldar el namespace `wireguard`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
velero backup create wireguard-backup --include-namespaces wireguard --wait
|
velero backup create wireguard-backup --include-namespaces wireguard --wait
|
||||||
velero backup describe wireguard-backup --details
|
velero backup describe wireguard-backup --details
|
||||||
```
|
```
|
||||||
|
|
||||||
Puedes excluir recursos innecesarios (ej. CRDs de KubeVirt):
|
Puedes excluir recursos innecesarios (ej. CRDs de KubeVirt):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
velero backup create smoke --include-namespaces default --exclude-resources uploadtokenrequests.upload.cdi.kubevirt.io --wait
|
velero backup create smoke --include-namespaces default --exclude-resources uploadtokenrequests.upload.cdi.kubevirt.io --wait
|
||||||
```
|
```
|
||||||
|
|
||||||
## Programar backups (Schedules)
|
## Programar backups (Schedules)
|
||||||
|
|
||||||
Ejemplo de programación diaria a las 03:15, TTL de 30 días:
|
Ejemplo de programación diaria a las 03:15, TTL de 30 días:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
velero schedule create daily-wireguard --schedule "15 3 * * *" --include-namespaces wireguard --ttl 720h --default-volumes-to-fs-backup
|
velero schedule create daily-wireguard --schedule "15 3 * * *" --include-namespaces wireguard --ttl 720h --default-volumes-to-fs-backup
|
||||||
```
|
```
|
||||||
@@ -63,7 +69,9 @@ velero schedule create daily-wireguard --schedule "15 3 * * *" --include-nam
|
|||||||
Los schedules también se pueden definir por YAML en `schedules/schedules.yaml`.
|
Los schedules también se pueden definir por YAML en `schedules/schedules.yaml`.
|
||||||
|
|
||||||
## Restaurar un backup
|
## Restaurar un backup
|
||||||
|
|
||||||
### Restaurar al mismo namespace (desastre real)
|
### Restaurar al mismo namespace (desastre real)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1) Borrar el namespace roto
|
# 1) Borrar el namespace roto
|
||||||
kubectl delete ns wireguard
|
kubectl delete ns wireguard
|
||||||
@@ -74,13 +82,15 @@ velero restore describe wireguard-restore --details
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Restaurar a otro namespace (ensayo)
|
### Restaurar a otro namespace (ensayo)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl create ns wireguard-restore
|
kubectl create ns wireguard-restore
|
||||||
velero restore create wireguard-restore-test --from-backup wireguard-backup --namespace-mappings wireguard:wireguard-restore --wait
|
velero restore create wireguard-restore-test --from-backup wireguard-backup --namespace-mappings wireguard:wireguard-restore --wait
|
||||||
```
|
```
|
||||||
|
|
||||||
## Notas
|
## Notas
|
||||||
- MinIO requiere `s3ForcePathStyle=true`.
|
|
||||||
- Si usas CA propia, añade `spec.config.caCert` en los BSL.
|
* MinIO requiere `s3ForcePathStyle=true`.
|
||||||
- `ServiceMonitor` requiere Prometheus Operator; ajusta `metadata.labels.release` al valor que use tu Prometheus.
|
* Si usas CA propia, añade `spec.config.caCert` en los BSL.
|
||||||
- Importa el dashboard JSON en Grafana (datasource `prometheus`).
|
* `ServiceMonitor` requiere Prometheus Operator; ajusta `metadata.labels.release` al valor que use tu Prometheus.
|
||||||
|
* Importa el dashboard JSON en Grafana (datasource `prometheus`).
|
||||||
10
minio-velero/secrets/cloud-credentials.yaml
Normal file
10
minio-velero/secrets/cloud-credentials.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: cloud-credentials
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
cloud: |
|
||||||
|
[default]
|
||||||
|
aws_access_key_id = velero
|
||||||
|
aws_secret_access_key = Velero12345
|
||||||
8
minio-velero/secrets/minio-root.yaml
Normal file
8
minio-velero/secrets/minio-root.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: minio-root
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
MINIO_ROOT_USER: admin
|
||||||
|
MINIO_ROOT_PASSWORD: "Pozuelo12345"
|
||||||
19
minio-velero/services/minio-a.yaml
Normal file
19
minio-velero/services/minio-a.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: minio-a
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-a
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-a
|
||||||
|
ports:
|
||||||
|
- name: s3
|
||||||
|
port: 9000
|
||||||
|
targetPort: 9000
|
||||||
|
- name: console
|
||||||
|
port: 9001
|
||||||
|
targetPort: 9001
|
||||||
19
minio-velero/services/minio-b.yaml
Normal file
19
minio-velero/services/minio-b.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: minio-b
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-b
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: minio
|
||||||
|
app.kubernetes.io/instance: minio-b
|
||||||
|
ports:
|
||||||
|
- name: s3
|
||||||
|
port: 9000
|
||||||
|
targetPort: 9000
|
||||||
|
- name: console
|
||||||
|
port: 9001
|
||||||
|
targetPort: 9001
|
||||||
28
minio-velero/values-velero.yaml
Normal file
28
minio-velero/values-velero.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# No crear credenciales ni BSL/VSL desde Helm (los gestionas por YAML)
|
||||||
|
credentials:
|
||||||
|
useSecret: false
|
||||||
|
|
||||||
|
configuration:
|
||||||
|
# En v1.16 ya no es necesario el flag, pero si quieres forzar el motor FS:
|
||||||
|
# features: EnableFSBackup
|
||||||
|
features: "" # déjalo vacío si no quieres forzar nada
|
||||||
|
backupStorageLocation: [] # BSL por YAML
|
||||||
|
volumeSnapshotLocation: [] # evita el error "spec.provider: Required value"
|
||||||
|
defaultVolumesToFsBackup: true # hace FS-backup por defecto (Kopia/node-agent)
|
||||||
|
|
||||||
|
# Despliega el node-agent (DaemonSet en todos los nodos)
|
||||||
|
deployNodeAgent: true
|
||||||
|
nodeAgent:
|
||||||
|
podConfig:
|
||||||
|
# Tómatelo como "catch-all" para cualquier taint
|
||||||
|
tolerations:
|
||||||
|
- operator: "Exists"
|
||||||
|
|
||||||
|
# Plugin S3 para MinIO (obligatorio para que el server tenga el provider "aws")
|
||||||
|
initContainers:
|
||||||
|
- name: velero-plugin-for-aws
|
||||||
|
image: velero/velero-plugin-for-aws:v1.9.0
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
volumeMounts:
|
||||||
|
- name: plugins
|
||||||
|
mountPath: /target
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
# MinIO en Kubernetes — c2et.net (Site A/B)
|
|
||||||
|
|
||||||
Este paquete contiene manifiestos sin Helm para desplegar **dos instancias independientes de MinIO**,
|
|
||||||
una por site, usando tus StorageClasses `sc-me5-site-a` y `sc-me5-site-b`, y forzando programación por zona.
|
|
||||||
|
|
||||||
## Estructura
|
|
||||||
|
|
||||||
```
|
|
||||||
minio-k8s-c2et-net/
|
|
||||||
site-a/
|
|
||||||
namespace.yaml
|
|
||||||
secret-root.yaml
|
|
||||||
pvc.yaml
|
|
||||||
statefulset.yaml
|
|
||||||
service.yaml
|
|
||||||
ingress-api.yaml
|
|
||||||
ingress-console.yaml
|
|
||||||
site-b/
|
|
||||||
(idéntico con valores del site B)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Credenciales de administración
|
|
||||||
- Usuario: **admin**
|
|
||||||
- Password: **Pozuelo12345**
|
|
||||||
|
|
||||||
> Cambia estas credenciales en `secret-root.yaml` antes de ir a producción.
|
|
||||||
|
|
||||||
## Dominios
|
|
||||||
- Site A API: `s3-a.c2et.net`
|
|
||||||
- Site A Consola: `console.s3-a.c2et.net`
|
|
||||||
- Site B API: `s3-b.c2et.net`
|
|
||||||
- Site B Consola: `console.s3-b.c2et.net`
|
|
||||||
|
|
||||||
Requisitos previos:
|
|
||||||
- IngressClass `nginx` operativo.
|
|
||||||
- `cert-manager` con `ClusterIssuer` llamado `letsencrypt-prod`.
|
|
||||||
- DNS apuntando los hosts anteriores al Ingress Controller.
|
|
||||||
|
|
||||||
## Despliegue rápido
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl apply -f site-a/namespace.yaml
|
|
||||||
kubectl apply -f site-a/secret-root.yaml
|
|
||||||
kubectl apply -f site-a/pvc.yaml
|
|
||||||
kubectl apply -f site-a/service.yaml
|
|
||||||
kubectl apply -f site-a/statefulset.yaml
|
|
||||||
kubectl apply -f site-a/ingress-api.yaml
|
|
||||||
kubectl apply -f site-a/ingress-console.yaml
|
|
||||||
|
|
||||||
kubectl apply -f site-b/namespace.yaml
|
|
||||||
kubectl apply -f site-b/secret-root.yaml
|
|
||||||
kubectl apply -f site-b/pvc.yaml
|
|
||||||
kubectl apply -f site-b/service.yaml
|
|
||||||
kubectl apply -f site-b/statefulset.yaml
|
|
||||||
kubectl apply -f site-b/ingress-api.yaml
|
|
||||||
kubectl apply -f site-b/ingress-console.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
## Probar
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export AWS_ACCESS_KEY_ID=admin
|
|
||||||
export AWS_SECRET_ACCESS_KEY='Pozuelo12345'
|
|
||||||
export AWS_S3_FORCE_PATH_STYLE=true
|
|
||||||
|
|
||||||
aws --endpoint-url https://s3-a.c2et.net s3 mb s3://mi-bucket-a
|
|
||||||
aws --endpoint-url https://s3-a.c2et.net s3 ls
|
|
||||||
|
|
||||||
aws --endpoint-url https://s3-b.c2et.net s3 mb s3://mi-bucket-b
|
|
||||||
aws --endpoint-url https://s3-b.c2et.net s3 ls
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notas
|
|
||||||
|
|
||||||
- Los PVC usan `WaitForFirstConsumer` a través de tus StorageClasses; el `nodeSelector` del StatefulSet garantiza
|
|
||||||
que cada volumen se cree en el **site** correcto.
|
|
||||||
- Imagen MinIO: `quay.io/minio/minio:RELEASE.2025-02-20T00-00-00Z` (ajústala a la que certifiques).
|
|
||||||
- Tamaño del PVC por defecto: `2Ti` (modifícalo a tu necesidad).
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: minio
|
|
||||||
namespace: minio-site-a
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: minio
|
|
||||||
ports:
|
|
||||||
- name: api
|
|
||||||
port: 9000
|
|
||||||
targetPort: 9000
|
|
||||||
protocol: TCP
|
|
||||||
- name: console
|
|
||||||
port: 9001
|
|
||||||
targetPort: 9001
|
|
||||||
protocol: TCP
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: minio
|
|
||||||
namespace: minio-site-b
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: minio
|
|
||||||
ports:
|
|
||||||
- name: api
|
|
||||||
port: 9000
|
|
||||||
targetPort: 9000
|
|
||||||
protocol: TCP
|
|
||||||
- name: console
|
|
||||||
port: 9001
|
|
||||||
targetPort: 9001
|
|
||||||
protocol: TCP
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: test-http
|
|
||||||
# annotations:
|
|
||||||
# metallb.universe.tf/address-pool: default
|
|
||||||
spec:
|
|
||||||
# type: NodePort
|
|
||||||
type: LoadBalancer
|
|
||||||
loadBalancerIP: 192.168.200.10
|
|
||||||
selector:
|
|
||||||
app: test-http
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 80
|
|
||||||
# nodePort: 30080
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: test-http
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: test-http
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: test-http
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: test-http
|
|
||||||
image: nginx:alpine
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: velero.io/v1
|
|
||||||
kind: BackupStorageLocation
|
|
||||||
metadata:
|
|
||||||
name: default
|
|
||||||
namespace: velero
|
|
||||||
spec:
|
|
||||||
provider: aws
|
|
||||||
objectStorage:
|
|
||||||
bucket: velero
|
|
||||||
config:
|
|
||||||
region: minio
|
|
||||||
s3Url: https://s3-a.c2et.net
|
|
||||||
s3ForcePathStyle: "true"
|
|
||||||
credential:
|
|
||||||
name: cloud-credentials-site-a
|
|
||||||
key: cloud
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: velero.io/v1
|
|
||||||
kind: BackupStorageLocation
|
|
||||||
metadata:
|
|
||||||
name: site-b
|
|
||||||
namespace: velero
|
|
||||||
spec:
|
|
||||||
provider: aws
|
|
||||||
objectStorage:
|
|
||||||
bucket: velero
|
|
||||||
config:
|
|
||||||
region: minio
|
|
||||||
s3Url: https://s3-b.c2et.net
|
|
||||||
s3ForcePathStyle: "true"
|
|
||||||
credential:
|
|
||||||
name: cloud-credentials-site-b
|
|
||||||
key: cloud
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
credentials:
|
|
||||||
useSecret: true
|
|
||||||
existingSecret: ""
|
|
||||||
secretContents:
|
|
||||||
cloud: |
|
|
||||||
[default]
|
|
||||||
aws_access_key_id=velero-a
|
|
||||||
aws_secret_access_key=Clave-Velero-A
|
|
||||||
|
|
||||||
configuration:
|
|
||||||
features: EnableCSI
|
|
||||||
backupStorageLocation:
|
|
||||||
- name: default
|
|
||||||
provider: aws
|
|
||||||
bucket: velero
|
|
||||||
config:
|
|
||||||
region: minio
|
|
||||||
s3Url: https://s3-a.c2et.net
|
|
||||||
s3ForcePathStyle: "true"
|
|
||||||
|
|
||||||
initContainers:
|
|
||||||
- name: velero-plugin-for-aws
|
|
||||||
image: velero/velero-plugin-for-aws:v1.9.0
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
volumeMounts:
|
|
||||||
- name: plugins
|
|
||||||
mountPath: /target
|
|
||||||
- name: velero-plugin-for-csi
|
|
||||||
image: velero/velero-plugin-for-csi:v0.7.0
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
volumeMounts:
|
|
||||||
- name: plugins
|
|
||||||
mountPath: /target
|
|
||||||
|
|
||||||
nodeAgent:
|
|
||||||
enabled: true
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# values-combined.yaml
|
|
||||||
credentials:
|
|
||||||
useSecret: false # Secrets y BSLs los aplicas tú por YAML (como ya hiciste)
|
|
||||||
|
|
||||||
configuration:
|
|
||||||
features: ""
|
|
||||||
backupStorageLocation: [] # ninguno desde Helm (los gestionas por YAML)
|
|
||||||
defaultVolumesToFsBackup: true # copia datos de PV vía node-agent/Kopia al BSL
|
|
||||||
|
|
||||||
# Dejamos SOLO el plugin de AWS; el CSI externo se quita (viene integrado en Velero 1.16)
|
|
||||||
initContainers:
|
|
||||||
- name: velero-plugin-for-aws
|
|
||||||
image: velero/velero-plugin-for-aws:v1.9.0
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
volumeMounts:
|
|
||||||
- name: plugins
|
|
||||||
mountPath: /target
|
|
||||||
|
|
||||||
# **activar** el node-agent (DaemonSet) y darle tolerations "catch-all"
|
|
||||||
deployNodeAgent: true
|
|
||||||
nodeAgent:
|
|
||||||
podConfig:
|
|
||||||
tolerations:
|
|
||||||
- key: "node-role.kubernetes.io/master"
|
|
||||||
operator: "Exists"
|
|
||||||
effect: "NoSchedule"
|
|
||||||
- key: "node-role.kubernetes.io/control-plane"
|
|
||||||
operator: "Exists"
|
|
||||||
effect: "NoSchedule"
|
|
||||||
- operator: "Exists" # tolera cualquier otro taint
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"gnetId": null,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"type": "stat",
|
|
||||||
"title": "Backups - Total",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(velero_backup_total)",
|
|
||||||
"legendFormat": "total"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": 1,
|
|
||||||
"datasource": {
|
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "prometheus"
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"reduceOptions": {
|
|
||||||
"calcs": [
|
|
||||||
"lastNotNull"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeSeries",
|
|
||||||
"title": "Backups por estado",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum by (phase) (increase(velero_backup_attempt_total[1h]))",
|
|
||||||
"legendFormat": "{{phase}}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": 2,
|
|
||||||
"datasource": {
|
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "prometheus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeSeries",
|
|
||||||
"title": "Duraci\u00f3n de backups (p95)",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "histogram_quantile(0.95, sum(rate(velero_backup_duration_seconds_bucket[5m])) by (le))",
|
|
||||||
"legendFormat": "p95"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": 3,
|
|
||||||
"datasource": {
|
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "prometheus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeSeries",
|
|
||||||
"title": "Errores del node-agent",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(velero_node_agent_errors_total[5m]))",
|
|
||||||
"legendFormat": "errores"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": 4,
|
|
||||||
"datasource": {
|
|
||||||
"type": "prometheus",
|
|
||||||
"uid": "prometheus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"schemaVersion": 37,
|
|
||||||
"style": "dark",
|
|
||||||
"tags": [
|
|
||||||
"velero",
|
|
||||||
"backup"
|
|
||||||
],
|
|
||||||
"templating": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-24h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"title": "Velero (MinIO S3)",
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: ServiceMonitor
|
|
||||||
metadata:
|
|
||||||
name: velero
|
|
||||||
namespace: velero
|
|
||||||
labels:
|
|
||||||
release: prometheus # ajusta al selector de tu Prometheus
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: velero
|
|
||||||
namespaceSelector:
|
|
||||||
matchNames: ["velero"]
|
|
||||||
endpoints:
|
|
||||||
- port: metrics
|
|
||||||
interval: 30s
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
apiVersion: velero.io/v1
|
|
||||||
kind: Schedule
|
|
||||||
metadata:
|
|
||||||
name: nightly-a
|
|
||||||
namespace: velero
|
|
||||||
spec:
|
|
||||||
schedule: "0 2 * * *"
|
|
||||||
template:
|
|
||||||
ttl: 168h
|
|
||||||
includedNamespaces:
|
|
||||||
- gitea
|
|
||||||
- apolo
|
|
||||||
storageLocation: default
|
|
||||||
---
|
|
||||||
apiVersion: velero.io/v1
|
|
||||||
kind: Schedule
|
|
||||||
metadata:
|
|
||||||
name: nightly-b
|
|
||||||
namespace: velero
|
|
||||||
spec:
|
|
||||||
schedule: "30 2 * * *"
|
|
||||||
template:
|
|
||||||
ttl: 168h
|
|
||||||
includedNamespaces:
|
|
||||||
- giteay
|
|
||||||
- apolo
|
|
||||||
storageLocation: site-b
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: cloud-credentials-site-a
|
|
||||||
namespace: velero
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
cloud: |
|
|
||||||
[default]
|
|
||||||
aws_access_key_id=velero-a
|
|
||||||
aws_secret_access_key=Pozuelo12345
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: cloud-credentials-site-b
|
|
||||||
namespace: velero
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
cloud: |
|
|
||||||
[default]
|
|
||||||
aws_access_key_id=velero-b
|
|
||||||
aws_secret_access_key=Pozuelo12345
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
apiVersion: velero.io/v1
|
|
||||||
kind: VolumeSnapshotLocation
|
|
||||||
metadata:
|
|
||||||
name: default
|
|
||||||
namespace: velero
|
|
||||||
spec:
|
|
||||||
provider: velero.io/csi
|
|
||||||
Reference in New Issue
Block a user