This project demonstrates simple build and deployment processes using GitHub Actions workflows. The deployed application is just a simple containerized web server that displays the environment name and project version number in the index.html file. Different environments are emulated using different ports on a target server using Docker containers for the deployed application/web server - one for each environment (DEV, QA, PROD).
- Git
- SSH client
- Docker and Docker Compose if you want to run the project locally
-
Fork and clone the repository:
git clone https://github.com/yourusername/your-repo.git cd your-repo -
Setup a public Ubuntu server:
- Create a new (simple and public) Ubuntu server (e.g. a simple Linode VM or DigitalOcean Droplet)
- Take note of the root password and the public IP address of the server
-
Copy setup files to the remote server:
- Use
scpto copy theubuntu-setup.shanddocker-compose.ymlfiles to the remote server:
scp env/ubuntu-setup.sh root@remote-server:/tmp scp env/docker-compose.yml root@remote-server:/tmp
- Use
-
Run the setup script on the remote server with root privileges:
ssh root@remote-server 'bash /tmp/ubuntu-setup.sh'The
ubuntu-setup.shscript will:- Change the default SSH port to 50022
- Disable SSH root login
- Disable SSH password authentication
- Permit key-based SSH authentication
- Create a new user
ubuntuwith sudo privileges (no password for sudo required) - Add the
ubuntuuser to thedockergroup - Generate SSH keys for the
ubuntuuser - Configure UFW rules
- Open port 50022 for SSH
- Open port 55000 for the Docker insecure registry
- Open port 58080 for the Registry UI
- Open port 59443 for Portainer
- Open port 40080 for the DEV environment
- Open port 40081 for the QA environment
- Open port 40082 for the PROD environment
- Install Docker and Docker Compose
- Call
docker-compose.ymlto set up the following Docker containers- Insecure Docker registry on port 55000
- Unsecured Registry UI on port 58080
- Portainer on port 59443
- Display the private SSH key for the
ubuntuuser and connection instructions
Point your browser tohttps://remote-server:59443to access Portainer and set the admin credentials. Note that Portainer is not secured with a signed certificate, so you will need to accept the browser warning. -
Set GitHub variables:
Name Value APP_NAMEdevops-test-appDEV_PORT40080PROD_PORT40082QA_PORT40081REGISTRY_URL[remote-host-IP]:55000REMOTE_HOST[remote-host-IP]SSH_PORT50022SSH_USERubuntuSTATUS_FAILFAILSTATUS_PASSPASSSTATUS_PENDINGPENDING -
Set GitHub secrets:
SSH_KEY: Private SSH key for theubuntuuser (copy from the setup script output)UAT: User Access Token for the GitHub repository (to trigger workflows with tag push from other workflows)
-
Trigger the workflows:
- Push to
mainto trigger the build (CI) workflow - The deployment (CD) workflow is triggered automatically after a successful build - will deploy, pending tests, the built version to the DEV environment
- If all tests succeed for any environment (DEV/QA), a success tag will trigger a promotion to the next environment (DEV -> QA, QA -> PROD).
- Note that the deployment script will prevent a version from being promoted to a higher environment if it has not been deployed to a lower environment first and all tests have passed in that environment.
- Push to
The build workflow (.github/workflows/build.yml) automates the process of building, tagging, and pushing Docker images.
- Record CI start time
- Print environment variables and context
- Checkout code
- Fetch all tags
- Calculate PATCH version and tag Git
- Push to the repo
- Set up Docker Buildx
- Configure Docker daemon for insecure registry
- Wait for Docker to restart and be available
- Build and push Docker image
- Trigger deploy (to DEV) workflow
-
Success Job:
- Tags the repository with a successful CI tag
- Creates a git notes object with CI details
-
Failure Job:
- Tags the repository with a failed CI tag
- Creates a git notes object with CI failure details
The deploy (promotion) workflow (.github/workflows/deploy.yml) automates the deployment process to different environments.
- Record CI start time
- Print environment variables and context
- Checkout code
- Fetch all tags
- Validate Deployment
- Determine external port
- Deploy to remote host via SSH
- Run all defined tests
- Tag the repo according to test results
- Update the devops notes JSON object for the promotion
- Trigger on specific tags
- Extract and validate tag information
- Checkout project at specified (version) tag
- Configure Git for pushing changes
- Remove any corresponding PENDING for test tag
- Check if all required tests for the environment are satisfied
- Tag environment as passed
- Trigger on environment/test passed tags
- Extract and validate tag information
- Checkout the project code at the specific tag
- Configure Git for pushing changes and fetching tags
- Remove any pending tags associated with the environment and test
- Check if all required tests for the environment are satisfied
- Tag the environment as passed if all tests are satisfied
- The workflow is triggered by a specific tag format that indicates that all tests have passed for an environment.
- It extracts and validates tag information, ensuring it matches the required pattern.
- If the tag is valid, the workflow proceeds to check out the code and configure Git.
- It removes any existing PENDING tags for the specified environment and test.
- The workflow then checks if all required tests for the environment have been satisfied.
- If all tests are satisfied, the environment is tagged as passed, and the tag is pushed to the repository.
- If the tag does not match the required pattern, or if not all tests are satisfied, the workflow exits gracefully without making any changes.
- SSH: Port 50022
- Portainer: Port 59443 (point browser to
https://remote-server:59443) - Docker Insecure Registry: Port 55000 (validate with e.g.
curl http://remote-server:55000/v2/_catalog) - Registry UI: Port 58080 (point browser to
http://remote-server:58080) - Custom Services:
- DEV: Port 40080 (point browser to
http://remote-server:40080) - QA: Port 40081 (point browser to
http://remote-server:40081) - PROD: Port 40082 (point browser to
http://remote-server:40082)
- DEV: Port 40080 (point browser to