-
Notifications
You must be signed in to change notification settings - Fork 0
michaelvdang/wordle
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Current state of the app: - App is deployed on a Droplet at https://mikespace.xyz - Things to fix: - none WordList.db - all the 5 letter words in the dictionary stats.db - tables: users (id, username) games(user_id, game_id, finished, guesses, won) views: streaks, wins users.db - tables: users(guid, user_id, username) Answers.db: tables: Answers(word_id, gameword) Games(game_id, word_id) game1.db: tables: tables: games(guid, user_id, game_id, finished, guesses, won) streaks(user_id, streak, beginning, ending) wins(user_id, count(won)) Issues faced while developing app: - Understanding which process has permission and how to grant permission or elevate privilege, reason: couldn't delete some files or directory - Understanding that Jenkins doesn't have elevated privilege but the docker containers services do, and thus can create and manipulate files - Manage the credentials and keep them updated - Understanding where the project root is and where the current working directory is, also mapping between system and container START HERE: Hardware: - Get a server with static IP and Ubuntu 22.04 LTS - See Ubuntu server section for further instructions - Setup non-root user for security: https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04 - Setup basic firewall to limit access to server - This should be done after we've set up the backend so we can see how the firewall works - Networking>Firewall - Enable: SSH, HTTP, HTTPS CICD with Jenkins to Ubuntu:Jammy server: - Install Jenkins in Docker: https://www.jenkins.io/doc/book/installing/docker/#on-windows - Add credentials: with these credentialId's - .env: wordle-env-file - redis.conf: redis-conf-file - REDIS_AUTH_PASSWORD: redis-secret - server SSH key: AWS-EC2 - Github fine-grained PAT: github-token (but not necessary) - Debug: check docker logs of each container in addition to Jenkins Console Output Deployment: - Pull from git repo - git clone https://github.com/michaelvdang/wordle.git - cd wordle - git checkout docker - Install Docker - https://www.docker.com/products/docker or https://docs.docker.com/engine/install/ubuntu/ - Install npm - sudo apt install npm - Copy .env, redis.conf, and crontab.txt (using FileZilla) - Build and copy frontend code (maybe download npm and just build it on server) - place the wordle-frontend/dist/ folder in /wordle/wordle-frontend/ (including dist/) Deployment details: Deploy backend using Docker: - Docker Volume NOTE: using named volume in docker compose means that the volume will be managed by Docker (it won't be in the local ./var directory, but will create new .db files in the virtual ./var directory, the local ones, if they have .db files will not reflect what happens in the app when it is running) - Most of the above steps are done automatically using docker compose, except for 3 things: - Copying the .env file (was .gitignored so git clone doesn't work) - Copying the redis.conf file (also .gitignored) - Setting up crontab for TopTen leaderboard - During dev: run: python bin/TopTen.py manually from one of the containers (usually Orchestrator) - .env and redis.conf has passwords so they must be copied manually into root folder - Setting up crontab to update Leaderboard (probably best done manualy since it's outside of docker) - NOTE: RUN AS ROOT to get permissions to the docker volumes - Must install pip in order to install redis: apt install python3-pip - Create a venv: python3 -m venv .venv - Activate the venv: source .venv/bin/activate - bin/TopTen.py will query game1.db, game2.db, etc. to find the top 10 longest streaks and winners - Find where the .db files are: docker volume ls && docker volume inspect wordle_db - Make sure the path in TopTen.py matches the path in Mountpoint - install dependencies: python3 -m pip install -r cron-requirements.txt - create a file to store cron-fragments.txt (not necessary) - view running cron jobs: crontab -l (to list) -e (to edit) - put running cron jobs into crontab.txt - * * * * * /home/<USER>/wordle/.venv/bin/python /home/<USER>/wordle/bin/TopTen.py >> /home/<USER>/wordle/cron.log 2>&1 - Use the correct username for line above, also note if python's dependencies are installed in a virtual environment - run: sudo crontab crontab.txt (sudo to put crontab into root) - Rotate cron.log so it doesn't become too big: https://serverfault.com/questions/703757/how-to-rotate-a-log-file-from-crontab - You can now access the server at http://<IP_ADDRESS>:<PORT#> where <PORT#> can be any of the 9000, 9100, 9200, 9300, 9400 - However, the frontend is unlikely to work because its origin is a public IP address - We'll need to go to the front end Wordle component and note the APP_SERVER value (either 'local' or 'remote') - 'local' allows the front end to work locally during development, both React and the Docker backend must be on the same machine - 'remote' uses HTTPS, which means we must make the API public, meaning we'll need the following: - Create an A record that points orchestrator.api.mikespace.dev to the server - Configure NGINX to proxy requests to orchestrator.api.mikespace.dev to <SERVER_IP>:9400 - Get the SSL certificate: - Link: https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/ - Link: https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04 - Finally, rebuild the front end and make sure APP_SERVER is on 'remote' with the correct API links, place in the path specified by the root line from NGINX config file - 'orc': 'https://orchestrator.api.mikespace.dev' - 'stats': 'https://stats.api.mikespace.dev' Deploy frontend (NGINX outside Docker): (follow this guide - https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04) - Configure NGINX reverse proxy so front end can talk to the APIs - Copy the NGINX <website-name>.conf file to /etc/nginx/sites-available/ - Create a symlink: ln -s /etc/nginx/sites-available/mikespace.dev.conf /etc/nginx/sites-enabled/mikespace.dev - Restart NGINX to take effect: sudo systemctl restart nginx OR sudo nginx -s reload - Not necessary but you can see how DNS works by adding an A record in Networking>Domains - have stats.api.mikespace.xyz point to the server - configure NGINX to proxy requeststo stats.api.mikespace.xyz to <SERVER_IP>:9000 - The APIs are now available at http://stats.api.mikespace.xyz, etc. - Go to Let's Encrypt: https://letsencrypt.org/getting-started/ to get TLS certificates - The above steps also configured the location of the dist/ folder: /usr/share/nginx/html - Build the app on local machine: npm run build - SFTP to the server using FileZilla and upload the dist/ content to /var/www/html - NOTE: for NGINX, put the dist/ content in /usr/share/nginx/html - The page should be viewable after a few seconds Deploy frontend (NGINX in Docker): - Build app on local machine: npm run build - SFTP to server using FileZilla and upload the dist/ content to /wordle/wordle-frontend/dist - Start Docker compose Ubuntu server (Digital Ocean Droplet): - On local machine, create ssh key-pair, go to <user>/.ssh: ssh-keygen - If you don't have access to server thru SSH, go to droplet's control panel and open Access>Droplet Console - On server, find .ssh/ folder: ls -al - Run: nano .ssh/authorized_keys - Add the public key to the file - Save and quit - Make sure .ssh/ has 700 permissions and authorized_keys has 600 permissions - Back to local machine, run: ssh -i <key file> <user>@<droplet IP> FileZilla setup: - This will use the same SSH key that is used to SSH into server - Edit>Settings...>SFTP: add SSH private key in 'Add key file' - Save and quit - File>Site Manager>New site: - Protocol: SFTP - Host: (IP or domain name) - Logon Type: Interactive - User: root - Connect Manual installation: Running on bare Linux: How to start the application: - Install all dependencies, some of the required modules are, but there could be others: - apt install python3-pip - venv: apt install python3.10-venv - Create virtual environment and use it - python3 -m venv .venv - chmod u+rwx .venv/bin/activate - source .venv/bin/activate - Install python dependencies - pip install Faker - python redis module: pip install redis - Redis server: sudo apt install redis-server - Redis-cli: apt install redis-tools - python httpx module: pip install httpx - uvicorn: pip install uvicorn - fastapi: pip install fastapi - pydantic_settings: pip install pydantic-settings - sqlite3: apt install sqlite3 - (other modules) - Download traefik to the application's root folder - LINK: https://github.com/traefik/traefik/releases/download/v2.10.4/traefik_v2.10.4_linux_amd64.tar.gz - tar -xvzf traefik_v2.10.4_linux_amd64.tar.gz - Install foreman - apt install ruby-rubygems - gem install foreman - Once all dependencies are installed, initialize the databases - open Terminal in the application's root folder: bin/init.sh - Start the application server: - in Terminal: - foreman start -m 'stats=3,check=1,validate=1,play=1,orchestratorb=1,traefik=1' - Add a cronjob to update Redis every 10 minutes - needs patience because it quietly fails (doesn't print to the current terminal): - NOTE: Run crontab as root because of permissions to the docker volumes - run: crontab -e - Enter: '*/10 * * * * /home/<USER>/wordle/.venv/bin/python3 /home/michael/wordle/bin/TopTen.py >> /home/<USER>/wordle/cron.log 2>&1' - NOTE: in DO Ubuntu Droplet, the 'Home' directory is /root for the root user, so the full file path for this is /root/wordle/bin/TopTen.py - NOTE: python's dependencies might be install within a virtual environment, be sure to include it in the path to call python3 - Also: chmod 744 bin/TopTen.py (has been added to Dockerfile for Orchestrator) - Save and quit editor - Redis: - Security: - We must set password to use ACL because the port is open to WAN, anyone can access it - Also, protected mode protects from non-localhost users but we still have to set a password anyway or otherwise we'd lose access - See list of ACL users: ACL LIST - More commands: https://redis.io/commands/acl-setuser/ - But the way to go is by using redis.conf file, (or an external ACL file) - https://redis.io/docs/management/config-file/ - To use redis.conf in docker compose: - services > redis: - command: redis-server /etc/redis.conf - volumes: - ./redis.conf:/etc/redis.conf - Data persistence: - Data is wiped from memory between restarts, but redis periodically dumps data to a file, just give it a volume like: - ./var/redis:/data - But this way we can lose a couple minutes' worth of data in case of an outage. so use Append Only File in redis.conf: - appendonly yes Project 4: NOTE: the .sql files are not used but kept for learning purposes Install Redis if you haven't done it, the Play service requires it Initialize the databases: (adjust num of stats and users in stats.py for speed) - run: bin/init.sh - What previous line did: Initialize the Answers.db and WordList.db databases: - WordList.db contains the valid five-letter words - Answers.db contains the answers and their word_id Initialize stats.db database: - create a database with 1 million stats and 100k users - run: python3 bin/stats.py - NOTE: this will create a ./var/stats.db file which the program uses, if you have a ./var/sqlite3-populated.db file, please change it to ./var/stats.db The program won't work without ./var/stats.db Shard the database into 1 users and 3 games shards with uuid as PRIMARY KEY: - run: python3 bin/shard.py Install traefik in the root folder of project Run the Procfile with services and start traefik reverse proxy/load balancing: - run: foreman start -m 'stats=3,check=1,validate=1,play=1,traefik=1' Add a cronjob to update Redis every 10 minutes: - run: crontab -e - Enter: '*/10 * * * * /usr/bin/python3 /{path to project folder}/bin/TopTen.py >> /{Path to project folder}/cron.log 2>&1' - Save and quit editor Project 3 team members: NOTE: the .sql files are not used but kept for learning purposes - run: bin/init.sh Initialize the Answers.db and WordList.db databases: - WordList.db contains the valid five-letter words - Answers.db contains the answers and their word_id Install traefik in the root folder of project Initialize stats.db database: - create a database with 1 million stats and 100k users - run: python3 bin/stats.py - NOTE: this will create a ./var/stats.db file which the program uses, if you have a ./var/sqlite3-populated.db file, please change it to ./var/stats.db The program won't work without ./var/stats.db Shard the database into 1 users and 3 games shards with uuid as PRIMARY KEY: - run: python3 bin/shard.py Start traefik reverse proxy/load balancing: - run: ./traefik --configFile=traefik.toml Run the Procfile with 3 users stats service, 1 check and 1 validate service: - run: foreman start -m 'stats=3,check=1,validate=1,play=1' The services and their documentation are located at the following addresses: - stats: localhost:9000/api/v1/ - check: localhost:9100/api/v1/ - validate: localhost:9200/api/v1/ - play: localhost:9300/api/v1/ Using the API - The best way to see how the API works is with the automatic documentation provided by FastAPI - Navigate to the microservice using the link provided by foreman in a browser - add '/docs' after the link in browser - The available functions are listed along with the required parameters - You can enter some values to see what gets returned for each function There are 2 microservices: WordValidation - Validate a word against a database of valid words - Add a valid word to database - Remove a word from database of valid words WordCheck - Check a word against the answer - Change the answer of a game Project 3: New microservice added: Stats - Posts a win or loss for a particular game along with timestamp and number of guesses - Retrieving the statistics for a user - Retrieving top 10 users by number of wins - Retrieving top 10 users by longest streak
About
Implementation of Wordle backend from scratch
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published