# MyJekyllBlog MyJekyllBlog is an all-in-one multi-user CMS and hosting platform for Jekyll blogs. Administrators can log into the web panel and configure hosted domains that users can host blogs under (i.e. hosted-blog.com). Users can log into the web panel and configure a blog on their own domain or a subdomain of the hosted domains (i.e. mycookingblog.hosted-blog.com). Once they have a blog, they can use the CMS features to create posts, upload media, and otherwise manage their blog. Whenever a user updates their blog, the blog is rebuilt and deployed the to webservers. Each update is a git commit, and a history panel allows users to restore their blog to any past state. MyJekyllBlog comes with a complete set of ansible roles to automate the installation. ## Meet The Servers This table shows a brief overview of the server types and their relationships. Additional descriptions for each server type follows the table. | Server | Description | Services | Talks To | | --------- | ------------------------------------ | ----------------- | --------------------- | | Panel | Runs customer-facing web interface | mjb.web, nginx | build, store | | Build | Runs site builders, deploys blogs | mjb.worker | store, webservers | | Store | Source of truth - Database, Gitea | postgresql, gitea | panel, build, certbot | | Certbot | Handles getting/updating SSL certs | mjb.certbot | store, webservers | | WebServer | Hosts customer blogs on the internet | nginx | certbot, build | ### Panel The Panel server hosts the web application that customers can use to provision blogs, publish articles, upload media and otherwise manage their blogs. Administrators can use it to check users/blogs on the system, run maintenance tasks, and configure some aspects of the system. ### Build The build server processes Jekyll git repositories into static websites and deploys the fully built website to the webservers for hosting. ### Store The store server hosts two database with postgresql. One database supports `MyJekyllBlog::DB` and another supports Minion. The Panel, Build, and Certbot servers all need access to these databases. The store server also hosts an installation of Gitea so that each Jekyll blog may have its own central git repository. The panel server will checkout and commit/push to this server. The build server will checkout the repository from this server for building. ### Certbot The CertBot server handles obtaining SSL certificates from Let's Encrypt and pushing them to the webservers. When HTTP challenges are used, `/.well-known/` is proxied from ALL webserver nodes to the certbot node and `--standalone` is used from the certbot node to obtain an SSL certificate. When DNS challenges are used, wildcard certificates may be obtained (and is recommended for hosts expecting many sub-domains to be made). The `/etc/letsencrypt` directory is synced with webserver nodes through rsync whenever new certificates are obtained. An administrator can update and sync SSL certificates from the admin panel. ### WebServer WebServers run nginx and host static content for Jekyll blogs. When a blog is provisioned, an SSL certificate will be requested for the site and an nginx configuration file will be created. The build servers will sync the blog content with webservers each time the blog is updated through the Panel. ## How does it work? ### Deploy a Jekyll blog ```mermaid sequenceDiagram Web Panel->>+Build Server: User update triggers build Build Server->>+Store Server: Request for updates Store Server-->>-Build Server: Latest repo updates sent back Build Server-->+Build Server: Builds static website content with the Jekyll builder Build Server-->+Web Server: Sync static website content ``` ## Installation Guide This guide will follow my process of installing the software to run on `mds-stage.com` and serve blogs on `mds-stage-blog.com`. If you follow along, you could have your own version up and running in a couple of hours. ### Designing The Network I need at least one of each server type. I will, however, use two WebServers and have one on the west coast and another on the east coast. I will need six servers, and I will name them `panel`, `store`, `build`, `certbot`, `web-west`, `west-east`. I want the database server running on `store` not to be exposed to the Internet at large, and I will use the private networking feature of my VPS provider to get private IPs for `panel`, `store`, `build`, and `certbot`. This means I also need to have all of those machines running in the same datacenter. Each machine should be running Debian 11. | Machine | Public IP | Private IP | Domain | | -------- | --------------- | --------------- | ------------------------- | | panel | 45.79.31.186 | 192.168.213.90 | panel.mjb-stage.com | | build | 45.33.25.211 | 192.168.188.226 | build.mjb-stage.com | | store | 69.164.204.212 | 192.168.216.75 | store.mjb-stage.com | | certbot | 96.126.122.198 | 192.168.163.105 | certbot.mjb-stage.com | | web-west | 173.255.249.43 | N/A | web-west.mjb-stage.com | | web-east | 173.255.225.48 | N/A | web-east.mjb-stage.com | Once I have these machines provisioned I lay out the information about them in the table above. I will need this information to begin writing the configuration file. For each of these machines, I update DNS records so that the domain maps to the public IP address for each machine. Additionally, I add two DNS records for `*.mds-stage-blog.com`, one A record with `173.255.249.43` and another A record with `173.255.225.48`. This maps all sub-domains of `mds-stage-blog.com` to the web servers so that they may serve the blogs to people on the Internet. People on the Internet will go to one or the other server. Before proceeding from this section, review the section checklist to ensure you have completed all item. | X | Section Checklist Items | | --- | --------------------------------------------------- | | [ ] | Provision machine for panel, build, store, certbot | | [ ] | Provision one or more machines for webservers | | [ ] | Create table with your machine information | | [ ] | Add DNS records for each machine | | [ ] | Add wildcard DNS record for each WebServer | ### Running The Installation With Ansible The installation process has been codified into a number of ansible roles that are included in this repository. I will need ansible installed so that I have access to the `ansible-playbook` command, and I will need to have SSH credentials to each of these machines so that ansible may install and configure the network. Clone this ```bash cd devops/ansible/ mkdir -p env/stage cp env/example/inventory.yml env/stage/inventory.yml vim env/stage/inventory.yml ``` I named the configuration file `env/stage/inventory.yml`, since this will be a staging environment. I placed this in its own directory because some environment specific files will be stored in the inventory directory, and keeping seperate directories will prevent file clobbering. One should pay special attention to go through this example config file and update it with details of their network. Once this is complete, the installation should be smooth sailing with ansible. I use the following command to get everything installed. ```bash ansible-playbook -i env/stage/inventory.yml site.yml ``` This command took about two and a half hours to complete, it should largely setup the whole platform across all of the machines. Before proceeding from this section, review the section checklist to ensure you have completed all item. | X | Section Checklist Items | | --- | --------------------------------------------------------- | | [ ] | Checked repo out to a machine with ansible and ssh access | | [ ] | Network specific ansible inventory file was created | | [ ] | Ansible runs through the entire playbook with no errors | ### Step 1: Machine Selection First, I should layout the servers. At least one panel, build, store and certbot server will be needed to run the platform. One or more webservers will be needed to serve blogs. These servers should all be Debian 11 machines. I will also need a machine to install from, which should have git, ansible, and SSH access to all of the other machines. I have choosen to lay out the machines as follows. The private IP addresses will be used to limit database access. | Machine | Public IP | Private IP | Domain | | -------- | --------------- | --------------- | ------------------------- | | panel | 45.79.91.170 | 192.168.134.89 | panel.myjekyllblog.net | | build | 173.255.209.214 | 192.168.202.60 | build.myjekyllblog.net | | store | 173.255.209.241 | 192.168.207.169 | store.myjekyllblog.net | | certbot | 104.200.24.149 | 192.168.210.55 | certbot.myjekyllblog.net | | web-west | 104.200.24.174 | N/A | web-west.myjekyllblog.net | | web-east | 45.79.171.182 | N/A | web-east.myjekyllblog.net | Each of these machines is now online, brought up on Linode with their default Debian 11 image. Next I will need to checkout the repository and update the configuration file. ```bash git clone ... cd devops/ansible/ mkdir -p env/staging cp config.example.yml env/staging/inventory.yml vim env/staging/inventory.yml ``` I named the configuration file `env/staging/inventory.yml`, since this will be a staging environment. I placed this in its own directory because some environment specific files will be stored in the inventory directory, and keeping seperate directories will prevent file clobbering. One should pay special attention to go through this example config file and update it with details of their network. Once this is complete, the installation should be smooth sailing with ansible. I use the following command to get everything installed. ```bash ansible-playbook -i env/staging/inventory.yml site.yml ``` This command took about two and a half hours to complete, it should largely setup the whole platform across all of the machines. ### Step 2: Manual Steps Now the ansible playbook has run successfully, and all of the machines are set up. During the installation process, an SSH keypair was created. The public key must be added to the Gitea user that was setup. This must be done through the Gitea web panel. 1. Login to Gitea on the store server, using the credentials for gitea user/pass from the inventory file. 2. Click the user drop down in the upper right 3. Click Settings from the drop down menu 4. Click "SSH / GPG Keys" 5. Click "Add Key" under "Manage SSH Keys" 6. Type a title 7. Paste the contents of env/staging/files/ssh/id\_rsa.pub 8. Click to add the key Once this is done, you'll need to create the mjb organization. 1. Click the + Plus button drop down 2. Click "New Organization" 3. Name the organization "mjb" 4. Click "Create Organization" Everything should be setup now. ### Step 3: Confirm It All Works! 1. Create a user account 2. Create a blog 3. Delete a post ## Development Guide ### MJB::Web Panel Development As root you will need to stop the MJB::Web app from running in production. ```bash systemctl stop mjb-web ``` As the manager user you can run the application in development mode. ```bash cd mjb/Web morbo ./script/mjb --listen http://127.0.0.1:8080 ``` Now it will automatically reload when you make changes to the libraries and templates. Additionally, it will show stack traces during crashes and debug information in your terminal. ### Jekyll You can run Jekyll by getting into a build server and running the following: ```bash alias jekyll="podman run -ti --rm -v .:/srv/jekyll -e JEKYLL_ROOTLESS=1 docker.io/jekyll/jekyll jekyll" ``` Once you've done that, `jekyll command` will work. ## Operations Guide