diff --git a/docs/installation-guide.md b/docs/installation-guide.md index 97c51e4..167372c 100644 --- a/docs/installation-guide.md +++ b/docs/installation-guide.md @@ -1,61 +1,223 @@ # 🛠️ Installation Guide -This document describes how to deploy the management tool web app to a Kubernetes environment. +This document describes how to run the management tool web app using Docker and Kubernetes. --- ## Table of Contents -- [Prerequisites](#prerequisites) - - [Host Requirements](#host-requirements) - - [Software Requirements](#software-requirements) -- [Host Setup](#host-setup) - - [Prepare host environment](#prepare-host-environment) - - [Install CLI tools](#install-cli-tools) - - [Install k3s](#install-k3s) - - [Install ArgoCD](#install-argocd) -- [Git Repo Access](#git-repo-access) -- [Deploy App](#deploy-app) - - [App config values](#app-config-values) - - [deployment.yaml](#deploymentyaml) - - [Apply deployment](#apply-deployment) -- [Deploy Secrets](#deploy-secrets) - - [Secret generation tools](#secret-generation-tools) - - [Secret config values](#secret-config-values) - - [secrets.yaml](#secretsyaml) - - [Apply secrets](#apply-secrets) -- [Admin Account](#admin-account) - - [Add admin account](#add-admin-account) -- [Verification](#verification) +- [Host Requirements](#host-requirements) +- [Install using Docker](#install-using-docker) + - [Software Requirements](#software-requirements-docker) + - [Docker compose](#docker-compose) +- [Install to Kubernetes](#install-to-kubernetes) + - [Software Requirements](#software-requirements-kubernetes) + - [Host Setup](#host-setup) + - [Prepare host environment](#prepare-host-environment) + - [Install CLI tools](#install-cli-tools) + - [Install k3s](#install-k3s) + - [Install ArgoCD](#install-argocd) + - [Git Repo Access](#git-repo-access) + - [Deploy App](#deploy-app) + - [App config values](#app-config-values) + - [deployment.yaml](#deploymentyaml) + - [Apply deployment](#apply-deployment) + - [Deploy Secrets](#deploy-secrets) + - [Secret generation tools](#secret-generation-tools) + - [Secret config values](#secret-config-values) + - [secrets.yaml](#secretsyaml) + - [Apply secrets](#apply-secrets) + - [Admin Account](#admin-account) + - [Add admin account](#add-admin-account) + - [Verification](#verification) --- -## Prerequisites +## Host Requirements -### Host Requirements +| Component | Minimum Requirement | +| --------- | --------------------------------------------- | +| OS | Centos Stream 9 / Almalinux 9 / Rocky Linux 9 | +| CPU | 8 cores | +| Memory | 16 GB | +| Storage | 100 GB | +| Network | Public IP or DNS-resolvable hostname | -| Component | Minimum Requirement | -| --- | --- | -| OS | Centos Stream 9 / Almalinux 9 / Rocky Linux 9 | -| CPU | 4 cores | -| Memory | 8 GB | -| Storage | 50 GB | -| Network | Public IP or DNS-resolvable hostname | +--- -### Software Requirements +# Install using Docker + +## Software Requirements (Docker) | Software | Version | -| --- | --- | -| k3s | ≥ 1.33 | -| ArgoCD | ≥ 3.1 | -| Helm | ≥ 3.19 | -| kubeseal | ≥ 0.33 | +| -------- | ------- | +| Docker | ≥ 28.5 | + +## Docker compose + +Example of docker-compose.yml file: + +```yaml +x-db-env: &db-env + POSTGRES_DB: em_db + POSTGRES_USER: em_db_user + POSTGRES_PASSWORD: postgres-secret + +x-opensearch-env: &opensearch-env + OPENSEARCH_INITIAL_ADMIN_PASSWORD: opensearch-secret + +x-app-env: &app-env + EMGR_ADMIN_EMAIL: admin@example.com + EMGR_ADMIN_PASSWORD: secret + APP_ENV: prod + APP_URL: http://emgr.office.lan:6400 + BACKEND_URL: http://emgr.office.lan:6500 + DATABASE_HOST: db + OPENSEARCH_HOST: opensearch + VALKEY_HOST: valkey + UV_CACHE_DIR: /workspace/.uv-cache + +x-app-config: &app-config + image: git.abyres.net/elixier/manager:latest + restart: unless-stopped + depends_on: + init: + condition: service_completed_successfully + +services: + frontend: + <<: *app-config + container_name: emgr-frontend + environment: + <<: [*db-env, *opensearch-env, *app-env] + entrypoint: ["/bin/sh", "-c"] + command: + - | + export DATABASE_NAME=$$POSTGRES_DB + export DATABASE_USER=$$POSTGRES_USER + export DATABASE_PASSWORD=$$POSTGRES_PASSWORD + export OPENSEARCH_ADMIN_PASSWORD=$$OPENSEARCH_INITIAL_ADMIN_PASSWORD + uv run reflex run --env prod --frontend-only + ports: + - "6400:3000" + + backend: + <<: *app-config + container_name: emgr-backend + environment: + <<: [*db-env, *opensearch-env, *app-env] + entrypoint: ["/bin/sh", "-c"] + command: + - | + export DATABASE_NAME=$$POSTGRES_DB + export DATABASE_USER=$$POSTGRES_USER + export DATABASE_PASSWORD=$$POSTGRES_PASSWORD + export OPENSEARCH_ADMIN_PASSWORD=$$OPENSEARCH_INITIAL_ADMIN_PASSWORD + uv run reflex run --env prod --backend-only + ports: + - "6500:8000" + + init: + <<: *app-config + container_name: emgr-init + environment: + <<: [*db-env, *opensearch-env, *app-env] + entrypoint: ["/bin/sh", "-c"] + command: + - | + export DATABASE_NAME=$$POSTGRES_DB + export DATABASE_USER=$$POSTGRES_USER + export DATABASE_PASSWORD=$$POSTGRES_PASSWORD + export OPENSEARCH_ADMIN_PASSWORD=$$OPENSEARCH_INITIAL_ADMIN_PASSWORD + uv run reflex db migrate + uv run python cli.py init-opensearch + uv run python cli.py create-admin --email=$$EMGR_ADMIN_EMAIL --password=$$EMGR_ADMIN_PASSWORD + depends_on: + db: + condition: service_healthy + opensearch: + condition: service_healthy + restart: "no" + + db: + image: docker.io/postgres:17.4 + container_name: emgr-db + restart: unless-stopped + environment: + <<: *db-env + volumes: + - emgr-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d postgres"] + interval: 5s + timeout: 5s + retries: 10 + + valkey: + image: docker.io/valkey/valkey:8.0.2 + container_name: emgr-valkey + restart: unless-stopped + + opensearch: + image: docker.io/opensearchproject/opensearch:2.19.0 + container_name: emgr-opensearch + restart: unless-stopped + environment: + <<: *opensearch-env + discovery.type: single-node + OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m + volumes: + - emgr-opensearch:/usr/share/opensearch/data + healthcheck: + test: + [ + "CMD-SHELL", + "curl -k https://localhost:9200/_cluster/health || exit 1", + ] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + +volumes: + emgr-app: + driver: local + emgr-db: + driver: local + emgr-opensearch: + driver: local +``` + +- Change values in `x-db-env`, `x-opensearch-env` & `x-app-env` to actual, secure values. +- Make sure to use strong password for `OPENSEARCH_INITIAL_ADMIN_PASSWORD`, refer https://docs.opensearch.org/latest/security/configuration/demo-configuration/ +- Get image tag at https://git.abyres.net/elixier/-/packages/container/manager/ and replace 'latest' with the actual value at `x-app-config.image`. +- If you're changing the port number for frontend and backend at `APP_URL` and `BACKEND_URL`, make sure to also change at `services.frontend.ports` and `services.backend.ports`. + +Run web app: + +```bash +sudo docker compose up -d +``` + +The management tool should be accessible at `APP_URL`. --- +# Install to Kubernetes + +## Software Requirements (Kubernetes) + +| Software | Version | +| -------- | ------- | +| k3s | ≥ 1.33 | +| ArgoCD | ≥ 3.1 | +| Helm | ≥ 3.19 | +| kubeseal | ≥ 0.33 | + ## Host Setup ### Prepare host environment: + ```bash # Install dependencies dnf install tar -y @@ -74,6 +236,7 @@ firewall-cmd --reload ``` ### Install CLI tools: + ```bash # Install helm mkdir -p /tmp/helm @@ -92,11 +255,13 @@ rm -fr kubeseal* ``` ### Install k3s: + ```bash curl -sfL https://get.k3s.io | sh -s - ``` ### Install ArgoCD: + ```bash helm --kubeconfig=/etc/rancher/k3s/k3s.yaml install -n argocd --create-namespace argocd --version 8.6.1 --set server.service.type=NodePort oci://ghcr.io/argoproj/argo-helm/argo-cd ``` @@ -108,33 +273,38 @@ helm --kubeconfig=/etc/rancher/k3s/k3s.yaml install -n argocd --create-namespace Configure ArgoCD to access to this Git repository. 1. Generate SSH key pairs. + ```bash ssh-keygen -t rsa -b 4096 -C "root@hostname" ``` 2. Add SSH key to Gitea. - 1. Login to https://git.abyres.net. - 2. Go to the top right menu > Settings > SSH / GPG Keys. - 3. Click ‘Add Key’: + 1. Login to https://git.abyres.net. + 2. Go to the top right menu > Settings > SSH / GPG Keys. + 3. Click ‘Add Key’: - - Key Name: `hostname` - - Content: *(paste content from ~/.ssh/id_rsa.pub)* + - Key Name: `hostname` + - Content: _(paste content from ~/.ssh/id_rsa.pub)_ 3. Add Gitea as a known host in ArgoCD. - 1. Run this command: - ```bash - ssh-keyscan -p 30025 gitssh.abyres.net - ``` - 2. Copy the output, for example: - ``` - [gitssh.abyres.net]:30025 ssh-rsa … - ``` - 3. Login to ArgoCD in the k3s cluster. - 4. Go to Settings > Repository certificates and known hosts > Add SSH Known Hosts + 1. Run this command: - - SSH Known Host Data: *(paste ssh-keyscan output)* + ```bash + ssh-keyscan -p 30025 gitssh.abyres.net + ``` + + 2. Copy the output, for example: + + ``` + [gitssh.abyres.net]:30025 ssh-rsa … + ``` + + 3. Login to ArgoCD in the k3s cluster. + 4. Go to Settings > Repository certificates and known hosts > Add SSH Known Hosts + + - SSH Known Host Data: _(paste ssh-keyscan output)_ --- @@ -143,8 +313,7 @@ ssh-keygen -t rsa -b 4096 -C "root@hostname" ### App config values - IMAGE_TAG = `1.1.0-build.1866.768ef2c8` -- SSH_PRIVATE_KEY = *(paste content from ~/.ssh/id_rsa)* - +- SSH*PRIVATE_KEY = *(paste content from ~/.ssh/id*rsa)* ### deployment.yaml @@ -160,12 +329,11 @@ spec: sourceRepos: - ssh://git@gitssh.abyres.net:30025/elixier/manager.git destinations: - - namespace: '*' - name: '*' + - namespace: "*" + name: "*" server: https://kubernetes.default.svc --- - apiVersion: v1 kind: Secret metadata: @@ -182,7 +350,6 @@ stringData: --- - apiVersion: v1 kind: Secret metadata: @@ -199,7 +366,6 @@ stringData: --- - apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -207,9 +373,9 @@ metadata: namespace: argocd spec: generators: - - list: - elements: - - tag: + - list: + elements: + - tag: template: metadata: name: infrastructure @@ -291,7 +457,7 @@ spec: automated: enabled: true syncOptions: - - ServerSideApply=true + - ServerSideApply=true ``` ### Apply deployment @@ -312,11 +478,11 @@ k3s kubectl apply -f deployment.yaml ### Secret config values -- NODE_IP = 192.168.0.10 (*assumption) -- FERNET_KEY = *(generated using tool above)* -- JWT_SECRET_KEY = *(generated using tool above)* -- PRIVATE_PEM = *(generated using tool above)* -- PUBLIC_PEM = *(generated using tool above)* +- NODE_IP = 192.168.0.10 (\*assumption) +- FERNET*KEY = *(generated using tool above)\_ +- JWT*SECRET_KEY = *(generated using tool above)\_ +- PRIVATE*PEM = *(generated using tool above)\_ +- PUBLIC*PEM = *(generated using tool above)\_ ### secrets.yaml @@ -337,7 +503,6 @@ stringData: JWT_SECRET_KEY: --- - apiVersion: v1 kind: Secret metadata: @@ -372,12 +537,14 @@ You are required to create an initial global admin account in order to login to ### Add admin account 1. Get one of the running backend pods name using this command: + ```bash k3s kubectl get pods -n emgr -l app=backend # Example pod name = backend-784d796dd9-647pw ``` 2. Create the initial global admin: + ```bash k3s kubectl exec -it backend-784d796dd9-647pw -n emgr -- uv run cli.py create-admin --email=admin@example.com --password=adminpass123 ``` @@ -389,10 +556,13 @@ k3s kubectl exec -it backend-784d796dd9-647pw -n emgr -- uv run cli.py create-ad Assuming the NODE_IP = 192.168.0.10, the management tool components can be accessed at the following addresses: - frontend: + ``` http://192.168.0.10:30430 ``` + - backend: + ``` http://192.168.0.10:30480 -``` \ No newline at end of file +```