Skip to content

Commit

Permalink
feat: move out private site content leaving only template for aws-site
Browse files Browse the repository at this point in the history
  • Loading branch information
matihost committed Dec 7, 2024
1 parent bbfafa9 commit 51d7f8a
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 61 deletions.
12 changes: 7 additions & 5 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ run-name: >-
${{ inputs.runner && inputs.runner != 'ubuntu-24.04' && format(' on {0}', inputs.runner) || '' }}
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
# TODO disable until new module is confirming to work
#
# workflow_run:
# workflows: ["CI"]
# types: [completed]
# branches: [main]

workflow_dispatch:
inputs:
Expand Down Expand Up @@ -41,7 +43,7 @@ on:
module:
description: 'Module to deploy'
required: true
default: 'aws/aws-site'
default: 'aws/aws-rds'
permissions:
contents: read
id-token: write
Expand Down
67 changes: 20 additions & 47 deletions terraform/aws/aws-site/module/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mateusz Nowakowski</title>
<title>Profile</title>
<style>
body {
font-family: Arial, sans-serif;
Expand Down Expand Up @@ -150,87 +150,60 @@
border-radius: 5px;
margin: 5px 0;
}
a.drummer {
color: #333;
}
</style>
</head>
<body>
<header>
<h1>Mateusz Nowakowski</h1>
<h3>Leader / SRE / Product / Engineer / Architect</h3>
<h1>[Your Name]</h1>
<h3>[Your Title or Roles]</h3>
<div class="social-links">
<a href="https://github.com/matihost/monorepo" target="_blank" class="github">GitHub</a>
<a href="https://www.linkedin.com/in/matihost/" target="_blank" class="linkedin">LinkedIn</a>
<a href="[GitHub Link]" target="_blank" class="github">GitHub</a>
<a href="[LinkedIn Link]" target="_blank" class="linkedin">LinkedIn</a>
</div>
</header>

<div class="container">
<div class="profile">
<img src="matz.jpg" alt="Mateusz Nowakowski" width="200" height="200">
<img src="[Profile Image]" alt="[Your Name]" width="200" height="200">
<h1>Professional Summary</h1>
<p>Hello! I'm Mateusz Nowakowski, a Site Reliability Engineer (SRE) / Leader / Product / Engineer / Architect.
<p>20+ years of career with many generations of software/architecture/operations & leadership</p>
<p>In my spare time, I'm an amateur <a href="https://www.youtube.com/watch?v=-UYgORr5Qhg" class="drummer">drummer</a>, an amateur ultra marathon biker and a tech geek.</p>
<p>Checkout my <a href="https://github.com/matihost/monorepo">GitHub</a> and <a href="https://www.linkedin.com/in/matihost/">LinkedIn</a> profile for details!</p>
<p>Hello! I'm [Your Name], a [Your Role(s)].</p>
<p>[Brief description of your career or expertise]</p>
<p>In my spare time, I'm a [Your Hobbies].</p>
<p>Checkout my <a href="[GitHub Link]">GitHub</a> and <a href="[LinkedIn Link]">LinkedIn</a> profile for details!</p>
<div class="clear"></div>
</div>

<div class="skills">
<div>
<h2>Areas of Expertise</h2>
<ul>
<li>Automation (Terraform, Ansible, Helm, GitHub Actions, Jenkins etc.)</li>
<li>Google Cloud</li>
<li>AWS</li>
<li>IBM Cloud</li>
<li>Azure</li>
<li>Software Development (Java, Spring Boot, MQ, Python, GoLang etc.)</li>
<li>Kubernetes (GKE & OpenShift)</li>
<li>Linux</li>
<li>Scrum, Kanban</li>
<li>Product Management</li>
<li>Management 3.0</li>
<li>[Skill 1]</li>
<li>[Skill 2]</li>
<li>[Skill 3]</li>
<!-- Add more skills as necessary -->
</ul>
</div>
<div>
<h2>Certifications</h2>
<div class="certificates">
<div>
<a href="https://www.credly.com/badges/a4b652bf-2d61-4ab9-9485-b2468980068e/linked_in_profile" target="_blank" class="badge">
<img src="https://images.credly.com/size/340x340/images/0e284c3f-5164-4b21-8660-0d84737941bc/image.png" alt="AWS Certified Solutions Architect - Associate">
<p>AWS Certified Solutions Architect - Associate</p>
</a>
</div>
<div>
<a href="https://www.credly.com/badges/0b278876-1f68-4a30-863e-fdfa6af7d100/linked_in_profile" target="_blank" class="badge">
<img src="https://images.credly.com/size/340x340/images/690e65a7-2b6e-4f7d-a016-7c10b40a9ac0/F1002700_BADGE_EMBLEM_FILE_AArchSRE.png" alt="IBM Certified Advanced Architect - Cloud v2, IBM Certified Professional SRE - Cloud v2">
<p>IBM Certified Professional Architect - Cloud v6,<br/>IBM Certified Advanced Architect - Cloud v2,<br/>IBM Certified Professional SRE - Cloud v2,<br/>IBM Certified Professional Developer - Cloud v6</p>
</a>
</div>
<div>
<a href="https://rhtapps.redhat.com/certifications/badge/verify/I6L2PTXATL46UOGXBAIYJVF34AAEQU3CUPSQX2KSDXT6RW46LQ3T7ULZ55KZZ56SKO7EQ3ETTLYZQ4U5NQYTCNA62RUWOCM34WWBUYQ=" target="_blank" class="badge">
<img src="https://images.credly.com/size/340x340/images/572de0ba-2c59-4816-a59d-b0e1687e45ee/image.png" alt="Certificate 3">
<p>Red Hat Certified System Administrator (RHCSA)</p>
</a>
</div>
<div>
<a href="https://learn.microsoft.com/en-us/users/mateusznowakowski-3122/credentials/290f9e0f2977f830" target="_blank" class="badge">
<img src="https://learn.microsoft.com/media/learn/certification/badges/microsoft-certified-fundamentals-badge.svg" alt="Certificate 4">
<p>Microsoft Azure Fundamentals (AZ-900)</p>
<a href="[Certification Link]" target="_blank" class="badge">
<img src="[Certificate Image]" alt="[Certificate Name]">
<p>[Certificate Name]</p>
</a>
</div>
<!-- Repeat for more certifications -->
</div>
</div>
</div>
</div>

<footer>
<p>&copy; <span id="year"></span> Mateusz Nowakowski</p>
<p>&copy; <span id="year"></span> [Your Name]</p>
<script>
document.getElementById("year").textContent = new Date().getFullYear();
</script>
<p>This page is hosted almost for free. See my full source code <a href="https://github.com/matihost/monorepo/tree/main/terraform/aws/aws-site">here</a> for free!</p>
<p>This page is hosted almost for free. See the full source code <a href="[Source Code Link]">here</a>!</p>
</footer>
</body>
</html>
Binary file removed terraform/aws/aws-site/module/matz.jpg
Binary file not shown.
6 changes: 3 additions & 3 deletions terraform/aws/aws-site/stage/dev/terragrunt.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
locals {
dns = "matihost.mooo.com"
dns = "yoursite.mooo.com"
tls_crt = try(file("~/.tls/${local.dns}/cert.pem"), get_env("TLS_CRT", ""))
tls_chain = try(file("~/.tls/${local.dns}/chain.pem"), get_env("TLS_CHAIN", ""))
tls_key = try(file("~/.tls/${local.dns}/privkey.pem"), get_env("TLS_KEY", ""))
Expand All @@ -16,12 +16,12 @@ terraform {

inputs = {
env = "dev"
name = "matihost-site"
name = "yoursite-site"
dns = local.dns
# use false to expose only HTTP exposure from S3 directly
# when true, you has to have TLS certificates present in ~/.tls/DNS directory,
# run to generate one:
# make generate-letsencrypt-cert DOMAIN=www.matihost.pl
# make generate-letsencrypt-cert DOMAIN=www.yoursite.pl
enable_tls = true
tls_crt = local.tls_crt
tls_chain = local.tls_chain
Expand Down
6 changes: 3 additions & 3 deletions terraform/aws/aws-site/stage/prod/terragrunt.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
locals {
dns = "www.matihost.pl"
dns = "www.yoursite.pl"
tls_crt = try(file("~/.tls/${local.dns}/cert.pem"), get_env("TLS_CRT", ""))
tls_chain = try(file("~/.tls/${local.dns}/chain.pem"), get_env("TLS_CHAIN", ""))
tls_key = try(file("~/.tls/${local.dns}/privkey.pem"), get_env("TLS_KEY", ""))
Expand All @@ -16,12 +16,12 @@ terraform {

inputs = {
env = "prod"
name = "matihost-site"
name = "yoursite-site"
dns = local.dns
# use false to expose only HTTP exposure from S3 directly
# when true, you has to have TLS certificates present in ~/.tls/DNS directory,
# run to generate one:
# make generate-letsencrypt-cert DOMAIn=www.matihost.pl
# make generate-letsencrypt-cert DOMAIn=www.yoursite.pl
enable_tls = true
tls_crt = local.tls_crt
tls_chain = local.tls_chain
Expand Down
16 changes: 16 additions & 0 deletions terraform/gcp/ghost/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ ifndef CONTENT_KEY
endif
cd stage/$(ENV)/posts-management && terragrunt validate && terragrunt $(MODE_STR)

remove-all-posts: ## delete all posts, usage: make remove-all-posts ENV=dev
ifndef ENV
$(error Environment ENV is not defined. Usage make run ENV=dev [MODE=apply] [DEBUG=true])
endif
ifndef ADMIN_KEY
$(error Environment ADMIN_KEY is not defined. See https://ghost.org/docs/admin-api/javascript/#authentication how get one. Usage make run ENV=dev ADMIN_KEY=... CONTENT_KEY=... [MODE=apply] [DEBUG=true])
endif
ifndef CONTENT_KEY
$(error Environment CONTENT_KEY is not defined. See https://ghost.org/docs/admin-api/javascript/#authentication how get one. Usage make run ENV=dev ADMIN_KEY=... CONTENT_KEY=... [MODE=apply] [DEBUG=true])
endif
URL="$$(cd stage/$(ENV)/posts-management && terragrunt output -raw removeAllPostsURL 2>/dev/null)" && \
curl -m 70 -X POST "$${URL}" \
-H "Authorization: bearer $$(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d '{}'

google-authentication: prepare ## authenticate to GCP and prepare Application Default Credentials token (~/.config/gcloud/application_default_credentials.json) for Terraform usage
gcloud init
gcloud auth application-default login
Expand Down
70 changes: 70 additions & 0 deletions terraform/gcp/ghost/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Ghost Blog Deployment

Ghost blog deployment using Cloud Run, Cloud DB and GLB.

## Repository Structure

This Git repository is structured as follows:

* [module](module) - contains Terraform modules for Ghost deployment and Cloud Function for posts management.
* [stage](stage) - contains Terragrunt deployment configurations per environment (dev, prod)
* [docs](docs) - contains documentation including architecture, user guide, sre guide, etc. For PDF/HTML version of the documentation - see artifacts in Releases.
* [Makefile](Makefile) - entrypoint for SRE engineer to perform deployment

## Prerequisites

* Free Tier GCP Project
* [Compute Engine API enabled](https://console.cloud.google.com/apis/library/compute.googleapis.com) - needed to configure gcloud command fully, deployment does not use VM at all
* terragrunt, terraform, make, zip, gcloud - present on your machine, tested on Ubuntu 22.10
* (Optionally, but recommended) Enable remaining required GCP APIs. Deployments ensure that particular API is enabled first, but Google often claims that API is enabled, but later on deployment claims it is not yet, and several minutes waiting is really required that API is truly enabled on GCP side.
The list of required APIs: [Cloud Run](https://console.cloud.google.com/apis/library/run.googleapis.com), [SQL Component](https://console.cloud.google.com/apis/library/sql-component.googleapis.com), [SQL Admin](https://console.cloud.google.com/apis/library/sqladmin.googleapis.com), [Binary Authz](https://console.cloud.google.com/apis/library/binaryauthorization.googleapis.com), [CloudFunctions](https://console.cloud.google.com/apis/library/cloudfunctions.googleapis.com), [ArtifactRegistry](https://console.cloud.google.com/apis/library/artifactregistry.googleapis.com), [CloudBuild](https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com)
* Ensure you have DNS domain for [stage/dev/ghost/terragrunt.hcl#input.url](stage/dev/ghost/terragrunt.hcl). Change input.url parameter to meet DNS domain you wish site will be accessible from internet. I use free DNS subdomains from [https://freedns.afraid.org/](https://freedns.afraid.org/)

* Authenticate to GCP:

```bash
# create separate gcloud config configuration to not mess with your current config
gcloud config configuration create dev-ghost
# init your gcloud command, select us-central1-a as zone for example
make google-authentication
```

## Usage

* Deploy Ghost

```bash
# show Ghost Terraform deployment plan (development option)
# agree on bucket creation for Terraform state
make run-ghost ENV=dev MODE=plan

# when plan looks good, perform deployment
make run-ghost ENV=dev MODE=apply

# configure A record
# for stage/dev/ghost/terragrunt.hcl#input.url DNS domain
# with IP returned from the deployment output of: ghost_glb_public_ip
# in your DNS domain provider
```

* Deploy Post Management

* Go to your site configured here: [stage/dev/ghost/terragrunt.hcl#input.url](stage/dev/ghost/terragrunt.hcl)

* Follow [Ghost Authentication](https://ghost.org/docs/admin-api/javascript/#authentication) procedure to retrieve Admin API key and Content API key

```bash
# store your ADMIN_KEY and CONTENT_KEY as environment variables
# (add space so that this command will be not stored in your .bash_history)
export ADMIN_KEY='...'
export CONTENT_KEY='...'
# show Ghost Terraform deployment plan (development option)
# agree on bucket creation for Terraform state
make run-posts-management ENV=dev MODE=plan
# when plan looks good, perform deployment
make run-posts-management ENV=dev MODE=apply
# when you wish to remove all posts invoke:
make remove-all-posts ENV=dev
```
12 changes: 12 additions & 0 deletions terraform/gcp/ghost/docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
clean: ## clean build data
rm -rf target

docs: ## generate html/pdf from *.ad in target directory
@mkdir -p target
asciidoctor -r asciidoctor-diagram -b html -D target *.ad
asciidoctor -r asciidoctor-pdf,asciidoctor-diagram -b pdf -D target *.ad

help: ## show usage and tasks (default)
@eval $$(sed -E -n 's/^([\*\.a-zA-Z0-9_-]+):.*?## (.*)$$/printf "\\033[36m%-30s\\033[0m %s\\n" "\1" "\2" ;/; ta; b; :a p' $(MAKEFILE_LIST))
.DEFAULT_GOAL := help
.PHONY: help clean docs
44 changes: 44 additions & 0 deletions terraform/gcp/ghost/docs/architecture.ad
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
= Ghost GCP Deployment : Architecture
Matihost
:doctype: book
:reproducible:
:source-highlighter: rouge
:listing-caption: Listing
:math:
:data-uri:
:imagesoutdir: ../target/generated-images
:stem: latexmath
:toc: left


== Architecture Proposal

The proposed application architecture is mainly based on SAAS GCP services: CloudRun, Global Load Balancer, CloudArmor/WAF and CloudSQL.
The architecture is a highly scalable and available architecture that can handle a large number of users and requests while protecting against malicious traffic and attacks.


The following diagram shows main components and how they interact with each other:
[plantuml, target=minecraft-infra, format=png]
....
include::diagrams/architecture.puml[]
....

Here is how the architecture works:

The Ghost blogging application is packaged into a container image and mirrored from DockeHub registry to GCP artifact registry.

A CloudSQL instance is used for Ghost blog backend. CloudSQL in HA mode (3 zones) scales automatically based on the workload, ensuring that there is enough database capacity to handle the data storage and retrieval requirements.

A CloudRun service is created to run Ghost application in at least two distinct regions. That helps with DR and HA in case region or geographical outages. CloudRun also automatically scales the application based on the incoming traffic, ensuring that there is always enough capacity to handle the traffic.

A DNS record is created to direct traffic to the Global Load Balancer (or to WAF in front of GLB).

The Global Load Balancer is set up to distribute traffic to the CloudRun services deployed in different regions. It is configured to monitor the health of the instances and direct traffic to the instances that are closest geographically to the client and are healthy. CloudArmor is configured to protect the application from DDoS attacks. Optionally client may add other SAAS WAF solution in front of GCP (Imperva?).
Cloud CDN is enabled for faster images and static content retrieval as well.

Users can access the application by navigating to the DNS record. The Global Load Balancer will distribute the traffic to the appropriate CloudRun instance based on the user's location and the health of the instances. The CloudRun instances will connect to the CloudSQL database to retrieve and store data.

Per client request - CloudFunction is created to clean all posts on demand.


Overall, this architecture provides a highly scalable and highly available solution for running containerized applications with a managed database service in the cloud. It offers automatic scaling, redundancy, and reliability, ensuring that the application is always available and responsive to user requests.
Loading

0 comments on commit 51d7f8a

Please sign in to comment.