{"id":17131309,"url":"https://github.com/s4ke/docker-lviv-2022","last_synced_at":"2025-10-12T05:31:29.127Z","repository":{"id":39700126,"uuid":"507121310","full_name":"s4ke/docker-lviv-2022","owner":"s4ke","description":null,"archived":false,"fork":false,"pushed_at":"2023-04-06T07:00:53.000Z","size":79,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-29T03:51:34.607Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s4ke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-24T19:22:39.000Z","updated_at":"2025-01-24T10:57:30.000Z","dependencies_parsed_at":"2023-02-12T18:45:43.806Z","dependency_job_id":null,"html_url":"https://github.com/s4ke/docker-lviv-2022","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4ke%2Fdocker-lviv-2022","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4ke%2Fdocker-lviv-2022/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4ke%2Fdocker-lviv-2022/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s4ke%2Fdocker-lviv-2022/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s4ke","download_url":"https://codeload.github.com/s4ke/docker-lviv-2022/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236169077,"owners_count":19106104,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:23:11.665Z","updated_at":"2025-10-12T05:31:23.780Z","avatar_url":"https://github.com/s4ke.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# docker-lviv-2022\n\nThis repository contains the code for the workshop at Lviv AI Summer School 2022 by [NeuroForge](https://neuroforge.de/)\n\n# Prerequisites\n\n## Optional: On your local machine: Install VSCode for remote development\n\nInstall VSCode:\n\nhttps://code.visualstudio.com/\n\nInstall the SSH Remote Development plugin:\n\nhttps://code.visualstudio.com/docs/remote/ssh\n\n## On your remote machine: Install Docker\n\nhttps://docs.docker.com/engine/install/ubuntu/\n\n# Notes\n\nFirst, check if Python is installed on your system:\n\n```bash\npython3 --version\n```\n\nIf you don't have Python installed, you can install it with this command on Ubuntu:\n\n```\napt-get update \u0026\u0026 apt-get install -y python3.8-venv\n```\n\n# A REST API for an AI\n\nThe following makes havy use of utility bash scripts. This is good practice\nwhen developing large applications as we don't have to remember commands by heart.\nFor the sake of this workshop, whenever there is a `*.sh` file mentioned\nin a code snippet, study the contents of it to familiarize yourself with the\nactual commands that run under the hood.\n\n## Set up the development environment (outside of docker)\n\nOn your command line, switch to the application directory.\nNext, we have to setup our development environment. For this we run the following:\n\n```bash\ngit clone https://github.com/s4ke/docker-lviv-2022\ncd docker-lviv-2022/application\nbash create_venv.sh\nsource venv/bin/activate\npip3 install -r requirements.txt --no-cache-dir\n```\n\n## Build the AI\n\nIn the file `application/ai/model.py`, build an AI using the keras library.\nYou can adapt this file to your liking, but the existing model works okay.\n\nSee https://keras.io/api/layers/ for more documentation on the available layers.\n\nYou can go to the next step to see how we can train the AI for evaluation of whether\nwe are doing a good job.\n\n## Train the AI\n\nIn order for our API to have proper weights, we have to first generate the weights.\nFor the purpose of this workshop, we already have a MLP defined which we are going to train\nfor 30 epochs which should be enough for the MNIST case we are solving.\n\nTo train the AI, we simply run:\n\n```\nsource venv/bin/activate\npython3 train_ai.py\n```\n\n## Test the API with the development server\n\nInside your application folder, we can start the Flask Dev Server with:\n\n```\nsource venv/bin/activate\nbash runDev.sh\n```\n\nThen, on another shell, switch to the test directory, and run a sample request against the AI:\n\n```\nbash run_request.sh img/8.bmp\n```\n\nThis should print something like this on your shell:\n\n```\n(venv) martinb@ubuntu:~/github/docker-lviv-2022/test$ bash run_request.sh img/8.bmp\n+ curl -F image=@img/8.bmp localhost:4000/classify\n{\"class\":\"8\"}\n```\n\nThis means that you have a flask app successfully running in dev mode with all weights properly there.\nWe can now stop the Test server by hitting `CTRL-C` on our keyboard.\n\n## Test the API with the gunicorn production server\n\nAs we can not use the flask dev server in production, we also have an already prepared gunicorn based run script.\nTo start this instead of the dev server, we simply run:\n\n```\nsource venv/bin/activate\nbash run.sh\n```\n\nThen, on another shell, we can run the test request in side the `test` directory again:\n\n```\nbash run_request.sh img/8.bmp\n```\n\nThis should again something like this on your shell:\n\n```\n(venv) martinb@ubuntu:~/github/docker-lviv-2022/test$ bash run_request.sh img/8.bmp\n+ curl -F image=@img/8.bmp localhost:4000/classify\n{\"class\":\"8\"}\n```\n\n# Docker\n\nNote: if you get a permission denied error during this workshop, check if the command works with a `sudo` in front of it.\nThis is due to Docker by default only working for the root user for security reasons. The only alternative would\nbe creating a new user group `docker` on the machine and adding your user to that group. For the sake\nof brevity in this workshop, using `sudo` is fine, though.\n\n## Install Docker on your machine\n\nThe following requires Docker to be installed on your machine.\nIf not done already, follow the tutorial at https://docs.docker.com/engine/install/ubuntu/\n\n## Build the docker image\n\nAs we want a self contained docker image for our AI based REST service, we have to\nbuild a docker image. To do so, inside the `application` folder we run:\n\n```\nbash build.sh\n```\n\nThis will run the appropriate docker cli command to build a docker image with our\nAI.\n\n## Test the docker image\n\nInside the `test` folder, we have a script `start_test_docker.sh`. We run it with:\n\n```bash\nbash start_test_docker.sh\n```\n\nThen, on another shell, we can run the test request in side the `test` directory again:\n\n```\nbash run_request.sh img/8.bmp\n```\n\nThis should again something like this on your shell:\n\n```\n(venv) martinb@ubuntu:~/github/docker-lviv-2022/test$ bash run_request.sh img/8.bmp\n+ curl -F image=@img/8.bmp localhost:4000/classify\n{\"class\":\"8\"}\n```\n\nWhen you are done testing, don't forget to stop the docker container by hitting `CTRL-C`.\n\n# Docker Swarm\n\nThis is not a full Docker Swarm tutorial. This is just here to give an idea\nof how to scale out Docker services. Real production clusters need an ingress (e.g. Traefik),\npossibly encrypted ingress networks, and are usually not managed via\ncli, but instead via stack files.\n\n## Create the Cluster\n\nTo scale out our deployment of the AI service, we use Docker Swarm - a built-in\nclustering technology in Docker.\n\nFirst, we initialize our single node swarm:\n\n```bash\ndocker swarm init\n```\n\nThis will initialize a docker swarm. In production you would now add more nodes to the cluster,\nbut for the sake of this tutorial, we don't need more nodes. The commands will all be the same.\n\n## Create a service\n\nWe can now create our AI service in the Docker swarm.\n\n```bash\ndocker service create --name myai_service --publish 4000:4000 ghcr.io/s4ke/docker-lviv-2022:main\n```\n\nBy default, this creates a service with 1 replica. We can again test it via another shell in the `test` directory:\n\n```bash\nbash run_request.sh img/8.bmp\n```\n\nTo see the logs, we can run:\n\n```bash\ndocker service logs myai_service\n```\n\nIf for whatever reason the single application can not handle all the requests, we could scale out our application by running:\n\n```bash\ndocker service scale myai_service=3\n```\n\nThis will create two extra replicas of our service. Load will be shared among all replicas via Docker Swarm's built in\nload balancing. In a multinode environment Docker Swarm would also try to spread the containers across all nodes of the cluster.\nBecause of Docker Swarms routing, it does not even matter which Node things are running on. Any physical node\nis able to serve requests on port 4000, even if no replica ended up on the node.\n\n\nNote: In production, we would not use a locally built image. For this,\nwe would `docker push` the image after building. This is however not part of this\ntutorial. As we are running the service creation on the same node we built the image on,\nthe service will be created successfully nonetheless.\n\n## Cleanup\n\nOnce finished, we can force leave the swarm with (This will destroy your single node swarm):\n\n```bash\ndocker swarm leave -f\n```\n\n# Self-Study Questions\n\n1. What is the purpose of the Flask DEV Server?\n2. What is the purpose of gunicorn in this application?\n3. What is the purpose of .dockerignore?\n\n# Further questions\n\nThe following questions are for advanced learners. To answer them, you must familiarize yourself\nwith docker concepts like volumes, image layer caching in Docker, ...\n\n1. How could we replace the weights of the application in production without rebuilding the whole container?\n2. Why do we `COPY` the requirements.txt before the rest of the application?\n3. How could we remove the sklearn dependency in production that we only need for training the AI?\n4. How could we use docker even during development? (Hint: development containers, docker-compose)\n5. How does load balancing work in Docker Swarm?\n\n# Additional resources\n\nYoutube Video: [Learn Docker in 7 Easy Steps - Full Beginner's Tutorial](https://www.youtube.com/watch?v=gAkwW2tuIqE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs4ke%2Fdocker-lviv-2022","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs4ke%2Fdocker-lviv-2022","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs4ke%2Fdocker-lviv-2022/lists"}