{"id":22305011,"url":"https://github.com/hamdiz0/vote-app","last_synced_at":"2026-02-13T11:30:43.217Z","repository":{"id":258342210,"uuid":"871737951","full_name":"hamdiz0/vote-app","owner":"hamdiz0","description":"A containerized voting app hosted on an EC2 instance built ,pushed \u0026 deployed with a CI/CD pipeline using docker ,jenkins \u0026 minikube","archived":false,"fork":false,"pushed_at":"2024-12-24T22:28:06.000Z","size":643,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T05:32:04.852Z","etag":null,"topics":["cicd","devops","docker","ec2-instance","jenkins","kubernetes","minikube","scripting","ssh"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hamdiz0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-10-12T19:49:34.000Z","updated_at":"2024-12-28T00:43:32.000Z","dependencies_parsed_at":"2024-10-18T14:45:02.209Z","dependency_job_id":"61c4847f-06a9-463f-a659-e71fe2d5453c","html_url":"https://github.com/hamdiz0/vote-app","commit_stats":null,"previous_names":["hamdiz0/vote-app"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hamdiz0/vote-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamdiz0%2Fvote-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamdiz0%2Fvote-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamdiz0%2Fvote-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamdiz0%2Fvote-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hamdiz0","download_url":"https://codeload.github.com/hamdiz0/vote-app/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamdiz0%2Fvote-app/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29352301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T01:03:07.613Z","status":"ssl_error","status_checked_at":"2026-02-12T01:00:51.346Z","response_time":97,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cicd","devops","docker","ec2-instance","jenkins","kubernetes","minikube","scripting","ssh"],"created_at":"2024-12-03T19:09:22.645Z","updated_at":"2026-02-13T11:30:43.202Z","avatar_url":"https://github.com/hamdiz0.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Vote-App\n\nBased on \u003ca href=\"https://github.com/dockersamples/example-voting-app\"\u003eexample-voting-app\u003c/a\u003e\n\n- [Prerequisites](#prerequisites-)\n- [Architecture/Containers](#architecturecontainers-)\n- [Testing the app using `docker-compose`](#testing-the-app-using-docker-compose-)\n- [CI/CD Pipeline](#cicd-pipeline-)\n  - [CI Pipeline](#ci-pipeline-)\n    - [Pushing docker images](#pushing-docker-images-)\n    - [Webhooks](#webhooks-)\n  - [CD Pipeline](#cd-pipeline-)\n    - [YAML files templates](#yaml-files-templates-)\n    - [Setting up minikube](#setting-up-minikube--)\n    - [Deploying the cluster manually](#deploying-the-cluster-manually-)\n    - [Using an SSH-agent to deploy the cluster](#using-an-ssh-agent-to-deploy-the-cluster-)\n  - [Results](#results-)\n\n\n* This app is structured into five services, each running in separate containers, it allows users to cast votes and see results in real-time.\n* This porject also includes a Kubernetes setup for running the app in a Minikube cluster\n* It also includes job triggers and automated deployment with ssh and scripts\n\n\u003cimg src=\"./imgs/voting-app.PNG\" style=\"width:100%\"\u003e\n\n### Prerequisites :\n\n* Docker\n* Docker Compose (for testing)\n* Minikube\n* jenkins (as a container)\n\n### Architecture/Containers :\n\n* vote service - Flask based web app (vote)\n* Result Service - Node.js app (view results)\n* Worker Service - .NET service (processes votes)\n* db service - PostgreSQL (store votes)\n\n### Testing the app using `docker-compose` :\n\n* using the Docker files \u003ca href=\"./docker-compose.yml\"\u003eview file here\u003c/a\u003e :\n  - docker compose up --build\n* using the docker images from DockerHub \u003ca href=\"./docker-compose-images.yml\"\u003eview file here\u003c/a\u003e :\n  - docker compose up -f ./docker-compose-images.yml up\n\n# CI/CD Pipeline :\n\n\u003cimg src=\"./imgs/cicd.PNG\" style=\"width:100%\"\u003e\n\n## CI Pipeline :\n\n### Pushing docker images :\n\n* Pushing vote ,result \u0026 worker to docker hub using \u003ca href=\"./jenkinsfile\"\u003eJenkinsFile\u003c/a\u003e \n* utilized a \u003ca href=\"./gs.groovy\"\u003eFunction\u003c/a\u003e template to build and push the images \n  - hamdiz0/va-vote:1.0\n  - hamdiz0/va-result:1.0\n  - hamdiz0/va-worker:1.0\n  - redis:alpine (default)\n  - postgres:15-alpine (default)\n\n### Webhooks :\n\n* configured a webhook between jenkins and github to trigger the builds automaticly \n  - in the github repo settings there is an option to add webhook trigger\n  \n    \u003cimg src=\"./imgs/webhook.PNG\" style=\"width:100%\"\u003e\n    \n  - make sure to configure jenkins to handle job triggers within a pipline\n  \n    \u003cimg src=\"./imgs/jen-hook.PNG\" style=\"width:100%\"\u003e\n    \n* more details about web hooks \u003ca href=\"https://github.com/hamdiz0/LearningDevOps/blob/main/jenkins/README.md#job-triggers\"\u003ehere\u003c/a\u003e\n\n## CD Pipeline :\n\n* added kubernetes configuration files to deploy the app on Minikube hosted on an EC2 instance\n\n\u003cimg src=\"./imgs/k8s-comps.PNG\" style=\"width:100%\"\u003e\n\n### YAML files templates :\n\n* utilized deployments for each service \n* configured the vote and result services with a NodePort service-type for easy access from outside the cluster\n  - \u003ca href=\"./k8s-specifications/vote-deployment_svc.yml\"\u003evote.YAML\u003c/a\u003e\n  - \u003ca href=\"./k8s-specifications/result-deployment_svc.yml\"\u003eresult.YAML\u003c/a\u003e\n* configured the db(postgres) and redis services with a ClusterIP service-type for an efficient comminication inside the cluster\n  - \u003ca href=\"./k8s-specifications/redis-deployment_svc.yml\"\u003eredis.YAML\u003c/a\u003e\n  - \u003ca href=\"./k8s-specifications/postgres-deployment_svc.yml\"\u003epostgres.YAML\u003c/a\u003e\n* no need to configure a service for the worker as it only forwards requests\n  - \u003ca href=\"./k8s-specifications/worker-deployment.yml\"\u003eworker.YAML\u003c/a\u003e\n\n### Setting up minikube  :\n\n  * follow instructions \u003ca href=\"https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download\"\u003ehere\u003c/a\u003e\n  * check out this repo for minikube quick setup \u003ca href=\"https://github.com/hamdiz0/minikube-setup\"\u003ehamdiz0/minikube-setup\u003c/a\u003e\n\n### Deploying the cluster manually :\n\n  * you can run the script : \u003ca href=\"./k8s-specifications/minikube-run.sh\"\u003eminikube-run.sh\u003c/a\u003e\n  * alternativly you can use this script : \u003ca href=\"./k8s-specifications/k8s-cluster-run.sh\"\u003ecluster-run.sh\u003c/a\u003e to run it on a kubeadm setup\n    - here is guide to how to setup a k8s cluster with vagrant and kubeadm \u003ca href=\"https://github.com/vancanhuit/vagrant-k8s\"\u003evagrant-k8s\u003c/a\u003e\n  * make sure execute permissions are added and pass in the version \"-v\" (1.0 , 2.0 , latest) :\n    ```\n    chmod +x minikube-run.sh -v 2.0\n    ```\n### Using an SSH-agent to deploy the cluster :\n\n  * create an EC2 instance or setup minikube locally :\n    \u003cimg src=\"./imgs/instance.PNG\" style=\"width:100%\"\u003e\n  * make sure to allow http ,https and ssh and add custom ports if needed :\n    \u003cimg src=\"./imgs/ports.PNG\" style=\"width:100%\"\u003e\n  * install the \"ssh agent\" plugin in jenkins :\n  * generate an ssh key in the remote machine running minikube :\n    ```sh\n      ssh-keygen\n    ```\n  * copy the private key of the remote machine in the \"Private Key\" field when creating an \"SSH Username with private key\" credential in jenkins :\n    - in the home directory of the remote machine:\n      ```\n      ls .ssh\n      ```\n      \u003cimg src=\"./imgs/ssh-agent.PNG\" style=\"width:100%\"\u003e\n  * genrate an ssh key in jenkins container \n      ```sh\n      docker exec -it \u003cjenkins container Id\u003e bash\n      ssh-keygen\n      ```\n  * make sure to copy the public key of jenkins to the authorized_keys file of the remote machine :\n    ```\n    nano .ssh/authorized_keys\n    ```\n  * utilized a function that uses the ssh agent to run a deployment script in the remote machine :\n    - \u003ca href=\"./gs.groovy\"\u003esee function here\u003c/a\u003e\n    - here is an example of using the function in a stage\n    ```\n    gs.deploy(\n      'ubuntu',                                           // remote user name\n      '51.44.12.190',                                     // Ip address of the remote machine\n      '2.0',                                              // version\n      'minikube-instance',                                // credentail Id in jenkins (SSH Username with private key)\n      'https://github.com/hamdiz0/vote-app.git',          // git repositry\n      'vote-app/k8s-specifications/',                     // Repo_Name/Path/ (path to the deployment script in the git repo)\n      'minikube-run.sh'                                   // Deployment_Script_Name (in the specified path above)\n    )\n    ```\n    - the function clones the repository and runs the deployment script in the minikube machine\n    - than it passes the version to the deployment script as it accept a version option `-v`\n  * deployment script :\n    - \u003ca href=\"./k8s-specifications/minikube-run.sh\"\u003eview the deployment script here\u003c/a\u003e\n    - the deployment script changes the docker image version in the YAML files than applies them all using a `for` loop\n    - the YAML file cantains the docker images along with a place-holder `\u003c\u003cVERSION\u003e\u003e` the script changes this to the passed in version\n      ```\n      spec:\n      containers:\n        - name : vote\n          image: hamdiz0/va-vote:\u003c\u003cVERSION\u003e\u003e \n          ports :\n          - containerPort: 80\n      ```\n    - the script changes the `\u003c\u003cVERSION\u003e\u003e` with the passed in version throught this command :\n      ```\n      sed -i \"s/\u003c\u003cVERSION\u003e\u003e/${VER}/\" \"$file\" # VER contains the values of the passed in version\n      ```\n    - the script than forwards both the vote and result services ports so we can access them using the public ip of the EC2 instance :\n      ```\n      minikube kubectl -- port-forward svc/vote-svc 30000:80 --address 0.0.0.0 \u0026\n      minikube kubectl -- port-forward svc/result-svc 30001:80 --address 0.0.0.0 \u0026\n      ```\n      * these commands will keep the script in an continious execution mode wish causes the jenkins job to never end \n      * the forwarding processes must be seperated from the script by capturing their PIDs and disowning from the script :\n        ```\n        minikube kubectl -- port-forward svc/vote-svc 30000:80 --address 0.0.0.0 \u0026\n        VOTE_PID=$!  # get port forwarding proccess id\n        disown $VOTE_PID # seperate the process from the script\n        ```\n      * these processes can also cause problems when you want to deploy again (port is in use error) :\n        \u003cimg src=\"./imgs/pros.PNG\" style=\"width:100%\"\u003e\n      * they must be killed before reusing the ports again for the next deployment :\n        ```\n        kill $(ps aux | grep 'kubectl port-forward' | awk '{print $2}')\n        ```\n    - the vote and result services are accessable throught :\n      * http://\u003cPublic_IP@_of_the_EC2_Instance\u003e:30000\n      * http://\u003cPublic_IP@_of_the_EC2_Instance\u003e:30001\n\n### Results :\n\n  #### Kubectl get svc,deploy,pods :\n\n  \u003cimg src=\"./imgs/cluster.PNG\" style=\"width:100%\"\u003e\n\n  #### Jenkins build result :\n\n  \u003cimg src=\"./imgs/build.PNG\" style=\"width:100%\"\u003e\n\n## Checkout my \u003ca href=\"https://github.com/hamdiz0/LearningDevOps\"\u003eLearningDevops\u003c/a\u003e repo for more details about these tools and devops in general do not hesitate to contribute\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamdiz0%2Fvote-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhamdiz0%2Fvote-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamdiz0%2Fvote-app/lists"}