From 20d506407abec8d19fc2eaecd7bc20095e364438 Mon Sep 17 00:00:00 2001 From: alessandro Date: Fri, 17 Jul 2026 09:42:52 +0200 Subject: [PATCH] primo --- GatewayAPI.txt | 126 + Object mapping.xlsx | Bin 0 -> 5463 bytes Platform STS.docx | Bin 0 -> 32758 bytes README.md | 2566 +++++++++++++++++ Runbook Infrastruttura RKE2 e.docx | Bin 0 -> 24250 bytes ServiceModel.xlsx | Bin 0 -> 13388 bytes add-listener.js | 69 + add-listener.sh | 81 + add-on/CephCsi.txt | 118 + add-on/CephCsi_install.txt | 122 + add-on/Istruzioni_kubeedge.txt | 276 ++ add-on/NodeRed.yaml | 189 ++ add-on/Nodered_token_istruction.txt | 11 + add-on/RabbitMQ.txt | 59 + add-on/act-runner-host.txt | 44 + add-on/backup.txt | 21 + add-on/cicd.txt | 344 +++ add-on/clusterussuer.yaml | 17 + add-on/confmap.png | Bin 0 -> 66536 bytes add-on/create_ns.sh | 118 + add-on/dbgate.yaml | 59 + add-on/devops.txt | 113 + add-on/edgenodeport.yaml | 30 + add-on/get_helm.sh | 347 +++ add-on/gitea-action.txt | 88 + add-on/gitea-action2.txt | 161 ++ add-on/gitea-workflow.txt | 87 + add-on/gitea.sh | 100 + add-on/grafana.txt | 123 + add-on/harbor.sh | 201 ++ add-on/influxdb.png | Bin 0 -> 14435 bytes add-on/influxdb.txt | 76 + add-on/install_edgenode.sh | 164 ++ add-on/installa-cert-manager.sh | 11 + add-on/installaRancher.sh | 7 + add-on/installa_helm.sh | 7 + add-on/k8s-secret.jpg | Bin 0 -> 10013 bytes add-on/longhornui.yaml | 23 + add-on/minio.txt | 111 + add-on/mosquitto.yaml | 104 + add-on/mqtt.png | Bin 0 -> 19344 bytes add-on/mysql.txt | 38 + add-on/nginx-controller-service-nodeport.yaml | 30 + add-on/package-lock.json | 38 + add-on/package.json | 5 + add-on/pgdb.txt | 62 + add-on/pipeline.txt | 71 + add-on/pipeline_segregata.sh | 169 ++ add-on/pipelines.sh | 349 +++ add-on/poc-test-volume.yaml | 31 + add-on/poc-testweb.yaml | 66 + add-on/postgresql.sh | 315 ++ add-on/python.png | Bin 0 -> 37415 bytes add-on/rancher-httproute.yaml | 19 + add-on/redis.png | Bin 0 -> 27739 bytes add-on/redis.sh | 82 + add-on/run_validate_css.cmd | 1 + add-on/sonaquebe.txt | 87 + add-on/tekton.sh | 197 ++ add-on/validate-scss.ps1 | 134 + add-on/vol.png | Bin 0 -> 9719 bytes addworker.sh | 27 + build-listener-from-endpoint.sh | 34 + demo.yaml | 189 ++ doc.txt | 2377 +++++++++++++++ dockerbuild.sh | 0 gateway.yaml | 134 + haproxy.html | 289 ++ hosts | 7 + installa_haproxy.sh | 123 + installazione.md | 2437 ++++++++++++++++ iscsiadm-open-iscsi.sh | 4 + istruzioni.doc | Bin 0 -> 41472 bytes istruzioni.docx | Bin 0 -> 5168 bytes istruzioni.txt | 46 + master23-installa_rke.sh | 56 + master_1_installa_rke.sh | 61 + pipeline/DEV_build_deploy.md | 154 + pipeline/DEV_build_deploy.yaml | 36 + pipeline/add-listener.sh | 81 + pipeline/addlistener.sh | 34 + pipeline/build_container.sh | 58 + pipeline/customize.sh | 55 + pipeline/deploy.sh | 60 + prepara_nodo.sh | 36 + primo.sh | 9 + st_migrationlist.xlsx | Bin 0 -> 11205 bytes st_size.csv | 297 ++ st_size.xlsx | Bin 0 -> 20121 bytes template.txt | 135 + todo_list.txt | 32 + utils.txt | 59 + worker-installa_rke2.sh | 29 + 93 files changed, 14526 insertions(+) create mode 100644 GatewayAPI.txt create mode 100644 Object mapping.xlsx create mode 100644 Platform STS.docx create mode 100644 README.md create mode 100644 Runbook Infrastruttura RKE2 e.docx create mode 100644 ServiceModel.xlsx create mode 100644 add-listener.js create mode 100644 add-listener.sh create mode 100644 add-on/CephCsi.txt create mode 100644 add-on/CephCsi_install.txt create mode 100644 add-on/Istruzioni_kubeedge.txt create mode 100644 add-on/NodeRed.yaml create mode 100644 add-on/Nodered_token_istruction.txt create mode 100644 add-on/RabbitMQ.txt create mode 100644 add-on/act-runner-host.txt create mode 100644 add-on/backup.txt create mode 100644 add-on/cicd.txt create mode 100644 add-on/clusterussuer.yaml create mode 100644 add-on/confmap.png create mode 100644 add-on/create_ns.sh create mode 100644 add-on/dbgate.yaml create mode 100644 add-on/devops.txt create mode 100644 add-on/edgenodeport.yaml create mode 100644 add-on/get_helm.sh create mode 100644 add-on/gitea-action.txt create mode 100644 add-on/gitea-action2.txt create mode 100644 add-on/gitea-workflow.txt create mode 100644 add-on/gitea.sh create mode 100644 add-on/grafana.txt create mode 100644 add-on/harbor.sh create mode 100644 add-on/influxdb.png create mode 100644 add-on/influxdb.txt create mode 100644 add-on/install_edgenode.sh create mode 100644 add-on/installa-cert-manager.sh create mode 100644 add-on/installaRancher.sh create mode 100644 add-on/installa_helm.sh create mode 100644 add-on/k8s-secret.jpg create mode 100644 add-on/longhornui.yaml create mode 100644 add-on/minio.txt create mode 100644 add-on/mosquitto.yaml create mode 100644 add-on/mqtt.png create mode 100644 add-on/mysql.txt create mode 100644 add-on/nginx-controller-service-nodeport.yaml create mode 100644 add-on/package-lock.json create mode 100644 add-on/package.json create mode 100644 add-on/pgdb.txt create mode 100644 add-on/pipeline.txt create mode 100644 add-on/pipeline_segregata.sh create mode 100644 add-on/pipelines.sh create mode 100644 add-on/poc-test-volume.yaml create mode 100644 add-on/poc-testweb.yaml create mode 100644 add-on/postgresql.sh create mode 100644 add-on/python.png create mode 100644 add-on/rancher-httproute.yaml create mode 100644 add-on/redis.png create mode 100644 add-on/redis.sh create mode 100644 add-on/run_validate_css.cmd create mode 100644 add-on/sonaquebe.txt create mode 100644 add-on/tekton.sh create mode 100644 add-on/validate-scss.ps1 create mode 100644 add-on/vol.png create mode 100644 addworker.sh create mode 100644 build-listener-from-endpoint.sh create mode 100644 demo.yaml create mode 100644 doc.txt create mode 100644 dockerbuild.sh create mode 100644 gateway.yaml create mode 100644 haproxy.html create mode 100644 hosts create mode 100644 installa_haproxy.sh create mode 100644 installazione.md create mode 100644 iscsiadm-open-iscsi.sh create mode 100644 istruzioni.doc create mode 100644 istruzioni.docx create mode 100644 istruzioni.txt create mode 100644 master23-installa_rke.sh create mode 100644 master_1_installa_rke.sh create mode 100644 pipeline/DEV_build_deploy.md create mode 100644 pipeline/DEV_build_deploy.yaml create mode 100644 pipeline/add-listener.sh create mode 100644 pipeline/addlistener.sh create mode 100644 pipeline/build_container.sh create mode 100644 pipeline/customize.sh create mode 100644 pipeline/deploy.sh create mode 100644 prepara_nodo.sh create mode 100644 primo.sh create mode 100644 st_migrationlist.xlsx create mode 100644 st_size.csv create mode 100644 st_size.xlsx create mode 100644 template.txt create mode 100644 todo_list.txt create mode 100644 utils.txt create mode 100644 worker-installa_rke2.sh diff --git a/GatewayAPI.txt b/GatewayAPI.txt new file mode 100644 index 0000000..d7de8e9 --- /dev/null +++ b/GatewayAPI.txt @@ -0,0 +1,126 @@ +# 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 - <O?j3B4%<=~6@x=^c?= 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 **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 < /dev/null < /dev/null < /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 < 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 < 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 < 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 < 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 < 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 < 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 - < + 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 <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 < 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 < 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 - < "$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 </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 < "$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 < 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 < 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: "", 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** + ------------------------------------------------------------------------------------------------------------------------------ + + + diff --git a/Runbook Infrastruttura RKE2 e.docx b/Runbook Infrastruttura RKE2 e.docx new file mode 100644 index 0000000000000000000000000000000000000000..02cccafc20c8d7084ca02479669b2313a3a78515 GIT binary patch literal 24250 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 literal 0 HcmV?d00001 diff --git a/ServiceModel.xlsx b/ServiceModel.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af255c4dcea0312ea3b12ea755ec652c1113b8e1 GIT binary patch literal 13388 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 [gateway-file] + * + * Esempio: + * node add-listener.js sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret + * node add-listener.js grafana.italiadatacenter.com https-grafana grafana-secret gateway.yaml + */ + +const fs = require('fs'); +const path = require('path'); + +const [,, hostname, name, secretName, gatewayFile = 'gateway.yaml'] = process.argv; + +if (!hostname || !name || !secretName) { + console.error('Uso: node add-listener.js [gateway-file]'); + console.error('Es.: node add-listener.js sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret'); + process.exit(1); +} + +const filepath = path.resolve(__dirname, gatewayFile); + +if (!fs.existsSync(filepath)) { + console.error(`File non trovato: ${filepath}`); + process.exit(1); +} + +const content = fs.readFileSync(filepath, 'utf8'); + +// Controlla se il listener esiste già (per nome o hostname) +if (content.includes(`name: ${name}`) || content.includes(`hostname: ${hostname}`)) { + console.warn(`Attenzione: un listener con name "${name}" o hostname "${hostname}" esiste già. Nessuna modifica applicata.`); + process.exit(0); +} + +const newBlock = ` - allowedRoutes: + namespaces: + from: All + hostname: ${hostname} + name: ${name} + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: ${secretName} + mode: Terminate`; + +// Inserisce il nuovo block prima della riga "kind: List" o in fondo alla lista listeners +// Trova l'ultima occorrenza di "mode: Terminate" e appende subito dopo +const lastTerminateIdx = content.lastIndexOf(' mode: Terminate'); +if (lastTerminateIdx === -1) { + console.error('Impossibile trovare il punto di inserimento (mode: Terminate). File non modificato.'); + process.exit(1); +} + +const insertAfter = lastTerminateIdx + ' mode: Terminate'.length; +const updated = content.slice(0, insertAfter) + '\n' + newBlock + content.slice(insertAfter); + +fs.writeFileSync(filepath, updated, 'utf8'); +console.log(`Listener aggiunto:`); +console.log(` hostname : ${hostname}`); +console.log(` name : ${name}`); +console.log(` secret : ${secretName}`); +console.log(`File aggiornato: ${filepath}`); diff --git a/add-listener.sh b/add-listener.sh new file mode 100644 index 0000000..d989537 --- /dev/null +++ b/add-listener.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Aggiunge un listener HTTPS direttamente sulla risorsa K8s Gateway +# main-gateway nel namespace nginx-gateway, tramite kubectl patch. +# +# Uso: +# ./add-listener.sh +# +# Esempio: +# ./add-listener.sh sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret + +set -euo pipefail + +GATEWAY_NAME="main-gateway" +GATEWAY_NS="nginx-gateway" + +HOSTNAME_VAL="${1:-}" +NAME_VAL="${2:-}" +SECRET_NAME="${3:-}" + +if [[ -z "$HOSTNAME_VAL" || -z "$NAME_VAL" || -z "$SECRET_NAME" ]]; then + echo "Uso: $0 " + echo "Es.: $0 sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret" + exit 1 +fi + +# Controlla idempotenza: verifica se il listener esiste già per nome o hostname +EXISTING=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \ + -o jsonpath='{.spec.listeners[*].name}') + +if echo "$EXISTING" | grep -qw "$NAME_VAL"; then + echo "Attenzione: listener con name '${NAME_VAL}' già presente. Nessuna modifica." + exit 0 +fi + +EXISTING_HOSTS=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \ + -o jsonpath='{.spec.listeners[*].hostname}') + +if echo "$EXISTING_HOSTS" | grep -qw "$HOSTNAME_VAL"; then + echo "Attenzione: listener con hostname '${HOSTNAME_VAL}' già presente. Nessuna modifica." + exit 0 +fi + +# JSON Patch: aggiunge il nuovo listener in append alla lista +PATCH=$(cat < ceph-csi-rbd-values.yaml +csiConfig: + - clusterID: "004ee854-86cc-4ddc-b7d6-75e4fe962296" + monitors: + - "172.20.1.33:6789" + - "172.20.1.34:6789" + - "172.20.1.35:6789" + +provisioner: + name: provisioner + replicaCount: 2 +EOF + + +helm install --namespace ceph-csi ceph-csi --values ceph-csi-rbd-values.yaml ./ + +Examples on how to configure a storage class and start using the driver are here: +https://github.com/ceph/ceph-csi/tree/devel/examples/rbd + + +echo "kubernetes" | tr -d '\n' | base64 +a3ViZXJuZXRlcw== + +cat > ceph-admin-secret.yaml << EOF +apiVersion: v1 +kind: Secret +metadata: + name: csi-rbd-secret + namespace: default +stringData: + userID: kubernetes + userKey: AQD2zo5pm8aZIRAAPzWS+dROeX7iJtv5EukfKA== +EOF + +# create a secret for admin user. +kubectl apply -f ceph-admin-secret.yaml + + + +----- Create StorageClass ------ + +cat > ceph-rbd-sc.yaml < create-ceph-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 + +# create pvc +kubectl apply -f create-ceph-pvc.yaml + +cat < create-pod-with-pvc.yaml +apiVersion: v1 +kind: Pod +metadata: + name: ceph-pod-pvc +spec: + containers: + - name: ceph-pod-pvc + image: busybox + command: ["sleep", "infinity"] + volumeMounts: + - mountPath: /mnt/ceph_rbd + name: volume + volumes: + - name: volume + persistentVolumeClaim: + claimName: raw-block-pvc +EOF + +# create pod +kubectl apply -f create-pod-with-pvc.yaml \ No newline at end of file diff --git a/add-on/CephCsi_install.txt b/add-on/CephCsi_install.txt new file mode 100644 index 0000000..38ee348 --- /dev/null +++ b/add-on/CephCsi_install.txt @@ -0,0 +1,122 @@ +cat < 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 < 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 < 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 < 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 < 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 < 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 \ No newline at end of file diff --git a/add-on/Istruzioni_kubeedge.txt b/add-on/Istruzioni_kubeedge.txt new file mode 100644 index 0000000..db9ca36 --- /dev/null +++ b/add-on/Istruzioni_kubeedge.txt @@ -0,0 +1,276 @@ +Cloudside: + +wget https://github.com/kubeedge/kubeedge/releases/download/v1.17.0/keadm-v1.17.0-linux-amd64.tar.gz +tar -zxvf keadm-v1.17.0-linux-amd64.tar.gz +cp keadm-v1.17.0-linux-amd64/keadm/keadm /usr/local/bin/keadm + +keadm init --advertise-address="89.105.78.161" --kubeedge-version=v1.22.0 --kube-config=/root/.kube/config + +keadm manifest generate --advertise-address="89.105.78.161" --kube-config=/root/.kube/config > kubeedge-cloudcore.yaml + +patch: + + cloudcore.yaml: | + apiVersion: cloudcore.config.kubeedge.io/v1alpha2 + commonConfig: + monitorServer: + bindAddress: 127.0.0.1:9091 + tunnelPort: 10354 + featureGates: + requireAuthorization: false + kind: CloudCore + +kubectl apply -f kubeedge-cloudcore.yaml -n kubeedge +kubectl apply -f edgenodeport.yaml -n kubeedge + + + +---------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Edge Side: +export KUBEEDGE_VERSION=v1.22.0 +export CLOUD_MASTER_IP=34.132.113.155 +export EDGE_NODE_NAME=edge-node-02 + + +Step 1 : Download & unpack containerd package +Containerd versions can be found in this location : https://github.com/containerd/containerd/releases + +Download : + wget https://github.com/containerd/containerd/releases/download/v1.6.14/containerd-1.6.14-linux-amd64.tar.gz +Unpack : + sudo tar Cxzvf /usr/local containerd-1.6.14-linux-amd64.tar.gz + +Step 2 : Install runc +Runc is a standardized runtime for spawning and running containers on Linux according to the OCI specification + + wget https://github.com/opencontainers/runc/releases/download/v1.1.3/runc.amd64 + sudo install -m 755 runc.amd64 /usr/local/sbin/runc + +Step 3: Download and install CNI plugins : + wget https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz + sudo mkdir -p /opt/cni/bin + sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.1.1.tgz + +Step 4: Configure containerd + +Create a containerd directory for the configuration file +config.toml is the default configuration file for containerd +Enable systemd group . Use sed command to change the parameter in config.toml instead of using vi editor +Convert containerd into service + + sudo mkdir /etc/containerd + containerd config default | sudo tee /etc/containerd/config.toml + sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml + sudo curl -L https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /etc/systemd/system/containerd.service + +Step 4.1 +remove SystemdCgroup = true line from /etc/containerd/config.toml. + +Step 4.2 +mkdir -p /etc/cni/net.d/ + +$ cat >/etc/cni/net.d/10-containerd-net.conflist </dev/null || true + fi +done + +# ------------------------------- +# 2. Cleanup containerd (safe) +# ------------------------------- +echo "[2] Cleaning containerd..." + +if command -v crictl &> /dev/null; then + echo "Using crictl..." + + # rimuove solo container stopped + crictl ps -a --state Exited -q | xargs -r crictl rm || true + + # rimuove pod sandbox non più validi + crictl pods --state NotReady -q | xargs -r crictl rmp || true + + # pulizia immagini dangling (SAFE) + crictl rmi --prune || true +else + echo "crictl not found, skipping..." +fi + +# ------------------------------- +# 3. Restart containerd +# ------------------------------- +echo "[3] Restarting containerd..." +systemctl restart containerd + +# ------------------------------- +# 4. Cleanup kubelet (soft) +# ------------------------------- +echo "[4] Cleaning kubelet state (soft)..." + +# Rimuove solo pod non più esistenti +find /var/lib/kubelet/pods -mindepth 1 -maxdepth 1 -type d -mtime +1 -exec rm -rf {} + || true + +# ------------------------------- +# 5. Restart kube services +# ------------------------------- +echo "[5] Restarting kubelet + edgecore..." + +systemctl restart kubelet || true +systemctl restart edgecore || true + +# ------------------------------- +# 6. Verifica connessione CloudCore +# ------------------------------- +echo "[6] Checking connectivity to CloudCore..." + +CLOUD_IP="89.105.78.161" +PORT="10002" + +if timeout 5 bash -c "(set campo password password: "", nella sezione adminAuth) + +kubectl delete pods node-red-bd88bc7df-knqfw -n nodered + + +user: admin +password:KAYQE1QA7uwUZ8uI \ No newline at end of file diff --git a/add-on/RabbitMQ.txt b/add-on/RabbitMQ.txt new file mode 100644 index 0000000..91f35a5 --- /dev/null +++ b/add-on/RabbitMQ.txt @@ -0,0 +1,59 @@ +Install Operator + +kubectl apply -f "https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml" +# namespace/rabbitmq-system created +# customresourcedefinition.apiextensions.k8s.io/rabbitmqclusters.rabbitmq.com created +# serviceaccount/rabbitmq-cluster-operator created +# role.rbac.authorization.k8s.io/rabbitmq-cluster-leader-election-role created +# clusterrole.rbac.authorization.k8s.io/rabbitmq-cluster-operator-role created +# rolebinding.rbac.authorization.k8s.io/rabbitmq-cluster-leader-election-rolebinding created +# clusterrolebinding.rbac.authorization.k8s.io/rabbitmq-cluster-operator-rolebinding created +# deployment.apps/rabbitmq-cluster-operator created + +Define an instance +Mqinstance.yaml: + +apiVersion: rabbitmq.com/v1beta1 +kind: RabbitmqCluster +metadata: + name: production-ready +spec: + replicas: 1 + resources: + requests: + cpu: 1 + memory: 1Gi + limits: + cpu: 1 + memory: 1Gi + rabbitmq: + additionalConfig: | + cluster_partition_handling = pause_minority + disk_free_limit.relative = 1.0 + collect_statistics_interval = 10000 + persistence: + storageClassName: csi-rbdfs-sc + storage: "5Gi" + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - production-ready + topologyKey: kubernetes.io/hostname + override: + statefulSet: + spec: + template: + spec: + containers: [] + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: "topology.kubernetes.io/zone" + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/name: production-ready \ No newline at end of file diff --git a/add-on/act-runner-host.txt b/add-on/act-runner-host.txt new file mode 100644 index 0000000..4b8b41f --- /dev/null +++ b/add-on/act-runner-host.txt @@ -0,0 +1,44 @@ +Download act_runner binary + 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 < con il relativo valore letto dal file values.env della directory corrispondente all'input fornito +1) build_container.sh : script che effettua la docker build di tutti i container presenti nella directory containers usando il nome della directory sotto containers come nome del container,nome della root directory come nome del repository e sha del commit come tag. lo script deve eseguire anche la push su un registry con precedente login con credenziali lette dal file properties.env +2) deploy.sh: script che effettua il deploy in kubernetes del file infrastructure.yaml contenuto nella directory kubernetes + +struttura directory di progetto: + progetto-A + properties.env + build_src.sh + .gitea/ + workflows + pipeline.yaml + containers + frontend + dockerfile + backend + dockerfile + env + dev + values.env + qa + values.env + prod + values.env + kubernetes + infrastructure.yaml + src + + + Provisioning: + env: + $organization= nome della società + $project=nome del progetto + + + 1) Gitea: creazione Organization--> $organization(if not alreay exist) + 2) Gitea: creazione progetto nome--> $project + 3) Harbor: creazione project $project + 4) X3 k8s creazione NAMSPACE,SA, ROLE, ROLEBINDING,KUBECONFIG per deploy su namespace (kube-provisioning.sh) per dev qa e prod + 5) X3 Gitea: creazione secret KUBECONFIG_DEV, KUBECONFIG_QA e KUBECONFIG_PROD con kubeconfig generato da kube-provisioning.sh + 6) harbor: creazione robot-user "git" con permessi full su repo del progetto ---> $registry_user, $registry_pass + 7) harbor: creazione robot-user "k8spull" con permessi full su repo del progetto ---> $registry_user, $registry_pass + 8) creazione secret e patch SA default + kubectl -n <$project> create secret docker-registry harbor-pull \ + --docker-server=harbor.italiadatacenter.com \ + --docker-username=robot\$<$project>+k8spull \ + --docker-password= \ + --docker-email=harbor@italiadatacenter.com + + kubectl patch serviceaccount default -n athleteos-dev -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}' + kubectl patch serviceaccount default -n <$project-qa> -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}' + kubectl patch serviceaccount default -n <$project-prod> -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}' + 9) Gitea: creazione secret REGISTRY_USER -->$registry_user, REGISTRY_PASS -->$registry_pass + 10)Gitea: poplazione repo con skeleton template + + + gitea-api-token + 65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88 + + https://git.italiadatacenter.com/api/swagger + + + List Template + curl -X 'GET' \ + 'https://git.italiadatacenter.com/api/v1/repos/search?q=tmpl&topic=false&includeDesc=true&token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' + + + List repository: + curl -X 'GET' \ + 'https://git.italiadatacenter.com/api/v1/repos/search?token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' + + + Create repo from template: + curl -X 'POST' \ + 'https://git.italiadatacenter.com/api/v1/repos/STS_Lab/tmpl_nginx_node/generate?token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "avatar": true, + "default_branch": "string", + "description": "string", + "git_content": true, + "git_hooks": true, + "labels": true, + "name": "secondo", + "owner": "STS_Lab", + "private": true, + "protected_branch": true, + "topics": true, + "webhooks": true +}' + + +creazione din una applicazione backstage che permetta la creazione di un repo da template con questa api: + curl -X 'POST' \ + 'https://git.italiadatacenter.com/api/v1/repos/STS_Lab/tmpl_nginx_node/generate?token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "avatar": true, + "default_branch": "string", + "description": "string", + "git_content": true, + "git_hooks": true, + "labels": true, + "name": "secondo", + "owner": "STS_Lab", + "private": true, + "protected_branch": true, + "topics": true, + "webhooks": true +}' + +la lista dei template disponbili è fornita dall'api: + curl -X 'GET' \ + 'https://git.italiadatacenter.com/api/v1/repos/search?q=tmpl&topic=false&includeDesc=true&token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' + + l'utente deve poter creare un nuovo repo fornendo nome e template scelto dalla lista + + --------------------------------- + Harbor api: + https://harbor.italiadatacenter.com/devcenter-api-2.0 + + admin/KAYQE1QA7uwUZ8uI + + + + Create project + curl -X 'POST' \ + -u admin:KAYQE1QA7uwUZ8uI \ + 'https://harbor.italiadatacenter.com/api/v2.0/projects' \ + -H 'accept: application/json' \ + -H 'X-Resource-Name-In-Location: false' \ + -H 'Content-Type: application/json' \ + -d '{ + "project_name": "secondo", + "public": false, + "metadata": { + "public": "false", + "enable_content_trust": "string", + "enable_content_trust_cosign": "string", + "prevent_vul": "string", + "severity": "string", + "auto_scan": "string", + "auto_sbom_generation": "string", + "reuse_sys_cve_allowlist": "string", + + "proxy_speed_kb": "string", + "max_upstream_conn": "string" + }, + "cve_allowlist": { + "id": 0, + "project_id": 0, + "expires_at": 0, + "items": [ + { + "cve_id": "string" + } + ], + "creation_time": "2026-03-21T17:12:18.108Z", + "update_time": "2026-03-21T17:12:18.108Z" + }, + "storage_limit": 0 + +}' + + +creazionnr robot user + +curl -X 'POST' \ + -u admin:KAYQE1QA7uwUZ8uI \ + 'https://harbor.italiadatacenter.com/api/v2.0/robots' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "secondobot", + "description": "scondo bot", + "secret": "string", + "level": "system", + "disable": true, + "duration": -1, + "permissions": [ + { + "kind": "project", + "namespace": "secondo", + "access": [ + { + "action": "delete", + "resource": "repository" + }, + { + "action": "list", + "resource": "repository" + }, + { + "action": "push", + "resource": "repository" + }, + { + "action": "read", + "resource": "repository" + }, + { + "action": "update", + "resource": "repository" + }, + { + "action": "pull", + "resource": "repository" + } + ] + } + ] +}' + + + +{ + "creation_time": "2026-03-21T18:24:57.665Z", + "expires_at": -1, + "id": 44, + "name": "robot$secondobot", + "secret": "YxlIfu6o73zsQnKkgaWMdSc6MEwftVzk" +} + + + + + +####### template modulari######################################################### + + +# 🧠 🎯 Cos’è `git subtree` (in pratica) + +👉 `subtree` ti permette di: + +```text +includere un altro repository dentro una sottocartella +MANTENENDO la possibilità di aggiornarlo +``` + +👉 differenza chiave: + +* il codice viene **copiato nel repo** +* ma mantiene un **legame logico con l’origine** + +--- + +# 🔥 Perché è perfetto per template modulari + +Scenario IDP: + +```text +template-node +template-k8s +template-ci +``` + +👉 li vuoi combinare in: + +```text +my-service/ + app/ + k8s/ + ci/ +``` + +👉 `subtree` fa ESATTAMENTE questo, in modo pulito + +--- + +# 🏗️ Setup iniziale (step-by-step) + +## 1️⃣ Aggiungi il repo template + +```bash +git remote add template-node https://gitea/template-node.git +git fetch template-node +``` + +--- + +## 2️⃣ Importa il template + +```bash +git subtree add \ + --prefix=app \ + template-node main \ + --squash +``` + +--- + +### 🔍 Cosa succede + +* copia contenuto in `app/` +* crea 1 commit (grazie a `--squash`) +* mantiene riferimento al repo remoto + +--- + +# 🔄 Aggiornare il template + +👉 quando il template evolve: + +```bash +git subtree pull \ + --prefix=app \ + template-node main \ + --squash +``` + +--- + +👉 risultato: + +* aggiorna solo quella cartella +* merge automatico + +--- + +# 🚀 Multi-template (use case IDP) + +```bash +git subtree add --prefix=app template-node main --squash +git subtree add --prefix=k8s template-k8s main --squash +git subtree add --prefix=ci template-ci main --squash +``` + +--- + +👉 ottieni: + +```text +repo finale: + app/ + k8s/ + ci/ +``` + diff --git a/add-on/clusterussuer.yaml b/add-on/clusterussuer.yaml new file mode 100644 index 0000000..23eeecc --- /dev/null +++ b/add-on/clusterussuer.yaml @@ -0,0 +1,17 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + email: alessandro.barucci66@gmail.com + server: https://acme-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: letsencrypt-production-key + solvers: + - http01: + gatewayHTTPRoute: + parentRefs: + - name: gateway + namespace: default + kind: Gateway \ No newline at end of file diff --git a/add-on/confmap.png b/add-on/confmap.png new file mode 100644 index 0000000000000000000000000000000000000000..7996ad1da19fedc39b3e9360ef0dadd7c8596d92 GIT binary patch literal 66536 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/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 ROLE +############################################ + +cat </dev/null 2>&1 || \ +kubectl create rolebinding namespace-deployer-binding \ + --role=namespace-deployer \ + --serviceaccount=${NAMESPACE}:${SERVICE_ACCOUNT} \ + -n "$NAMESPACE" + +############################################ +# GENERATE TOKEN +############################################ + +TOKEN=$(kubectl create token "$SERVICE_ACCOUNT" -n "$NAMESPACE") + +############################################ +# 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 < "$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" diff --git a/add-on/dbgate.yaml b/add-on/dbgate.yaml new file mode 100644 index 0000000..cf79c63 --- /dev/null +++ b/add-on/dbgate.yaml @@ -0,0 +1,59 @@ +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 \ No newline at end of file diff --git a/add-on/devops.txt b/add-on/devops.txt new file mode 100644 index 0000000..e334296 --- /dev/null +++ b/add-on/devops.txt @@ -0,0 +1,113 @@ +kubectl create namespace devops + + +dbdevops.yaml: + +--- +apiVersion: v1 +kind: Secret +metadata: + name: pg-app-user + namespace: demo-apps +type: kubernetes.io/basic-auth +stringData: + username: devops + password: KAYQE1QA7uwUZ8uI +--- +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" + + + +--- 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: KAYQE1QA7uwUZ8uI + - name: PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION + value: "false" +--- +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 diff --git a/add-on/edgenodeport.yaml b/add-on/edgenodeport.yaml new file mode 100644 index 0000000..388cf33 --- /dev/null +++ b/add-on/edgenodeport.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + meta.helm.sh/release-name: cloudcore + meta.helm.sh/release-namespace: kubeedge + labels: + k8s-app: kubeedge + kubeedge: cloudcore + name: kubeedge-nodeport + namespace: kubeedge +spec: + ports: + - appProtocol: tcp + name: cloudhub + nodePort: 30868 + port: 10000 + protocol: TCP + targetPort: 10000 + - appProtocol: tcp2 + name: cloudhub2 + nodePort: 30869 + port: 10002 + protocol: TCP + targetPort: 10002 + selector: + k8s-app: kubeedge + kubeedge: cloudcore + sessionAffinity: None + type: NodePort \ No newline at end of file diff --git a/add-on/get_helm.sh b/add-on/get_helm.sh new file mode 100644 index 0000000..5f265a5 --- /dev/null +++ b/add-on/get_helm.sh @@ -0,0 +1,347 @@ +#!/usr/bin/env bash + +# Copyright The Helm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The install script is based off of the MIT-licensed script from glide, +# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get + +: ${BINARY_NAME:="helm"} +: ${USE_SUDO:="true"} +: ${DEBUG:="false"} +: ${VERIFY_CHECKSUM:="true"} +: ${VERIFY_SIGNATURES:="false"} +: ${HELM_INSTALL_DIR:="/usr/local/bin"} +: ${GPG_PUBRING:="pubring.kbx"} + +HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)" +HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)" +HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)" +HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)" +HAS_GIT="$(type "git" &> /dev/null && echo true || echo false)" +HAS_TAR="$(type "tar" &> /dev/null && echo true || echo false)" + +# initArch discovers the architecture for this system. +initArch() { + ARCH=$(uname -m) + case $ARCH in + armv5*) ARCH="armv5";; + armv6*) ARCH="armv6";; + armv7*) ARCH="arm";; + aarch64) ARCH="arm64";; + x86) ARCH="386";; + x86_64) ARCH="amd64";; + i686) ARCH="386";; + i386) ARCH="386";; + esac +} + +# initOS discovers the operating system for this system. +initOS() { + OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') + + case "$OS" in + # Minimalist GNU for Windows + mingw*|cygwin*) OS='windows';; + esac +} + +# runs the given command as root (detects if we are root already) +runAsRoot() { + if [ $EUID -ne 0 -a "$USE_SUDO" = "true" ]; then + sudo "${@}" + else + "${@}" + fi +} + +# verifySupported checks that the os/arch combination is supported for +# binary builds, as well whether or not necessary tools are present. +verifySupported() { + local supported="darwin-amd64\ndarwin-arm64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-loong64\nlinux-ppc64le\nlinux-s390x\nlinux-riscv64\nwindows-amd64\nwindows-arm64" + if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then + echo "No prebuilt binary for ${OS}-${ARCH}." + echo "To build from source, go to https://github.com/helm/helm" + exit 1 + fi + + if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then + echo "Either curl or wget is required" + exit 1 + fi + + if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then + echo "In order to verify checksum, openssl must first be installed." + echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment." + exit 1 + fi + + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + if [ "${HAS_GPG}" != "true" ]; then + echo "In order to verify signatures, gpg must first be installed." + echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment." + exit 1 + fi + if [ "${OS}" != "linux" ]; then + echo "Signature verification is currently only supported on Linux." + echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually." + exit 1 + fi + fi + + if [ "${HAS_GIT}" != "true" ]; then + echo "[WARNING] Could not find git. It is required for plugin installation." + fi + + if [ "${HAS_TAR}" != "true" ]; then + echo "[ERROR] Could not find tar. It is required to extract the helm binary archive." + exit 1 + fi +} + +# checkDesiredVersion checks if the desired version is available. +checkDesiredVersion() { + if [ "x$DESIRED_VERSION" == "x" ]; then + # Get tag from release URL + local latest_release_url="https://get.helm.sh/helm3-latest-version" + local latest_release_response="" + if [ "${HAS_CURL}" == "true" ]; then + latest_release_response=$( curl -L --silent --show-error --fail "$latest_release_url" 2>&1 || true ) + elif [ "${HAS_WGET}" == "true" ]; then + latest_release_response=$( wget "$latest_release_url" -q -O - 2>&1 || true ) + fi + TAG=$( echo "$latest_release_response" | grep '^v[0-9]' ) + if [ "x$TAG" == "x" ]; then + printf "Could not retrieve the latest release tag information from %s: %s\n" "${latest_release_url}" "${latest_release_response}" + exit 1 + fi + else + TAG=$DESIRED_VERSION + fi +} + +# checkHelmInstalledVersion checks which version of helm is installed and +# if it needs to be changed. +checkHelmInstalledVersion() { + if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then + local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}") + if [[ "$version" == "$TAG" ]]; then + echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" + return 0 + else + echo "Helm ${TAG} is available. Changing from version ${version}." + return 1 + fi + else + return 1 + fi +} + +# downloadFile downloads the latest binary package and also the checksum +# for that binary. +downloadFile() { + HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" + DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" + CHECKSUM_URL="$DOWNLOAD_URL.sha256" + HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" + HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" + HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" + echo "Downloading $DOWNLOAD_URL" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" + curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" + wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" + fi +} + +# verifyFile verifies the SHA256 checksum of the binary package +# and the GPG signatures for both the package and checksum file +# (depending on settings in environment). +verifyFile() { + if [ "${VERIFY_CHECKSUM}" == "true" ]; then + verifyChecksum + fi + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + verifySignatures + fi +} + +# installFile installs the Helm binary. +installFile() { + HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME" + mkdir -p "$HELM_TMP" + tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" + echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" + echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +} + +# verifyChecksum verifies the SHA256 checksum of the binary package. +verifyChecksum() { + printf "Verifying checksum... " + local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') + local expected_sum=$(cat ${HELM_SUM_FILE}) + if [ "$sum" != "$expected_sum" ]; then + echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." + exit 1 + fi + echo "Done." +} + +# verifySignatures obtains the latest KEYS file from GitHub main branch +# as well as the signature .asc files from the specific GitHub release, +# then verifies that the release artifacts were signed by a maintainer's key. +verifySignatures() { + printf "Verifying signatures... " + local keys_filename="KEYS" + local github_keys_url="https://raw.githubusercontent.com/helm/helm/main/${keys_filename}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}" + fi + local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg" + local gpg_homedir="${HELM_TMP_ROOT}/gnupg" + mkdir -p -m 0700 "${gpg_homedir}" + local gpg_stderr_device="/dev/null" + if [ "${DEBUG}" == "true" ]; then + gpg_stderr_device="/dev/stderr" + fi + gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}" + gpg --batch --no-default-keyring --keyring "${gpg_homedir}/${GPG_PUBRING}" --export > "${gpg_keyring}" + local github_release_url="https://github.com/helm/helm/releases/download/${TAG}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + fi + local error_text="If you think this might be a potential security issue," + error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md" + local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_sha} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!" + echo -e "${error_text}" + exit 1 + fi + local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_tar} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!" + echo -e "${error_text}" + exit 1 + fi + echo "Done." +} + +# fail_trap is executed if an error occurs. +fail_trap() { + result=$? + if [ "$result" != "0" ]; then + if [[ -n "$INPUT_ARGUMENTS" ]]; then + echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS" + help + else + echo "Failed to install $BINARY_NAME" + fi + echo -e "\tFor support, go to https://github.com/helm/helm." + fi + cleanup + exit $result +} + +# testVersion tests the installed client to make sure it is working. +testVersion() { + set +e + HELM="$(command -v $BINARY_NAME)" + if [ "$?" = "1" ]; then + echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' + exit 1 + fi + set -e +} + +# help provides possible cli installation arguments +help () { + echo "Accepted cli arguments are:" + echo -e "\t[--help|-h ] ->> prints this help" + echo -e "\t[--version|-v ] . When not defined it fetches the latest release tag from the Helm CDN" + echo -e "\te.g. --version v3.0.0 or -v canary" + echo -e "\t[--no-sudo] ->> install without sudo" +} + +# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 +cleanup() { + if [[ -d "${HELM_TMP_ROOT:-}" ]]; then + rm -rf "$HELM_TMP_ROOT" + fi +} + +# Execution + +#Stop execution on any error +trap "fail_trap" EXIT +set -e + +# Set debug if desired +if [ "${DEBUG}" == "true" ]; then + set -x +fi + +# Parsing input arguments (if any) +export INPUT_ARGUMENTS="${@}" +set -u +while [[ $# -gt 0 ]]; do + case $1 in + '--version'|-v) + shift + if [[ $# -ne 0 ]]; then + export DESIRED_VERSION="${1}" + if [[ "$1" != "v"* ]]; then + echo "Expected version arg ('${DESIRED_VERSION}') to begin with 'v', fixing..." + export DESIRED_VERSION="v${1}" + fi + else + echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary" + exit 0 + fi + ;; + '--no-sudo') + USE_SUDO="false" + ;; + '--help'|-h) + help + exit 0 + ;; + *) exit 1 + ;; + esac + shift +done +set +u + +initArch +initOS +verifySupported +checkDesiredVersion +if ! checkHelmInstalledVersion; then + downloadFile + verifyFile + installFile +fi +testVersion +cleanup diff --git a/add-on/gitea-action.txt b/add-on/gitea-action.txt new file mode 100644 index 0000000..668b333 --- /dev/null +++ b/add-on/gitea-action.txt @@ -0,0 +1,88 @@ +apiVersion: v1 +kind: Secret +metadata: + name: gitea-runner-secret +type: Opaque +stringData: + token: G3uEVBJ1TrlFhlwiWfE7seymUPnN1OgyeQHy7T7e +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: act-runner-vol +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: csi-rbdfs-sc +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: act-runner + name: act-runner +spec: + replicas: 1 + selector: + matchLabels: + app: act-runner + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: act-runner + spec: + hostAliases: + - ip: "140.82.121.3" + hostnames: + - "github.com" + restartPolicy: Always + volumes: + - name: docker-certs + emptyDir: {} + - name: runner-data + persistentVolumeClaim: + claimName: act-runner-vol + - name: dockersock + hostPath: + path: /var/run/docker.sock + containers: + - name: runner + image: gitea/act_runner:nightly-dind-rootless + command: ["sh", "-c", "while ! nc -z localhost 2376 /tmp/app.ini; test -e /act-runner-data/.runner || gitea actions generate-runner-token > /act-runner-data/token + env: + - name: GITEA_APP_INI + value: /tmp/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + + volumeMounts: + - name: gitea-shared-storage + mountPath: /data + readOnly: true + - name: gitea-act-runner-data + mountPath: /act-runner-data + containers: + - name: runner + image: gitea/act_runner:nightly + + # Container only for gitea, so we can choose the dind variant (rootless or not) + image: gitea/act_runner:0.2.10 + + env: + - name: DOCKER_HOST + value: tcp://127.0.0.1:2376 + - name: DOCKER_CERT_PATH + value: /certs/client + - name: DOCKER_TLS_VERIFY + value: "1" + - name: GITEA_RUNNER_REGISTRATION_TOKEN_FILE + value: /data/token + - name: CONFIG_FILE + value: /config.yaml + - name: GITEA_INSTANCE_URL + value: https://git.italiadatacenter.com + - name: CONFIG_FILE + value: /actrunner/config.yaml + + volumeMounts: + - name: gitea-act-runner-data + mountPath: /data + - name: docker-certs + mountPath: /certs/client + - name: runner-config + mountPath: /actrunner + ## Avoid subPath because it cannot be updated + ## https://github.com/kubernetes/kubernetes/issues/50345 + #mountPath: /actrunner/config.yaml + #subPath: config.yaml + + + - name: daemon + ## Rootless works for simple cases, but not for docker buildx + #image = "docker:27.1.2-dind-rootless" + image = "docker:27.1.2-dind" + env: + - name: DOCKER_TLS_CERTDIR + value: /certs + - name: DOCKER_HOST + value: tcp://127.0.0.1:2376 + - name: DOCKER_TLS_VERIFY + value: 1 + securityContext: + privileged: true + volumeMounts: + - name: docker-certs + mountPath: /certs/client + - name: gitea-docker-daemon-config + ## This one for rootless variant + #mountPath: /home/rootless/.config/docker + ## This other for regular variant + mountPath: /etc/docker + ## Avoid subPath because it cannot be updated + ## https://github.com/kubernetes/kubernetes/issues/50345 + #mountPath: /home/rootless/.config/docker/daemon.json + #mountPath: /etc/docker/daemon.json + #subPath: daemon.json + + + volumes: + - name: docker-certs + emptyDir: {} + - name: gitea-act-runner-config + configMap: + name: gitea-act-runner-config + - name: gitea-act-runner-data + persistentVolumeClaim: + claimName: gitea-act-runner + - name: gitea-shared-storage + persistentVolumeClaim: + claimName: gitea-shared-storage + readOnly: true + - name: gitea-docker-daemon-config + configMap: + name: gitea-docker-daemon-config + + volumeClaimTemplates: + - metadata: + name: gitea-act-runner-data + namespace: gitea + spec: + accessModes: + - ReadWriteOnce + storageClassName: standard + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/add-on/gitea-workflow.txt b/add-on/gitea-workflow.txt new file mode 100644 index 0000000..1576bac --- /dev/null +++ b/add-on/gitea-workflow.txt @@ -0,0 +1,87 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main + +jobs: + docker: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: harbor.italiadatacenter.com + username: ${{ secrets.HARBOR_USERNAME }} + password: ${{ secrets.HARBOR_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: harbor.italiadatacenter.com/test/nginxtest:${{ github.sha }} + + #run: /root/work/pipeline/build_container.sh ${{ github.event.repository.name }} + +--------------------------------------------------------------------- +name: Build and Push Docker Images from Containers + +on: + push: + branches: [ main ] + workflow_dispatch: # Permette esecuzione manuale + +jobs: + # Job per elencare le directory con Dockerfile e creare una matrice + list-containers: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set matrix for containers + id: set-matrix + run: | + # Trova tutte le directory contenenti Dockerfile in containers/ + dirs=$(find containers -name Dockerfile -exec dirname {} \; | sort) + # Converte in JSON per la matrice (es. [{"dir": "containers/app1"}, {"dir": "containers/app2"}]) + matrix=$(echo "$dirs" | jq -R -s -c 'split("\n")[:-1] | map({dir: .})') + echo "matrix=$matrix" >> $GITHUB_OUTPUT + + - name: Log in to Docker registry + uses: docker/login-action@v3 + with: + registry: ghcr.io # Esempio: GitHub Container Registry; cambia se usi Docker Hub + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Job per buildare e pushare ogni immagine usando la matrice + build: + needs: list-containers + runs-on: ubuntu-latest + strategy: + matrix: + container: ${{ fromJson(needs.list-containers.outputs.matrix) }} + + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.container.dir }} # Contesto del build (la directory del Dockerfile) + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/add-on/gitea.sh b/add-on/gitea.sh new file mode 100644 index 0000000..4ee8b10 --- /dev/null +++ b/add-on/gitea.sh @@ -0,0 +1,100 @@ + +kubectl cnpg psql pg-devops -n devops + +CREATE DATABASE giteadb; +CREATE USER gitea WITH PASSWORD 'KAYQE1QA7uwUZ8uI'; +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 <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: KAYQE1QA7uwUZ8uI + email: gitadmin@italiadatacenter.com + + config: + database: + DB_TYPE: postgres + HOST: pg-devops-rw.devops.svc:5432 + NAME: giteadb + USER: gitea + PASSWD: KAYQE1QA7uwUZ8uI + 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 diff --git a/add-on/grafana.txt b/add-on/grafana.txt new file mode 100644 index 0000000..4e6da51 --- /dev/null +++ b/add-on/grafana.txt @@ -0,0 +1,123 @@ +Aggiungere repository Helm Grafana + +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: KAYQE1QA7uwUZ8uI5 + +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: KAYQE1QA7uwUZ8uI5 + + + +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) \ No newline at end of file diff --git a/add-on/harbor.sh b/add-on/harbor.sh new file mode 100644 index 0000000..eb9d1e6 --- /dev/null +++ b/add-on/harbor.sh @@ -0,0 +1,201 @@ +#HARBOR +kubectl create namespace harbor + +helm repo add harbor https://helm.goharbor.io +helm repo update + +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 < 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: "KAYQE1QA7uwUZ8uI" + +# ----------------------- +# 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: "KAYQE1QA7uwUZ8uI" + database: registry + sslmode: require + +# ----------------------- +# REDIS (EXTERNAL) +# ----------------------- +redis: + type: external + external: + addr: redis.redis.svc.cluster.local:6379 + password: "KAYQE1QA7uwUZ8uI" + 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 'KAYQE1QA7uwUZ8uI'; +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 - 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 S1mvqhD0yimDX@fb(Ek7`bnR&X literal 0 HcmV?d00001 diff --git a/add-on/influxdb.txt b/add-on/influxdb.txt new file mode 100644 index 0000000..8e9357a --- /dev/null +++ b/add-on/influxdb.txt @@ -0,0 +1,76 @@ +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: KAYQE1QA7uwUZ8uI5 + 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: KAYQE1QA7uwUZ8uI5 + + TEST API: + curl http://localhost:8086/health + + + link svc: + influxdb-influxdb2.influxdb.svc.cluster.local \ No newline at end of file diff --git a/add-on/install_edgenode.sh b/add-on/install_edgenode.sh new file mode 100644 index 0000000..4b4a4e9 --- /dev/null +++ b/add-on/install_edgenode.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Configurazione (override con variabili d'ambiente prima di eseguire lo script) +KUBEEDGE_VERSION="${KUBEEDGE_VERSION:-v1.22.0}" +CLOUD_MASTER_IP="${CLOUD_MASTER_IP:-89.105.78.161}" +EDGE_NODE_NAME="${EDGE_NODE_NAME:-edge-node-02}" +KUBEEDGE_TOKEN="${KUBEEDGE_TOKEN:-26477f03e3d0209870acdf724478f1d875e1328f8ea3408d74f3d7d109ad02be.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NzQ5OTI1NzB9.YxfikSixBrX7bvQxgxXYpbwLJHg0SAQ9mQ7kblBWQrk}" + +CONTAINERD_VERSION="1.6.14" +RUNC_VERSION="1.1.3" +CNI_VERSION="1.1.1" + +echo "==== EDGE NODE INSTALL START ====" +echo "KUBEEDGE_VERSION=${KUBEEDGE_VERSION}" +echo "CLOUD_MASTER_IP=${CLOUD_MASTER_IP}" +echo "EDGE_NODE_NAME=${EDGE_NODE_NAME}" +echo "KUBEEDGE_TOKEN=${KUBEEDGE_TOKEN}" +if ! command -v sudo >/dev/null 2>&1; then + echo "Errore: sudo non trovato" + exit 1 +fi + +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "${TMP_DIR}"' EXIT + +echo "[1/8] Download e installazione containerd ${CONTAINERD_VERSION}" +cd "${TMP_DIR}" +wget -q "https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz" +sudo tar Cxzvf /usr/local "containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz" + +echo "[2/8] Download e installazione runc ${RUNC_VERSION}" +wget -q "https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.amd64" +sudo install -m 755 runc.amd64 /usr/local/sbin/runc + +echo "[3/8] Download e installazione CNI plugins ${CNI_VERSION}" +wget -q "https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz" +sudo mkdir -p /opt/cni/bin +sudo tar Cxzvf /opt/cni/bin "cni-plugins-linux-amd64-v${CNI_VERSION}.tgz" + +echo "[4/8] Configurazione containerd" +sudo mkdir -p /etc/containerd +containerd config default | sudo tee /etc/containerd/config.toml >/dev/null +sudo sed -i '/SystemdCgroup = true/d' /etc/containerd/config.toml +sudo curl -fsSL https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /etc/systemd/system/containerd.service + +echo "[5/8] Configurazione rete CNI" +sudo mkdir -p /etc/cni/net.d +cat <<'EOF' | sudo tee /etc/cni/net.d/10-containerd-net.conflist >/dev/null +{ + "cniVersion": "1.0.0", + "name": "containerd-net", + "plugins": [ + { + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "promiscMode": true, + "ipam": { + "type": "host-local", + "ranges": [ + [{ + "subnet": "10.88.0.0/16" + }], + [{ + "subnet": "2001:db8:4860::/64" + }] + ], + "routes": [ + { "dst": "0.0.0.0/0" }, + { "dst": "::/0" } + ] + } + }, + { + "type": "portmap", + "capabilities": {"portMappings": true} + } + ] +} +EOF + +echo "[6/8] Avvio e abilitazione containerd" +sudo systemctl daemon-reload +sudo systemctl enable --now containerd + +echo "[7/8] Installazione keadm ${KUBEEDGE_VERSION}" +cd "${TMP_DIR}" +curl -fsSL "https://github.com/kubeedge/kubeedge/releases/download/${KUBEEDGE_VERSION}/keadm-${KUBEEDGE_VERSION}-linux-amd64.tar.gz" | tar -xz +sudo mv "keadm-${KUBEEDGE_VERSION}-linux-amd64/keadm/keadm" /usr/local/bin/ + +echo "[8/8] Join edge node al cloud master" +sudo keadm join \ + --cloudcore-ipport="${CLOUD_MASTER_IP}:10000" \ + --edgenode-name="${EDGE_NODE_NAME}" \ + --token="${KUBEEDGE_TOKEN}" \ + --kubeedge-version="${KUBEEDGE_VERSION}" + +echo "Creazione script di cleanup edge" +cat <<'EOF' | sudo tee /usr/local/bin/edge-cleanup.sh >/dev/null +#!/usr/bin/env bash + +set -euo pipefail + +echo "==== EDGE NODE CLEANUP START ====" +echo "Timestamp: $(date)" + +echo "[1] Checking orphan processes..." +ORPHANS=$(ps -eo pid,ppid,cmd | awk '$2 == 1 {print $1}') +for PID in ${ORPHANS}; do + if [ "${PID}" != "1" ]; then + echo "Killing orphan PID: ${PID}" + kill -9 "${PID}" 2>/dev/null || true + fi +done + +echo "[2] Cleaning containerd..." +if command -v crictl >/dev/null 2>&1; then + crictl ps -a --state Exited -q | xargs -r crictl rm || true + crictl pods --state NotReady -q | xargs -r crictl rmp || true + crictl rmi --prune || true +else + echo "crictl not found, skipping..." +fi + +echo "[3] Restarting containerd..." +systemctl restart containerd || true + +echo "[4] Cleaning kubelet state (soft)..." +find /var/lib/kubelet/pods -mindepth 1 -maxdepth 1 -type d -mtime +1 -exec rm -rf {} + || true + +echo "[5] Restarting kubelet + edgecore..." +systemctl restart kubelet || true +systemctl restart edgecore || true + +echo "[6] Checking MQTT port..." +ss -tulnp | grep 1883 || echo "WARNING: Mosquitto not listening" + +echo "==== EDGE NODE CLEANUP DONE ====" +EOF + +sudo chmod +x /usr/local/bin/edge-cleanup.sh + +echo "Creazione service systemd edge-cleanup" +cat <<'EOF' | sudo tee /etc/systemd/system/edge-cleanup.service >/dev/null +[Unit] +Description=Edge Node Cleanup +After=network.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/edge-cleanup.sh +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target +EOF + +sudo systemctl daemon-reload +sudo systemctl enable edge-cleanup + +echo "==== EDGE NODE INSTALL DONE ====" diff --git a/add-on/installa-cert-manager.sh b/add-on/installa-cert-manager.sh new file mode 100644 index 0000000..835e67a --- /dev/null +++ b/add-on/installa-cert-manager.sh @@ -0,0 +1,11 @@ +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 \ No newline at end of file diff --git a/add-on/installaRancher.sh b/add-on/installaRancher.sh new file mode 100644 index 0000000..ab93a02 --- /dev/null +++ b/add-on/installaRancher.sh @@ -0,0 +1,7 @@ +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=admin diff --git a/add-on/installa_helm.sh b/add-on/installa_helm.sh new file mode 100644 index 0000000..6e52865 --- /dev/null +++ b/add-on/installa_helm.sh @@ -0,0 +1,7 @@ +# 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 diff --git a/add-on/k8s-secret.jpg b/add-on/k8s-secret.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a055fdd4e9583fde023a1e24ae6261505f7f612f GIT binary patch literal 10013 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 P00000NkvXXu0mjfJbQKM literal 0 HcmV?d00001 diff --git a/add-on/mysql.txt b/add-on/mysql.txt new file mode 100644 index 0000000..5c9a401 --- /dev/null +++ b/add-on/mysql.txt @@ -0,0 +1,38 @@ +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="sakila" + + +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 | ++-------------+ + +This section utilizes the {innodbclustername}.{namespace}.svc.cluster.local form when connecting; and typically refers to the {innodbclustername} shorthand form that assumes the default namespace. See Section 3.4, “MySQL InnoDB Cluster Service Explanation” for additional information. \ No newline at end of file diff --git a/add-on/nginx-controller-service-nodeport.yaml b/add-on/nginx-controller-service-nodeport.yaml new file mode 100644 index 0000000..88aae65 --- /dev/null +++ b/add-on/nginx-controller-service-nodeport.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + meta.helm.sh/release-name: rke2-ingress-nginx + meta.helm.sh/release-namespace: kube-system + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: rke2-ingress-nginx + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: rke2-ingress-nginx + app.kubernetes.io/part-of: rke2-ingress-nginx + app.kubernetes.io/version: 1.13.4 + helm.sh/chart: rke2-ingress-nginx-4.13.400 + name: ingress-nginx-nodeport + namespace: kube-system +spec: + ports: + - appProtocol: https + name: https + nodePort: 30864 + port: 443 + protocol: TCP + targetPort: https + selector: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: rke2-ingress-nginx + app.kubernetes.io/name: rke2-ingress-nginx + sessionAffinity: None + type: NodePort \ No newline at end of file diff --git a/add-on/package-lock.json b/add-on/package-lock.json new file mode 100644 index 0000000..ac67fbc --- /dev/null +++ b/add-on/package-lock.json @@ -0,0 +1,38 @@ +{ + "name": "add-on", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "chokidar": "^5.0.0" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + } + } +} diff --git a/add-on/package.json b/add-on/package.json new file mode 100644 index 0000000..e7ab627 --- /dev/null +++ b/add-on/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chokidar": "^5.0.0" + } +} diff --git a/add-on/pgdb.txt b/add-on/pgdb.txt new file mode 100644 index 0000000..537bcfe --- /dev/null +++ b/add-on/pgdb.txt @@ -0,0 +1,62 @@ + +kubectl create namespace + + +dbxx.yaml.yaml: + +--- +apiVersion: v1 +kind: Secret +metadata: + name: pg-app-user + namespace: +type: kubernetes.io/basic-auth +stringData: + username: devops + password: KAYQE1QA7uwUZ8uI +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: pg-devops + namespace: +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" + + + + +kubectl cnpg psql pg-devops -n devops + +CREATE DATABASE giteadb; +CREATE USER gitea WITH PASSWORD 'KAYQE1QA7uwUZ8uI'; +GRANT ALL PRIVILEGES ON DATABASE giteadb TO gitea; +ALTER DATABASE giteadb OWNER TO gitea; \ No newline at end of file diff --git a/add-on/pipeline.txt b/add-on/pipeline.txt new file mode 100644 index 0000000..3c53da5 --- /dev/null +++ b/add-on/pipeline.txt @@ -0,0 +1,71 @@ +#!/bin/bash +# Usage: ./customize.sh dev|qa|prod + +ENV=$1 +VALUES_FILE="env/$ENV/values.env" +YAML_FILE="kubernetes/infrastructure.yaml" + +if [ ! -f "$VALUES_FILE" ]; then + echo "File $VALUES_FILE non trovato." + exit 1 +fi + +if [ ! -f "$YAML_FILE" ]; then + echo "File $YAML_FILE non trovato." + exit 1 +fi + +while IFS='=' read -r key value; do + sed -i "s|<$key>|$value|g" "$YAML_FILE" +done < "$VALUES_FILE" + +echo "Sostituzione completata per ambiente $ENV." + + + + + +#!/bin/bash + +# Carica variabili dal file properties.env +source properties.env + +# Controlla che le variabili siano impostate +if [ -z "$REGISTRY_URL" ] || [ -z "$REGISTRY_USER" ] || [ -z "$REGISTRY_PASS" ]; then + echo "REGISTRY_URL, REGISTRY_USER o REGISTRY_PASS non impostate in properties.env" + exit 1 +fi + +REPO_NAME=$(basename "$PWD") +COMMIT_SHA=$(git rev-parse --short HEAD) + +# Login al registry +echo "$REGISTRY_PASS" | docker login "$REGISTRY_URL" -u "$REGISTRY_USER" --password-stdin + +for dir in containers/*/; do + CONTAINER_NAME=$(basename "$dir") + DOCKERFILE="$dir/dockerfile" + IMAGE_TAG="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}" + 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 + + + +#!/bin/bash +# Effettua il deploy del file infrastructure.yaml in Kubernetes + +YAML_FILE="kubernetes/infrastructure.yaml" + +if [ ! -f "$YAML_FILE" ]; then + echo "File $YAML_FILE non trovato." + exit 1 +fi + +kubectl apply -f "$YAML_FILE" +echo "Deploy completato." \ No newline at end of file diff --git a/add-on/pipeline_segregata.sh b/add-on/pipeline_segregata.sh new file mode 100644 index 0000000..72cd8c7 --- /dev/null +++ b/add-on/pipeline_segregata.sh @@ -0,0 +1,169 @@ +# creazione secret, SA, Role e Rolebinding all'interno del namespace applicativo + +kubectl create secret docker-registry harbor-regcred \ + -n poc \ + --docker-server=harbor.pigreco66.it \ + --docker-username=robot$tekton \ + --docker-password=p2oZtqcUJafMAlX0eUzgHax9fv0ML8te \ + --docker-email=alessandro.barucci66@gmail.com + +kubectl create secret generic harbor-push-secret -n poc --from-file=config.json=config.json + + +cat < apply -f su namespace centralizzato tekton-pipelines +#kubectl apply -f pipedep.yaml -n tekton-pipelines +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: deploy-infrastructure +spec: + description: | + This pipeline clones a git repo, builds a Docker image with Kaniko and + pushes it to a registry + params: + - name: repo-url + type: string + - name: image-reference + type: string + - name: dockerfile + type: string + - name: namespace + type: string + workspaces: + - name: shared-data + - name: docker-credentials + tasks: + - name: fetch-source + taskRef: + resolver: cluster + params: + - name: name + value: git-clone + - name: namespace + value: tekton-pipelines + - name: kind + value: task + workspaces: + - name: output + workspace: shared-data + params: + - name: url + value: $(params.repo-url) + - name: build-push + runAfter: ["fetch-source"] + taskRef: + resolver: cluster + params: + - name: name + value: kaniko + - name: namespace + value: tekton-pipelines + - name: kind + value: task + workspaces: + - name: source + workspace: shared-data + - name: dockerconfig + workspace: docker-credentials + params: + - name: IMAGE + value: $(params.image-reference) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: deploy + runAfter: [build-push] + taskRef: + resolver: cluster + params: + - name: name + value: kubectl-apply + - name: namespace + value: tekton-pipelines + - name: kind + value: task + params: + - name: namespace + value: $(params.namespace) + workspaces: + - name: source + workspace: shared-data + + +#PipelineRun creato su namespace applicativo che refenzia pipeline remote +#kubectl create -f pipedeprun2.yaml -n poc +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + generateName: clone-build-push-run- +spec: + serviceAccountName: tekton-deployer + pipelineRef: + resolver: cluster + params: + - name: namespace + value: tekton-pipelines + - name: name + value: deploy-infrastructure + - name: kind + value: pipeline + podTemplate: + securityContext: + fsGroup: 65532 + workspaces: + - name: shared-data + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 1Gi + - name: docker-credentials + secret: + secretName: harbor-push-secret + params: + - name: repo-url + value: https://gitea.pigreco66.it/pigreco/poc.git + - name: image-reference + value: harbor.pigreco66.it/library/nginx:1.30 + - name: dockerfile + value: ./container/nginx/dockerfile + - name: namespace + value: poc diff --git a/add-on/pipelines.sh b/add-on/pipelines.sh new file mode 100644 index 0000000..41dce87 --- /dev/null +++ b/add-on/pipelines.sh @@ -0,0 +1,349 @@ + +#installo task community "git-clone" +#kubectl apply -f https://api.hub.tekton.dev/v1/resource/tekton/task/git-clone/0.10/raw -n +kubectl apply -f https://github.com/tektoncd/catalog/raw/main/task/git-clone/0.10/git-clone.yaml -n tekton-pipelines + + +cat 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
  • +o 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

    pnaj{UQ8;;x4Df5_6Bk5}Z9!WESl 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 redisvalues.yaml - +architecture: replication + +auth: + enabled: true + password: KAYQE1QA7uwUZ8uI + +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 KAYQE1QA7uwUZ8uI + + + +########################################################################################################################### +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 \ No newline at end of file diff --git a/add-on/run_validate_css.cmd b/add-on/run_validate_css.cmd new file mode 100644 index 0000000..23ca434 --- /dev/null +++ b/add-on/run_validate_css.cmd @@ -0,0 +1 @@ +powershell -ExecutionPolicy Bypass -File .\add-on\validate-scss.ps1 -RootPath "C:\Users\Public\git\limec-frontend\src\" \ No newline at end of file diff --git a/add-on/sonaquebe.txt b/add-on/sonaquebe.txt new file mode 100644 index 0000000..bc7e4c1 --- /dev/null +++ b/add-on/sonaquebe.txt @@ -0,0 +1,87 @@ +########### repo helm ################ +helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube +helm repo update + +########### creazione ns e secret db ################ +kubectl create namespace sonarqube + +kubectl create secret generic sonarqube-database-cred \ + --from-literal=username=sonarqube \ + --from-literal=password=KAYQE1QA7uwUZ8uI \ + -n sonarqube + + +########### creazione database ################ +kubectl cnpg psql pg-devops -n devops + +CREATE DATABASE sonarqube; +CREATE USER sonarqube WITH PASSWORD 'KAYQE1QA7uwUZ8uI'; +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 ################ +helm upgrade -f sonarvalues.yaml --install -n sonarqube sonarqube sonarqube/sonarqube --set community.enabled=true,monitoringPasscode="KAYQE1QA7uwUZ8uI" + + + +########### httproute ################ +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 + + +admin +$KAYQE1QA7uwUZ8uI \ No newline at end of file diff --git a/add-on/tekton.sh b/add-on/tekton.sh new file mode 100644 index 0000000..ad163d6 --- /dev/null +++ b/add-on/tekton.sh @@ -0,0 +1,197 @@ +kubectl create namespace tekton-pipelines + +kubectl apply -f https://infra.tekton.dev/tekton-releases/pipeline/previous/v1.7.0/release.yaml + +kubectl edit cm feature-flags -n tekton-pipelines +data: + enable-api-fields: "stable" + disable-affinity-assistant: "false" + enable-tekton-oci-bundles: "true" + enable-custom-tasks: "true" + + +kubectl edit cm config-defaults -n tekton-pipelines +data: + default-timeout-minutes: "60" + default-service-account: "tekton-sa" + +cat < tekton-sa.yaml - +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tekton-sa + namespace: tekton-pipelines +EOF + +kubectl apply -f tekton-sa.yaml + +#STEP 1– Creare Robot Account in Harbor +#Harbor UI → Projects → (es. library o apps) → Robot Accounts +#Nome: k8s-pull +#Permessi: +#✔️ FULL permission + + +kubectl create secret docker-registry harbor-regcred \ + -n tekton-pipelines \ + --docker-server=harbor.italiadatacenter.com \ + --docker-username=robot$tekton \ + --docker-password=pyyMRe2kRIp6LQmIh8jaSWquL1mDtz04 \ + --docker-email=harbor@italiadatacenter.com + + +cat < config.json +{ + "auths": { + "harbor.italiadatacenter.com": { + "username": "robot\$tekton", + "password": "pyyMRe2kRIp6LQmIh8jaSWquL1mDtz04", + "email": "harbor@italiadatacenter.com", + "auth": "$(echo -n 'robot$tekton:pyyMRe2kRIp6LQmIh8jaSWquL1mDtz04' | base64)" + } + } +} +EOF + + +kubectl create secret generic harbor-push-secret -n tekton-pipelines --from-file=config.json=config.json + + + + +kubectl patch sa tekton-sa \ + -n tekton-pipelines \ + -p '{"imagePullSecrets":[{"name":"harbor-regcred"}]}' + +cat < tekton-workspace.yaml - +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: tekton-workspace + namespace: tekton-pipelines +spec: + accessModes: + - ReadWriteOnce + storageClassName: csi-rbdfs-sc + resources: + requests: + storage: 10Gi +EOF + +kubectl apply -f tekton-workspace.yaml -n tekton-pipelines + +#Tekton Triggers +kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml -n tekton-pipelines + +#Tekton Dashboard +kubectl apply -f https://storage.googleapis.com/tekton-releases/dashboard/latest/release.yaml -n tekton-pipelines + +cat < tekton-ingress.yaml - +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: tekton-dashboard + namespace: tekton-pipelines + annotations: + kubernetes.io/ingress.class: "nginx" + cert-manager.io/cluster-issuer: "letsencrypt-production" +spec: + tls: + - hosts: + - tekton.pigreco66.it + secretName: myapp-tls + rules: + - host: tekton.pigreco66.it + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: tekton-dashboard + port: + number: 9097 +EOF + +cat < tekton-httproute.yaml - +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: tekton + namespace: tekton-pipelines +spec: + hostnames: + - tekton.italiadatacenter.com + parentRefs: + - name: main-gateway + namespace: nginx-gateway + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: tekton-dashboard + port: 9097 +EOF + +kubectl apply -f tekton-httproute.yaml -n tekton-pipelines + +kubectl label namespace tekton-pipelines \ + pod-security.kubernetes.io/enforce=privileged \ + pod-security.kubernetes.io/audit=privileged \ + pod-security.kubernetes.io/warn=privileged \ + --overwrite + + +#installazione cli + +curl -LO https://github.com/tektoncd/cli/releases/download/v0.43.0/tkn_0.43.0_Linux_x86_64.tar.gz +tar xvf tkn_0.43.0_Linux_x86_64.tar.gz +sudo mv tkn /usr/local/bin/ + + +#TEST + + + +cat < tekton-test.yaml - +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: hello + namespace: tekton-pipelines +spec: + steps: + - name: echo + image: alpine + script: | + echo "Tekton OK" +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: hello-pipeline + namespace: tekton-pipelines +spec: + tasks: + - name: hello + taskRef: + name: hello +--- +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + name: hello-pipeline-run +spec: + pipelineRef: + name: hello-pipeline + params: + - name: username + value: "Tekton" +EOF +kubectl apply -f tekton-test.yaml -n tekton-pipelines + + + + diff --git a/add-on/validate-scss.ps1 b/add-on/validate-scss.ps1 new file mode 100644 index 0000000..e3b88fa --- /dev/null +++ b/add-on/validate-scss.ps1 @@ -0,0 +1,134 @@ +param ( + [Parameter(Mandatory = $true)] + [string]$RootPath, + + [string]$NpxSassPackage = "sass@1.77.8", + + [bool]$NoErrorCss = $true +) + +$ErrorActionPreference = 'Stop' + +try { + $resolvedRoot = (Resolve-Path -Path $RootPath).Path +} catch { + Write-Host "ERROR: Root path not found: $RootPath" -ForegroundColor Red + exit 2 +} + +Write-Host "Scanning SCSS files in: $resolvedRoot" -ForegroundColor Cyan + +function Get-SassRunner { + param( + [Parameter(Mandatory = $true)] + [string]$StartPath, + + [Parameter(Mandatory = $true)] + [string]$PinnedNpxPackage + ) + + $current = $StartPath + while ($true) { + $localSass = Join-Path $current "node_modules\\.bin\\sass.cmd" + if (Test-Path $localSass) { + return @{ + Command = $localSass + PrefixArgs = @() + Source = "local" + } + } + + $parent = Split-Path -Path $current -Parent + if (-not $parent -or $parent -eq $current) { + break + } + $current = $parent + } + + $npxCmd = Get-Command npx.cmd -ErrorAction SilentlyContinue + if ($npxCmd) { + cmd /d /c "npx -y -p $PinnedNpxPackage sass --version >nul 2>&1" | Out-Null + if ($LASTEXITCODE -eq 0) { + return @{ + Command = $npxCmd.Source + PrefixArgs = @("-y", "-p", $PinnedNpxPackage, "sass") + Source = "npx:$PinnedNpxPackage" + } + } + } + + $sassCmd = Get-Command sass.cmd -ErrorAction SilentlyContinue + if ($sassCmd) { + cmd /d /c "sass --version >nul 2>&1" | Out-Null + if ($LASTEXITCODE -eq 0) { + return @{ + Command = $sassCmd.Source + PrefixArgs = @() + Source = "global" + } + } + } + + return $null +} + +$sassRunner = Get-SassRunner -StartPath $resolvedRoot -PinnedNpxPackage $NpxSassPackage +if (-not $sassRunner) { + Write-Host "ERROR: No working Sass runner found (local sass.cmd, global sass, or npx sass)." -ForegroundColor Red + exit 3 +} + +Write-Host "Using Sass runner: $($sassRunner.Source) [$($sassRunner.Command)]" -ForegroundColor DarkCyan + +# Find all SCSS files recursively from the given root path. +$files = Get-ChildItem -Path $resolvedRoot -Recurse -File -Filter "*.scss" + +if ($files.Count -eq 0) { + Write-Host "No SCSS files found." -ForegroundColor Yellow + exit 0 +} + +$hasErrors = $false +$checkedCount = 0 +$errorCount = 0 + +foreach ($file in $files) { + $checkedCount += 1 + Write-Host "Checking: $($file.FullName)" -ForegroundColor Gray + + # Syntax-only validation: compile to NUL to avoid creating/opening any CSS files. + $sassArgs = @($sassRunner.PrefixArgs) + @('--no-source-map', '--style=compressed', '--quiet', "--load-path=$resolvedRoot") + if ($NoErrorCss) { + $sassArgs += '--no-error-css' + } + $sassArgs += @($file.FullName, 'NUL') + + $previousErrorAction = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { + $result = & $sassRunner.Command @sassArgs 2>&1 | Out-String + $exitCode = $LASTEXITCODE + } finally { + $ErrorActionPreference = $previousErrorAction + } + + if ($exitCode -ne 0) { + $hasErrors = $true + $errorCount += 1 + Write-Host "" + Write-Host "ERROR in $($file.FullName)" -ForegroundColor Red + Write-Host $result -ForegroundColor Yellow + Write-Host "----------------------------------------" + } +} + +Write-Host "" +Write-Host "Checked files: $checkedCount" + +if (-not $hasErrors) { + Write-Host "All SCSS files are syntactically valid." -ForegroundColor Green + exit 0 +} + +Write-Host "SCSS files with errors: $errorCount" -ForegroundColor Red +exit 1 \ No newline at end of file diff --git a/add-on/vol.png b/add-on/vol.png new file mode 100644 index 0000000000000000000000000000000000000000..a8cec65a3cb97b2a2dba45651558c5ab84db57b4 GIT binary patch literal 9719 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 "${FILE}.tmp" && mv "${FILE}.tmp" "$FILE" + +echo "✅ Line added after tls-san:" +echo " $NEW_LINE" diff --git a/build-listener-from-endpoint.sh b/build-listener-from-endpoint.sh new file mode 100644 index 0000000..cfb17d8 --- /dev/null +++ b/build-listener-from-endpoint.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usa properties.env nella directory corrente, oppure un path passato come primo argomento. +PROPERTIES_FILE="${1:-properties.env}" + +if [[ ! -f "$PROPERTIES_FILE" ]]; then + echo "Errore: file non trovato: $PROPERTIES_FILE" >&2 + exit 1 +fi + +# Estrae endpoint ignorando commenti e spazi, supportando anche endpoint = valore +endpoint_raw="$({ grep -E '^[[:space:]]*endpoint[[:space:]]*=' "$PROPERTIES_FILE" | tail -n1 || true; } | sed -E 's/^[[:space:]]*endpoint[[:space:]]*=[[:space:]]*//')" + +# Rimuove eventuali virgolette e spazi ai bordi +endpoint="$(echo "$endpoint_raw" | sed -E 's/^[[:space:]"\x27]+//; s/[[:space:]"\x27]+$//')" + +if [[ -z "$endpoint" ]]; then + echo "La chiave endpoint non e valorizzata in $PROPERTIES_FILE" >&2 + exit 1 +fi + +# Per calcolare il token usa host pulito (senza schema e path) +host_for_token="${endpoint#*://}" +host_for_token="${host_for_token%%/*}" +token="${host_for_token%%.*}" + +if [[ -z "$token" ]]; then + echo "Impossibile estrarre il token da endpoint: $endpoint" >&2 + exit 1 +fi + +# Esegue lo script richiesto con la stringa costruita +/root/work/pipeline/add-listener.sh "$endpoint https-$token $token-secret" diff --git a/demo.yaml b/demo.yaml new file mode 100644 index 0000000..9a4749e --- /dev/null +++ b/demo.yaml @@ -0,0 +1,189 @@ +---DEMO --- + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: demo-apps +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-v1 + namespace: demo-apps +spec: + replicas: 2 + selector: + matchLabels: + app: demo + version: v1 + template: + metadata: + labels: + app: demo + version: v1 + spec: + containers: + - name: app + image: hashicorp/http-echo + args: + - "-text=Hello from App v1" + ports: + - containerPort: 5678 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-v2 + namespace: demo-apps +spec: + replicas: 2 + selector: + matchLabels: + app: demo + version: v2 + template: + metadata: + labels: + app: demo + version: v2 + spec: + containers: + - name: app + image: hashicorp/http-echo + args: + - "-text=Hello from App v2" + ports: + - containerPort: 5678 +--- +apiVersion: v1 +kind: Service +metadata: + name: app-v1 + namespace: demo-apps +spec: + selector: + app: demo + version: v1 + ports: + - port: 80 + targetPort: 5678 +--- +apiVersion: v1 +kind: Service +metadata: + name: app-v2 + namespace: demo-apps +spec: + selector: + app: demo + version: v2 + ports: + - port: 80 + targetPort: 5678 + + + + + +---- HTTP ROUTE --- +kind: HTTPRoute +metadata: + name: demo-route + namespace: demo-apps +spec: + parentRefs: + - name: main-gateway + namespace: nginx-gateway + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: app-v1 + port: 80 + + + + + + + + + + + +***to add for https *** + + # HTTP'S Listener (Add Muliple HTTPS listner) + - name: https-api-artha-link + port: 443 + protocol: HTTPS + hostname: domain1.com + tls: + mode: Terminate + certificateRefs: + - kind: Secret + name: domain1-ssl + namespace: default + allowedRoutes: + namespaces: + from: All + + - name: https-app-artha-link + port: 443 + protocol: HTTPS + hostname: domain2.com + tls: + mode: Terminate + certificateRefs: + - kind: Secret + name: domain2-ssl + namespace: default + allowedRoutes: + namespaces: + from: All + + +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: demo-route + namespace: demo-apps +spec: + parentRefs: + - name: main-gateway + namespace: nginx-gateway + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: app-v1 + port: 80 + + + + + + + +kubectl create -n demo-apps -f - < /dev/null < /dev/null < /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: +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** +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=admin + + +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** +cat < 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 < 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 < 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 < 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 < 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 < 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** + # 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 - < + 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 +------------------------------------------------------------------------------------------------------------------------------ + +--- + +## Servizi DevOps + +-- **db devops** + +apiVersion: v1 +kind: Secret +metadata: + name: pg-app-user + namespace: demo-apps +type: kubernetes.io/basic-auth +stringData: + username: devops + password: KAYQE1QA7uwUZ8uI +--- +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** + +kubectl cnpg psql pg-devops -n devops + +CREATE DATABASE giteadb; +CREATE USER gitea WITH PASSWORD 'KAYQE1QA7uwUZ8uI'; +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 <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: KAYQE1QA7uwUZ8uI + email: gitadmin@italiadatacenter.com + + config: + database: + DB_TYPE: postgres + HOST: pg-devops-rw.devops.svc:5432 + NAME: giteadb + USER: gitea + PASSWD: KAYQE1QA7uwUZ8uI + 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** +#HARBOR +kubectl create namespace harbor + +helm repo add harbor https://helm.goharbor.io +helm repo update + +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 < 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: "KAYQE1QA7uwUZ8uI" + +# ----------------------- +# 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: "KAYQE1QA7uwUZ8uI" + database: registry + sslmode: require + +# ----------------------- +# REDIS (EXTERNAL) +# ----------------------- +redis: + type: external + external: + addr: redis.redis.svc.cluster.local:6379 + password: "KAYQE1QA7uwUZ8uI" + 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 'KAYQE1QA7uwUZ8uI'; +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 - < "$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 </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 < "$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** +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: admin +--- +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=admin" -- 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: KAYQE1QA7uwUZ8uI +--- +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 + + + + + +-------------------------------------------------------------------------------- + +cat < 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** + ------------------------------------------------------------------------------------------------------------------------------ +kubectl create namespace redis +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo update + + +cat < redisvalues.yaml - +architecture: replication + +auth: + enabled: true + password: KAYQE1QA7uwUZ8uI + +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 KAYQE1QA7uwUZ8uI + + + +########################################################################################################################### +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** + ------------------------------------------------------------------------------------------------------------------------------ +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: KAYQE1QA7uwUZ8uI5 + 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: KAYQE1QA7uwUZ8uI5 + + TEST API: + curl http://localhost:8086/health + + + link svc: + influxdb-influxdb2.influxdb.svc.cluster.local +- **MongoDB** + ------------------------------------------------------------------------------------------------------------------------------ + +- **DbGate** + ------------------------------------------------------------------------------------------------------------------------------ +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** +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: "", nella sezione adminAuth) + +kubectl delete pods node-red-bd88bc7df-knqfw -n nodered + ------------------------------------------------------------------------------------------------------------------------------ + +- **Grafana** + Aggiungere repository Helm Grafana + +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: KAYQE1QA7uwUZ8uI5 + +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: KAYQE1QA7uwUZ8uI5 + + + +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 ################ +helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube +helm repo update + +########### creazione ns e secret db ################ +kubectl create namespace sonarqube + +kubectl create secret generic sonarqube-database-cred \ + --from-literal=username=sonarqube \ + --from-literal=password=KAYQE1QA7uwUZ8uI \ + -n sonarqube + + +########### creazione database ################ +kubectl cnpg psql pg-devops -n devops + +CREATE DATABASE sonarqube; +CREATE USER sonarqube WITH PASSWORD 'KAYQE1QA7uwUZ8uI'; +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 ################ +helm upgrade -f sonarvalues.yaml --install -n sonarqube sonarqube sonarqube/sonarqube --set community.enabled=true,monitoringPasscode="KAYQE1QA7uwUZ8uI" + + + +########### httproute ################ +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** + ------------------------------------------------------------------------------------------------------------------------------ + + + \ No newline at end of file diff --git a/dockerbuild.sh b/dockerbuild.sh new file mode 100644 index 0000000..e69de29 diff --git a/gateway.yaml b/gateway.yaml new file mode 100644 index 0000000..30cf492 --- /dev/null +++ b/gateway.yaml @@ -0,0 +1,134 @@ +apiVersion: v1 +items: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: main-gateway + namespace: nginx-gateway + spec: + gatewayClassName: nginx + listeners: + - allowedRoutes: + namespaces: + from: All + name: http-all + port: 80 + protocol: HTTP + - 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: poc2.italiadatacenter.com + name: https2 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: poc2-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: k8s.italiadatacenter.com + name: https3 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: k8s-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: poc3.italiadatacenter.com + name: https4 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: poc3-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: git.italiadatacenter.com + name: https5 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: git-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: harbor.italiadatacenter.com + name: https6 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: harbor-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: git.pigreco66.it + name: httpsp66 + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: p66-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: tekton.italiadatacenter.com + name: https-grafana + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: grafana-secret + mode: Terminate + - allowedRoutes: + namespaces: + from: All + hostname: prometheus.italiadatacenter.com + name: https-prometheus + port: 443 + protocol: HTTPS + tls: + certificateRefs: + - group: "" + kind: Secret + name: prometheus-secret + mode: Terminate + \ No newline at end of file diff --git a/haproxy.html b/haproxy.html new file mode 100644 index 0000000..ec75aea --- /dev/null +++ b/haproxy.html @@ -0,0 +1,289 @@ + +Statistics Report for HAProxy on POC-Kube-Balancer + + + +

    HAProxy version 2.8.16-0ubuntu0.24.04.2, released 2026/04/15

    +

    Statistics Report for pid 15647 on POC-Kube-Balancer

    +
    +

    > General process information

    +
    +

    pid = 15647 (process #1, nbproc = 1, nbthread = 2)
    +uptime = 0d 16h25m19s; warnings = 2
    +system limits: memmax = unlimited; ulimit-n = 40082
    +maxsock = 40082; maxconn = 20000; reached = 0; maxpipes = 0
    +current conns = 7; current pipes = 0/0; conn rate = 1/sec; bit rate = 20.393 kbps
    +Running tasks: 0/61; idle = 100 %
    +

    + + + + + + +
     active UP  backup UP
    active UP, going down backup UP, going down
    active DOWN, going up backup DOWN, going up
    active or backup DOWN  not checked
    active or backup DOWN for maintenance (MAINT)  
    active or backup SOFT STOPPED for maintenance  
    +Note: "NOLB"/"DRAIN" = UP with load-balancing disabled.
    Display option:External resources:
    + + +
    rke2_registration_frontend
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    0
    Max connection rate:0/s
    Max session rate:0/s
    -00200000
    Cum. connections:0
    Cum. sessions:0
    00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    rke2_registration_backend
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend00000020000
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    k8s_api_frontend
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    0
    Max connection rate:0/s
    Max session rate:0/s
    -00200000
    Cum. connections:0
    Cum. sessions:0
    00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    k8s_api_backend
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend00000020000
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    stats
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend1
    Current connection rate:1/s
    Current session rate:1/s
    Current request rate:1/s
    1
    Max connection rate:1/s
    Max session rate:1/s
    Max request rate:1/s
    -11200005
    Cum. connections:5
    Cum. sessions:5
    - HTTP/1 sessions:5
    - HTTP/2 sessions:0
    - HTTP/3 sessions:0
    - other sessions:0
    Cum. HTTP requests:5
    - HTTP/1 requests:5
    - HTTP/2 requests:0
    - HTTP/3 requests:0
    - other requests:0
    - HTTP 1xx responses:0
    - HTTP 2xx responses:3
      Compressed 2xx:0(0%)
    - HTTP 3xx responses:0
    - HTTP 4xx responses:1
    - HTTP 5xx responses:0
    - other responses:0
    Intercepted requests:5
    Cache lookups:0
    Cache hits:0(0%)
    Failed hdr rewrites:0
    Internal errors:0
    445266919
    Response bytes in:266919
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN
    Backend00000020000
    Cum. sessions:0
    New connections:0
    Reused connections:0(0%)
    Cum. HTTP requests:0
    - HTTP 1xx responses:0
    - HTTP 2xx responses:0
      Compressed 2xx:0(0%)
    - HTTP 3xx responses:0
    - HTTP 4xx responses:0
    - HTTP 5xx responses:0
    - other responses:0
    Cache lookups:0
    Cache hits:0(0%)
    Failed hdr rewrites:0
    Internal errors:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Responses time:0 / 0ms
    - Total time:0 / 0ms
    00s445266919
    Response bytes in:266919
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 2 client, 0 server
    0016h25m UP 0/000 0 

    + + +
    nginx_frontend_443
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    32
    Max connection rate:32/s
    Max session rate:32/s
    -23420000467
    Cum. connections:467
    Cum. sessions:467
    322973010172550
    Response bytes in:10172550
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    nginx_frontend_80
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    13
    Max connection rate:13/s
    Max session rate:13/s
    -013200002114
    Cum. connections:2114
    Cum. sessions:2114
    11493523721025
    Response bytes in:3721025
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    001OPEN

    + + +
    nginx_backend_80
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-040
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    4-705
    Cum. sessions:705
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1 / 0ms
    - Total time:85512 / 762ms
    70549s402634641847000
    Connection resets during transfers: 11 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-050
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    5-704
    Cum. sessions:704
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1441 / 3ms
    - Total time:50001 / 519ms
    7041m39s358180453761000
    Connection resets during transfers: 4 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-040
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    4-704
    Cum. sessions:704
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1 / 0ms
    - Total time:83147 / 1152ms
    70449s3885382625417000
    Connection resets during transfers: 12 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend0001301320002114
    Cum. sessions:2114
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1441 / 2ms
    - Total time:85512 / 741ms
    211349s11493523721025
    Response bytes in:3721025
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 28 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    nginx_backend_443
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-0110
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    11-156
    Cum. sessions:156
    Max / Avg over last 1024 success. conn.
    - Queue time:5000 / 158ms
    - Connect time:1 / 1ms
    - Total time:1998998 / 183855ms
    1565m14s13437622750246000
    Connection resets during transfers: 19 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-0101
    Current active connections:1
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    11-156
    Cum. sessions:156
    Max / Avg over last 1024 success. conn.
    - Queue time:5001 / 208ms
    - Connect time:1 / 1ms
    - Total time:1998140 / 133971ms
    1567s10336693925075000
    Connection resets during transfers: 19 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-0111
    Current active connections:1
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    12-155
    Cum. sessions:155
    Max / Avg over last 1024 success. conn.
    - Queue time:5001 / 212ms
    - Connect time:1 / 1ms
    - Total time:1999230 / 108736ms
    1558m36s8522993497229000
    Connection resets during transfers: 22 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend000322342000467
    Cum. sessions:467
    Max / Avg over last 1024 success. conn.
    - Queue time:5001 / 193ms
    - Connect time:1 / 1ms
    - Total time:1999230 / 142259ms
    4677s322973010172550
    Response bytes in:10172550
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 60 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    kubeedge_10000
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    2
    Max connection rate:2/s
    Max session rate:2/s
    -242000035
    Cum. connections:35
    Cum. sessions:35
    968175148781529
    Response bytes in:48781529
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    kubeedge_backend_10000
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-010
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    2-12
    Cum. sessions:12
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:32992202 / 2751494ms
    121h17m967781048778458001
    Connection resets during transfers: 2 client, 1 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-011
    Current active connections:1
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    2-12
    Cum. sessions:12
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:10001 / 2729ms
    121h17m17881094001
    Connection resets during transfers: 1 client, 1 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-011
    Current active connections:1
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    2-11
    Cum. sessions:11
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:10004 / 1444ms
    111h17m21531977001
    Connection resets during transfers: 2 client, 1 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend000224200035
    Cum. sessions:35
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:32992202 / 944759ms
    351h17m968175148781529
    Response bytes in:48781529
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0003
    Connection resets during transfers: 5 client, 3 server
    0016h25m UP 3/330 00s

    + + +
    kubeedge_10002
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    5
    Max connection rate:5/s
    Max session rate:5/s
    -022000066
    Cum. connections:66
    Cum. sessions:66
    2611735698
    Response bytes in:35698
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    kubeedge_backend_10002
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-020
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    1-22
    Cum. sessions:22
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:16153 / 2135ms
    223h57m816711327000
    Connection resets during transfers: 7 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-020
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    1-22
    Cum. sessions:22
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:9139 / 1031ms
    223h57m888512049000
    Connection resets during transfers: 6 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-020
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    1-22
    Cum. sessions:22
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:10005 / 1384ms
    222h44m906512322000
    Connection resets during transfers: 7 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend000502200066
    Cum. sessions:66
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:16153 / 1516ms
    662h44m2611735698
    Response bytes in:35698
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 20 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    kubeedge_10004
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    0
    Max connection rate:0/s
    Max session rate:0/s
    -00200000
    Cum. connections:0
    Cum. sessions:0
    00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    kubeedge_backend_10004
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-000
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    0-0
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend00000020000
    Cum. sessions:0
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:0 / 0ms
    0?00
    Response bytes in:0
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 0 client, 0 server
    0016h25m UP 3/330 00s

    + + +
    mqtt_broker
    + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    Frontend0
    Current connection rate:0/s
    Current session rate:0/s
    2
    Max connection rate:2/s
    Max session rate:2/s
    -24200001062
    Cum. connections:1062
    Cum. sessions:1062
    37998817426
    Response bytes in:17426
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    000OPEN

    + + +
    mqtt_broker_backend
    + + + + + + +
    QueueSession rateSessionsBytesDeniedErrorsWarningsServer
    CurMaxLimitCurMaxLimitCurMaxLimitTotalLbTotLastInOutReqRespReqConnRespRetrRedisStatusLastChkWghtActBckChkDwnDwntmeThrtle
    POC-Master000-010
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    2-354
    Cum. sessions:354
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:32945639 / 143010ms
    3542m26s2674995353000
    Connection resets during transfers: 354 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master100-010
    Current active connections:0
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    1-354
    Cum. sessions:354
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1 / 1ms
    - Total time:50218 / 50087ms
    3541m30s566404248000
    Connection resets during transfers: 354 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    POC-Master200-012
    Current active connections:2
    Current used connections:0
    Current idle connections:0
    - unsafe:0
    - safe:0
    Estimated need of connections:1
    Active connections limit:-
    Idle connections limit:-
    2-354
    Cum. sessions:354
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:0 / 0ms
    - Total time:50223 / 49332ms
    35434s558497825000
    Connection resets during transfers: 349 client, 0 server
    0016h25m UP L4OK in 0ms
    Layer4 check passed
    1/1Y-0
    Failed Health Checks
    00s-
    Backend00022320001062
    Cum. sessions:1062
    Max / Avg over last 1024 success. conn.
    - Queue time:0 / 0ms
    - Connect time:1 / 0ms
    - Total time:32945639 / 75592ms
    106234s37998817426
    Response bytes in:17426
    Compression in:0
    Compression out:0(0%)
    Compression bypass:0
    Total bytes saved:0(0%)
    0000
    Connection resets during transfers: 1057 client, 0 server
    0016h25m UP 3/330 00s

    + \ No newline at end of file diff --git a/hosts b/hosts new file mode 100644 index 0000000..02db40f --- /dev/null +++ b/hosts @@ -0,0 +1,7 @@ +10.20.1.100 POC-Kube-Balancer +10.20.1.101 POC-Master0 +10.20.1.102 POC-Master1 +10.20.1.103 POC-Master2 +10.20.1.104 POC-Worker0 +10.20.1.105 POC-Worker1 +10.20.1.106 POC-Worker2 diff --git a/installa_haproxy.sh b/installa_haproxy.sh new file mode 100644 index 0000000..bf9e489 --- /dev/null +++ b/installa_haproxy.sh @@ -0,0 +1,123 @@ +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 \ No newline at end of file diff --git a/installazione.md b/installazione.md new file mode 100644 index 0000000..8a0f5e1 --- /dev/null +++ b/installazione.md @@ -0,0 +1,2437 @@ +# 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 < /dev/null < /dev/null < /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 < 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 < 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 < 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 < 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 < 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 < 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 - < + 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 <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 < 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 < 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 - < "$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 </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 < "$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 < 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 < 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: "", 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** + ------------------------------------------------------------------------------------------------------------------------------ + + + diff --git a/iscsiadm-open-iscsi.sh b/iscsiadm-open-iscsi.sh new file mode 100644 index 0000000..5f9ee6b --- /dev/null +++ b/iscsiadm-open-iscsi.sh @@ -0,0 +1,4 @@ +sudo apt install open-iscsi +systemctl enable open-iscsi +systemctl enable iscsid +systemctl restart iscsid.service \ No newline at end of file diff --git a/istruzioni.doc b/istruzioni.doc new file mode 100644 index 0000000000000000000000000000000000000000..96c2fb0a9894b2a72e1a9cfb98f9efdb01588c0d GIT binary patch literal 41472 zcmeI52S8I-+s98>q7ubL-ChwDl#L9j0*VL@6a`nUVn_l+LK2dIII1|PT3f{qwbi;; zwOV)8;;33jowZeK)w)NWII4B>{hym$NFXTs_WQo~eVYrvoN>>(&)MgmoE$k^?cBW8 zbhai%}SLJIJ9$N-XV;2m~?M2v7{pJlhrUNr9KM_s}|Xn zr57B0%WED~FqlcbQ_G-#nOalu4wW*2jJ7TYOH&nzW^cp(U8vKcwlZ;->xL({VQfF_ z7sEc|EquI0?ONM#zocr1a1ac0fJ#?+)k)15+W?R4JPh%Zzt)Bz%svG<2=SWTgh8+` zOBK>1_!GhtRN=XW`(!=^LpahyB5hdTaHph7@x>wiCBrFcCi$3NLO7;&<se7`%6IFNu|?EluC(Sp;mDznF^U)ERNL5CBtBr zb2_;;ORfzSivx^Wu4k7hf6hH!qSMQ@K61TO=HV&!=dz@lOi!*=N)o4)q;vkh(8P4$ zdL?D5^qHK0ptqkdCl+&D8-*r}Gx+uMR;r~ErMacQCCdP#rC99e6~GNqYt!UfPNkN~ zb)ltV4zw7vF=B&PFNt|1M%yHdw$dV7#Ljh9OJrPoiBh7HA_?v>?c%iR?4cfF&d(?S zKcfKrEGHvXuh-~&=#$fACM78qQiYgPXt-pxc8EkPQ>aoP4ug=Ch$_h#lea72E{N8Hfp$+<9-(J%bi!f;U*h+AWV+=L*)>Y25FP<>EM_T{OFNBAC`&` zC`=vPNcqq>m@HJ7R3+;y2sWA)|y zJj8~I>>m({3g96aycEU;B@y-1n}VlWBNS!cX_1RmiH0f=x$^hm(9kH76;cVO%S1J( z5z{X&s%O_u-MVnDRAWMYe3}MCQnm1L<=#yd)iheRA*))P5CNB!&dIY4)df{6EHuP7 z#McK+oLmRzmNUE>THRP+N7W#)Ox3yOub>gQKRGlPWW*dF2#3T_zL_P|Ecm>$oVD zR6A6oM~e9jlIoS5M59p-<-C#wPno3K+OY%!-S4-toPh*3tORYsuDP$n}164|lT8n;ChD=kd zbZVuXGddOrd2mw7Otk9!BTz1qhg5jrLOJAsg-lc$9b_tsa!{}`)<&^dqn3u!hZjkE zh&;*Ql5>Yv=%lH<%assZD4zy{L{Y6hjdI~o%}tVJ?2L?kzCy)m<$5_s;h}VKyl^>> zqA0zx)XL0sxglD2dPZaTSyGaNPOsLYz@t1$ka0rlNj$uWmEDVIejx-6@VpFxC=_qZN9&#G;=Rx>M{&Fv3raM4P0> z2+Kl5sr;>CUqxnkOOU7O)hbKA^!%M-Uqxo)`bmj_0@is%pa!T3YJp~;IdBIZKolrA zvuDk;AqlM#N_a1?F}zl4bXs>7)DlSI=^

    TxBy>m-2Zq5b`<)ddlz50Ok2IV4Ck+VWzyl z1StQX04f8{$X_Z4%>b1J44$xDX=m^pPm5s}L_b$z)zg%$HsD}xLG?g3F2LaPwfy`&EzG~#&Xx0-E0%obim!fV3w~Gw%8ML8b#)4$`nm!L zb#@>0HfW1`f&|bTq=K{g^K;MU=gXty`T3rP>@xfGdwEN5p_R8FIXm42Zwvpn@NfGn|K*hY|3_(iooBYxtCs1XiK$%{@^vusRSCv{t-u*= zOLfozD1i!00GmNRI0S5+7_$RyKu3@Sq#y%K0Mo%!@C-Z$_F}wgGA$`q!~d#4OhwJ> z&HIJ=RF{Q(A7jk>d}IDsLH-J5pgr_d4h93EEW8grm4}T$C=(Yi{)oSC=kdzC@q@M#OT&`Zf?F=r@xY$CQ%W6P2Va1VU=!F3c7r|O2{3I7?9djR z%|ClKe@^ZksQLImo1Z%;Hz!+@6PJ@aXHNcEX4O{$#2RkpML z>N~#)&rhnE!R)b(uCRNcQZrV$qli@oST987-+{6}O+WQggrv6`0JaK@Eg z&kVhmg_RLnLbAsI-yQ>edkpaH`Q;;8Mk&3@Q`)Uiz0A62ehtQSmFIQ*y?I~oCOyyJ z(|@oUlxI|z*MsjsKDY^lx_uY=d!QcbbA8YN_yAu(^}ZjVx<3?9{T~TdgL_vFZaz4B z;>e*aW9Xov_eS39HnGiSlakc%1a@?D6hJLSdaj6 zz-TZLd+wCV!%KQx!HxY(+%U|nsn0g=uLYlWFYzh3u`P!io0qsLu`XPV z^13mI0PR3~&>3_ALK%pMp2|Tlpa+>C3ycJ#fKWarKu=}l1F#G%2VaBrU;_}!&Q|ED z{A>s3z1+yFNLZG)@_sC+d5;UEIE16@HZkO86Wr9htw6d(g=few5GCW9#;4@?D5 z*u5v`pB&w_Y1b3Bx=8m`k!~Jzb2c4)!lpyB>F6$8oB01-yO1zLU&3K`tD!Hk#k|pA zNBcbub_Vl|66S;=v*niM&0DbYwwumHDJbp%+1sWSz^eb}NdTK-gzXH5xA?R%gMWkU zVd>?vMxl`_6)M`;W8BSRVpEHAm&ubAD&r0g;SRmQ7mK@KLs6701-lEUhjE>BdTl1f zr%0rYMsnC2wu!)DdxeL3r|da zw`uz+w4Ei}&5z-37Wfh@0*k?Fum)@f-+^=BJop1#2L<3RxCb79KY>}lYBju>KyC}U zN2=NSN%0M|_ISIGcfj;knBD=W$Y=vbiT=MH^@)_XGl7u53!xYCcop0!=40S18jjEum|*gqb2)80HiVsv#3yQopo)2jdaL>b1dyvB+zWEm~5Y6!JvK zkMiclpQds(+t2YPckY%M+MC=lOU`xEp#K4S^LKnAOy$dJ4XSuDzr0jYqg7+PxmCN{ zHD0B%a|KQECfd8@sqC7r!c2bEE}PoGDQ_3Xz1Hi$O9J$ch4@!4YZ&Izv&zF<9#)f8 z0#*rFC190+RRUHCSS4VUfK>uk30NgymB9bM1nN3e!`p2U%f>2E1zH^Dv7@hru^C|Y zD+q#@SCNHvqgPQ@7-8^R*1i7?`=xkZAzV7;NYIz1ZY+nJl73UM8W#gf+W!;H8e{(Y zqa4+SEO*;t?-95H9s&nDoHhlTfgsQk^ajZw3%n1efwKHjxbBGKSIGMsc++tH>Hb0> zyT0Hr;0Srv2ED;ZkO$_1RbV?f0&>7PPyle0Aae#B@C6aTdhh>EE|%IAI=roWX9w(^ zbzq4xBD?e$XFCf^@W@Jg=?V-q4_#gWeEr^2_YEHmErhvSxldxK6{Ir znpVdhn5dGi(x9=2jfAXiXe%{N2u$N16n;-91sc=o>mY_K=+qn?PLxq0O%wy2)cC$3%x5A|2ZUYK7?w+!VR1$|(z!T>lYJ<^kgr{A?P{W*r#`+h`{Vbj+vscd z^-)u6KEC?Lt?^Eja)atJ3C$Z_0;{{cKSL;J&JiY_Oe&!S!*I3oBlCo&*VMx zM{M8wxtq(rU)L<@_4HBKqY3SoaxO~GlL3!rzkN}=rBQ|TJ7>&V()pWv?OVFts24q8 z(b?ecCs*{IIJ$1_Hj+hEXFS-vHQzmWd`_o{U-Vow{E8-}>zcNc^R$if79M>PVzWOm zt69$ToGJ%1dz`L)W8&11i~Ax^T(;e?szKkjw=X}Q;dUW!-RAJ1ZeMqfbR4^O`^UfB zT>7}3kAukwf0cdl}pn0PpatK)TL z+uO4z-aK`@pECc<P^R^#Ubqxz46L<$S>B~mkaEjqsB>UPK} zNW_2nnk8!GN}Z24eU6)Qc<@eNr}1}kw~YPiy&gw*)^3{RvnVgpXTf{yy!)tB*LtVw z-8j{Kk3*&J9LFuZ^XRL#FIG-^eE!p`oXZ!>9Clua4Bwx6KCA7vQDgSyE?m;ov46Ja z{RP8MtxwF4`)NU&gMRfE#cc5Xta{Z6C%fnOT-jds(>~{;ouf{*N^%>uWNJp@iAFPK z*Etu|_2)lbdY%X#v~tSQeT8dx`W~3w{rgehcwUaOQw4w8RdFe4-cp~7@muni%9s7p z@W}d&kB>jcqm+Kow4u{n#^OOg0MuMy|4q8w$K*5Cc(M&jh|_`CN;^5{i1Y8w8DW_A(x^PE*d{DJ(S_k?c?MtCl% zxAeD|`agIo&+n;q<&W&z`__F_<7}tBx*qQ}IhL2ZV*MHJ`1+Q|8sG6eGjNmJ;I@;Z z`&|3^k;jFo8!ekvvI()A_@l0ef9q5M`wTe4X_Y3)HavnK73`b+4yx(}J8ymK+S|^< z)t(+3ly!R86z z9bYkTZfv9Zk=pgHOQ?KJ^gwpE-+r30_uBD;-NQGHKGCbQ|Cord={GhuSk$@JjV@iZ zY=7T@7wz|*|E}>{b$S=Lf4r#gx%eY9S1;VxI6Xhx|EJ^0JxLen%q<&O?@60~m z3=ALo_{qLI-^>Yn@A;)Z>!LqN=+|rZY41Zd`rJO#{p_>*&$>K#qz>OY{9$@+p8?NK zC2k$|bkCeS@m2ez5B?zd?3V1O_kNgpXWNS#S5ri{>e)ORkUb^2!~EfwYBsp*R3)|R z`TNb^%kY~x@Pn^fuAbVw`qjR7svkMqI?K$Hf{=Q?=)ys#L=G5;J+2Y%t z7k+9Z3+WTmxOI=1QG2FTtT^j-<3+OvJo+O-vu}K*whL2h{5-eQuyuE$eojc8o_ub4 z#su!KQ)>^tuwA#W-?d*(UD_4BDd2(kRop|f`UUgkhHkdSe z(v2AV+1(s3F0E0+BhtTu&7m71&%NKj>7sucRxc&2r~9z_M>macJ!I;sXW1=(=vQq= zmSXniUk_jQ8|U6}{JXj5o}S*;_(`2-IrnpKkBpdj_4eAe-@mt4>%B;kb4}gR_1ykb z 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 Mgc4^}|D7c8KkciGqyPW_ literal 0 HcmV?d00001 diff --git a/istruzioni.docx b/istruzioni.docx new file mode 100644 index 0000000000000000000000000000000000000000..2256132e993b6631ee5190fc5384b397ad5497c5 GIT binary patch literal 5168 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 q`al2Q*#EoppQXJ6-EXtHP38ZApt>>!=G{w}w master1.local + master2.local + master3.local + + +1-3 worker nodes: + worker-a.local + worker-b.local + worker-c.local + + +1 Load Balancer (HAProxy) https/http pubblici + loadbalancer.internal + +DNS: rancher.pigreco66.it → IP esterno --> ip loadbalancer.internal + +Sequenza: + +Ogni server + 1)installazione vi (vi.txt) + +Ogni nodomaster e worker + 1) prepara_nodo.sh + +master1: + 1)master_1_installa_rke.sh + +master23: + 1)master23-installa_rke.sh + +Loadbalancer: + 1)installa_haproxy.sh + +worker: + 1)worker-installa_rke2.sh + +Add_on: +1) installa_helm.sh +2) installa-cert-manager.sh +3) kubectt apply -f nginx-controller-service-nodeport.yaml + +poc: +poc-testweb.yaml \ No newline at end of file diff --git a/master23-installa_rke.sh b/master23-installa_rke.sh new file mode 100644 index 0000000..de0c4c4 --- /dev/null +++ b/master23-installa_rke.sh @@ -0,0 +1,56 @@ +# 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 < /dev/null </` dentro `containers//`. +- Rileva il Dockerfile (`dockerfile` oppure `Dockerfile`). +- Costruisce due tag immagine: + - `${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}` + - `${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:latest` +- Esegue push su registry. +- Scrive su file `./imglist` una riga per container: + - `IMAGE_TAG_=` + +Supporto multi-arch: +- Se presente `containers//platform.conf` con chiave `platform=...`, usa `docker buildx build --platform ... --push`. +- In assenza di `platform.conf` usa `docker build` + `docker push` classico. + +Input (argomenti): +- `$1`: `REPO_NAME` (passato dalla pipeline con `${{ github.event.repository.name }}`). +- `$2`: `REGISTRY_URL` (passato da `${{ vars.REGISTRY }}`). + +Prerequisiti runtime: +- Docker daemon disponibile nel runner. +- Permessi push su registry. +- Struttura cartelle coerente tra `containers/` e `src/`. + +### 2) `customize.sh` +Responsabilità funzionale attesa: +- Carica variabili da: + - `env//values.env` + - `properties.env` + - file temporaneo con: + - `TAG=` + - contenuto di `imglist` (generato da `build_container.sh`) +- Cerca tutti i manifest `kubernetes/**/*.yaml`. +- Esegue sostituzione placeholder nel formato `` con i valori trovati. + +Input (argomenti): +- `$1`: ambiente (`dev|qa|prod`). + - In pipeline attuale viene usato `dev`. + +Placeholder parametrizzabili nei manifest: +- Tutte le chiavi presenti in `env//values.env`. +- Tutte le chiavi presenti in `properties.env`. +- `TAG`. +- `IMAGE_TAG_` (una per ciascun container buildato). + +Output: +- Manifest Kubernetes in-place con valori sostituiti. + +### 3) `deploy.sh` +Responsabilità: +- Cerca manifest CNPG (`apiVersion: postgresql.cnpg.io/v1`). +- Se trovato: + - Estrae `metadata.name` del cluster PostgreSQL. + - Ricava namespace dal role `namespace-deployer` nel cluster. + - Crea/aggiorna ConfigMap `service-config` con: + - `tipodb=postgres` + - `urldb=..svc.cluster.local` +- Applica tutti i manifest YAML trovati in `kubernetes/` (directory per directory). +- Invoca `/root/work/pipeline/addlistener.sh` per configurare listener HTTPS sul Gateway. + +Prerequisiti runtime: +- `kubectl` disponibile nel runner. +- `./kubeconfig` presente e valido. +- Opzionale `yq` (se assente, usa fallback con `awk` per estrazione nome CNPG). + +### 4) `addlistener.sh` +Responsabilità: +- Legge la chiave `endpoint` da `properties.env`. +- Calcola token host-based dal dominio. +- Invoca `/root/work/pipeline/add-listener.sh` passando: + - `` + - `https-` + - `-secret` + +Input (argomenti): +- `$1` opzionale: path file properties (default `properties.env`). + +Comportamento: +- Se file non esiste o `endpoint` non valorizzato: termina senza errore bloccante (`exit 0`). + +### 5) `add-listener.sh` +Responsabilità: +- Effettua patch JSON sulla risorsa Gateway Kubernetes: + - Gateway: `main-gateway` + - Namespace: `nginx-gateway` +- Aggiunge un listener HTTPS con certificato TLS da Secret. +- Verifica idempotenza per `name` e `hostname` già presenti. + +Input (argomenti): +- `$1`: `hostname` +- `$2`: `name` +- `$3`: `secret-name` + +## Parametrizzazione complessiva + +### Variabili Gitea Actions +- `vars.REGISTRY`: URL registry target. + +### Secret Gitea Actions +- `secrets.HARBOR_USERNAME` +- `secrets.HARBOR_PASSWORD` +- `secrets.KUBECONFIG_DEV` + +### File di configurazione repository +- `env/dev/values.env` (o `qa`, `prod` se si cambia argomento di `customize.sh`) +- `properties.env` +- `containers//platform.conf` (opzionale) + +### Parametri indiretti derivati +- Nome repository da `${{ github.event.repository.name }}`. +- SHA commit da `git rev-parse HEAD`. +- Tag immagine per servizio in `imglist`. + +## Note operative importanti +1. Il file `imglist` viene creato/appeso da `build_container.sh` e poi letto da `customize.sh`; il job deve mantenere lo stesso workspace tra step. +2. I manifest Kubernetes vengono modificati in-place da `sed -i`. +3. La parte Gateway dipende da: + - presenza di `endpoint` in `properties.env` + - esistenza della risorsa `Gateway/nginx-gateway/main-gateway` + - esistenza del Secret TLS con nome `-secret` + + diff --git a/pipeline/DEV_build_deploy.yaml b/pipeline/DEV_build_deploy.yaml new file mode 100644 index 0000000..211db6d --- /dev/null +++ b/pipeline/DEV_build_deploy.yaml @@ -0,0 +1,36 @@ +name: Build and Deploy + +on: + push: + branches: + - main + +jobs: + docker: + runs-on: POC-Master0 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Harbor + uses: docker/login-action@v3 + with: + registry: ${{ vars.REGISTRY }} + username: ${{ secrets.HARBOR_USERNAME }} + password: ${{ secrets.HARBOR_PASSWORD }} + + - name: Build container + run: /root/work/pipeline/build_container.sh ${{ github.event.repository.name }} ${{ vars.REGISTRY }} + + - name: Create kubeconfig + run: | + echo "${{ secrets.KUBECONFIG_DEV }}" > ./kubeconfig + chmod 600 ./kubeconfig + + - name: variables sustitution + run: /root/work/pipeline/customize.sh dev + + - name: k8s deploy + run: /root/work/pipeline/deploy.sh + diff --git a/pipeline/add-listener.sh b/pipeline/add-listener.sh new file mode 100644 index 0000000..d989537 --- /dev/null +++ b/pipeline/add-listener.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Aggiunge un listener HTTPS direttamente sulla risorsa K8s Gateway +# main-gateway nel namespace nginx-gateway, tramite kubectl patch. +# +# Uso: +# ./add-listener.sh +# +# Esempio: +# ./add-listener.sh sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret + +set -euo pipefail + +GATEWAY_NAME="main-gateway" +GATEWAY_NS="nginx-gateway" + +HOSTNAME_VAL="${1:-}" +NAME_VAL="${2:-}" +SECRET_NAME="${3:-}" + +if [[ -z "$HOSTNAME_VAL" || -z "$NAME_VAL" || -z "$SECRET_NAME" ]]; then + echo "Uso: $0 " + echo "Es.: $0 sonarqube.italiadatacenter.com https-sonarqube sonarqube-secret" + exit 1 +fi + +# Controlla idempotenza: verifica se il listener esiste già per nome o hostname +EXISTING=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \ + -o jsonpath='{.spec.listeners[*].name}') + +if echo "$EXISTING" | grep -qw "$NAME_VAL"; then + echo "Attenzione: listener con name '${NAME_VAL}' già presente. Nessuna modifica." + exit 0 +fi + +EXISTING_HOSTS=$(kubectl get gateway "$GATEWAY_NAME" -n "$GATEWAY_NS" \ + -o jsonpath='{.spec.listeners[*].hostname}') + +if echo "$EXISTING_HOSTS" | grep -qw "$HOSTNAME_VAL"; then + echo "Attenzione: listener con hostname '${HOSTNAME_VAL}' già presente. Nessuna modifica." + exit 0 +fi + +# JSON Patch: aggiunge il nuovo listener in append alla lista +PATCH=$(cat <&2 + exit 0 +fi + +# Estrae endpoint ignorando commenti e spazi, supportando anche endpoint = valore +endpoint_raw="$({ grep -E '^[[:space:]]*endpoint[[:space:]]*=' "$PROPERTIES_FILE" | tail -n1 || true; } | sed -E 's/^[[:space:]]*endpoint[[:space:]]*=[[:space:]]*//')" + +# Rimuove eventuali virgolette e spazi ai bordi +endpoint="$(echo "$endpoint_raw" | sed -E 's/^[[:space:]"\x27]+//; s/[[:space:]"\x27]+$//')" + +if [[ -z "$endpoint" ]]; then + echo "La chiave endpoint non e valorizzata in $PROPERTIES_FILE" >&2 + exit 0 +fi + +# Per calcolare il token usa host pulito (senza schema e path) +host_for_token="${endpoint#*://}" +host_for_token="${host_for_token%%/*}" +token="${host_for_token%%.*}" + +if [[ -z "$token" ]]; then + echo "Impossibile estrarre il token da endpoint: $endpoint" >&2 + exit 1 +fi + +# Output richiesto: https- -secret +/root/work/pipeline/add-listener.sh $endpoint https-$token $token-secret diff --git a/pipeline/build_container.sh b/pipeline/build_container.sh new file mode 100644 index 0000000..17dd172 --- /dev/null +++ b/pipeline/build_container.sh @@ -0,0 +1,58 @@ +#!/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 + + # Cerca sia dockerfile che Dockerfile + if [ -f "$dir/dockerfile" ]; then + DOCKERFILE="$dir/dockerfile" + elif [ -f "$dir/Dockerfile" ]; then + DOCKERFILE="$dir/Dockerfile" + else + echo "Dockerfile non trovato in $dir" + continue + fi + + + + IMAGE_TAG="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:${COMMIT_SHA}" + IMAGE_TAG_LATEST="${REGISTRY_URL}/${REPO_NAME}/${CONTAINER_NAME}:latest" + echo "IMAGE_TAG_${CONTAINER_NAME}=$IMAGE_TAG" >> ./imglist + + PLATFORM_CONF="$dir/platform.conf" + BUILD_PLATFORM="" + if [ -f "$PLATFORM_CONF" ]; then + BUILD_PLATFORM=$(grep -E '^[[:space:]]*platform[[:space:]]*=' "$PLATFORM_CONF" | tail -n 1 | cut -d '=' -f 2- | tr -d '[:space:]') + if [ -n "$BUILD_PLATFORM" ]; then + echo "platform.conf trovato in $dir: uso platform=$BUILD_PLATFORM" + else + echo "platform.conf trovato in $dir ma variabile platform non valorizzata, uso build standard" + fi + fi + + if [ -f "$DOCKERFILE" ]; then + if [ -n "$BUILD_PLATFORM" ]; then + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker buildx create --driver docker-container --use + docker buildx inspect --bootstrap + + docker buildx build --platform "$BUILD_PLATFORM" -t "$IMAGE_TAG" -t "$IMAGE_TAG_LATEST" -f "$DOCKERFILE" "$dir" --push + else + docker build -t "$IMAGE_TAG" -t "$IMAGE_TAG_LATEST" -f "$DOCKERFILE" "$dir" + docker push "$IMAGE_TAG" + docker push "$IMAGE_TAG_LATEST" + fi + echo "Build e push completate: $IMAGE_TAG" + else + echo "Dockerfile non trovato in $dir" + fi +done diff --git a/pipeline/customize.sh b/pipeline/customize.sh new file mode 100644 index 0000000..50c6b8e --- /dev/null +++ b/pipeline/customize.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Usage: ./customize.sh dev|qa|prod +echo "tetst" $IMAGE_TAG_backend +ENV=$1 +VALUES_DIR="env/$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) +TMP_VALUES_FILE=$(mktemp) +echo "TAG=$TAG" > "$TMP_TAG_FILE" +cat ./imglist >> "$TMP_TAG_FILE" + + +if [ ! -d "$VALUES_DIR" ] || ! ls "$VALUES_DIR"/*.env &>/dev/null; then + echo "Nessun file .env trovato in $VALUES_DIR" + exit 1 +fi + +if [ ! -f "$PROPERTIES_FILE" ]; then + echo "File $PROPERTIES_FILE non trovato." + exit 1 +fi + +# Crea una lista key=value temporanea partendo da tutti i file *.env in env/$ENV e aggiunge env dinamico +> "$TMP_VALUES_FILE" +for env_file in "$VALUES_DIR"/*.env; do + cat "$env_file" >> "$TMP_VALUES_FILE" + printf '\n' >> "$TMP_VALUES_FILE" +done +printf '\nenv=%s\n' "$ENV" >> "$TMP_VALUES_FILE" + +# 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 < "$TMP_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 +cat "$TMP_VALUES_FILE" +rm -f "$TMP_TAG_FILE" "$TMP_VALUES_FILE" \ No newline at end of file diff --git a/pipeline/deploy.sh b/pipeline/deploy.sh new file mode 100644 index 0000000..113c138 --- /dev/null +++ b/pipeline/deploy.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -euo pipefail + +# Esegue kubectl apply per ogni sottodirectory di kubernetes separatamente + +YAML_DIR="kubernetes" + +# --------------------------------------------------------------------------- +# Cerca risorse postgresql.cnpg.io/v1 nei manifest e deploya una ConfigMap +# --------------------------------------------------------------------------- +CNPG_FILE=$(grep -rl "postgresql.cnpg.io/v1" "$YAML_DIR" 2>/dev/null | head -1 || true) + +if [ -n "$CNPG_FILE" ]; then + echo "Trovata risorsa postgresql.cnpg.io/v1 in: $CNPG_FILE" + + # Estrae metadata.name dal manifest CNPG preferendo yq, altrimenti awk + if command -v yq >/dev/null 2>&1; then + PG_NAME=$(yq eval 'select(.apiVersion == "postgresql.cnpg.io/v1") | .metadata.name' "$CNPG_FILE") + else + PG_NAME=$(awk '/postgresql\.cnpg\.io\/v1/{found=1} found && /^metadata:/{meta=1} meta && /^\s+name:/{print $2; exit}' "$CNPG_FILE") + fi + + # Ricava il namespace dal Role namespace-deployer presente nel cluster + PG_NS=$(kubectl --kubeconfig=./kubeconfig get role namespace-deployer \ + --no-headers \ + -o custom-columns='NS:.metadata.namespace' 2>/dev/null | head -1 || true) + #PG_NS="${PG_NS:-default}" + + if [ -z "$PG_NAME" ]; then + echo "⚠️ Impossibile estrarre metadata.name dal cluster CNPG, skip ConfigMap." >&2 + else + echo " → cluster: $PG_NAME namespace: $PG_NS" + kubectl --kubeconfig=./kubeconfig apply -f - < /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." + +echo "Eseguo check su endpoint da pubblicare" +/root/work/pipeline/addlistener.sh diff --git a/prepara_nodo.sh b/prepara_nodo.sh new file mode 100644 index 0000000..7701675 --- /dev/null +++ b/prepara_nodo.sh @@ -0,0 +1,36 @@ +# 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 <>/etc/hosts diff --git a/st_migrationlist.xlsx b/st_migrationlist.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d56cd14393558a0704e42239db337af803b1821e GIT binary patch literal 11205 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 I`FZvK00yv8uK)l5 literal 0 HcmV?d00001 diff --git a/st_size.csv b/st_size.csv new file mode 100644 index 0000000..c6dcf6d --- /dev/null +++ b/st_size.csv @@ -0,0 +1,297 @@ +namespace;tipo;nome;replicas;cpu_req_cores;mem_req_Mi +accessi-enba;deployments;stqrcodeapi;1;0,002;0 +accessi-enbadev;deployments;stqrcodeapi;1;0,002;90 +accessi-enbaqa;deployments;stqrcodeapi;0;0,002;62 +accessi;deployments;nest;1;0,01;200 +accessidev;deployments;nest;1;0,01;200 +accessiqa;deployments;nest;0;0,01;200 +adria;deployments;nginx;1;0,001;0 +agimdev;deployments;allegati;0;0,01;200 +agimdev;deployments;apigateway;0;0,01;200 +agimdev;deployments;geocode;0;0,01;200 +agimdev;deployments;postgrest;0;0;0 +algoranddev;deployments;algo;0;0,01;100 +allenamenti;deployments;nest;1;0,2;1024 +allenamentidev;deployments;nest;1;0,1;512 +allenamentiqa;deployments;nest;0;0,1;512 +anagrafiche;deployments;nest;1;0,5;4096 +anagrafichedev;deployments;nest;1;0,1;512 +anagraficheqa;deployments;nest;0;0,1;512 +cert-manager;deployments;cert-manager;1;0;0 +cert-manager;deployments;cert-manager-cainjector;1;0;0 +cert-manager;deployments;cert-manager-webhook;1;0;0 +companies;deployments;nest;1;0,5;2048 +companiesdev;deployments;nest;1;0,1;512 +companiesqa;deployments;nest;0;0,1;512 +convocazioni;deployments;nest;1;0,1;0 +convocazionidev;deployments;nest;1;0,1;0 +convocazioniqa;deployments;nest;0;0,1;0 +db;deployments;adminer;1;0,002;0 +db;deployments;filebrowser2;1;0,01;0 +db;deployments;nfs-server;1;0,002;0 +db;deployments;nginx;1;0,001;0 +db;deployments;php;1;0,002;0 +db;deployments;postgrestadmin;0;0;0 +db;deployments;postgrestadminmatch;0;0;0 +dbdev;deployments;filebrowser2;1;0,001;8 +dbdev;deployments;nfs-server;1;0,002;186 +dbdev;deployments;nginx;1;0,001;4 +dbdev;deployments;php;1;0,002;8 +dbqa;deployments;filebrowser2;0;0,1;0 +dbqa;deployments;nfs-server;0;0,001;186 +dbqa;deployments;nginx;0;0,001;7 +dbqa;deployments;php;0;0,002;8 +default;deployments;locust-master;0;0;0 +default;deployments;locust-worker;0;0;0 +demodev;deployments;nginx;2;0,01;100 +dev;deployments;nginx;0;0;0 +enbabeapi;deployments;nest;1;0,1;0 +enbabeapidev;deployments;nest;1;0,1;0 +enbafe;deployments;nginx;1;0;0 +enbafedev;deployments;nginx;1;0,001;8 +eventi-wow;deployments;nest;1;0,1;0 +eventi-wowdev;deployments;nest;1;0,1;0 +eventi-wowqa;deployments;nest;0;0,1;0 +eventi;deployments;nest;1;0,1;0 +eventidev;deployments;nest;1;0,1;0 +eventiqa;deployments;nest;0;0,1;0 +frontend-club;deployments;nginx;1;0,1;200 +frontend-clubdev;deployments;nginx;1;0,001;3 +frontend-clubqa;deployments;nginx;0;0,001;3 +frontend-limec;deployments;nginx;1;0;0 +frontend-limecdev;deployments;nginx;1;0;0 +frontend-limecqa;deployments;nginx;1;0;0 +frontend-matchdev;deployments;nginx;1;0;0 +frontend-sestra;deployments;nginx;1;0;0 +frontend-sestradev;deployments;nginx;1;0;0 +frontend-sestraqa;deployments;nginx;0;0;0 +frontend-wow;deployments;nginx;1;0,001;0 +frontend-wowdev;deployments;nginx;1;0,001;3 +frontend-wowqa;deployments;nginx;0;0,001;3 +gare;deployments;nest;1;0,3;0 +garedev;deployments;nest;1;0,1;0 +gareqa;deployments;nest;0;0,1;768 +gateway-accessi;deployments;nest;1;0,1;0 +gateway-accessidev;deployments;nest;1;0,1;0 +gateway-accessiqa;deployments;nest;0;0,1;0 +gateway-club;deployments;nest;4;0,5;4096 +gateway-clubdev;deployments;nest;1;0,3;1024 +gateway-clubqa;deployments;nest;0;0,1;0 +gateway-csi;deployments;nest;1;0,1;0 +gateway-csidev;deployments;nest;1;0,1;0 +gateway-csiqa;deployments;nest;0;0,1;0 +gateway-limec;deployments;nest;4;0,5;4096 +gateway-limecdev;deployments;nest;1;0,3;1024 +gateway-match;deployments;nest;1;0,1;0 +gateway-matchdev;deployments;nest;1;0,1;0 +gateway-matchqa;deployments;nest;0;0,1;0 +geco;deployments;nginx;1;0,001;0 +gecobe;deployments;nest;1;0,1;0 +gecobedev;deployments;nest;1;0,1;0 +gecobeqa;deployments;nest;0;0,1;0 +gecodev;deployments;nginx;1;0,001;3 +gecoqa;deployments;nginx;0;0,001;3 +giornalistadev;deployments;nginx;1;0,01;100 +gke-connect;deployments;gke-connect-agent-20210430-00-00;1;0,002;0 +grafana;deployments;grafana;0;0,002;0 +homepage;deployments;nginx;1;0;0 +homepagedev;deployments;nginx;1;0,002;5 +ingress-nginx2;deployments;nginx2-nginx-ingress;1;1;2000 +isclubdev;deployments;apiserver;1;0,1;0 +isclubdev;deployments;identityserver;1;0,1;0 +isclubdev;deployments;isadmin;1;0,1;0 +isclubqa;deployments;apiserver;0;0,1;0 +isclubqa;deployments;identityserver;0;0,1;0 +isclubqa;deployments;isadmin;0;0,1;0 +iscsi;deployments;apiserver;1;0,002;0 +iscsi;deployments;identityserver;1;0,002;0 +iscsi;deployments;isadmin;1;0,002;0 +iscsidev;deployments;apiserver;1;0,1;0 +iscsidev;deployments;identityserver;1;0,1;0 +iscsidev;deployments;isadmin;1;0,1;0 +iscsiqa;deployments;apiserver;0;0,002;70 +iscsiqa;deployments;identityserver;0;0,002;78 +iscsiqa;deployments;isadmin;0;0,002;70 +isenba;deployments;apiserver;1;0,1;0 +isenba;deployments;identityserver;1;0,1;0 +isenba;deployments;isadmin;1;0,1;0 +isenbadev;deployments;apiserver;1;0,1;0 +isenbadev;deployments;identityserver;1;0,1;0 +isenbadev;deployments;isadmin;1;0,1;0 +isenbaqa;deployments;apiserver;0;0,1;0 +isenbaqa;deployments;identityserver;0;0,1;0 +isenbaqa;deployments;isadmin;0;0,1;0 +ismatch;deployments;apiserver;1;0,002;0 +ismatch;deployments;identityserver;1;0,002;0 +ismatch;deployments;isadmin;1;0,002;0 +ismatchdev;deployments;apiserver;1;0,1;0 +ismatchdev;deployments;identityserver;1;0,1;0 +ismatchdev;deployments;isadmin;1;0,1;0 +ismatchqa;deployments;apiserver;0;0,002;64 +ismatchqa;deployments;identityserver;0;0,002;80 +ismatchqa;deployments;isadmin;0;0,002;59 +ispub;deployments;apiserver;1;0,002;0 +ispub;deployments;identityserver;1;0,002;0 +ispub;deployments;isadmin;1;0,002;0 +ispubdev;deployments;apiserver;1;0,1;0 +ispubdev;deployments;identityserver;1;0,1;0 +ispubdev;deployments;isadmin;1;0,1;0 +ispubqa;deployments;apiserver;0;0,002;69 +ispubqa;deployments;identityserver;0;0,002;84 +ispubqa;deployments;isadmin;0;0,002;60 +isstaysafe-primario;deployments;apiserver;1;0,1;0 +isstaysafe-primario;deployments;identityserver;1;0,1;0 +isstaysafe-primario;deployments;isadmin;1;0,1;0 +isstaysafe-primariodev;deployments;apiserver;1;0,1;0 +isstaysafe-primariodev;deployments;identityserver;1;0,1;0 +isstaysafe-primariodev;deployments;isadmin;1;0,1;0 +isstaysafe-primarioqa;deployments;apiserver;0;0,1;0 +isstaysafe-primarioqa;deployments;identityserver;0;0,1;0 +isstaysafe-primarioqa;deployments;isadmin;0;0,1;0 +isstaysafe;deployments;apiserver;1;0,002;0 +isstaysafe;deployments;identityserver;1;0,002;0 +isstaysafe;deployments;isadmin;1;0,002;0 +isstaysafedev;deployments;apiserver;1;0,002;80 +isstaysafedev;deployments;identityserver;1;0,002;76 +isstaysafedev;deployments;isadmin;1;0,002;61 +isstaysafeqa;deployments;apiserver;0;0,002;67 +isstaysafeqa;deployments;identityserver;0;0,002;83 +isstaysafeqa;deployments;isadmin;0;0,002;59 +kube-downscaler;deployments;kube-downscaler;1;0,03;0 +kube-system;deployments;konnectivity-agent;8;0,015;60 +kube-system;deployments;konnectivity-agent-autoscaler;1;0,01;10 +kube-system;deployments;kube-dns;3;0,27;155 +kube-system;deployments;kube-dns-autoscaler;1;0,02;10 +kube-system;deployments;kube-state-metrics;1;0;0 +kube-system;deployments;l7-default-backend;1;0,01;20 +kube-system;deployments;metrics-server-v1,35,1;1;0,065;231 +kube-system;deployments;tiller-deploy;0;0;0 +licenze;deployments;nest;1;0,01;200 +licenzedev;deployments;nest;1;0,01;200 +licenzeqa;deployments;nest;0;0,01;200 +limecaccessi;deployments;nest;1;0,01;200 +limecaccessidev;deployments;nest;1;0,01;200 +limecallenamenti;deployments;nest;1;0,2;1024 +limecallenamentidev;deployments;nest;1;0,1;512 +limecanagrafiche;deployments;nest;1;0,5;2048 +limecanagrafichedev;deployments;nest;1;0,1;512 +limeccarriere;deployments;nest;1;0,5;2048 +limeccarrieredev;deployments;nest;1;0,1;512 +limeccompanies;deployments;nest;1;0,5;2048 +limeccompaniesdev;deployments;nest;1;0,1;512 +limecconvocazioni;deployments;nest;1;0,1;0 +limecconvocazionidev;deployments;nest;1;0,1;0 +limeceventi;deployments;nest;1;0,1;0 +limeceventidev;deployments;nest;1;0,1;0 +limecgare;deployments;nest;1;0,3;0 +limecgaredev;deployments;nest;1;0,1;0 +limeclicenze;deployments;nest;1;0,01;200 +limeclicenzedev;deployments;nest;1;0,01;200 +limecmailsms;deployments;nest;1;0,1;0 +limecmailsmsdev;deployments;nest;1;0,1;0 +limecmoduli;deployments;nest;1;0,5;2048 +limecmodulidev;deployments;nest;1;0,1;0 +limecnotifiche;deployments;nest;1;0,01;200 +limecnotifichedev;deployments;nest;1;0,01;200 +limecsponsor;deployments;nest;1;0,5;2048 +limecsponsordev;deployments;nest;1;0,1;0 +limecstrutture;deployments;nest;1;0,1;0 +limecstrutturedev;deployments;nest;1;0,1;0 +limecsupertokens;deployments;supertoken;1;0,1;200 +limecsupertokensdev;deployments;supertoken;1;0,1;200 +localita;deployments;nest;1;0,1;0 +localitadev;deployments;nest;1;0,1;0 +localitaqa;deployments;nest;0;0,1;0 +mailsms;deployments;nest;1;0,1;0 +mailsmsdev;deployments;nest;1;0,1;0 +mailsmsqa;deployments;nest;0;0,1;0 +mockapidev;deployments;node;1;0,001;35 +moduli;deployments;nest;1;0,5;2048 +modulidev;deployments;nest;1;0,1;0 +moduliqa;deployments;nest;0;0,1;0 +mongodb;deployments;mongo-express;1;0,002;0 +mongodb;deployments;mongodb-kubernetes-operator;1;0,002;0 +mongodbdev;deployments;mongo-express;1;0,002;61 +mongodbdev;deployments;mongodb-kubernetes-operator;1;0,002;18 +mongodbqa;deployments;mongodb-kubernetes-operator;0;0,002;16 +nocodbdev;deployments;nocodb;1;0,01;100 +notifiche;deployments;nest;1;0,01;200 +notifichedev;deployments;nest;1;0,01;200 +notificheqa;deployments;nest;0;0,01;200 +pgare;deployments;nginx;0;0,002;0 +pgare;deployments;nodepgare;1;0,001;0 +pgare;deployments;postgrest;1;0,004;0 +pgare;deployments;pwafed;1;0,001;0 +pgaredev;deployments;nginx;0;0,001;3 +pgaredev;deployments;nodepgare;0;0,1;0 +pgaredev;deployments;postgrest;0;0,003;19 +pgaredev;deployments;pwafed;0;0,001;4 +prenotazione;deployments;prenotazione;1;0,003;0 +prenotazionedev;deployments;prenotazione;1;0,1;0 +prenotazioneqa;deployments;prenotazione;0;0,003;121 +prenotazioni-clubdev;deployments;prenotazione;1;0,1;0 +prenotazioni-clubqa;deployments;prenotazione;0;0,002;37 +prenotazioni-csi;deployments;prenotazione;1;0,003;0 +prenotazioni-csidev;deployments;prenotazione;1;0,1;0 +prenotazioni-csiqa;deployments;prenotazione;0;0,003;122 +prenotazioni-enba;deployments;prenotazione;1;0,1;0 +prenotazioni-enbadev;deployments;prenotazione;1;0,1;0 +prenotazioni-enbaqa;deployments;prenotazione;0;0,1;0 +qrcodeapi;deployments;stqrcodeapi;0;0,002;0 +rabbitmq-system;deployments;rabbitmq-cluster-operator;1;0,2;500 +referti;deployments;nest;1;0,1;256 +refertidev;deployments;nest;1;0,1;256 +refertiqa;deployments;nest;0;0,1;0 +sestrabeapi;deployments;nest;1;0,1;0 +sestrabeapidev;deployments;nest;1;0,1;0 +sestrabeapiqa;deployments;nest;0;0,1;0 +sonarq;deployments;sonarqube;0;0,009;0 +sponsor;deployments;nest;1;0,5;2048 +sponsordev;deployments;nest;1;0,1;0 +sponsorqa;deployments;nest;0;0,1;0 +stayrsa;deployments;nginx;1;0,001;0 +stayrsadev;deployments;nginx;1;0,001;8 +stayrsaqa;deployments;nginx;0;0,001;4 +staysafebeapi;deployments;nest;1;0,1;0 +staysafebeapidev;deployments;nest;1;0,1;0 +staysafebeapiqa;deployments;nest;0;0,06;581 +staysafebeqa;deployments;nodestaysafebe;0;0,08;505 +strutture;deployments;nest;1;0,1;0 +strutturedev;deployments;nest;1;0,1;0 +struttureqa;deployments;nest;0;0,1;0 +supertokens;deployments;supertoken;1;0,1;200 +supertokens;deployments;supertoken-match;1;0,1;200 +supertokens;deployments;supertoken-sestra;1;0,101;200 +supertokensdev;deployments;supertoken;1;0,1;200 +supertokensdev;deployments;supertoken-match;1;0,1;200 +supertokensdev;deployments;supertoken-sestra;1;0,101;200 +supertokensqa;deployments;supertoken;0;0,1;200 +supertokensqa;deployments;supertoken-match;0;0,1;200 +supertokensqa;deployments;supertoken-sestra;0;0,101;200 +tornei;deployments;nest;1;0,01;200 +torneidev;deployments;nest;1;0,01;200 +torneiqa;deployments;nest;0;0,01;200 +utenti-sporteamsdev;deployments;nest;1;0,1;0 +utenti-sporteamsqa;deployments;nest;0;0,1;0 +verdaccio;deployments;verdaccio;1;0,001;0 +webhooks;deployments;nest;1;0,01;200 +webhooksdev;deployments;nest;1;0,01;200 +webhooksqa;deployments;nest;0;0,01;200 +db;statefulsets;limecrabbitmqcluster-server;1;0,1;1024 +db;statefulsets;postgresql-1-postgresql;1;1,5;3000 +db;statefulsets;rabbitmqcluster-server;1;0,1;1024 +db16;statefulsets;postgresql-16-postgresql;1;0,1;100 +dbdev;statefulsets;limecrabbitmqcluster-server;1;0,1;1024 +dbdev;statefulsets;postgresql-1-postgresql;1;0,1;100 +dbdev;statefulsets;rabbitmqcluster-server;3;0,1;1024 +dbdev16;statefulsets;postgresql-16-postgresql;1;0,1;100 +dbqa;statefulsets;limecrabbitmqcluster-server;1;0,1;1024 +dbqa;statefulsets;postgresql-1-postgresql;0;0,1;100 +dbqa;statefulsets;rabbitmqcluster-server;1;0,1;1024 +dbqa16;statefulsets;postgresql-16-postgresql;1;0,1;100 +isdb;statefulsets;postgresql-1-postgresql;1;0,1;100 +isdbdev;statefulsets;postgresql-1-postgresql;0;0,1;100 +isdbqa;statefulsets;postgresql-1-postgresql;0;0,1;100 +mongodb;statefulsets;mongodb-replica-set;3;0,4;400 +mongodbdev;statefulsets;mongodb-replica-set;3;0,4;400 +mongodbqa;statefulsets;mongodb-replica-set;0;0,4;400 \ No newline at end of file diff --git a/st_size.xlsx b/st_size.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..33e42db2753fdbdd9f6bf12f4f4b1aa415eb3ba2 GIT binary patch literal 20121 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 etcd +gestione centralizzta log +-->idp portal - k8s Ui + log +idp portal - identificare legobrick +idp portal - formato file legobrick +idp portal - formato file architettura +idp portal - display architettura componente +idp portal - display palette legobrick +idp portal - funzioni UI di drag and drop +idp portal - comandi git di merge template repo +idp portal - remove componenti +idp portal - autenticazione/profilazione +idp portal - accesso log applicativi +Kyverno - installazione +Kyverno - predisposizione policy per taint autmatica edgenode +-->kubeedge - label/taint per progetto +git - gestione release + + +git tagging +curl -X 'POST' \ + 'https://git.italiadatacenter.com/api/v1/repos/STS_Lab/idcidp/tags?access_token=65fc5f07365bd3f89c5b9ddca3d2d2e7b3777a88' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "message": "string", + "tag_name": "v1.0", + "target": "ab3e33a214" +}' \ No newline at end of file diff --git a/utils.txt b/utils.txt new file mode 100644 index 0000000..f9dfcc8 --- /dev/null +++ b/utils.txt @@ -0,0 +1,59 @@ + +#master node + cd /etc/rancher/rke2/ + sudo systemctl restart rke2-server.service + journalctl -xeu rke2-server.service -f + +#worker node +sudo systemctl restart rke2-agent.service +sudo systemctl stop rke2-agent.service + +sudo systemctl start rke2-agent.service +journalctl -xeu rke2-agent.service -f +cat /var/lib/rancher/rke2/agent/logs/kubelet.log + + +#load balancer +/etc/haproxy/haproxy.cfg +sudo systemctl restart haproxy +journalctl -xeu haproxy.service -f +tail -f /var/log/haproxy.log +http://:8080/stats (admin/admin) + +Console rancher: https://rancher.pigreco66.it/ (AdminJapp0cam) + + +************Check risorse*************** +kubectl resource-capacity + + +kubectl create -n demo-apps -f - < /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