{"id":20959240,"url":"https://github.com/codophobia/flask-hello-world-devops-project","last_synced_at":"2025-05-14T06:32:58.023Z","repository":{"id":45631564,"uuid":"487437880","full_name":"codophobia/flask-hello-world-devops-project","owner":"codophobia","description":"Build and deploy simple flask application using Jenkins and Kubernetes","archived":false,"fork":false,"pushed_at":"2023-01-03T17:50:55.000Z","size":5341,"stargazers_count":81,"open_issues_count":1,"forks_count":103,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T14:11:17.011Z","etag":null,"topics":["devops-pipeline","devops-project","docker","flask","jenkins","kubernetes","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codophobia.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-05-01T03:41:27.000Z","updated_at":"2024-12-01T00:34:11.000Z","dependencies_parsed_at":"2023-02-01T08:31:45.492Z","dependency_job_id":null,"html_url":"https://github.com/codophobia/flask-hello-world-devops-project","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/codophobia%2Fflask-hello-world-devops-project","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codophobia%2Fflask-hello-world-devops-project/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codophobia%2Fflask-hello-world-devops-project/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codophobia%2Fflask-hello-world-devops-project/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codophobia","download_url":"https://codeload.github.com/codophobia/flask-hello-world-devops-project/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254084750,"owners_count":22011939,"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":["devops-pipeline","devops-project","docker","flask","jenkins","kubernetes","python"],"created_at":"2024-11-19T01:52:18.440Z","updated_at":"2025-05-14T06:32:53.015Z","avatar_url":"https://github.com/codophobia.png","language":"Python","readme":"# flask-hello-world-devops-project\nBuild and deploy code a simple flask application using Jenkins and Kubernetes\n \nIn this project, we are going to build a simple [CI/CD](https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment) pipeline from scratch using tools like Flask, Docker, Git, Github, Jenkins and Kubernetes.\n \n## Prerequisites\n \n* Python\n* Flask\n* Docker\n* Git and Github\n* Jenkins\n* Kubernetes\n* Linux machine\n \n## Steps in the CI/CD pipeline\n1. Create a \"Hello world\" Flask application\n2. Write basic test cases\n3. Dockerise the application\n4. Test the code locally by building docker image and running it\n5. Create a github repository and push code to it\n6. Start a Jenkins server on a host\n7. Write a Jenkins pipeline to build, test and push the docker image to Dockerhub.\n8. Set up Kubernetes on a host using [Minikube](https://minikube.sigs.k8s.io/docs/start/)\n9. Create a Kubernetes deployment and service for the application.\n10. Use Jenkins to deploy the application on Kubernetes\n \n## Project structure\n \n* app.py - Flask application which will print \"Hello world\" when you run it\n* test.py - Test cases for the application\n* requirements.txt - Contains dependencies for the project\n* Dockerfile - Contains commands to build and run the docker image\n* Jenkinsfile - Contains the pipeline script which will help in building, testing and deploying the application\n* deployment.yaml - [Kubernetes deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) file for the application\n* service.yaml - [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) file for the application\n \n## Create a project repository on Github\n \nLogin to your github account and create a new repository. Do make sure that you have given a unique name for the repository. It's good to add a README file, Python .gitignore template and choose a licence.\n \n![create-github-repo](images/create_github_repo_border.png)\n \nClick on \"create repository\". Now, the repository is created.\n \n![github-repo](images/github_repo_border.png)\n \n## Clone the repository on your system\n \nGo to your Github repository. Click on the \"Code\" section and note down the HTTPS url for the project.\n \n![clone-url](images/clone_url_border.png)\n \nOpen terminal on your local machine(Desktop/laptop) and run the below commands.\n \n```\ngit clone https://github.com/codophobia/flask-hello-world-devops-project.git # Replace the url with the url of your project\ncd flask-hello-world-devops-project\n```\n \nRun ls command and you should be able to see a local copy of the github repository.\n \n![local-repo](images/local_repo_border.png)\n \n## Set up virtual Python environment\n \nSetting up a [virtual Python environment](https://docs.python.org/3/library/venv.html) will help in testing the code locally and also collecting all the dependencies.\n \n```bash\npython3 -m venv venv # Create the virtual env named venv\nsource venv/bin/activate # Activate the virtual env\n```\n \n## Create a Flask application\n \nInstall the flask module.\n \n```bash\npip install flask\n```\n \nOpen the repository that you have just cloned in your favourite text editor.\nCreate a new file named \"app.py\" and add the below code.\n \n```python\nfrom flask import Flask\nimport os\n \napp = Flask(__name__)\n \n \n@app.route(\"/\")\ndef hello():\n   return \"Hello world!!!\"\n \n \nif __name__ == \"__main__\":\n   port = int(os.environ.get(\"PORT\", 5000))\n   app.run(debug=True, host='0.0.0.0', port=port)\n```\n \nThe above code when run will start a web server on port number 5000. You can test the code by running it.\n \n```bash\npython app.py\n```\n \nYou should see the output below after running the above command.\n \n![run-app](images/run_app_border.png)\n \nOpen your browser and visit [](http://127.0.0.1:5000). You should see \"Hello world\" printed on the browser.\n \n![hello-world-browser](images/hello_world_browser_border.png)\n \n## Write test cases using pytest\n \nInstall [pytest](https://docs.pytest.org/en/7.1.x/) module. We will use pytest for testing.\n \n```bash\npip install pytest\n```\n \nCreate a new file named \"test.py\" and add a basic test case.\n \n```python\nfrom app import app\n \n \ndef test_hello():\n   response = app.test_client().get('/')\n   assert response.status_code == 200\n   assert response.data == b'Hello world!!!'\n```\n \nRun the test file using pytest.\n \n```bash\npytest test.py\n```\n \n## Run code quality tests using flake\n \nIt's always recommended to write quality code with proper coding standards, proper code formatting and code with no syntax errors.\n \n[Flake8](https://flake8.pycqa.org/en/latest/) can be used to check the quality of the code in Python.\n \nInstall the flake8 module.\n \n```bash\npip install flake8\n```\n \nRun flake8 command.\n \n```bash\nflake8 --exclude venv # Ignore files in venv for quality check\n```\n \nIf you do not see any output, it means that everything is alright with code quality.\n \nTry removing the spaces between commas in the app.py and then run flake8 command again. You should see the following errors.\n \n![flake-error](images/flake_error_border.png)\n \nAdd space again and you will not see the error now.\n \n## Dockerise the application\n \nInstall docker on your system. Follow [https://docs.docker.com/get-docker/](https://docs.docker.com/get-docker/)\n \nCreate a file named \"Dockerfile\" and add the below code.\n \n```\nFROM python:3.6\nMAINTAINER Shivam Mitra \"shivamm389@gmail.com\" # Change the name and email address\nCOPY app.py test.py /app/\nWORKDIR /app\nRUN pip install flask pytest flake8 # This downloads all the dependencies\nCMD [\"python\", \"app.py\"]\n```\n \nBuild the docker image.\n \n```bash\ndocker build -t flask-hello-world .\n```\n \nRun the application using docker image.\n \n```bash\ndocker run -it -p 5000:5000 flask-hello-world\n```\n \nRun test case\n \n```bash\ndocker run -it flask-hello-world pytest test.py\n```\n \nRun flake8 tests\n \n```bash\ndocker run -it flask-hello-world flake8\n```\n \nYou can verify if the application is running by opening the page in the browser.\n \nPush the image to dockerhub. You will need an account on docker hub for this.\n \n```bash\ndocker login # Login to docker hub\ndocker tag flask-hello-world shivammitra/flask-hello-world # Replace \u003cshivammitra\u003e with your docker hub username\ndocker push shivammitra/flask-hello-world\n```\n \n## Push the code to github\n \nTill now, we haven't pushed the code to our remote repository. Let's try some basic git commands to push the code.\n \n```bash\ngit add .\ngit commit -m \"Add flask hello world application, test cases and dockerfile\"\ngit push origin main\n```\n \nIf you go to the github repository, you should see the changes.\n \n![git-code-push](images/git_code_push_border.png)\n \n## Install Jenkins\n \nIn this example, we will be installing Jenkins on Ubuntu 20.04 Linux machine. If you have a different Linux distribution, follow steps mentioned at [https://www.jenkins.io/doc/book/installing/linux/](https://www.jenkins.io/doc/book/installing/linux/)\n \nRun the following commands on the server.\n \n```bash\n# Install jenkins\n \ncurl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \\\n/usr/share/keyrings/jenkins-keyring.asc \u003e /dev/null\n \necho deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \\\nhttps://pkg.jenkins.io/debian-stable binary/ | sudo tee \\\n/etc/apt/sources.list.d/jenkins.list \u003e /dev/null\n \nsudo apt-get update\nsudo apt install openjdk-11-jre\nsudo apt-get install jenkins\n \n# Install docker and add jenkins user to docker group\n# Installing docker is important as we will be using jenkins to run docker commands to build and run the application.\nsudo apt install docker.io\nsudo usermod -aG docker jenkins\nsudo service jenkins restart\n```\n \nOpen your browser and visit [http://127.0.0.1:8080](http://127.0.0.1:8080). If you have installed Jenkins on a Aws/Azure/GCP virtual machine, use the public address of the VM in place of localhost. If you are not able to access the cloud, make sure that port 8080 is added to the inbound port.\n \n![jenkins-homepage](images/jenkins_homepage_border.png)\n \nCopy the admin password from the path provided on the jenkins homepage and enter it. Click on \"Install suggested plugins\". It will take some time to install the plugins.\n \n![install-plugins](images/install_plugins_border.png)\n \nCreate a new user.\n \n![create-jenkins-user](images/create_jenkins_user_border.png)\n \nYou should now see the jenkins url. Click next and you should see the \"Welcome to jenkins\" page now.\n \n## Create a Jenkins pipeline\n \nWe will now create a Jenkins pipeline which will help in building, testing and deploying the application.\n \nClick on \"New Item\" on the top left corner of the homepage.\n \n![new-item-jenkins](images/new_item_jenkins_border.png)\n \nEnter a name, select \"Pipeline\" and click next.\n \n![select-jenkins-item](images/select_jenkins_item_border.png)\n \nWe now need to write a [pipeline script](https://www.jenkins.io/doc/book/pipeline/syntax/) in Groovy for building, testing and deploying code.\n \n![jenkins-script](images/jenkins_script_border.png)\n \nEnter the below code in the pipeline section and click on \"Save\".\n \n```\npipeline {\n   agent any\n  \n   environment {\n       DOCKER_HUB_REPO = \"shivammitra/flask-hello-world\"\n       CONTAINER_NAME = \"flask-hello-world\"\n \n   }\n  \n   stages {\n       stage('Checkout') {\n           steps {\n               checkout([$class: 'GitSCM', branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[url: 'https://github.com/codophobia/flask-hello-world-devops-project']]])\n           }\n       }\n       stage('Build') {\n           steps {\n               echo 'Building..'\n               sh 'docker image build -t $DOCKER_HUB_REPO:latest .'\n           }\n       }\n       stage('Test') {\n           steps {\n               echo 'Testing..'\n               sh 'docker stop $CONTAINER_NAME || true'\n               sh 'docker rm $CONTAINER_NAME || true'\n               sh 'docker run --name $CONTAINER_NAME $DOCKER_HUB_REPO /bin/bash -c \"pytest test.py \u0026\u0026 flake8\"'\n           }\n       }\n       stage('Deploy') {\n           steps {\n               echo 'Deploying....'\n               sh 'docker stop $CONTAINER_NAME || true'\n               sh 'docker rm $CONTAINER_NAME || true'\n               sh 'docker run -d -p 5000:5000 --name $CONTAINER_NAME $DOCKER_HUB_REPO'\n           }\n       }\n   }\n}\n```\n \nClick on the \"Build Now\" button at the left of the page.\n \n![build-now-jenkins](images/build_now_jenkins_border.png)\n \nThe pipelines should start running now and you should be able to see the status of the build on the page.\n \n![jenkins-build-status](images/jenkins_build_status_border.png)\n \nIf the build is successful, you can visit [http://127.0.0.1:5000](http://127.0.0.1:5000) and you should see \"Hello world\" on the browser. If you have installed Jenkins on a Aws/Azure/GCP virtual machine, use the public address of the VM in place of localhost. If you are not able to access the cloud, make sure that port 5000 is added to the inbound port.\n \n## Pipeline script from SCM\n \nWe can also store the Jenkins pipeline code in our github repository and ask Jenkins to execute this file.\n \nGo to the \"flask-hello-world\" pipeline page and click on \"Configure\".\n \nChange definition from \"Pipeline script\" to \"Pipeline script from SCM\" and fill details on SCM and github url. Save the pipeline.\n \n![pipeline-from-scm](images/pipeline_from_scm_border.png)\n \nNow, create a new file named \"Jenkins\" in our local code repository and add the below pipeline code.\n \n```\npipeline {\n   agent any\n  \n   environment {\n       DOCKER_HUB_REPO = \"shivammitra/flask-hello-world\"\n       CONTAINER_NAME = \"flask-hello-world\"\n \n   }\n  \n   stages {\n       /* We do not need a stage for checkout here since it is done by default when using the \"Pipeline script from SCM\" option. */\n      \n \n       stage('Build') {\n           steps {\n               echo 'Building..'\n               sh 'docker image build -t $DOCKER_HUB_REPO:latest .'\n           }\n       }\n       stage('Test') {\n           steps {\n               echo 'Testing..'\n               sh 'docker stop $CONTAINER_NAME || true'\n               sh 'docker rm $CONTAINER_NAME || true'\n               sh 'docker run --name $CONTAINER_NAME $DOCKER_HUB_REPO /bin/bash -c \"pytest test.py \u0026\u0026 flake8\"'\n           }\n       }\n       stage('Deploy') {\n           steps {\n               echo 'Deploying....'\n               sh 'docker stop $CONTAINER_NAME || true'\n               sh 'docker rm $CONTAINER_NAME || true'\n               sh 'docker run -d -p 5000:5000 --name $CONTAINER_NAME $DOCKER_HUB_REPO'\n           }\n       }\n   }\n}\n```\n \nPush the code to github.\n \n```bash\ngit add .\ngit commit -m \"Add Jenkinsfile\"\ngit push origin main\n```\n \nGo to \"flask-hello-world\" pipeline page and click on \"Build Now\"\n \n![jenkins-build-scm](images/jenkins_build_scm_border.png)\n \n## Install Kubernetes\n \nIn this example, we will be installing kubernetes on Ubuntu 20.04 Linux machine using minikube. If you are on cloud, you can create a new instance and install kubernetes on that.\n \n```bash\n# https://minikube.sigs.k8s.io/docs/start/\n \n# Install docker for managing containers\nsudo apt-get install docker.io\n \n# Install minikube\ncurl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64\nsudo install minikube-linux-amd64 /usr/local/bin/minikube\n \n# Add the current USER to docker group\nsudo usermod -aG docker $USER \u0026\u0026 newgrp docker\n \n# Start minikube cluster\nminikube start\n \n# Add an alias for kubectl command\nalias kubectl=\"minikube kubectl --\"\n```\n \nCreate a new file named \"deployment.yaml\" in your project and add the below code.\n \n```\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: flask-hello-deployment # name of the deployment\n \nspec:\n template: # pod defintion\n   metadata:\n     name: flask-hello # name of the pod\n     labels:\n       app: flask-hello\n       tier: frontend\n   spec:\n     containers:\n       - name: flask-hello\n         image: shivammitra/flask-hello-world:latest\n replicas: 3\n selector: # Mandatory, Select the pods which needs to be in the replicaset\n   matchLabels:\n     app: flask-hello\n     tier: frontend\n```\n \nTest the deployment manually by running the following command:\n \n```bash\n$ kubectl apply -f deployment.yaml\ndeployment.apps/flask-hello-deployment created\n$ kubectl get deployments flask-hello-deployment\nNAME                     READY   UP-TO-DATE   AVAILABLE   AGE\nflask-hello-deployment   3/3     3            3           45s\n```\n \nCreate a new file named \"service.yaml\" and add the following code\n \n```\napiVersion: v1\nkind: Service\nmetadata:\n name: flask-hello-service-nodeport # name of the service\n \nspec:\n type: NodePort # Used for accessing a port externally\n ports:\n   - port: 5000 # Service port\n     targetPort: 5000 # Pod port, default: same as port\n     nodePort: 30008 # Node port which can be used externally, default: auto-assign any free port\n selector: # Which pods to expose externally ?\n   app: flask-hello\n   tier: frontend\n```\n \nTest the service manually by running below commands.\n \n```bash\n$ kubectl apply -f service.yaml\nservice/flask-hello-service-nodeport created\n$ kubectl get service flask-hello-service-nodeport\nNAME                           TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE\nflask-hello-service-nodeport   NodePort   10.110.46.59   \u003cnone\u003e        5000:30008/TCP   36s\n```\n \nRun below command to access the application on the browser.\n \n```bash\nminikube service flask-hello-service-nodeport\n```\n \n![kubernetes-service](images/kubernetes_service_border.png)\n \nFinally, push the updated code to github.\n \n```bash\ngit add .\ngit commit -m \"Add kubernetes deployment and service yaml\"\ngit push origin main\n```\n \n## Deploy using jenkins on kubernetes\n \nFirst, we will add our docker hub credential in jenkins. This is needed as we have to first push the docker image before deploying on kubernetes.\n \nOpen jenkins credentials page.\n \n![open-jenkins-credentials](images/open_jenkins_credentials_border.png)\n \nClick on 'global'.\n \n![jenkins-global-credentials](images/jenkins_global_credentials_border.png)\n \nAdd the credentials for docker hub account.\n \n![add-jenkins-credentials](images/add_jenkins_credentials_border.png)\n \nWe will now modify our Jenkinsfile in the project to push the image and then deploy the application on kubernetes.\n \n```\npipeline {\n   agent any\n  \n   environment {\n       DOCKER_HUB_REPO = \"shivammitra/flask-hello-world\"\n       CONTAINER_NAME = \"flask-hello-world\"\n       DOCKERHUB_CREDENTIALS=credentials('dockerhub-credentials')\n   }\n  \n   stages {\n       /* We do not need a stage for checkout here since it is done by default when using \"Pipeline script from SCM\" option. */\n      \n       stage('Build') {\n           steps {\n               echo 'Building..'\n               sh 'docker image build -t $DOCKER_HUB_REPO:latest .'\n           }\n       }\n       stage('Test') {\n           steps {\n               echo 'Testing..'\n               sh 'docker stop $CONTAINER_NAME || true'\n               sh 'docker rm $CONTAINER_NAME || true'\n               sh 'docker run --name $CONTAINER_NAME $DOCKER_HUB_REPO /bin/bash -c \"pytest test.py \u0026\u0026 flake8\"'\n           }\n       }\n       stage('Push') {\n           steps {\n               echo 'Pushing image..'\n               sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'\n               sh 'docker push $DOCKER_HUB_REPO:latest'\n           }\n       }\n       stage('Deploy') {\n           steps {\n               echo 'Deploying....'\n               sh 'minikube kubectl -- apply -f deployment.yaml'\n               sh 'minikube kubectl -- apply -f service.yaml'\n           }\n       }\n   }\n}\n```\n \nCommit the changes to github.\n \n```bash\ngit add .\ngit commit -m \"Modify Jenkinsfile to deploy on kubernetes\"\ngit push origin main\n```\n \nGo to \"flask-hello-world\" pipeline page and click on \"Build Now\"\n \n![jenkins-deploy-kubernetes](images/jenkins_deploy_kubernetes_border.png)\n \n## Is the Kubernetes host different from the jenkins host ?\n \nIn case you have set up kubernetes on a different virtual machine, we will need to ssh to this machine from jenkins machine, copy the deployment and service files and then run kubernetes commands.\n \nCreate a ssh key pair on jenkins server.\n \n```bash\n$ cd ~/.ssh # We are on jenkins server\n$ ssh-keygen -t rsa # select the default options\n$ cat id_rsa.pub # Copy the public key\n```\n \nAdd the public key we created to authorized_keys on kubernetes server.\n \n```bash\n$ cd ~/.ssh # We are on kubernetes server\n$ echo \"\u003cpublic key\u003e\" \u003e\u003e authorized_keys\n```\n \nModify the 'Deploy' section of Jenkinsfile. Replace \u003cusername\u003e and \u003cip address\u003e with the username and ip address of kubernetes host respectively.\n \n```\nstage('Deploy') {\n   steps {\n       echo 'Deploying....'\n       sh 'scp -r -o StrictHostKeyChecking=no deployment.yaml service.yaml \u003c username\u003e@\u003cip address\u003e:~/'\n \n       sh 'ssh \u003cusername\u003e\u003cip address\u003e kubectl apply -f ~/deployment.yaml'\n       sh 'ssh \u003cusername\u003e\u003cip address\u003e kubectl apply -f ~/service.yaml'\n   }\n}\n```\n \nCommit the code. Build the pipeline again on Jenkins server.\n \n## Conclusion\n \nIn this tutorial, we have tried to build a very simple CI/CD pipeline using jenkins and kubernetes. Do note that this is only for learning purposes. If you are creating a CI/CD pipeline for production use, follow the official docs of jenkins and kubernetes for the best practices.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodophobia%2Fflask-hello-world-devops-project","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodophobia%2Fflask-hello-world-devops-project","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodophobia%2Fflask-hello-world-devops-project/lists"}