# Installazione Piattaforma – Istruzioni

> **Nota**: Documento generato a partire dal file sorgente fornito (`doc.txt`). Le sezioni e i blocchi di codice sono mantenuti fedeli all'originale. Se desideri, posso rifinire l'impaginazione (sottosezioni, sommario, evidenziazione dei comandi per `bash`, `yaml`, ecc.).

```yaml
---

Configurazione iniziale:

master node
3 server con queste caratteristiche: 
 2 vcpu, 4gb ram 20gb HD Ubuntu 25.10 

Worker node
3 server con queste caratteristiche:
 2 vcpu, 4gb ram 50gb HD Ubuntu 25.10 
 
Load Balancer (HAproxy)
1 server con queste caratteristiche:
  1 vcpu, 1gb ram 10gb HD Ubuntu 25.10 
  indirizzo pubblico definito sul gatewa ruotato sul Balancer, porte aperte:
  80,443 per servizi applicativi
  10000,10002,10003,10004 per kubeedge
```

------------------------------------------------------------------------------------------------------------------------------  
Installazione:

Su ogni nodo master e worker

```bash
# 1. Aggiorna OS
sudo apt update && sudo apt -y upgrade   # Ubuntu/Debian
sudo apt install -y iputils-ping
sudo apt install -y telnetd telnet
sudo snap install -y kubectl --classic
sudo apt install -y iptables
sudo apt install -y iptables-persistent

# 2. Disabilita SWAP (necessario)
sudo swapoff -a
sudo sed -i.bak '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

# 3. Config kernel requisiti Kubernetes (es. bridge netfilter)
cat <<EOF | sudo tee /etc/sysctl.d/99-k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.inotify.max_user_watches         = 524288
EOF
sudo sysctl --system

# 4. Sincronizza orologio
sudo apt install -y chrony
sudo systemctl enable --now chrony

#installa yq
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod a+x /usr/local/bin/yq
# 5. Imposta hostname (es.)
#sudo hostnamectl set-hostname $1
```

------------------------------------------------------------------------------------------------------------------------------
Sul master 1
```bash
# 1. Installa RKE2 (script ufficial)
curl -sfL https://get.rke2.io | sh -
sudo systemctl enable rke2-server.service

# 2. Crea config (personalizza token e tls-san se serve)
sudo mkdir -p /etc/rancher/rke2
sudo tee /etc/rancher/rke2/config.yaml > /dev/null <<EOF

# RKE2 Server Configuration - First Master Node
write-kubeconfig-mode: "0644"

# CRITICAL: Add all possible API server access points to the certificate
tls-san:
  - "POC-Kube-Balancer" 
  - "10.20.1.100"
  - "POC-Master0"
  - "10.20.1.101"
  - "POC-Master1"
  - "10.20.1.102"
  - "POC-Master2"
  - "10.20.1.103"
  - "POC-Worker0"
  - "10.20.1.104" 
  - "POC-Worker1"
  - "10.20.1.105" 
  - "POC-Worker2"
  - "10.20.1.106" 
  

# Network configuration
#cluster-cidr: "10.42.64.0/18"
#service-cidr: "10.42.0.0/18"
#cluster-dns: "10.42.0.10"

# Security hardening
#profile: "cis-1.6"
selinux: true
secrets-encryption: true

# Node configuration
node-taint:
  - "CriticalAddonsOnly=true:NoExecute"  
  
ingress-controller: none  
  
EOF

# 3. Avvia RKE2 server
sudo systemctl start rke2-server

# Attendi che i pod kube-system siano up (sul master 1)

#installa kubectl
curl -LO https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl


mkdir /root/.kube
cp /etc/rancher/rke2/rke2.yaml /root/.kube/config
echo "MASTER TOKEN TO COPY"
cat /var/lib/rancher/rke2/server/node-token

sui master 2 e 3:
# 1. Installa RKE2 (script ufficial)
curl -sfL https://get.rke2.io | sh -
sudo systemctl enable rke2-server.service

# 2. Crea config (personalizza token e tls-san se serve)
sudo mkdir -p /etc/rancher/rke2
sudo tee /etc/rancher/rke2/config.yaml > /dev/null <<EOF

# RKE2 Server Configuration - Additional Master
server: https://POC-Master0:9345  # Direct connection for initial join
token: "K10b8b252de84e5aab8bc1d2a8e4aad3e329ee84d638892b8638de0260b7cb8212a::server:34b189ab7b91fc924500ba0b3608b80b"
write-kubeconfig-mode: "0644"

# Same TLS SANs as master-1 - consistency is crucial!
tls-san:
  - "POC-Kube-Balancer" 
  - "10.20.1.100"
  - "POC-Master0"
  - "10.20.1.101"
  - "POC-Master1"
  - "10.20.1.102"
  - "POC-Master2"
  - "10.20.1.103"
  - "POC-Worker0"
  - "10.20.1.104" 
  - "POC-Worker1"
  - "10.20.1.105" 
  - "POC-Worker2"
  - "10.20.1.106" 
  
# Identical network configuration
#cluster-cidr: "10.42.64.0/18"
#service-cidr: "10.42.0.0/18"
#cluster-dns: "10.42.0.10"

# Same backup and security settings
#profile: "cis-1.6"
selinux: true
secrets-encryption: true

# Node configuration
node-taint:
  - "CriticalAddonsOnly=true:NoExecute"
  
ingress-controller: none    
  
EOF

# 3. Avvia RKE2 server
sudo systemctl start rke2-server

curl -LO https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

mkdir /root/.kube
cp /etc/rancher/rke2/rke2.yaml /root/.kube/config


#installa helm
# scarica helm (esempio Linux AMD64)
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

# verifica
helm version
```

------------------------------------------------------------------------------------------------------------------------------
su ogni nodo worker:
```bash
sudo apt install open-iscsi
systemctl enable open-iscsi
systemctl enable iscsid
systemctl restart iscsid.service
# Install RKE2 agent
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" sudo sh -

# Create configuration
sudo mkdir -p /etc/rancher/rke2

# Worker configuration - connects through the load balancer!
sudo tee /etc/rancher/rke2/config.yaml > /dev/null << EOF
# RKE2 Agent Configuration
server: https://POC-Kube-Balancer:9345  # Using the main load balancer!
token: "K10b8b252de84e5aab8bc1d2a8e4aad3e329ee84d638892b8638de0260b7cb8212a::server:34b189ab7b91fc924500ba0b3608b80b"

# Node labels for workload scheduling
node-label:
  - "node.kubernetes.io/worker=true"
  - "workload-type=general"

# Optional: Reserve resources for system stability
# kubelet-arg:
#   - "system-reserved=cpu=500m,memory=1Gi"
#   - "kube-reserved=cpu=500m,memory=1Gi"
EOF

# Start the worker
sudo systemctl enable rke2-agent.service
sudo systemctl start rke2-agent.service

# Check status
sudo systemctl status rke2-agent.service
```

------------------------------------------------------------------------------------------------------------------------------
sul Balancer:
```bash
sudo apt update && sudo apt install -y haproxy

sudo tee /etc/haproxy/haproxy.cfg > /dev/null <<'EOF'
global
  log /dev/log local0
  maxconn 20000
  tune.bufsize 16384
 # SSL configuration for future HTTPS endpoints
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private
        
        # Modern SSL configuration - only secure protocols
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

frontend rke2_registration_frontend
    bind *:9345
    mode tcp
    option tcplog
    default_backend rke2_registration_backend

#---------------------------------------------------------------------
# RKE2 Supervisor/Registration Backend
# Round-robin between masters for node registration
#---------------------------------------------------------------------
backend rke2_registration_backend
    mode tcp
    balance roundrobin
    option tcp-check
    # Health check ensures we only send traffic to healthy masters
    server POC-Master0 POC-Master0:9345 check
    server POC-Master1 POC-Master1:9345 check
    server POC-Master2 POC-Master2:9345 check

#---------------------------------------------------------------------
# Kubernetes API Frontend
# This is where kubectl commands and apps connect
#---------------------------------------------------------------------
frontend k8s_api_frontend
    bind *:6443
    mode tcp
    option tcplog
    default_backend k8s_api_backend

#---------------------------------------------------------------------
# Kubernetes API Backend
# Distributes API requests across all masters
#---------------------------------------------------------------------
backend k8s_api_backend
    mode tcp
    balance roundrobin
    option tcp-check
    # TCP health checks on the API port
    server POC-Master0 POC-Master0:6443 check
    server POC-Master1 POC-Master1:6443 check
    server POC-Master2 POC-Master2:6443 check

#---------------------------------------------------------------------
# Statistics Page (Optional but useful for monitoring)
#---------------------------------------------------------------------
listen stats
    bind *:8080
    stats enable
    stats uri /stats
    stats refresh 30s
    stats show-node
    stats auth admin:admin  # Change this password!

#---------------------------------------------------------------------
# nginx ingress
# This is where kubectl commands and apps connect
#---------------------------------------------------------------------
frontend nginx_frontend_443
    bind *:443
    mode tcp
    option tcplog
    default_backend nginx_backend

frontend nginx_frontend_80
    bind *:80
    mode http
    http-response set-header Access-Control-Allow-Origin %[hdr(origin)]
    default_backend nginx_backend_http
#---------------------------------------------------------------------
# Kubernetes API Backend
# Distributes API requests across all masters
#---------------------------------------------------------------------
backend nginx_backend
    mode tcp
    balance roundrobin
    option tcp-check
    # TCP health checks on the API port
    server POC-Master0 POC-Master0:30864 check 
    server POC-Master1 POC-Master1:30864 check
    server POC-Master2 POC-Master2:30864 check

backend nginx_backend_http
    mode http
    balance roundrobin
    # TCP health checks on the API port
    server POC-Master0 POC-Master0:30864 check ssl verify none
    server POC-Master1 POC-Master1:30864 check ssl verify none
    server POC-Master2 POC-Master2:30864 check ssl verify none
EOF

sudo systemctl enable --now haproxy
```

------------------------------------------------------------------------------------------------------------------------------
Installazione componenti k8s



- **Rancher**  
```bash
helm repo add rancher-stable https://releases.rancher.com/server-charts/stable
kubectl create namespace cattle-system

helm install rancher rancher-stable/rancher \
  --namespace cattle-system \
  --set hostname=k8s.italiadatacenter.com \
  --set bootstrapPassword=*****


patch gateway add under listener:
  - allowedRoutes:
      namespaces:
        from: cattle-system
    hostname: k8s.italiadatacenter.com
    name: k8s-https
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - group: ""
        kind: Secret
        name: k8s-secret
      mode: Terminate
  - allowedRoutes:
      namespaces:
        from: cattle-system
    hostname: k8s.italiadatacenter.com
    name: k8s-http
    port: 80
    protocol: HTTP

creazione httproute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: rancher
  namespace: cattle-system
spec:
  hostnames:
  - k8s.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: rancher
      port: 80
```

 ------------------------------------------------------------------------------------------------------------------------------

- **CephCsi**  
```bash
cat <<EOF > csi-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "004ee854-86cc-4ddc-b7d6-75e4fe962296",
        "monitors": [
          "72.20.1.33:6789",
          "72.20.1.34:6789",
          "72.20.1.35:6789"
        ]
      }
    ]
metadata:
  name: ceph-csi-config
EOF
kubectl apply -f csi-config-map.yaml


 cat <<EOF > csi-kms-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {}
metadata:
  name: ceph-csi-encryption-kms-config
EOF
kubectl apply -f csi-kms-config-map.yaml


cat <<EOF > ceph-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  ceph.conf: |
    [global]
    auth_cluster_required = cephx
    auth_service_required = cephx
    auth_client_required = cephx
  # keyring is a required key and its value should be empty
  keyring: |
metadata:
  name: ceph-config
EOF
kubectl apply -f ceph-config-map.yaml



cat <<EOF > csi-rbd-secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: default
stringData:
  userID: kubernetes
  userKey: AQD2zo5pm8aZIRAAPzWS+dROeX7iJtv5EukfKA==
EOF






kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml

wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml
kubectl apply -f csi-rbdplugin-provisioner.yaml
wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin.yaml
kubectl apply -f csi-rbdplugin.yaml

------- TEST-----

cat <<EOF > csi-rbd-sc.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: 004ee854-86cc-4ddc-b7d6-75e4fe962296
   pool: k8s-rbd
   imageFeatures: layering
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
   - discard
EOF
kubectl apply -f csi-rbd-sc.yaml


cat <<EOF > raw-block-pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: raw-block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc
EOF
kubectl apply -f raw-block-pvc.yaml
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Gateway API** 
```bash
 # Install Gateway API CRDs 
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml

kubectl get crd | grep gateway

kubectl create namespace nginx-gateway

kubectl apply --server-side -f https://raw.githubusercontent.com/nginx/nginx-gateway-fabric/v2.4.1/deploy/crds.yaml
kubectl apply -f https://raw.githubusercontent.com/nginx/nginx-gateway-fabric/v2.4.1/deploy/nodeport/deploy.yaml

---- Gatway configuration ----
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: nginx-gateway
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: nginx
  listeners:
  - allowedRoutes:
      namespaces:
        from: All
    hostname: poc1.italiadatacenter.com
    name: https
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - group: ""
        kind: Secret
        name: poc1-secret
      mode: Terminate
  - allowedRoutes:
      namespaces:
        from: All
    hostname: poc1.italiadatacenter.com
    name: http
    port: 80
    protocol: HTTP


----- Nodeport service ---
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/instance: nginx-gateway
    app.kubernetes.io/managed-by: nginx-gateway-nginx
    app.kubernetes.io/name: main-gateway-nginx
    gateway.networking.k8s.io/gateway-name: main-gateway
  name: gateway-nginx-nodeport
  namespace: nginx-gateway
spec:
  ports:
  - name: port-80
    nodePort: 30864
    port: 80
    protocol: TCP
    targetPort: 80
  - name: port-443
    nodePort: 30874
    port: 443
    protocol: TCP
    targetPort: 443	
  selector:
    app.kubernetes.io/instance: nginx-gateway
    app.kubernetes.io/managed-by: nginx-gateway-nginx
    app.kubernetes.io/name: main-gateway-nginx
    gateway.networking.k8s.io/gateway-name: main-gateway
  sessionAffinity: None
  type: NodePort
EOF

----- httproute ---

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: demo-route
  namespace: demo-apps
spec:
  hostnames:
  - poc2.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app-v1
      port: 80

--- work area

kubectl  edit  gateway main-gateway -n nginx-gateway

add

- allowedRoutes:
      namespaces:
        from: cattle-system
    hostname: k8s.italiadatacenter.com
    name: k8s-https
    port: 443
    protocol: HTTPS
    tls:
      certificateRefs:
      - group: ""
        kind: Secret
        name: k8s-secret
      mode: Terminate
  - allowedRoutes:
      namespaces:
        from: cattle-system
    hostname: k8s.italiadatacenter.com
    name: k8s-http
    port: 80
    protocol: HTTP
```

 ------------------------------------------------------------------------------------------------------------------------------ 

- **CertManager**  
```bash
kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update

# Install versione consigliata
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --set installCRDs=true \
  --wait
# Verifica
kubectl -n cert-manager get pods

Crea cluster issuer per let’s Encrypt:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: <insert a valid email-address>
    privateKeySecretRef:
      name: letsencrypt-production-key
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
        gatewayHTTPRoute:
          parentRefs:
          - group: gateway.networking.k8s.io
            kind: Gateway
            name: main-gateway
            namespace: nginx-gateway
```

------------------------------------------------------------------------------------------------------------------------------

```yaml
---
```

## Servizi DevOps

-- **db devops**

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: pg-app-user
  namespace: demo-apps
type: kubernetes.io/basic-auth
stringData:
  username: devops
  password: ****************
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-devops
  namespace: devops
spec:
  instances: 3
 
  storage:
    size: 1Gi
    storageClass: csi-rbdfs-sc
  
  walStorage:
    storageClass: csi-rbdfs-sc
    size: 1Gi	

  bootstrap:
    initdb:
      database: devops
      owner: admin
      secret:
        name: pg-app-user

  postgresql:
    parameters:
      max_connections: "300"
      shared_buffers: "1GB"

  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2"
      memory: "2Gi"
```

- **Gitea**  

```bash
kubectl cnpg psql pg-devops -n devops

CREATE DATABASE giteadb;
CREATE USER gitea WITH PASSWORD '****************';
GRANT ALL PRIVILEGES ON DATABASE giteadb TO gitea;
ALTER DATABASE giteadb OWNER TO gitea;

helm repo add gitea https://dl.gitea.io/charts/
helm repo update


kubectl create namespace gitea

cat <<EOF |cat >values.yaml -
replicaCount: 1

image:
  repository: gitea/gitea
  tag: 1.22.0
  pullPolicy: IfNotPresent

strategy:
  type: Recreate

service:
  http:
    type: ClusterIP
    port: 3000
  ssh:
    type: ClusterIP
    port: 22

redis-cluster:
  enabled: false

redis:
  enabled: false
  
ingress:
  enabled: false

persistence:
  enabled: true
  storageClass: csi-rbdfs-sc
  size: 10Gi

postgresql:
  enabled: false

postgresql-ha:
  enabled: false
  
gitea:
  admin:
    username: gitadmin
    password: ****************
    email: gitadmin@italiadatacenter.com

  config:
    database:
      DB_TYPE: postgres
      HOST: pg-devops-rw.devops.svc:5432
      NAME: giteadb
      USER: gitea
      PASSWD: ****************
      SSL_MODE: disable

    server:
      ROOT_URL: https://git.italiadatacenter.com/
      SSH_DOMAIN: git.italiadatacenter.com
      SSH_PORT: 22

    security:
      INSTALL_LOCK: true

EOF

helm upgrade --install gitea gitea-charts/gitea  --namespace gitea -f values.yaml


apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: gitea
  namespace: gitea
spec:
  hostnames:
  - git.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: gitea-http
      port: 3000
```

------------------------------------------------------------------------------------------------------------------------------

- **Harbor**  
```bash
#HARBOR
kubectl create namespace harbor

helm repo add harbor https://helm.goharbor.io
helm repo update

cat <<EOF | cat > harbor-cert.yaml -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: harbor-tls
  namespace: harbor
spec:
  secretName: harbor-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - harbor.italiadatacenter.com
  
EOF

kubectl apply -f harbor-cert.yaml




cat <<EOF | cat > harborvalues.yaml -
# -----------------------
# EXPOSURE
# -----------------------
expose:
  # Set how to expose the service. Set the type as "ingress", "clusterIP", "nodePort" or "loadBalancer"
  # and fill the information in the corresponding section
  type: clusterIP
externalURL: https://harbor.italiadatacenter.com

# -----------------------
# ADMIN
# -----------------------
harborAdminPassword: "****************"

# -----------------------
# PERSISTENCE
# -----------------------
persistence:
  enabled: true
  persistentVolumeClaim:
    registry:
      storageClass: csi-rbdfs-sc
      size: 50Gi
    jobservice:
      storageClass: csi-rbdfs-sc
      size: 2Gi
    trivy:
      storageClass: csi-rbdfs-sc
      size: 2Gi

# -----------------------
# POSTGRESQL (EXTERNAL)
# -----------------------
database:
  type: external
  external:
    host: pg-devops-rw.devops.svc
    port: 5432
    username: harbor
    password: "****************"
    database: registry
    sslmode: require

# -----------------------
# REDIS (EXTERNAL)
# -----------------------
redis:
  type: external
  external:
    addr: redis.redis.svc.cluster.local:6379
    password: "****************"
    database: 0

# -----------------------
# DISABLE INTERNAL SERVICES
# -----------------------
postgresql:
  enabled: false

redisInternal:
  enabled: false

# -----------------------
# COMPONENTS
# -----------------------
trivy:
  enabled: true

metrics:
  enabled: false
EOF

kubectl cnpg psql pg-devops -n devops

CREATE DATABASE registry;
CREATE USER harbor WITH PASSWORD '****************';
GRANT ALL PRIVILEGES ON DATABASE registry TO harbor;
ALTER DATABASE registry OWNER TO harbor;
#test
kubectl run psql-test  --rm -it --image=postgres:16 -- psql  -h pg-prod-rw.database.svc  -U harbor
kubectl run redis-test --rm -it  --image=redis:7 -- redis-cli  -h redis.redis.svc.cluster.local  -a Japp0cam



helm install harbor harbor/harbor  -n harbor -f harborvalues.yaml



--- httproute & body setting nginx ----
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: harbor
  namespace: harbor
spec:
  hostnames:
  - harbor.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: harbor
      port: 80
---
apiVersion: gateway.nginx.org/v1alpha1
kind: ClientSettingsPolicy
metadata:
  name: gateway-client-settings
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: main-gateway
  body:
    maxSize: "0" 
EOF



TEST:
#push
docker login harbor.italiadatacenter.com
docker pull nginx:1.25
docker tag nginx:1.25 harbor.italiadatacenter.com/library/nginx:1.25
docker push harbor.italiadatacenter.com/library/nginx:1.25

#pull
docker rmi harbor.italiadatacenter.com/library/nginx:1.25
docker pull harbor.italiadatacenter.com/library/nginx:1.25

#pull da k8s

#STEP 1– Creare Robot Account in Harbor
#Harbor UI → Projects → (es. library o apps) → Robot Accounts
#Nome: k8s-pull
#Permessi:
#✔️ Repository → Pull


kubectl create secret docker-registry harbor-pull \
  -n default \
  --docker-server=harbor.italiadatacenter.com \
  --docker-username=robot$k8s-pull \
  --docker-password=******************************** \
  --docker-email=harbor@italiadatacenter.com

#per namespace nuovi
kubectl patch serviceaccount default  -n default  -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}'

#applicare per i vecchi:
kubectl patch serviceaccount default  -n primo-dev  -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}'

#test
kubectl run test-nginx   --image=harbor.italiadatacenter.com/library/nginx:1.25   --restart=Never -n poc



kubectl -n primo-dev create secret docker-registry harbor-pull \
  --docker-server=harbor.italiadatacenter.com \
  --docker-username=robot\$primo+primo \
  --docker-password=******************************** \
  --docker-email=harbor@italiadatacenter.com
  
kubectl patch serviceaccount default  -n primo-dev  -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}'
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Gitea Act Runner**  
Download act_runner binary
```bash
 mv act_runner-0.3.0-linux-amd64 act_runner
 chmod +x ./act_runner
 ./act_runner --version
./act_runner register

Git server: https.//git.italiadatacener.com
token: SiP2B1Wth0FwORkLrIX7WYhho78IVaW3ZppO9vrx
label: runner1:host

start:
nohup ./act_runner daemon &


Install node
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl ca-certificates gnupg
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs

verifica
node -v
npm -v

Installa docker:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Pipeline CICD**  
  ------------------------------------------------------------------------------------------------------------------------------

customize.sh
```bash
#!/bin/bash
# Usage: ./customize.sh dev|qa|prod
echo "tetst" $IMAGE_TAG_backend
ENV=$1
VALUES_FILE="env/$ENV/values.env"
PROPERTIES_FILE="properties.env"
YAML_DIR="kubernetes"

# Estrai l'hash completo del commit e crea una variabile temporanea per la sostituzione
TAG=$(git rev-parse HEAD)
TMP_TAG_FILE=$(mktemp)
echo "TAG=$TAG" > "$TMP_TAG_FILE"
cat ./imglist >> "$TMP_TAG_FILE"


if [ ! -f "$VALUES_FILE" ]; then
  echo "File $VALUES_FILE non trovato."
  exit 1
fi

if [ ! -f "$PROPERTIES_FILE" ]; then
  echo "File $PROPERTIES_FILE non trovato."
  exit 1
fi

# Trova tutti i file .yaml nella directory kubernetes e sottodirectory
find "$YAML_DIR" -type f -name "*.yaml" | while read YAML_FILE; do

  while IFS='=' read -r key value; do
    sed -i "s|<$key>|$value|g" "$YAML_FILE"
  done < "$VALUES_FILE"

  while IFS='=' read -r key value; do
    sed -i "s|<$key>|$value|g" "$YAML_FILE"
  done < "$PROPERTIES_FILE"

  # Sostituzione dinamica della chiave TAG
  while IFS='=' read -r key value; do
    sed -i "s|<$key>|$value|g" "$YAML_FILE"
  done < "$TMP_TAG_FILE"

  echo "Sostituzione completata per file $YAML_FILE ambiente $ENV."
  cat $YAML_FILE
done

rm -f "$TMP_TAG_FILE"
---
deploy.sh
#!/bin/bash
# Esegue kubectl apply per ogni sottodirectory di kubernetes separatamente

YAML_DIR="kubernetes"
# Trova tutte le sottodirectory (inclusa la principale) che contengono file .yaml
find "$YAML_DIR" -type d | while read DIR; do
  if ls "$DIR"/*.yaml 1> /dev/null 2>&1; then
    echo "Deploy delle risorse nella directory $DIR..."
    kubectl --kubeconfig=./kubeconfig apply -f "$DIR"
  fi
done

echo "Deploy completato di tutte le directory YAML."
---
build_container.sh:
#!/bin/bash
set -e
set -o pipefail

echo "progetto"  $1
REPO_NAME=$1
COMMIT_SHA=$(git rev-parse HEAD)
REGISTRY_URL=$2

for dir in containers/*/; do
  CONTAINER_NAME=$(basename "$dir")
  cp -R src/${CONTAINER_NAME}/. containers/${CONTAINER_NAME}/.
  ls -la $dir
  DOCKERFILE="$dir/dockerfile"
  IMAGE_TAG="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}"
  echo "IMAGE_TAG_${CONTAINER_NAME}=$IMAGE_TAG" >> ./imglist
  if [ -f "$DOCKERFILE" ]; then
    docker build -t "$IMAGE_TAG" -f "$DOCKERFILE" "$dir"
    docker push "$IMAGE_TAG"
    echo "Build e push completate: $IMAGE_TAG"
  else
    echo "Dockerfile non trovato in $dir"
  fi
done

----
kube-provisioning.sh
#!/usr/bin/env bash
###########################################################
#./kube-provisioning.sh dev cicd-user kubeconfig-dev.yaml
#arg1 = namespace
#arg2 = env  (dev|qa|prod)
###########################################################

set -euo pipefail

############################################
# CONFIG
############################################

NAMESPACE=${1:-dev}-$2
SERVICE_ACCOUNT="deployer"
KUBECONFIG_FILE=${NAMESPACE}.yaml

echo "Namespace: $NAMESPACE"
echo "ServiceAccount: $SERVICE_ACCOUNT"
echo "Output kubeconfig: $KUBECONFIG_FILE"

############################################
# CHECK REQUIREMENTS
############################################

if ! command -v kubectl >/dev/null 2>&1; then
  echo "kubectl not found"
  exit 1
fi

############################################
# CREATE NAMESPACE
############################################

kubectl get ns "$NAMESPACE" >/dev/null 2>&1 || kubectl create namespace "$NAMESPACE"

############################################
# CREATE SERVICE ACCOUNT
############################################

kubectl -n "$NAMESPACE" get sa "$SERVICE_ACCOUNT" >/dev/null 2>&1 || \
kubectl -n "$NAMESPACE" create serviceaccount "$SERVICE_ACCOUNT"

############################################
# CREATE SECRET FOR SERVICE ACCOUNT TOKEN (legacy, validità illimitata)
############################################

SECRET_NAME="${SERVICE_ACCOUNT}-token"
if ! kubectl -n "$NAMESPACE" get secret "$SECRET_NAME" >/dev/null 2>&1; then
  kubectl -n "$NAMESPACE" create secret generic "$SECRET_NAME" \
    --type='kubernetes.io/service-account-token' \
    --dry-run=client -o yaml > tmp-secret.yaml

  # Inserisci correttamente l'annotazione YAML
  yq eval ".metadata.annotations.\"kubernetes.io/service-account.name\" = \"$SERVICE_ACCOUNT\"" -i tmp-secret.yaml

  kubectl apply -f tmp-secret.yaml
  rm tmp-secret.yaml
fi
# Attendi che il token venga popolato nel secret
for i in {1..10}; do
  TOKEN=$(kubectl -n "$NAMESPACE" get secret "$SECRET_NAME" -o jsonpath='{.data.token}' 2>/dev/null | base64 --decode || true)
  if [[ -n "$TOKEN" ]]; then break; fi
  sleep 1
done

if [[ -z "$TOKEN" ]]; then
  echo "Errore: il token non è stato generato."
  exit 1
fi

############################################
# CREATE ROLE
############################################

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: namespace-deployer
  namespace: $NAMESPACE
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
  resources: ["*"]
  verbs: ["*"]
EOF

############################################
# CREATE ROLE BINDING
############################################

kubectl -n "$NAMESPACE" get rolebinding namespace-deployer-binding >/dev/null 2>&1 || \
kubectl create rolebinding namespace-deployer-binding \
  --role=namespace-deployer \
  --serviceaccount=${NAMESPACE}:${SERVICE_ACCOUNT} \
  -n "$NAMESPACE"

############################################
# GET CLUSTER INFO
############################################

CLUSTER_NAME=$(kubectl config view --minify -o jsonpath='{.clusters[0].name}')
CLUSTER_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
CLUSTER_CA=$(kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')

############################################
# GENERATE KUBECONFIG
############################################

cat <<EOF > "$KUBECONFIG_FILE"
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: ${CLUSTER_CA}
    server: ${CLUSTER_SERVER}
  name: ${CLUSTER_NAME}

contexts:
- context:
    cluster: ${CLUSTER_NAME}
    namespace: ${NAMESPACE}
    user: ${SERVICE_ACCOUNT}
  name: ${SERVICE_ACCOUNT}-${CLUSTER_NAME}

current-context: ${SERVICE_ACCOUNT}-${CLUSTER_NAME}

users:
- name: ${SERVICE_ACCOUNT}
  user:
    token: ${TOKEN}
EOF

echo
echo "Kubeconfig generated:"
echo "$KUBECONFIG_FILE"

echo
echo "Test command:"
echo "kubectl --kubeconfig=$KUBECONFIG_FILE get pods"

---
```

## Servizi Database

- **CloudNativePG**  
```bash
kubectl apply --server-side -f  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.28/releases/cnpg-1.28.0.yaml--force-conflicts
curl -sSfL  https://github.com/cloudnative-pg/cloudnative-pg/raw/main/hack/install-cnpg-plugin.sh |  sudo sh -s -- -b /usr/local/bin


kubectl create namespace database


database.yaml:

---
apiVersion: v1
kind: Secret
metadata:
  name: pg-app-user
  namespace: demo-apps
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: *****
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-test
  namespace: demo-apps
spec:
  instances: 3
 
  storage:
    size: 1Gi
    storageClass: csi-rbdfs-sc
  
  walStorage:
    storageClass: csi-rbdfs-sc
    size: 1Gi	

  bootstrap:
    initdb:
      database: testdb
      owner: admin
      secret:
        name: pg-app-user

  postgresql:
    parameters:
      max_connections: "300"
      shared_buffers: "1GB"

  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2"
      memory: "2Gi"


#test
kubectl run psql-client -n database --rm -it --image=postgres:16 --env="PGPASSWORD=*****" -- psql -h pg-test-rw.demo-apps.svc -U admin -d appdb

kubectl patch pvc pg-test-1-wal -n demo_apps -p '{"spec":{"resources":{"requests":{"storage":"32Gi"}}}}'

backup:
  barmanObjectStore:
    destinationPath: s3://pg-backups/prod
    endpointURL: http://minio.minio.svc:9000
    s3Credentials:
      accessKeyId:
        name: s3-creds
        key: ACCESS_KEY
      secretAccessKey:
        name: s3-creds
        key: SECRET_KEY




--- pgadmin -------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgadmin-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pgadmin
  template:
    metadata:
      labels:
        app: pgadmin
    spec:
      containers:
        - name: pgadmin
          image: dpage/pgadmin4
          ports:
            - containerPort: 80
          env:
            - name: PGADMIN_DEFAULT_EMAIL
              value: pgadmin@italiadatacenter.com
            - name: PGADMIN_DEFAULT_PASSWORD
              value: ****************
---
apiVersion: v1
kind: Service
metadata:
  name: pgadmin-service
spec:
  selector:
    app: pgadmin
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: demo-route
  namespace: demo-apps
spec:
  hostnames:
  - poc3.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: pgadmin-service
      port: 80
```

--------------------------------------------------------------------------------	  

```bash
cat <<EOF | kubectl -n database apply -f -
# This StorageClass is optimized for use with CloudNativePG.
# It disables storage-level replication and ensures data is local to the pod.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: longhorn-cnpg-strict-local
provisioner: driver.longhorn.io
 # allowVolumeExpansion is crucial for scaling database storage without downtime.
allowVolumeExpansion: true
 # reclaimPolicy: Delete ensures that when a PVC is deleted, the underlying
 # Longhorn volume is also removed, preventing orphaned storage.
reclaimPolicy: Delete
parameters:
  # This is the most important setting. We rely on CloudNativePG for replication,
  # so we only need one copy at the storage layer to avoid write amplification.
  numberOfReplicas: "1"
  # dataLocality: strict-local guarantees that the volume data will be stored
  # on the same node as the pod that uses it. This is essential for performance
  # and for aligning with a true shared-nothing architecture.
  dataLocality: "strict-local"
  # A longer timeout for stale replicas is suitable for database workloads.
  staleReplicaTimeout: "2880" # 48 hours in minutes
  # Default filesystem.
  fsType: "ext4"
EOF
 
 



kubectl cnpg psql pg-devops -n devops

CREATE DATABASE giteadb;
CREATE USER gitea WITH PASSWORD '****************';
GRANT ALL PRIVILEGES ON DATABASE giteadb TO gitea;
ALTER DATABASE giteadb OWNER TO gitea;


cluster production ready:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: mycluster
  namespace: database
spec:
  instances: 3                  # → 3 nodi per HA reale

  primaryUpdateStrategy: unsupervised  
  failover:                     # → Failover automatico
    promoteTimeout: 5m
    targetPromotionRule: "prefer-high-promotion-score"

  # ---------------------------
  # STORAGE (PRODUCTION)
  # ---------------------------
  storage:
    size: 200Gi
    storageClass: fast-rbd     # Ceph, SSD, GP3 ecc.
    resizeInUse: true

  walStorage:                   # Consigliato in produzione
    size: 50Gi
    storageClass: fast-rbd

  # ---------------------------
  # WAL ARCHIVING (S3/MINIO)
  # ---------------------------
  walArchive:
    enabled: true
    destinationPath: "s3://mybucket/wal/"
    s3Credentials:
      accessKeyId:
        name: cnpg-s3-creds
        key: ACCESS_KEY_ID
      secretAccessKey:
        name: cnpg-s3-creds
        key: SECRET_ACCESS_KEY
    endpointURL: "https://s3.myregion.amazonaws.com"
    region: "myregion"
    encryption: AES256

  # ---------------------------
  # BACKUP AUTOMATICI
  # ---------------------------
  backup:
    barmanObjectStore:
      destinationPath: "s3://mybucket/basebackup/"
      endpointURL: "https://s3.myregion.amazonaws.com"
      s3Credentials:
        accessKeyId:
          name: cnpg-s3-creds
          key: ACCESS_KEY_ID
        secretAccessKey:
          name: cnpg-s3-creds
          key: SECRET_ACCESS_KEY
      wal:
        compression: bzip2
        encryption: AES256
    retentionPolicy: "30d"  # → 30 giorni di retention
  monitoring:
    enablePodMonitor: true

  # ---------------------------
  # TLS INTERNO (RACCOMANDATO)
  # ---------------------------
  certificates:
    serverTLSSecret: cnpg-server-tls
    clientTLSSecret: cnpg-client-tls

  # ---------------------------
  # RESOURCE MANAGEMENT
  # ---------------------------
  resources:
    requests:
      cpu: "1"
      memory: "2Gi"
    limits:
      cpu: "4"
      memory: "8Gi"

  # ---------------------------
  # ANTI-AFFINITY & PDB
  # ---------------------------
  affinity:
    enablePodAntiAffinity: true
    topologyKey: "kubernetes.io/hostname"

  podDisruptionBudget:
    minAvailable: 2

  # ---------------------------
  # STARTUP & HEALTH
  # ---------------------------
  postgresql:
    shared_preload_libraries:
      - "pg_stat_statements"
      - "auto_explain"
    parameters:
      max_connections: "300"
      shared_buffers: "2GB"
      effective_cache_size: "6GB"
      maintenance_work_mem: "512MB"
      wal_compression: "on"
      wal_level: "replica"
      max_wal_size: "4GB"
      checkpoint_timeout: "15min"
      synchronous_commit: "remote_apply"

  # ---------------------------
  # SYNCHRONOUS REPLICATION
  # ---------------------------
  replication:
    synchronous:
      mode: " quorum "
      number: 1   # One sync replica; others async

  # ---------------------------
  # SERVICE & NETWORKING
  # ---------------------------
  service:
    type: ClusterIP
    primary:
      type: ClusterIP
    replicas:
      type: ClusterIP

  # ---------------------------
  # ENCRYPTION AT REST (OPZIONALE)
  # ---------------------------
  encryption:
    enabled: true
    mode: aes256-gcm
```

  ------------------------------------------------------------------------------------------------------------------------------

- **MySql Operator**  
```bash
helm repo add mysql-operator https://mysql.github.io/mysql-operator/
helm repo update

helm install my-mysql-operator mysql-operator/mysql-operator  --namespace mysql-operator --create-namespace

--db instance
kubectl create secret generic mypwds \
        --from-literal=rootUser=root \
        --from-literal=rootHost=% \
        --from-literal=rootPassword="******"


apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mycluster
spec:
  secretName: mypwds
  tlsUseSelfSigned: true
  instances: 3
  router:
    instances: 1		
	
	
--- test
$> kubectl run --rm -it myshell --image=container-registry.oracle.com/mysql/community-operator -- mysqlsh root@mycluster --sql
If you don't see a command prompt, try pressing enter.
******

MySQL mycluster SQL> SELECT @@hostname

+-------------+
| @@hostname  |
+-------------+
| mycluster-0 |
+-------------+	
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Redis**  
  ------------------------------------------------------------------------------------------------------------------------------
```bash
kubectl create namespace redis
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update


cat <<EOF | cat > redisvalues.yaml -
architecture: replication

auth:
  enabled: true
  password: **************** 

master:
  persistence:
    enabled: true
    storageClass: csi-rbdfs-sc
    size: 5Gi
  resources:
    requests:
      cpu: 100m
      memory: 256Mi

replica:
  replicaCount: 2
  persistence:
    enabled: true
    storageClass: csi-rbdfs-sc
    size: 5Gi
  resources:
    requests:
      cpu: 100m
      memory: 256Mi

sentinel:
  enabled: true
  replicas: 3
  resources:
    requests:
      cpu: 50m
      memory: 64Mi

metrics:
  enabled: false
EOF

helm install redis bitnami/redis  -n redis   -f redisvalues.yaml

#test
kubectl run redis-client -n redis --rm -it --image=redis:7.2 -- redis-cli  -h redis.redis.svc.cluster.local  -a ****************



###########################################################################################################################
Redis(R) can be accessed via port 6379 on the following DNS name from within your cluster:

    redis.redis.svc.cluster.local for read only operations

For read/write operations, first access the Redis(R) Sentinel cluster, which is available in port 26379 using the same domain name above.

To get your password run:

    export REDIS_PASSWORD=$(kubectl get secret --namespace redis redis -o jsonpath="{.data.redis-password}" | base64 -d)

To connect to your Redis(R) server:

1. Run a Redis(R) pod that you can use as a client:

   kubectl run --namespace redis redis-client --restart='Never'  --env REDIS_PASSWORD=$REDIS_PASSWORD  --image registry-1.docker.io/bitnami/redis:latest --command -- sleep infinity

   Use the following command to attach to the pod:

   kubectl exec --tty -i redis-client \
   --namespace redis -- bash

2. Connect using the Redis(R) CLI:
   REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h redis -p 6379 # Read only operations
   REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h redis -p 26379 # Sentinel access

To connect to your database from outside the cluster execute the following commands:

    kubectl port-forward --namespace redis svc/redis 6379:6379 &
    REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h 127.0.0.1 -p 6379
```

- **InfluxDB**  
  ------------------------------------------------------------------------------------------------------------------------------
```bash
helm repo add influxdata https://helm.influxdata.com/
helm repo update
----
kubectl create namespace influxdb
----
influxdb-values.yaml:
image:
  repository: influxdb
  tag: 2.7

persistence:
  enabled: true
  size: 20Gi

resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 1
    memory: 1Gi

service:
  type: ClusterIP
  port: 8086

adminUser:
  organization: sts-lab
  bucket: demo-bucket
  user: admin
  password: *****************
  token: my-super-token


----  
helm install influxdb influxdata/influxdb2  --namespace influxdb  -f influxdb-values.yaml
  
 --- 
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: influxdb
spec:
  hostnames:
  - poc2.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: influxdb-influxdb2
      port: 8086

  
  
  
******************  TEST ****************************
  
echo $(kubectl get secret influxdb-influxdb2-auth -o "jsonpath={.data['admin-password']}" --namespace influxdb | base64 --decode)
  
  logon UI
  http://localhost:8086
  
  user: admin
  password: *****************
  
  TEST API:
  curl http://localhost:8086/health
  
  
  link svc:
  influxdb-influxdb2.influxdb.svc.cluster.local
```

- **MongoDB**  
  ------------------------------------------------------------------------------------------------------------------------------

- **DbGate**  
  ------------------------------------------------------------------------------------------------------------------------------
```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dbgate
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dbgate
  namespace: dbgate

spec:
  replicas: 1

  selector:
    matchLabels:
      app: dbgate

  template:
    metadata:
      labels:
        app: dbgate

    spec:
      containers:
        - name: dbgate
          image: dbgate/dbgate:latest

          ports:
            - containerPort: 3000

          env:
            - name: CONNECTIONS
              value: ""

          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"

            limits:
              cpu: "500m"
              memory: "512Mi"  
---
apiVersion: v1
kind: Service
metadata:
  name: dbgate
  namespace: dbgate

spec:
  selector:
    app: dbgate

  ports:
    - port: 80
      targetPort: 3000

  type: ClusterIP			  
  
 httproute:
 apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: demo-route
  namespace: demo-apps
spec:
  hostnames:
  - poc3.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: dbgate
      port: 3000
---
```

------------------------------------------------------------------------------------------------------------------------------
## Servizi Applicativi / Utility

- **NodeRed**  
```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: nodered
---  
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: node-red-pvc
  namespace: nodered
  labels:
    app: node-red
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
---	  
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-red
  namespace: nodered
  labels:
    app: node-red
spec:
  replicas: 1
  selector:
    matchLabels:
      app: node-red
  template:
    metadata:
      labels:
        app: node-red
    spec:
      securityContext:
        fsGroup: 1000
      containers:
      - name: nodered
        image: nodered/node-red:4.1
        args: ["--settings", "/config/settings.js"]
        env:
        - name: NODE_OPTIONS
          value: "--trace-warnings"
        ports:
        - containerPort: 1880
        securityContext:
          runAsUser: 1000
          runAsGroup: 1000
          allowPrivilegeEscalation: false
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"
        livenessProbe:
          httpGet:
            path: /
            port: 1880
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 1880
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: node-red-storage
          mountPath: /data
        - name: node-red-settings
          mountPath: /config/settings.js
          subPath: settings.js
      volumes:
      - name: node-red-storage
        persistentVolumeClaim:
          claimName: node-red-pvc
      - name: node-red-settings
        configMap:
          name: node-red-settings
---		  
apiVersion: v1
kind: ConfigMap
metadata:
  name: node-red-settings
  namespace: nodered
data:
  settings.js: |
    module.exports = {
      httpAdminRoot: '/',
      httpNodeRoot: '/',
      userDir: '/data',
      flowFile: 'flows.json',
      credentialSecret: 'yzM0ol6Zn5kd1234',
      adminAuth: {
        type: "credentials",
        users: [{
          username: "admin",
          password: "",
          permissions: "*"
        }]
      },
      uiPort: process.env.PORT || 1880,
      mqttReconnectTime: 15000,
      serialReconnectTime: 15000,
      debugMaxLength: 1000,
      functionGlobalContext: {},
      exportGlobalContextKeys: false,
      logging: {
        console: {
          level: "info",
          metrics: false,
          audit: false
        }
      },
      editorTheme: {
        projects: {
          enabled: false
        }
      }
    };
---
apiVersion: v1
kind: Service
metadata:
  name: node-red-service
  namespace: nodered
  labels:
    app: node-red
spec:
  type: ClusterIP
  ports:
  - port: 1880
    targetPort: 1880
    protocol: TCP
    name: http
  selector:
    app: node-red
---	
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: node-red-hpa
  namespace: nodered
  labels:
    app: node-red
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: node-red
  minReplicas: 1
  maxReplicas: 3
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
---		
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: node-red
  namespace: nodered
spec:
  hostnames:
  - nodered.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: node-red-service
      port: 1880
	  
Istruzioni per set token influxdb	  
kubectl get pods -n nodered
kubectl exec -it node-red-bd88bc7df-knqfw -n nodered -- node-red admin hash-pw  

kubectl edit configmap node-red-settings -n nodered  --->(set campo password password: "<bcrypt-hash-here>", nella sezione adminAuth)

kubectl delete pods node-red-bd88bc7df-knqfw -n nodered
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Grafana**
  Aggiungere repository Helm Grafana

```bash
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
----
kubectl create namespace grafana
----
grafana-values.yaml:
replicas: 1

adminUser: admin
adminPassword: *****************

service:
  type: ClusterIP
  port: 80

persistence:
  enabled: true
  size: 10Gi

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi
---	
helm install grafana grafana/grafana  -n grafana  -f grafana-values.yaml
---  
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: grafana
spec:
  hostnames:
  - tekton.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: grafana
      port: 80 
  
  
  
******************  TEST ****************************
Accesso alla UI Grafana
  
Aprire browser:

http://localhost:3000

Login:

user: admin
password: *****************



link svc:
  grafana.grafana.svc.cluster.local


---------------------------------------
Aggiungere InfluxDB come datasource


In Grafana:

Connections
   ↓
Data Sources
   ↓
Add data source
   ↓
InfluxDB

Configurazione:

URL
http://influxdb:8086

Organization:
demo-org

Token:
my-super-token

Bucket:
demo-bucket

Salva.

6️⃣ Test datasource

Click:

Save & Test

Se corretto:

Datasource is working
7️⃣ Creare dashboard

In Grafana:

Create
  ↓
Dashboard
  ↓
Add panel

Query esempio (InfluxDB Flux):

from(bucket: "demo-bucket")
  |> range(start: -1h)
```

  ------------------------------------------------------------------------------------------------------------------------------

- **Prometheus**  
  ------------------------------------------------------------------------------------------------------------------------------

- **SonarQube**  
  ------------------------------------------------------------------------------------------------------------------------------
###########  repo helm ################
```bash
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
helm repo update
```

###########  creazione ns e secret db ################
```bash
kubectl create namespace sonarqube

kubectl create secret generic sonarqube-database-cred \
  --from-literal=username=sonarqube \
  --from-literal=password=**************** \
  -n sonarqube
```

###########  creazione database ################ 
```bash
kubectl cnpg psql pg-devops -n devops

CREATE DATABASE sonarqube;
CREATE USER sonarqube WITH PASSWORD '****************';
GRANT ALL PRIVILEGES ON DATABASE sonarqube TO sonarqube;
ALTER DATABASE sonarqube OWNER TO sonarqube;  
```

########### Values.yaml per installazione helm  ################  

service:
  type: ClusterIP

postgresql:
  enabled: false

jdbcOverwrite:
  enabled: true
  jdbcUrl: "jdbc:postgresql://pg-devops-rw.devops.svc.cluster.local:5432/sonarqube"
  jdbcUsername: "postgres"
  jdbcSecretName: "sonarqube-database-cred"
  jdbcSecretPasswordKey: "password"

readinessProbe:
  initialDelaySeconds: 300  # Increase initial delay to accommodate the database start time
  timeoutSeconds: 60
  periodSeconds: 10
  successThreshold: 1
  failureThreshold: 3

livenessProbe:
  initialDelaySeconds: 360  # Ensure the application has enough time to start
  timeoutSeconds: 60
  periodSeconds: 10
  successThreshold: 1
  failureThreshold: 3

startupProbe:
  initialDelaySeconds: 300  # Allow for sufficient startup time
  timeoutSeconds: 60
  periodSeconds: 10
  successThreshold: 1
  failureThreshold: 3  
  
  
###########  installazione helm ################  
```bash
helm upgrade -f sonarvalues.yaml --install -n sonarqube sonarqube sonarqube/sonarqube --set community.enabled=true,monitoringPasscode="****************"
```

###########  httproute ################  
```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: sonarqube
  namespace: sonarqube
spec:
  hostnames:
  - sonarqube.italiadatacenter.com
  parentRefs:
  - name: main-gateway
    namespace: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: sonarqube-sonarqube
      port: 9000
```

- **KubeEdge**  
  ------------------------------------------------------------------------------------------------------------------------------

- **Knative**  
  ------------------------------------------------------------------------------------------------------------------------------

- **Locust**  
  ------------------------------------------------------------------------------------------------------------------------------


    
