diff --git a/docs/docsite/rst/administration/backup_restore.rst b/docs/docsite/rst/administration/backup_restore.rst new file mode 100644 index 000000000..ed4e1cab3 --- /dev/null +++ b/docs/docsite/rst/administration/backup_restore.rst @@ -0,0 +1,208 @@ +.. _ag_backup_restore: + +************************ +Backup and Restore +************************ + +.. index:: + single: backup + single: restore + pair: backup; database + pair: restore; secret key + +Ascender is backed up through the Ascender Operator, which captures the database, the secrets that encrypt it, and the deployment configuration. `ascender-install `_ drives that process and copies the result off the cluster for you, so use it if you installed with it. + +What a backup contains +======================= + +A backup is three files: + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - File + - Contents + * - ``tower.db`` + - The PostgreSQL database, as a ``pg_dump`` custom-format archive + * - ``secrets.yml`` + - The secret key, admin password, database credentials, and any TLS, custom CA, and receptor secrets + * - ``awx_object`` + - The Ascender resource specification + +Project files are not included. Projects that sync from source control are fetched again after a restore, so they need nothing. Manual projects, whose files exist only on the volume, need backing up separately. + +The secret key in ``secrets.yml`` is what makes the database readable. Restore it alongside the database and stored credentials work; restore the database on its own against a new deployment and every credential in it is unreadable, with no way to recover them. + +Backing up with the installer +============================== + +Run the backup from the ``ascender-install`` directory, with the ``custom.config.yml`` you installed with:: + + ./setup.sh -b + +The files land in ``ascender_install_artifacts/backups/``, in a timestamped directory, with a ``current`` symlink pointing at the newest. Copy that directory somewhere off the machine, because nothing does it for you, and treat it as credentials when you do. + +See `Backup and restore `_ in the installer repository for the full procedure. + +.. note:: + + A backup needs a running deployment, because it executes commands inside a live task pod. Take them routinely rather than reaching for one after something has already broken. + +Restoring with the installer +============================= + +Restore reads from ``ascender_install_artifacts/backups/current``, so put the backup you want there first:: + + ./setup.sh -r + +.. warning:: + + Restoring is destructive. It scales the deployment down, deletes the PostgreSQL volume, and rebuilds the database from the backup. It also replaces the current administrator password with the one from the backup. There is no confirmation prompt. + +After a restore, confirm the pods come back and sign in with the administrator credentials from the backup, not the ones you were using before. If you are restoring into a disaster recovery deployment that should not run jobs, disable schedules before it starts reconciling. + +To restore onto a rebuilt or replacement machine, see `Restoring onto a rebuilt k3s node `_ in the installer repository, or `Restoring onto a new cluster`_ below. + +Backing up and restoring manually +=================================== + +To back up and restore by hand, create the operator's ``AWXBackup`` and ``AWXRestore`` resources yourself. This is what ``ascender-install`` does on your behalf, with the addition of copying the files off the cluster. + +Taking a backup +---------------- + +Create an ``AWXBackup`` naming the deployment to back up:: + + apiVersion: awx.ansible.com/v1beta1 + kind: AWXBackup + metadata: + name: ascender-backup-2026-08-28 + namespace: ascender + spec: + deployment_name: ascender-app + backup_storage_requirements: + requests: + storage: 20Gi + +Give each backup a distinct name, with the date in it. Re-applying an existing ``AWXBackup`` updates that resource rather than producing a new backup, and the resources stay in the namespace as the record of what you have. + +``deployment_name`` is the only required field. The operator creates a persistent volume claim named ``-backup-claim`` unless you point ``backup_pvc`` at one you made yourself. Set ``backup_storage_requirements``, because the default is empty and leaves the size to whatever your storage class does with an unset value. + +Apply it and wait for the operator to finish:: + + kubectl apply -f backup.yml + kubectl get awxbackup ascender-backup-2026-08-28 -n ascender -o yaml + +The backup is complete when ``status.backupDirectory`` and ``status.backupClaim`` are populated. The directory is timestamped, so the claim accumulates one per run. + +Copying a backup off the cluster +--------------------------------- + +The operator writes to the claim and stops there. To get the files out, run a pod that mounts the claim and copy from it:: + + kubectl run backup-access -n ascender --image=busybox:stable --restart=Never \ + --overrides='{"spec":{"containers":[{"name":"backup-access","image":"busybox:stable","command":["sleep","3600"],"volumeMounts":[{"name":"backup","mountPath":"/backups"}]}],"volumes":[{"name":"backup","persistentVolumeClaim":{"claimName":"ascender-app-backup-claim"}}]}}' + + BACKUP_DIR=$(kubectl get awxbackup ascender-backup-2026-08-28 -n ascender \ + -o jsonpath='{.status.backupDirectory}') + + kubectl cp ascender/backup-access:$BACKUP_DIR ./ascender-backup + kubectl delete pod backup-access -n ascender + +.. note:: + + A backup is a set of credentials. ``secrets.yml`` holds the secret key, the administrator password, the database credentials, and any TLS private keys, all recoverable in plain text. Once it is off the cluster it has none of the protection a Kubernetes secret gave it, so store it where you would store those credentials, restrict who can read it, and keep it out of source control and shared drives. + +Restoring from a backup resource +--------------------------------- + +Restore rebuilds the deployment from the backup, so remove the old one first. Confirm you have a copy of the backup off the cluster before you start. + +Find the database volume claim. Its name is derived from the PostgreSQL version and the deployment name, so read it rather than assuming it:: + + kubectl get pvc -n ascender + +Then delete the Ascender resource and that claim:: + + kubectl delete awx ascender-app -n ascender + kubectl delete pvc postgres-15-ascender-app-postgres-15-0 -n ascender + +.. warning:: + + Do not delete the namespace. The backup claim lives in it, so removing the namespace destroys the backup you are about to restore from. + +Then create an ``AWXRestore`` pointing at the backup:: + + apiVersion: awx.ansible.com/v1beta1 + kind: AWXRestore + metadata: + name: ascender-restore + namespace: ascender + spec: + deployment_name: ascender-app + backup_name: ascender-backup-2026-08-28 + +Naming ``backup_name`` works when the original ``AWXBackup`` resource is still in the cluster, because the operator reads the claim and directory from its status. + +Apply it and watch:: + + kubectl apply -f restore.yml + kubectl get pods -n ascender -w + +The operator applies the secrets first, then recreates the Ascender resource from the backed-up specification, then loads the database. Applying the secrets before the resource is what preserves the secret key, so the restored deployment can still read its own credentials. + +Restoring onto a new cluster +----------------------------- + +After losing a cluster, there is no ``AWXBackup`` resource to name, so point the restore at the files directly. + +Install the operator and create the namespace first, as described in :ref:`in_manual_install`. + +Create a claim to hold the backup. On a new cluster nothing has made one yet, so this is the same shape the operator would have created:: + + kubectl apply -n ascender -f - <<'EOF' + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: ascender-app-backup-claim + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + EOF + +Add ``storageClassName`` if the cluster has no default. Size it for the database dump, not the deployment. + +Copy the files onto the claim, which is the reverse of getting them off:: + + kubectl run backup-access -n ascender --image=busybox:stable --restart=Never \ + --overrides='{"spec":{"containers":[{"name":"backup-access","image":"busybox:stable","command":["sleep","3600"],"volumeMounts":[{"name":"backup","mountPath":"/backups"}]}],"volumes":[{"name":"backup","persistentVolumeClaim":{"claimName":"ascender-app-backup-claim"}}]}}' + + kubectl cp ./ascender-backup ascender/backup-access:/backups/ascender-restore + kubectl delete pod backup-access -n ascender + +Then name the claim and the directory instead of a backup:: + + apiVersion: awx.ansible.com/v1beta1 + kind: AWXRestore + metadata: + name: ascender-restore + namespace: ascender + spec: + deployment_name: ascender-app + backup_pvc: ascender-app-backup-claim + backup_dir: /backups/ascender-restore + +``backup_dir`` is an absolute path, because the operator mounts the claim at ``/backups`` and checks that the directory exists before it starts. The three files must sit directly inside it. + +Apply it the same way. The restored deployment takes its name from ``deployment_name`` and its configuration from the ``awx_object`` file in the backup, so it comes back as it was rather than as a fresh install. + +External databases +=================== + +.. warning:: + + Backup and restore cover the database the operator manages. If you set ``ASCENDER_PGSQL_HOST`` to use your own PostgreSQL server, restore stops without doing anything, and backup runs without capturing your database. Back up an external database with your own tooling, and keep ``secrets.yml`` from an Ascender backup alongside it so the secret key survives. diff --git a/docs/docsite/rst/administration/index.rst b/docs/docsite/rst/administration/index.rst index d746867ff..c59792b15 100644 --- a/docs/docsite/rst/administration/index.rst +++ b/docs/docsite/rst/administration/index.rst @@ -31,6 +31,7 @@ Likewise content in this guide can be removed or replaced if it applies to funct metrics performance secret_handling + backup_restore security_best_practices awx-manage configure_ascender diff --git a/docs/docsite/rst/administration/init_script.rst b/docs/docsite/rst/administration/init_script.rst index cc76e1da7..d0a72fa91 100644 --- a/docs/docsite/rst/administration/init_script.rst +++ b/docs/docsite/rst/administration/init_script.rst @@ -3,15 +3,15 @@ Starting, Stopping, and Restarting Ascender --------------------------------------------- -To install Ascender: https://github.com/ansible/awx-operator/tree/devel/docs/installation +To install Ascender, see :ref:`in_start`. .. these instructions will be ported over to here in the near future (TBD) -To migrate from an old Ascender to a new Ascender instance: https://github.com/ansible/awx-operator/blob/devel/docs/migration/migration.md +To migrate from an old Ascender to a new Ascender instance: https://github.com/ctrliq/ascender-operator/blob/devel/docs/migration/migration.md .. these instructions will be ported over to here in the near future (TBD) -To upgrade you Ascender instance: https://github.com/ansible/awx-operator/blob/devel/docs/upgrade/upgrading.md +To upgrade your Ascender instance, see :ref:`in_install`. .. these instructions will be ported over to here in the near future (TBD) @@ -21,7 +21,7 @@ To restart an Ascender instance, you must first kill the container and restart i .. these instructions will need to be fleshed out (TBD) -To uninstall you Ascender instance: https://github.com/ansible/awx-operator/blob/devel/docs/uninstall/uninstall.md +To uninstall your Ascender instance: https://github.com/ctrliq/ascender-operator/blob/devel/docs/uninstall/uninstall.md .. these instructions will be ported over to here in the near future (TBD) diff --git a/docs/docsite/rst/administration/instances.rst b/docs/docsite/rst/administration/instances.rst index a053326e4..e4f45a3a2 100644 --- a/docs/docsite/rst/administration/instances.rst +++ b/docs/docsite/rst/administration/instances.rst @@ -149,7 +149,7 @@ Below are sample values used to configure each node in a mesh ingress topology: - [hop node] -In order to create a mesh ingress for Ascender, see the `Mesh Ingress `_ chapter of the AWX Operator Documentation for information on setting up this type of topology. The last step is to create a remote execution node and add the execution node to an instance group in order for it to be used in your job execution. Whatever execution environment image used to run a playbook needs to be accessible for your remote execution node. Everything you are using in your playbook also needs to be accessible from this remote execution node. +In order to create a mesh ingress for Ascender, see the `Mesh Ingress `_ chapter of the Ascender Operator documentation for information on setting up this type of topology. The last step is to create a remote execution node and add the execution node to an instance group in order for it to be used in your job execution. Whatever execution environment image used to run a playbook needs to be accessible for your remote execution node. Everything you are using in your playbook also needs to be accessible from this remote execution node. .. image:: ../common/images/instances-job-template-using-remote-execution-ig.png :alt: Job template using the instance group with the execution node to run jobs. @@ -334,13 +334,13 @@ The example health check shows the status updates with an error on node 'one': Using a custom Receptor CA --------------------------- -Refer to the AWX Operator Documentation, `Custom Receptor CA `_ for detail. +Refer to the Ascender Operator documentation, `Custom Receptor CA `_ for detail. Using a private image for the default EE ------------------------------------------ -Refer to the AWX Operator Documentation on `Default execution environments from private registries `_ for detail. +Refer to the Ascender Operator documentation on `Default execution environments from private registries `_ for detail. Troubleshooting diff --git a/docs/docsite/rst/administration/performance.rst b/docs/docsite/rst/administration/performance.rst index a806ee767..0690ba011 100644 --- a/docs/docsite/rst/administration/performance.rst +++ b/docs/docsite/rst/administration/performance.rst @@ -206,7 +206,7 @@ Capacity planning for Operator based Deployments .. index:: pair: Operator; deployment -For Operator based deployments, refer to `Ansible AWX Operator documentation `_. +For Operator based deployments, refer to `Ascender Operator documentation `_. Monitoring Ascender diff --git a/docs/docsite/rst/administration/secret_handling.rst b/docs/docsite/rst/administration/secret_handling.rst index 73e6b8b15..dfc786bf9 100644 --- a/docs/docsite/rst/administration/secret_handling.rst +++ b/docs/docsite/rst/administration/secret_handling.rst @@ -37,38 +37,31 @@ Secret handling for operational use Ascender contains the following secrets used operationally: -- ``/etc/awx/SECRET_KEY`` +- ``-secret-key`` - A secret key used for encrypting automation secrets in the - database (see below). If the ``SECRET_KEY`` changes or is unknown, + database (see below). If the secret key changes or is unknown, no encrypted fields in the database will be accessible. -- ``/etc/awx/awx.{cert,key}`` +- The ingress TLS secret - - SSL certificate and key for the Ascender web service. A - self-signed cert/key is installed by default; the customer can - provide a locally appropriate certificate and key. + - SSL certificate and key for the Ascender web service, supplied + when the deployment is created. -- Database password in ``/etc/awx/conf.d/postgres.py`` and message bus - password in ``/etc/awx/conf.d/channels.py`` +- ``-postgres-configuration`` - - Passwords for connecting to Ascender component services + - Host, database name, and password for connecting to PostgreSQL -These secrets are all stored unencrypted on the Ascender server, as they are all needed to be read by the Ascender service at startup -in an automated fashion. All secrets are protected by Unix permissions, and restricted to root and the Ascender service user awx. +These are Kubernetes secrets in the namespace Ascender is deployed to, readable by the Ascender service account so the service can read them at startup. Restrict who can read secrets in that namespace to control access to them. -If hiding of these secrets is required, the files that these secrets are read from are interpreted Python. These files can be adjusted to retrieve these secrets via some other mechanism anytime a service restarts. +List them with ``kubectl get secrets -n ``. .. note:: If the secrets system is down, Ascender will be unable to get the information and may fail in a way that would be recoverable once the service is restored. Using some redundancy on that system is highly recommended. -If, for any reason you believe the ``SECRET_KEY`` Ascender generated for you has been compromised and needs to be regenerated, you can run a tool from the installer that behaves much like Ascender backup and restore tool. - -To generate a new secret key, run ``setup.sh -k`` using the inventory from your install. - -A backup copy of the prior key is saved in ``/etc/awx/``. +If you believe the secret key has been compromised, replacing it is not a routine operation. Everything already encrypted with it, meaning every stored credential, becomes unreadable the moment it changes. Plan to re-enter those credentials, and keep a copy of the old secret alongside a database backup taken before the change. See :ref:`ag_backup_restore`. Secret handling for automation use diff --git a/docs/docsite/rst/administration/troubleshooting.rst b/docs/docsite/rst/administration/troubleshooting.rst index 39b97b560..d27e79b2b 100644 --- a/docs/docsite/rst/administration/troubleshooting.rst +++ b/docs/docsite/rst/administration/troubleshooting.rst @@ -33,7 +33,7 @@ Error logging and extra settings Ascender server errors are streamed and not logged, however you may be able to pass them in on the Ascender spec file. -With ``extra_settings``, you can pass multiple custom settings via the ``awx-operator``. The parameter ``extra_settings`` will be appended to the ``/etc/tower/settings.py`` file and can be an alternative to the ``extra_volumes`` parameter. +With ``extra_settings``, you can pass multiple custom settings via the Ascender Operator. The parameter ``extra_settings`` will be appended to the ``/etc/tower/settings.py`` file and can be an alternative to the ``extra_volumes`` parameter. +----------------+----------------+---------+ | Name | Description | Default | diff --git a/docs/docsite/rst/common/setup-playbook.rst b/docs/docsite/rst/common/setup-playbook.rst deleted file mode 100644 index c153e05ab..000000000 --- a/docs/docsite/rst/common/setup-playbook.rst +++ /dev/null @@ -1,24 +0,0 @@ - -.. index:: - pair: installation script; playbook setup - single: playbook setup - pair: playbook setup; setup.sh - -Ascender setup playbook script uses the ``inventory`` file and is invoked as ``./setup.sh`` from the path where you unpacked the Ascender installer tarball. - -:: - - root@localhost:~$ ./setup.sh - - -The setup script takes the following arguments: - -- ``-h`` -- Show this help message and exit -- ``-i INVENTORY_FILE`` -- Path to Ansible inventory file (default: ``inventory``) -- ``-e EXTRA_VARS`` -- Set additional Ansible variables as key=value or YAML/JSON (i.e. ``-e bundle_install=false`` forces an online installation) -- ``-b`` -- Perform a database backup in lieu of installing -- ``-r`` -- Perform a database restore in lieu of installing (a default restore path is used unless EXTRA_VARS are provided with a non-default path, as shown in the code example below) - -:: - - ./setup.sh -e 'restore_backup_file=/path/to/nondefault/location' -r diff --git a/docs/docsite/rst/index.rst b/docs/docsite/rst/index.rst index 36e5d017c..6d92d28ef 100644 --- a/docs/docsite/rst/index.rst +++ b/docs/docsite/rst/index.rst @@ -3,6 +3,12 @@ Ascender Documentation Ascender helps teams manage complex multi-tier deployments by adding control, knowledge, and delegation to Ansible-powered environments. +.. toctree:: + :maxdepth: 2 + :caption: Install + + installation/index + .. toctree:: :maxdepth: 2 :caption: Get started diff --git a/docs/docsite/rst/installation/index.rst b/docs/docsite/rst/installation/index.rst new file mode 100644 index 000000000..ba9699b5c --- /dev/null +++ b/docs/docsite/rst/installation/index.rst @@ -0,0 +1,18 @@ +.. _in_start: + +===================== +Installing Ascender +===================== + +Deploy Ascender on Kubernetes, from a single-node evaluation to a production cluster. + +Ascender runs on Kubernetes and is managed by the `Ascender Operator `_. +Install it with `ascender-install `_, which provisions the cluster where supported, deploys the operator, and creates the Ascender resource from a single configuration file. + +.. toctree:: + :maxdepth: 2 + :numbered: + + quick_start_k3s + install + manual_install diff --git a/docs/docsite/rst/installation/install.rst b/docs/docsite/rst/installation/install.rst new file mode 100644 index 000000000..d07f4ac3c --- /dev/null +++ b/docs/docsite/rst/installation/install.rst @@ -0,0 +1,123 @@ +.. _in_install: + +************************* +Installation Guide +************************* + +.. index:: + single: installation + pair: installation; ascender-install + pair: installation; configuration + +`ascender-install `_ is the best way to deploy Ascender. It runs Ansible under the hood, driven by a single configuration file, and handles cluster provisioning where supported, the Ascender Operator, and the Ascender resource itself. + +To evaluate Ascender on a single machine, start with :ref:`in_quick_start_k3s`. + +What the installer does +======================== + +When you run ``./setup.sh``, it: + +- Installs its own dependencies, including ansible-core, the required collections, and the Python Kubernetes client +- Provisions the cluster, on K3s only, when you set ``kube_install`` +- Stops and disables ``firewalld``, unless you set ``firewalld_disable: false`` +- Deploys the Ascender Operator with Kustomize, pinned to ``ASCENDER_OPERATOR_VERSION`` +- Creates the namespace, the administrator password secret, and any TLS, custom CA, or external database secrets your configuration calls for +- Creates the Ascender resource that the operator reconciles into a running deployment + +Requirements +============= + +You run the installer from a machine with ``git``, ``openssl``, and root or sudo access, on x86_64. Supported hosts are Enterprise Linux 8 or 9 such as Rocky Linux, or Ubuntu or Debian 24 or 26. + +Unless the machine is also a cluster node, as it is when the installer builds a K3s cluster on it, this is only an administrative host. It runs the install and takes no part in the cluster afterwards, so a small VM is enough. Keep it around, along with the configuration file you used, because upgrades run from the same place. + +For EKS, GKE, and AKS you need Enterprise Linux 9 specifically, plus the relevant CLI tool authenticated. Ubuntu and Debian are not supported for those three. + +Getting started +================ + +Clone the installer:: + + git clone https://github.com/ctrliq/ascender-install.git + cd ascender-install + +Create ``custom.config.yml`` with the settings for your deployment. Do not copy ``default.config.yml`` as a starting point, as it carries settings for every platform. + +Install with:: + + sudo ./setup.sh + +Platforms +========== + +Eight platforms are supported. Each has its own guide in the installer repository, with a working sample configuration file alongside it. + +K3s is the only platform where the installer builds the cluster, with ``kube_install: true``. On the cloud platforms it can create the cluster through the provider's API when you set the matching ``*_CLUSTER_STATUS`` to ``provision``. Everywhere else, RKE2 included, the cluster must already exist before you run the installer. + +.. list-table:: + :widths: 20 55 25 + :header-rows: 1 + + * - Platform + - Notes + - Guide + * - K3s + - Single node, and the installer builds the cluster. The shortest path from a bare machine to a running Ascender + - `K3s `_ + * - RKE2 + - Single node or highly available. You build the cluster, then run the installer against it + - `RKE2 `_ + * - Amazon EKS + - Needs the AWS CLI configured as root and an ACM certificate covering your hostnames + - `EKS `_ + * - Google GKE + - Needs the ``gcloud`` CLI authenticated and a project to deploy into + - `GKE `_ + * - Azure AKS + - Needs the ``az`` CLI authenticated + - `AKS `_ + * - DKP + - Takes one extra setting, ``DKP_CLUSTER_NAME`` + - `DKP `_ + * - TKGI + - Follows the common configuration + - `TKGI `_ + * - OpenShift + - Follows the common configuration + - `OCP `_ + +To install onto a Kubernetes cluster you manage yourself, such as kubeadm, Rancher, or Tanzu, use ``k8s_platform: rke2``. It is the most general of the platform types and assumes the cluster is already running, with an ingress controller, a default storage class, and a kubeconfig at ``~/.kube/config`` readable by root. + +For a worked K3s example with a self-signed certificate, see :ref:`in_quick_start_k3s`. + +Upgrading +========== + +Pull the latest installer first so you pick up any fixes, then raise ``ASCENDER_VERSION``, and ``ASCENDER_OPERATOR_VERSION`` if it also changed, and re-run:: + + git pull + sudo ./setup.sh + +The upgrade is carried out by the Ascender Operator. See `Upgrading `_ for the detail, and `Uninstalling `_ to remove a deployment. + +.. note:: + + The operator and Ascender are released independently. Ascender releases are at `ctrliq/ascender `_ and operator releases at `ctrliq/ascender-operator `_. + +Configuration +============== + +``default.config.yml`` in the installer documents every available setting, and each platform guide above ships a working sample configuration alongside it. ``config_vars.sh`` will generate a configuration file through a series of prompts. + +At minimum you set ``k8s_platform``, ``ASCENDER_HOSTNAME``, and ``ASCENDER_ADMIN_PASSWORD``. Note that ``k8s_platform`` defaults to ``eks``, so set it explicitly even for K3s. + +.. warning:: + + ``ascender_garbage_collect_secrets`` defaults to ``true``, which deletes the Kubernetes secrets when the Ascender resource is removed. Those include the secret key that encrypts stored credentials, and without it an existing database is unreadable. Set it to ``false`` where preserving a deployment matters more than tidy cleanup. + +.. warning:: + + An external PostgreSQL password cannot contain special characters. This is a limitation of how the installer passes it through its configuration. + +The installer repository also documents `automation mesh `_, `changing hostnames `_, and `backup and restore `_. diff --git a/docs/docsite/rst/installation/manual_install.rst b/docs/docsite/rst/installation/manual_install.rst new file mode 100644 index 000000000..78b30bddc --- /dev/null +++ b/docs/docsite/rst/installation/manual_install.rst @@ -0,0 +1,197 @@ +.. _in_manual_install: + +********************************** +Installing without the installer +********************************** + +.. index:: + single: installation + pair: installation; Kustomize + pair: installation; operator + +`ascender-install `_ is the best way to install Ascender. It does considerably more for you than the steps below, including provisioning the cluster on K3s, so start there if you can. See :ref:`in_install`. + +This page covers deploying the operator and creating the Ascender resource by hand, for readers who manage their own cluster or deploy through GitOps tooling. + +The deployment is two steps: install the operator, then create an ``AWX`` resource for the operator to reconcile. + +Requirements +============= + +- A running Kubernetes cluster and a ``kubectl`` context pointing at it +- A default storage class, used for the database volume +- An ingress controller, if you want to reach Ascender by hostname +- ``kubectl`` 1.14 or later, which includes Kustomize + +Install the operator +===================== + +In an empty directory, create ``kustomization.yaml``, replacing ``25.5.1`` with the version you want from the `operator releases `_:: + + apiVersion: kustomize.config.k8s.io/v1beta1 + kind: Kustomization + resources: + - github.com/ctrliq/ascender-operator/config/default?ref=25.5.1 + + images: + - name: ghcr.io/ctrliq/ascender-operator + newTag: 25.5.1 + + namespace: ascender + +Apply it:: + + kubectl apply -k . + +.. note:: + + The ``ref`` pins the manifests and the ``newTag`` pins the image, and they are separate. Setting only the ``ref`` leaves the operator running whatever image tag the manifests carry, so keep both at the same version. + +This creates the namespace, the ``awxs``, ``awxbackups``, ``awxrestores``, and ``awxmeshingresses`` custom resource definitions, the service account and roles, and the controller deployment. Confirm the operator is running:: + + kubectl get pods -n ascender + +Create the administrator secret +================================ + +Supply the administrator password yourself so you know what it is. The operator generates one otherwise:: + + kubectl create secret generic ascender-app-admin-password -n ascender \ + --from-literal=password= + +The key must be ``password``. The operator looks for a secret named after the Ascender resource, so ``ascender-app-admin-password`` is found automatically for a resource named ``ascender-app``. + +Create the TLS secret +====================== + +Terminate TLS at the ingress with a certificate for the hostname you will use:: + + kubectl create secret tls ascender-tls-secret -n ascender \ + --cert=ascender.crt --key=ascender.key + +Create the Ascender resource +============================= + +The following is a working starting point for a cluster with an ingress controller. Save it as ``ascender.yml``:: + + apiVersion: awx.ansible.com/v1beta1 + kind: AWX + metadata: + name: ascender-app + namespace: ascender + spec: + image: ghcr.io/ctrliq/ascender + image_version: 25.5.1 + init_container_image: ghcr.io/ctrliq/ascender-ee + init_container_image_version: 25.5.1 + control_plane_ee_image: ghcr.io/ctrliq/ascender-ee:25.5.1 + postgres_image: quay.io/sclorg/postgresql-15-c9s + postgres_image_version: "latest" + redis_image: ghcr.io/valkey-io/valkey + redis_image_version: "9-alpine" + replicas: 1 + admin_user: admin + admin_password_secret: ascender-app-admin-password + service_type: ClusterIP + ingress_type: ingress + ingress_path: "/" + ingress_path_type: Prefix + ingress_class_name: nginx + ingress_tls_secret: ascender-tls-secret + hostname: ascender.example.com + postgres_data_volume_init: true + postgres_storage_class: + postgres_storage_requirements: + requests: + storage: 20Gi + extra_settings: + - setting: CSRF_TRUSTED_ORIGINS + value: + - https://ascender.example.com + +Apply it and watch the operator build the deployment:: + + kubectl apply -f ascender.yml + kubectl get pods -n ascender -w + +.. note:: + + ``postgres_data_volume_init`` sets ownership on the database volume with an init container. Many storage classes provision volumes owned by root, which the PostgreSQL container cannot write to. The operator's default commands already do the right thing, so you rarely need ``postgres_init_container_commands``. OpenShift manages volume ownership itself. + +``ingress_class_name`` and ``postgres_storage_class`` both fall back to the cluster default when unset. Name them explicitly if the cluster has more than one, or no default. List what is available with ``kubectl get ingressclass`` and ``kubectl get storageclass``. + +The resource accepts far more than this example uses. Every field is described in the custom resource definition, which you can read from the cluster:: + + kubectl explain awx.spec + kubectl explain awx.spec.postgres_storage_class + +The Ascender Operator repository documents individual topics in more depth under `advanced configuration `_, covering things like persisting the projects directory, security contexts, node assignment, and horizontal pod autoscaling. + +Set ``CSRF_TRUSTED_ORIGINS`` to the URL you actually reach Ascender on, scheme included. Without it, signing in can fail with a CSRF error even though the pods are healthy, because the scheme Django computes behind an ingress does not always match the one the browser sent. + +Pin ``image_version``. Left unset, the operator falls back to ``latest``, so the version you get depends on when the image was last pushed. + +Using an external database +=========================== + +By default the operator deploys and manages PostgreSQL. To point at an existing server, create a secret describing it and reference it from the spec with ``postgres_configuration_secret``:: + + apiVersion: v1 + kind: Secret + metadata: + name: ascender-app-postgres-configuration + namespace: ascender + stringData: + host: postgres.example.com + port: '5432' + database: ascenderdb + username: ascender + password: + sslmode: prefer + type: unmanaged + type: Opaque + +Apply it with ``kubectl apply -f postgres-secret.yml``. The database must already exist. When you supply this secret, omit ``postgres_data_volume_init`` and the initialization commands, which apply only to the managed database. + +Secrets the operator generates +=============================== + +Unless you supply them, the operator creates these secrets on first reconcile: + +.. list-table:: + :widths: 40 60 + :header-rows: 1 + + * - Secret + - Contents + * - ``ascender-app-secret-key`` + - The key that encrypts credentials stored in the database + * - ``ascender-app-admin-password`` + - Administrator password, if you did not create it yourself + * - ``ascender-app-postgres-configuration`` + - Connection details for the managed database + * - ``ascender-app-broadcast-websocket`` + - Shared secret used between web pods + * - ``ascender-app-receptor-ca`` + - Certificate authority for the automation mesh + * - ``ascender-app-receptor-work-signing`` + - Key used to sign mesh work units + +.. warning:: + + The secret key encrypts every credential Ascender stores. A database restored alongside a different secret key leaves those credentials unreadable, and there is no way to recover them. Capture these secrets somewhere safe before you need them, and keep them with any database backup you take. + +Verifying +========== + +The deployment is up when the task and web pods are running:: + + kubectl get pods -n ascender + +If pods do not start, the operator log usually says why:: + + kubectl logs -n ascender deployment/awx-operator-controller-manager + +Reconcile errors also surface on the resource itself:: + + kubectl describe awx ascender-app -n ascender diff --git a/docs/docsite/rst/installation/quick_start_k3s.rst b/docs/docsite/rst/installation/quick_start_k3s.rst new file mode 100644 index 000000000..1d7c8db7c --- /dev/null +++ b/docs/docsite/rst/installation/quick_start_k3s.rst @@ -0,0 +1,90 @@ +.. _in_quick_start_k3s: + +******************** +Local Quick Start +******************** + +.. index:: + single: installation + pair: installation; K3s + pair: installation; quick start + +Get Ascender running on a single machine. The installer builds a K3s cluster and deploys Ascender onto it, so you need nothing but the machine itself. + +Before you start +================= + +You need a machine with 2 CPUs, 8 GB memory, and 20 GB free disk, running Enterprise Linux 8 or 9 such as Rocky Linux, or Ubuntu or Debian 24 or 26, with root or sudo access. Install ``git`` and ``openssl`` if they are missing. + +Get the installer +================== + +:: + + git clone https://github.com/ctrliq/ascender-install.git + cd ascender-install + +Create a certificate +===================== + +Run this from the ``ascender-install`` directory so the installer finds the files. Replace ```` with your machine's address:: + + openssl req -x509 -newkey rsa:4096 -keyout ascender.key -out ascender.crt -days 365 -nodes \ + -subj "/CN=ascender..nip.io" \ + -addext "subjectAltName=DNS:ascender..nip.io" + +``nip.io`` is a wildcard DNS service, so ``ascender..nip.io`` resolves to your machine with no DNS setup. Use a real hostname for anything beyond evaluation. + +Configure +========== + +Create ``custom.config.yml`` alongside the certificate, replacing ```` with your machine's address and ```` with the password you want:: + + k8s_platform: k3s + k8s_lb_protocol: https + kube_install: true + download_kubeconfig: true + k3s_master_node_ip: "" + + tls_crt_path: "{{ playbook_dir }}/../ascender.crt" + tls_key_path: "{{ playbook_dir }}/../ascender.key" + + ASCENDER_HOSTNAME: ascender..nip.io + ASCENDER_NAMESPACE: ascender + ASCENDER_ADMIN_USER: admin + ASCENDER_ADMIN_PASSWORD: + ASCENDER_VERSION: 25.5.1 + ASCENDER_OPERATOR_VERSION: 25.5.1 + + ascender_setup_playbooks: true + +Set ``ASCENDER_ADMIN_PASSWORD`` before you install. + +Run the installer +================== + +The default ``inventory`` targets ``localhost``, so it needs no changes:: + + sudo ./setup.sh + +A successful run ends with ``ASCENDER SUCCESSFULLY SETUP``. + +Verify +======= + +The installer writes the kubeconfig for the user it ran as, so under ``sudo`` it belongs to root. Copy it to your own user first:: + + sudo cp /root/.kube/config ~/.kube/config + sudo chown $(id -u):$(id -g) ~/.kube/config + kubectl get pods -n ascender + +Open ``https://ascender..nip.io`` and sign in. Your browser warns about the self-signed certificate, which is expected. + +If pods do not reach ``Running``, the operator log usually says why:: + + kubectl logs -n ascender deployment/awx-operator-controller-manager + +Next +===== + +Work through the :ref:`Ascender Quickstart ` to run your first playbook. diff --git a/docs/docsite/rst/quickstart/quick_start.rst b/docs/docsite/rst/quickstart/quick_start.rst index 8e125ed1e..03d20e229 100644 --- a/docs/docsite/rst/quickstart/quick_start.rst +++ b/docs/docsite/rst/quickstart/quick_start.rst @@ -6,6 +6,6 @@ Quick Start Welcome to the Ascender Quick Start Guide. At the end of the Quick Start, you will have a functioning Ascender application that you can use to launch more sophisticated playbooks. You can expect the Quick Start process to take less than thirty minutes. -To begin, you must install Ascender and you must choose a target system where an initial playbook can be deployed (provided by Ascender). +To begin, you must install Ascender and you must choose a target system where an initial playbook can be deployed (provided by Ascender). If you do not have Ascender running yet, see :ref:`in_quick_start_k3s` to get a single-node deployment up, or :ref:`in_install` for the full installation guide. This first playbook executes simple Ansible tasks, while teaching you how to use Ascender, as well as ensuring its proper setup. This can be any sort of system manageable by Ansible, as described in the `Managed nodes `_ section of the Ansible documentation. diff --git a/docs/docsite/rst/upgrade_migration/upgrade_considerations.rst b/docs/docsite/rst/upgrade_migration/upgrade_considerations.rst index c8bb4d602..d60656274 100644 --- a/docs/docsite/rst/upgrade_migration/upgrade_considerations.rst +++ b/docs/docsite/rst/upgrade_migration/upgrade_considerations.rst @@ -19,11 +19,9 @@ Upgrade Planning This section covers changes that you should keep in mind as you attempt to upgrade your Ascender instance. -- Clustered upgrades require special attention to instance and instance groups prior to starting the upgrade. See `:ref:`ag_clustering` for details. +- Clustered upgrades require special attention to instance and instance groups prior to starting the upgrade. See :ref:`ag_clustering` for details. +- Back up the deployment before upgrading. See :ref:`ag_backup_restore`. +For the upgrade procedure itself, see :ref:`in_install`. -Running the Setup Playbook ----------------------------- - -.. include:: ../common/setup-playbook.rst diff --git a/docs/docsite/rst/userguide/overview.rst b/docs/docsite/rst/userguide/overview.rst index a1c78bae6..03f99bdcd 100644 --- a/docs/docsite/rst/userguide/overview.rst +++ b/docs/docsite/rst/userguide/overview.rst @@ -69,7 +69,7 @@ Backup and Restore .. index:: pair: features; backup and restore -The ability to backup and restore your system(s) has been integrated into the Ascender setup playbook, making it easy for you to backup and replicate your instance as needed. +Ascender is backed up and restored through the Ascender Operator, which captures the database, the secrets that encrypt it, and the deployment configuration. See :ref:`ag_backup_restore`. Ansible Galaxy Integration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~