diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 6fe074b9..d34937ae 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -22,36 +22,25 @@ env: jobs: - build: - + build-ui: runs-on: ubuntu-latest permissions: contents: read packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. id-token: write - steps: - name: Checkout repository uses: actions/checkout@v4 - # Install the cosign tool except on PR - # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 with: cosign-release: 'v2.2.4' - # Set up BuildKit Docker container builder to be able to build - # multi-platform images and export cache - # https://github.com/docker/setup-buildx-action - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - # Login against a Docker registry except on PR - # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 @@ -60,22 +49,17 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action + - name: Set version (SemVer) + id: set_version + run: echo "VERSION=v${{ github.run_number }}" >> $GITHUB_ENV + - name: Extract Docker UI metadata id: meta-ui uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_UI }} + tags: ${{ env.VERSION }} - - name: Extract Docker Ansible metadata - id: meta-ansible - uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ANSIBLE }} - - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action - name: Build and push UI Docker image id: build-and-push-ui uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 @@ -87,38 +71,80 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Sign the published UI Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + TAGS: ${{ steps.meta-ui.outputs.tags }} + DIGEST: ${{ steps.build-and-push-ui.outputs.digest }} + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + + build-ansible: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + with: + cosign-release: 'v2.2.4' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set version (SemVer) + id: set_version + run: echo "VERSION=v${{ github.run_number }}" >> $GITHUB_ENV + + - name: Extract Docker Ansible metadata + id: meta-ansible + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_ANSIBLE }} + tags: ${{ env.VERSION }} + - name: Build and push Ansible Docker image id: build-and-push-ansible uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: context: ansible/. push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta-ui.outputs.tags }} - labels: ${{ steps.meta-ui.outputs.labels }} + tags: ${{ steps.meta-ansible.outputs.tags }} + labels: ${{ steps.meta-ansible.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --force to cosign below. - # https://github.com/sigstore/cosign - - name: Sign the published UI Docker image - if: ${{ github.event_name != 'pull_request' }} - env: - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TAGS: ${{ steps.meta-ui.outputs.tags }} - DIGEST: ${{ steps.build-and-push-ui.outputs.digest }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} - - name: Sign the published Ansible Docker image if: ${{ github.event_name != 'pull_request' }} env: - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable TAGS: ${{ steps.meta-ansible.outputs.tags }} DIGEST: ${{ steps.build-and-push-ansible.outputs.digest }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + + release: + needs: [build-ui, build-ansible] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: Create GitHub Release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.VERSION }} + release_name: Release ${{ env.VERSION }} + draft: false + prerelease: false \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index e74c4717..00000000 --- a/Makefile +++ /dev/null @@ -1,125 +0,0 @@ -PWD := $(shell pwd) -TERRAFORM_DIR := $(PWD)/terraform -ANSIBLE_DIR := $(PWD)/ansible -SSH_KEY := $(ANSIBLE_DIR)/ssh/key -TF_VARS_FILE := $(TERRAFORM_DIR)/variables.tfvars -PLAYBOOK_PAAS := $(ANSIBLE_DIR)/playbooks/paas/main.yml -PLAYBOOK_IMAGE := $(ANSIBLE_DIR)/playbooks/saas/image.yml -PLAYBOOK_DEPLOY := $(ANSIBLE_DIR)/playbooks/saas/main.yml -PLAYBOOK_OPERATE := $(ANSIBLE_DIR)/playbooks/saas/operate.yml - -.PHONY: all init terraform-init ansible-init check-vars check-ssh - -all: init - -init: terraform-init ansible-init - -terraform-init: check-ssh check-vars - @echo "Initializing Terraform..." - cd $(TERRAFORM_DIR) && terraform init - cd $(TERRAFORM_DIR) && terraform apply --var-file=$(TF_VARS_FILE) - -ansible-init: - @echo "Installing Ansible collections..." - cd $(ANSIBLE_DIR) && ansible-galaxy collection install -r requirements.yml -p ./collections - @echo "Running Ansible playbook..." - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_PAAS) - cd $(ANSIBLE_DIR) && mkdir group_vars host_vars - -image: - @echo "Build a new docker image" - @echo "Software name: $(software)" - @if [ -z "$(software)" ]; then \ - echo "Error: You must specify a software name using 'make image software='"; \ - exit 1; \ - fi - @echo "Instance name: $(instance)" - @if [ -z "$(instance)" ]; then \ - echo "Error: You must specify an instance name using 'make image instance='"; \ - exit 1; \ - fi - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_IMAGE) -e "software=$(software)" -l ${instance} - -deploy: - @echo "Deploy (or redeploy) a software" - @echo "Instance name: $(instance)" - @if [ -z "$(instance)" ]; then \ - echo "Error: You must specify an instance name using 'make deploy instance='"; \ - exit 1; \ - fi - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_DEPLOY) -l ${instance} - -operate: - @echo "Operate on a software" - @echo "Domain name: $(domain)" - @if [ -z "$(domain)" ]; then \ - echo "Error: You must specify a domain name using 'make operate domain='"; \ - exit 1; \ - fi - @echo "Software name: $(software)" - @if [ -z "$(software)" ]; then \ - echo "Error: You must specify a software name using 'make operate software='"; \ - exit 1; \ - fi - @echo "Instance name: $(instance)" - @if [ -z "$(instance)" ]; then \ - echo "Error: You must specify an instance name using 'make operate instance='"; \ - exit 1; \ - fi - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_OPERATE) -e "domain=$(domain)" -e "software=$(software)" -l ${instance} - -check-ssh: - @echo "Checking SSH key..." - @if [ ! -f $(SSH_KEY) ]; then \ - echo "SSH key not found. Generating a new one..."; \ - ssh-keygen -t rsa -b 4096 -P '' -f $(SSH_KEY); \ - else \ - echo "SSH key already exists. Skipping generation."; \ - fi - -check-vars: - @echo "Checking Terraform variable file..." - @if [ ! -f $(TF_VARS_FILE) ]; then \ - echo "Error: Terraform variable file '$(TF_VARS_FILE)' not found!"; \ - exit 1; \ - fi - -ansible-test-deploy: image - @echo "Testing a software lifecycle with Ansible" - @echo "Domain name: $(domain)" - @if [ -z "$(domain)" ]; then \ - echo "Error: You must specify a domain name using 'make ansible-test domain='"; \ - exit 1; \ - fi - @echo "Software name: $(software)" - @if [ -z "$(software)" ]; then \ - echo "Error: You must specify a software name using 'make ansible-test software='"; \ - exit 1; \ - fi - @echo "Instance name: $(instance)" - @if [ -z "$(instance)" ]; then \ - echo "Error: You must specify an instance name using 'make ansible-test instance='"; \ - exit 1; \ - fi - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_DEPLOY) -e "domain=$(domain)" -e "software=$(software)" -e "confirmation=yes" -l ${instance} - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_OPERATE) -e "domain=$(domain)" -e "software=$(software)" -e "task=backup" -l ${instance} - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_OPERATE) -e "domain=$(domain)" -e "software=$(software)" -e "task=restore" -l ${instance} - -ansible-test-destroy: - @echo "Testing a software lifecycle with Ansible" - @echo "Domain name: $(domain)" - @if [ -z "$(domain)" ]; then \ - echo "Error: You must specify a domain name using 'make ansible-test domain='"; \ - exit 1; \ - fi - @echo "Software name: $(software)" - @if [ -z "$(software)" ]; then \ - echo "Error: You must specify a software name using 'make ansible-test software='"; \ - exit 1; \ - fi - @echo "Instance name: $(instance)" - @if [ -z "$(instance)" ]; then \ - echo "Error: You must specify an instance name using 'make ansible-test instance='"; \ - exit 1; \ - fi - cd $(ANSIBLE_DIR) && ansible-playbook $(PLAYBOOK_OPERATE) -e "domain=$(domain)" -e "software=$(software)" -e "task=destroy" -l ${instance} diff --git a/README.md b/README.md index 14212c37..c1441863 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,63 @@ # Simple Stack -Welcome to the Simple Stack project! This project demonstrates just how easy it can be to self-host web applications without complexity. +![GitHub stars](https://img.shields.io/github/stars/your-repo/simple-stack?style=flat) +![License](https://img.shields.io/github/license/your-repo/simple-stack) -## Project Goals - -- Deploy cloud infrastructure with Terraform (or add onpremise servers) -- Manage your servers with Ansible -- Orchestrate your docker containers workload with Nomad - ---- - -## Project Structure - -The project consists of two main components: - -### **Terraform** - -A straightforward Terraform configuration to deploy a standalone cloud instance across multiple providers (e.g., OVH, Infomaniak, AWS, Scaleway). The objective is to minimize cloud resource usage and keep the deployment lightweight. - -### **Ansible** +**Simple Stack** is a reference implementation that shows how to self‑host modern web applications with minimal operational overhead. It combines Terraform for infrastructure, Ansible for configuration, and a lightweight UI for management. -A set of basic Ansible roles designed to configure and manage your server with minimal dependencies, reducing the need for frequent updates, limiting security vulnerabilities, and streamlining management. +## Philosophy -### Features +- **Simplicity first** – every component can be understood in a few minutes. +- **Transparency** – all code is declarative and version‑controlled. +- **Portability** – works on any major cloud provider or on‑premise hardware. +- **Extensibility** – add new services by extending the existing Ansible roles or Terraform modules. -**With this project, you get:** -- Simple container deployment using Ansible -- Basic container management with Ansible -- A lightweight control plane for orchestrating deployments -- Simple service discovery for your containers - ---- - -## Prerequisites - -You have two options for using this project: -1. **Direct Use**: Use this repository as-is to benefit from ongoing updates and minimal configuration. -2. **Fork and Contribute**: Fork this repository into your own GitHub namespace to add custom features. Feel free to contribute back to the community by submitting pull requests with your enhancements. - ---- - -## Supported Cloud Providers - -Currently, this code is compatible with **OVHcloud** and **Infomaniak** cloud providers, both offering OpenStack-based public cloud solutions. Be sure to download your OpenStack secrets file before beginning setup. +## Project Goals ---- +The project aims to give SREs and developers a solid, opinionated foundation for: -## Getting Started +- Managing and deploying cloud infrastructure with a mainstream IaC tool. +- Configuring servers using a widely adopted automation framework. +- Orchestrating the full application lifecycle with containers. +- Scaling up/down horizontally or vertically with proven patterns. -To initialize the project, run: +## Project Structure -```bash -make init -``` +The repository is organised into three logical layers: -This command will initialize both Terraform and Ansible to prepare your instance and deploy your containers. +### IaC – Terraform -## Build (or download) a docker image +Terraform modules provision a single compute instance on providers such as **OVHcloud**, **Infomaniak**, **AWS**, **Scaleway**, etc. The goal is to keep the footprint small while remaining provider‑agnostic. -Several Ansible roles are available in this repository (Traefik, nginx, php-fpm, WordPress, MariaDB). Some of these roles use the latest version from Docker Hub, while others need to be built directly on the server. +### Configuration – Ansible -```bash -make image -``` +Ansible roles install Docker, pull the required images and configure the host. Roles are deliberately minimal to reduce attack surface and maintenance effort. -## Deploy a software +### SaaS – UI -Once you have built your custom images, you can deploy one of them! +A small web UI (the `ui/` directory) lets you create projects, store all variables and trigger deployments without writing additional scripts. -```bash -make deploy -``` +## Features -Note: Ensure that an A record DNS entry points to your server. This step is not yet automated. +- One‑click container deployment via Ansible. +- Basic container lifecycle management. +- Lightweight control plane for orchestrating deployments. +- Self‑hosted web UI for project administration. -Once your domain will be deployed, a static yaml configuration file will be added on `host_vars//.yml`. You can adapt it to add : -- a domain alias -- a restricted list of IP address to whitelist -- a HTTP basic authentication +## Contributing +Contributions are welcome! Please: -## Operate on a software +- Fork the repository. +- Create a feature branch. +- Ensure code follows the existing style and passes `make lint`. +- Open a Pull Request with a clear description of the change. -Some applications may require maintenance tasks (such as backup, restoration, or restart), or may even need to be removed. This option covers those actions. +## License -```bash -make operate -``` +This project is licensed under the MIT License – see the [LICENSE](LICENSE) file for details. ---- +## Acknowledgements -Thank you for exploring Simple Stack! +Thanks to the open‑source community for the tools that make this stack possible: Terraform, Ansible, Docker and the many contributors of the underlying roles.