{"id":15083599,"url":"https://github.com/edwinrlambert/hello-cicd","last_synced_at":"2026-03-13T20:10:06.186Z","repository":{"id":247305376,"uuid":"825492707","full_name":"edwinrlambert/Hello-CICD","owner":"edwinrlambert","description":"Comprehensive DevOps project tutorial covering Flask app development, Docker containerization, Jenkins CI/CD pipeline, Kubernetes orchestration, and Terraform infrastructure management. Designed for hands-on learning of essential DevOps tools and practices.","archived":false,"fork":false,"pushed_at":"2024-07-10T04:11:51.000Z","size":68,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-12T14:13:51.715Z","etag":null,"topics":["automation","cicd","containerization","devops","docker","flask","infrastructure-as-code","jenkins","kubernetes","terraform"],"latest_commit_sha":null,"homepage":"","language":"Python","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/edwinrlambert.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}},"created_at":"2024-07-07T23:39:55.000Z","updated_at":"2024-07-10T04:11:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"ffa099c2-ca1e-49ae-b42a-d6cc1ba77938","html_url":"https://github.com/edwinrlambert/Hello-CICD","commit_stats":{"total_commits":23,"total_committers":2,"mean_commits":11.5,"dds":0.04347826086956519,"last_synced_commit":"0062b29f3e9b240a57ad51fdaf0a89172bbcc46c"},"previous_names":["edwinrlambert/hello-cicd"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edwinrlambert%2FHello-CICD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edwinrlambert%2FHello-CICD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edwinrlambert%2FHello-CICD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edwinrlambert%2FHello-CICD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edwinrlambert","download_url":"https://codeload.github.com/edwinrlambert/Hello-CICD/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243230101,"owners_count":20257644,"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":["automation","cicd","containerization","devops","docker","flask","infrastructure-as-code","jenkins","kubernetes","terraform"],"created_at":"2024-09-25T06:03:38.940Z","updated_at":"2025-12-24T20:09:35.910Z","avatar_url":"https://github.com/edwinrlambert.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hello CICD\n\n## **Introduction**\n\nThis project is a hands-on learning experience I undertook to understand how DevOps works and to get familiar with some of the essential tools used in DevOps workflows.\n\nHere is the high-level process of this project:\n\n1. **Create a Flask Web Application:**\n\n   Developed a small web application using Flask to understand the basics of web app development.\n\n2. **Run the Flask Application:**\n\n   Ensured the Flask application was up and running smoothly on the local environment.\n\n3. **Docker Integration:**\n\n   Created a DockerFile to containerize the Flask application, then built and ran the project using Docker for consistent and isolated environments.\n\n4. **CI/CD Pipeline with Jenkins:**\n\n   Set up Jenkins to create a CI/CD pipeline, automating the build, test, and deployment processes for the Flask application.\n\n5. **Infrastructure Management with Terraform:**\n\n   Utilized Terraform for efficient infrastructure provisioning, deployment, scaling, and monitoring. This allowed me to manage infrastructure as code.\n\n6. **Container Orchestration with Kubernetes:**\n\n   Employed Kubernetes to automate the operational tasks of container management. This included deploying applications, rolling out updates, scaling as needed, and monitoring the application to maintain optimal performance.\n\n7. **Expose Services with Ngrok:**\n\n   Used Ngrok to expose the local development environment to the internet securely, facilitating external access for testing and demonstrations.\n\n8. **Source Code Management with Git:**\n\n   Managed the source code using Git, keeping track of changes and maintaining version control throughout the project lifecycle.\n\n9. **Repository Hosting with GitHub:**\n\n   Stored, tracked, and version controlled the project on GitHub, utilizing its robust platform for collaborative development and project management.\n\n10. **Automation with GitHub Webhooks:**\n\n    Implemented GitHub Webhooks to automatically trigger actions in response to specific events within the repository, streamlining the workflow and integrating various DevOps tools.\n\n### **Project Tree Structure**\n\n```\nHello CICD\n├── .git                            // git folder\n├── app                             // flask application\n│   ├── __pycache__\n│   ├── static                      // flask static files\n│   │   ├── css\n│   │   │   └── style.css\n│   │   └── img\n│   │       └── ci-cd.png\n│   ├── templates                   // flask templates\n│   │   └── index.html\n│   ├── __init__.py\n│   └── views.py\n├── env                             // environment variables\n├── k8s                             // kubernetes files\n│   ├── deployment.yaml\n│   ├── ingress.yaml\n│   └── service.yaml\n├── terraform                       // terraform files\n│   ├── .terraform\n│   ├── .terraform.lock.hcl\n│   ├── terraform.tfstate.backup\n│   ├── main.tf\n│   ├── outputs.tf\n│   ├── variable.tf\n│   └── terraform.tfstate\n├── tests                           // python tests\n│   ├── __pycache__\n│   ├── __init__.py\n│   └── test_views.py\n├── .gitignore\n├── Dockerfile                      // Docker file\n├── Jenkinsfile                     // Jenkins file\n├── README.md\n├── run.py\n└── requirements.txt\n```\n\n## **I. Setting up the Folder for the Project**\n\nThe first step for this project was to set up the main folder for this project. Let's name it `Hello CICD`.\n\n## **II. Initialize Git in the folder**\n\nInside the folder, we first initialize git to keep track of changes made within the directory.\n\n**Note**: Go to [GitHub](https://github.com/) and create a repository for this project.\n\n```sh\n# Initialize git in this folder.\ngit init\n```\n\n**Note**: The following commands will only work if there are files in the folder as Git tracks files, not folders. But since we mentioned the initialization, I'm mentioning the rest of the process below here.\n\nFor this purpose, create a `.gitignore` file in the project folder.\n\n```sh\n# Add files to git\ngit add .\n```\n\nwhere **.** represents all files. if you want to push a single file, just mention the name of the file. For example:\n\n```sh\n# Add the .gitignore file to git\ngit add .gitignore\n```\n\n```sh\n# Check the status of git.\ngit status\n```\n\n```sh\n# Commit the changes made. Use -m to write a message. It helps.\ngit commit -m \"Created .gitignore file\"\n```\n\n```sh\n# Create a connection to the repository.\ngit remote add origin \u003cremote_repository_URL\u003e\n```\n\n```sh\n# Push the git commits to the main branch.\ngit push -u origin main\n```\n\n```sh\n# Pull any changes to confirm that the changes were updated correctly.\ngit pull origin main\n```\n\n## **III. Setting up the Virtual Environment (env) folder**\n\nInside the folder, we need to set up the virtual environment (env) for this project, so that any installs or updates done for the libraries used in this project won't impact the global python system.\n\n```sh\n# Creating a virtual environment.\npython.exe -m venv env\n```\n\n```sh\n# Activating the virtual environment.\n./env/Scripts/activate\n```\n\n**Note**: Add the `env/` folder to the .gitignore file. You really don't want to forget this step.\n\n```sh\n# If you accidentally forgot to add it to .gitignore and did git add. You can remove it from git\ngit rm -r --cached env\n```\n\n```sh\n# Create a requirements file and do this command each time you download a library to stay up to date.\npip freeze \u003e requirements.txt\n```\n\n## **IV. Setting up the Flask Application**\n\n### **IV. I: Setting up the app folder**\n\nFor creating a flask application, we create an `app` folder and inside we create folders for `static` and `templates`.\n\n- The `static` files will contain the img, css, (js...) and any assets for the project.\n- The `templates` folder will contain static data as well as placeholder for dynamic data, usually HTML files that uses Jinja template.\n\n```\n# Flask Application Folder Structure\nHello CICD\n├─ app\n│   │\n│   ├── static\n│   │   ├── css\n│   │   │   └── style.css\n│   │   └── img\n│   │       └── ci-cd.png\n│   ├── templates\n│   │   └── index.html\n│   ├── __init__.py\n│   └── views.py\n└─ run.py\n```\n\n### **IV. II. Creating the HTML Template**\n\nWe'll start with the `./app/templates/index.html` file. This is just going to be a small HTML document just for the purpose of running it.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003ctitle\u003eHello CICD\u003c/title\u003e\n    \u003clink\n      rel=\"stylesheet\"\n      href=\"{{ url_for('static', filename='css/style.css') }}\"\n    /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello CI/CD!\u003c/h1\u003e\n    \u003cp\u003eThis is a test application being done to learn DevOps.\u003c/p\u003e\n    \u003cp\u003eAuthor: Edwin Ronald Lambert\u003c/p\u003e\n    \u003cp\u003eLast Updated: July 8, 2024\u003c/p\u003e\n    \u003cimg\n      src=\"{{ url_for('static', filename='img/ci-cd.png') }}\"\n      alt=\"Sample Image for CI/CS pipeline\"\n    /\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n**Explanations**:\n\n- `\u003c!DOCTYPE html\u003e` declares the document type and the version of HTML used (HTML5).\n- The `\u003chtml\u003e` tags wraps the entire HTML document.\n- The `\u003chead\u003e` section contains the meta-information of the document.\n  - `{{ url_for('static', filename='css/style.css') }}` is a Jinja2 template syntax used in Flask to dynamically generate the URL for the CSS file located in the static folder.\n- The `\u003cbody\u003e` section contains the content of the HTML document.\n  - `src=\"{{ url_for('static', filename='img/ci-cd.png') }}\"` dynamically generates the URL for the image file located in the static folder using Jinja2 syntax.\n  - `alt=\"Sample Image for CI/CD pipeline\"` provides alternative text for the image, which is useful for accessibility and if the image fails to load.\n\n**Note**: Create the `./static/css/style.css` to add stylesheet and design to the HTML file.\n\n### **IV. III. Create views.py**\n\nSince we're using the [Flask](https://pypi.org/project/Flask/) library. Let's install that into our virtual env.\n\n```sh\n# Install Flask\npip install Flask\n```\n\nWe then create the `views.py` file.\n\n```py\nfrom flask import Blueprint, render_template\n\nmain = Blueprint(\"main\", __name__)\n\n@main.route(\"/\")\ndef home():\n    return render_template(\"index.html\")\n```\n\n**Explanations**:\n\n- Import the necessary libraries.\n  - `Blueprint` helps organize the application into modules, making it easier to manage large applications.\n  - `render_template` is used to render HTML templates.\n- Using the `Blueprint()` method, we create a new Blueprint instance.\n  - `main` is the name of the blueprint.\n  - `__name__` is the name of the current module.\n- `@main.route(\"/\")` is a route decorator that is associated with the path '/' with the home function.\n- The `home()` function handles request to the root URL and renders the index.html template.\n\n### **IV. IV. Creating the **init**.py file**\n\n```py\nfrom flask import Flask\n\ndef create_app():\n    app = Flask(__name__)\n    from .views import main\n    app.register_blueprint(main)\n    return app\n```\n\n**Explanations**:\n\n- Import the necessary libraries.\n  - `Flask` module is necessary to create flask applications.\n- `app = Flask(__name__)` creates an instance of the Flask application. `__name__` is passed to `Flask` constructor to determine the root path of the application, which it needs to location resources like static and template files.\n- `from .views import main` import the `main` blueprint from `views` module. The dot (`.`) indicates that `views` module is within the same package as the current module.\n- `app.register_blueprint(main)` registers the `main` blueprint with the Flask application instance.\n- The `create_app()` function returns the Flask application instance.\n\n### **IV. V. Creating the run.py file**\n\nWe now create the `run.py` file to run this on the terminal.\n\n```py\nfrom app import create_app\n\napp = create_app()\n\nif __name__ == '__main__':\n    app.run(host=\"0.0.0.0\", port=5000)\n```\n\n**Explanations**:\n\n- `from app import create_app` imports the `create_app` function from the `app` module.\n- `app = create_app()` creates an instance of the Flask application and assigns it to app.\n- `app.run(host=\"0.0.0.0\", port = 5000)` run the Flask application with the following settings:\n  - `host=\"0.0.0.0\"` makes the server public available, listening on all available IP addresses. This is make sure that the application is accessible from other devices on the network.\n  - `port=5000` is the default port for Flask where the app will listen for incoming connections.\n  - `debug=True` was the previous command. Mentioned to make sure that the application enables debugging.\n\n### **IV. VI. Running the Flask application**\n\nWe run the application first for testing using the command.\n\n```sh\n# Run the run.py file.\npy run.py\n```\n\nThis will run the Flask application on your web browser at `localhost:5000`.\n\n## **V. Prepare Flask Application for Testing**\n\nEnsure that your Flask application has a dedicated test directory. We'll use Python’s built-in unittest framework.\n\n```\n# Flask Test Folder Structure\nHello CICD\n└─ tests\n    ├── __init__.py\n    └── test_views.py\n```\n\n```py\nimport unittest\nfrom flask import url_for\nfrom app import create_app\n\nclass FlaskTestCase(unittest.TestCase):\n\n    def setUp(self):\n        # Set up test client before each test.\n        self.app = create_app()\n        self.app.testing = True\n        self.client = self.app.test_client()\n\n    def test_home_status_code(self):\n        # Test that the homepage is accessible.\n        response = self.client.get('/')\n        self.assertEqual(response.status_code, 200)\n\n    def test_home_data(self):\n        # Test the data returned by the home page.\n        response = self.client.get('/')\n        self.assertIn(b'Hello CI/CD!', response.data)\n\n\nif __name__ ==\"__main__\":\n    unittest.main()\n```\n\n**Explanations**:\n\n- Import the necessary libraries.\n  - `unittest` is the Python's built-in module for creating and running tests.\n  - `create_app` creates and configures an instance of the Flask application.\n- We first create a test class that inherits from `unittest.Testcase`, providing a framework for writing tests.\n  - The `setUp` is a special method that is run before each test method. It is used to set up the state that is shared among the test methods.\n    - `self.app = create_app()`: Creates an instance of the Flask application using the create_app function.\n    - `self.app.testing = True`: Puts the Flask app in testing mode, which provides better error messages and ensures that exceptions propagate rather than being handled by the Flask error handlers.\n    - `self.client = self.app.test_client()`: Creates a test client that can be used to simulate HTTP requests to the Flask application. This client is used to interact with the application during testing.\n  - The `test_home_status_code` is a test method to verify that the home page is accessible.\n    - `self.assertEqual(response.status_code, 200)`: Asserts that the HTTP status code of the response is 200, indicating success.\n  - The `test_home_data` is a test method to verify the content of the home page.\n    - `self.assertIn(b'Hello CI/CD!', response.data)`: Asserts that the response data contains the byte string b'Hello CI/CD!'. This ensures that the expected content is present on the home page.\n\n## **VI. Setting up Docker**\n\nNow we will use Docker to build, test, and deploy applications quickly.\n\n### **VI. I. Setting up Docker Desktop**\n\n**Note**: I'm using WIndows (Yeah Yeah, I know. I hear you!)\n\n1. Go to Docker website and download [Docker Desktop](https://www.docker.com/products/docker-desktop/) depending on the platform you use.\n2. Double-click and run the Docker Desktop installer executable.\n3. Once the installation is complete, click \"Close\" and \"Restart\" your computer if prompted.\n4. After installation and reboot, launch Docker Desktop from the Start Menu.\n5. Docker Desktop will complete the initial setup. Follow any on-screen instructions to finalize the configuration.\n6. (Optional) Sign in to Docker Hub.\n\n```sh\n# Verify Installation\ndocker --version\n```\n\n```sh\n# Run a test container.\ndocker run hello-world\n```\n\nThis command will download a test image and run it in a container. If successful, you'll see a `\"Hello from Docker!\"` message.\n\n### **VI. II. Creating a Dockerfile**\n\nA `Dockerfile` is a text file that contains collections of instructions and commands that will be automatically executed in sequence in the docker environment for building a new docker image.\n\n```\n# Dockerfile folder structure\nHello CICD\n└─ Dockerfile\n```\n\n```dockerfile\n# Use an official Python runtime as a parent image\nFROM python:3.9-slim\n\n# Set the working directory to /app\nWORKDIR /app\n\n# Copy the current directory contents into the container at /app\nCOPY . /app\n\n# Intall any needed packages specified in requirements.txt\nRUN pip install --no-cache-dir -r requirements.txt\n\n# Make port 5000 available to the world outside this container\nEXPOSE 5000\n\n# Defined environment variables\nENV NAME World\n\n# RUN run.py when the container launches\nCMD [ \"python\", \"run.py\" ]\n```\n\n**Explanations**:\n\n- `FROM python:3.9-slim` specifies the base image for the Docker image. `python:3.9-slim` is an official Docker Image for Python 3.9 with a minimal footprint.\n- `WORKDIR /app` sets the working directory inside the container to `/app`. All subsequent instructions will be run in this directory.\n- `COPY . /app` copies all the contents of the current directory on the host machine into the `/app` directory in the container.\n- `RUN pip install --no-cache-dir -r requirements.txt` installs all the Python packages specified in the `requirements.txt` using `pip`. The `--no-cache-dir` option prevents `pip` from caching the package files, which reduces the image size.\n- `ENV NAME World` sets an environment variable `NAME` with the value `World` inside the container.\n- `CMD [ \"python\", \"run.py\" ]` specifies the command to run when the container starts.\n\n### **VI. III. Build and Run the Docker Container**\n\nNow, we build and run the docker container and verify that the applications runs inside the container by accessing it via `localhost:5000`.\n\n```sh\n# Build the docker image.\ndocker build -t hello-cicd .\n```\n\n**Explanations**:\n\n- `docker build` command is used to build a Docker image from a Dockerfile.\n- `-t hello-cicd` is used to tag the image with the name hello-cicd.\n- The dot `.` at th end of the command specifies the build context, which is the current directory. Docker will look for a `Dockerfile` in this directory and use it to build the image.\n\n```sh\n# Run the Docker Container\ndocker run -p 5000:5000 hello-cicd\n```\n\n**Explanation**:\n\n- `docker run` is the command runs a Docker container from a specified Docker image.\n- `-p 5000:5000` is used to publish a container's port to the host. The format is `host_port:container_post`.\n\nVerify that the application runs by opening the web browser and navigate to `https://localhost:5000`.\n\n## **VII. Setting Up Terraform**\n\n### **VII. I. Install and Verify Terraform**\n\n1. Download the appropriate [Terraform](https://developer.hashicorp.com/terraform/install) for your OS from the website.\n2. Extract the binary to a directory included in your system's PATH, such as `C:\\Windows\\System32` or add the binary location to the environment variables `PATH` variable or manually add it.\n3. Open terminal/command prompt and enter `terraform -v` to ensure it's correctly installed and variables are set.\n\n### **VII. II. Create Terraform Configuration Files**\n\n1. Navigate to your project directory and create a subdirectory named terraform.\n2. Inside this directory, create the following files:\n   - `main.tf`: Main configuration file where you will define the provider and resources.\n   - `variables.tf`: File to declare variables.\n   - `outputs.tf`: File to declare outputs.\n\n```\n# Terraform Folder Structure\nHello CICD\n└─ terraform\n    ├── main.tf\n    ├── outputs.tf\n    └── variable.tf\n```\n\n**main.tf** file\n\n```hcl\nterraform {\n    required_providers {\n        kubernetes = {\n            source = \"hashicorp/kubernetes\"\n        }\n    }\n}\n\nprovider \"kubernetes\" {\n    config_path = \"~/.kube/config\"\n}\n\nresource \"kubernetes_namespace\" \"hello-cicd\" {\n    metadata {\n        name = \"hello-cicd-namespace\"\n    }\n}\n```\n\n**Explanations**:\n\n- `terraform` is the block is used to specify settings related to Terraform itself, including provider requirements.\n  - `required_providers`: This block specifies the providers that are required for this configuration.\n    - `kubernetes` is the name of the provider.\n      - `source = \"hashicorp/kubernetes\"`: This specifies the source of the provider, indicating that the Kubernetes provider plugin should be downloaded from the HashiCorp repository.\n- `provider \"kubernetes\"` block configures the Kubernetes provider.\n  - `config_path = \"~/.kube/config\"`: This specifies the path to the Kubernetes configuration file (kubeconfig).\n- `resource \"kubernetes_namespace\" \"hello-cicd\"`: This block defines a resource of type kubernetes_namespace with the identifier hello-cicd.\n  - `metadata` block specifies metadata for the Kubernetes namespace.\n    - `name = \"hello-cicd-namespace\"`: This sets the name of the namespace to hello-cicd-namespace.\n\n**outputs.tf** file\n\n```\noutput \"namespace\" {\n    value = kubernetes_namespace.hello-cicd.metadata[0].name\n}\n```\n\n**Explanations**:\n\n- `output \"namespace\"`: This declares an output block named namespace. Outputs are used to make information about your infrastructure available after the configuration has been applied.\n  - `value`: This specifies the value that the output will contain. In this case, the value is being set to kubernetes_namespace.hello-cicd.metadata[0].name.\n\n### **VII. III. Initialize and apply Terraform Configurations**\n\n1. Navigate to the `Terraform` folder inside the main directory.\n2. Run `terraform init` to initialize the directory with necessary Terraform configurations.\n3. Execute terraform plan to see the execution plan.\n4. Run terraform apply to apply the configurations and create the resources in your cluster.\n\n## **VIII. Setting up Container Orchestration with Kubernetes**\n\nKubernetes can be used to automatically provisions, deploys, scales, and manages containerized applications without worrying about the underlying infrastructure.\n\n### **VIII. I. Enabling Kubernetes in Docker Desktop**\n\n1. Open Docker Desktop.\n2. Go to Settings \u003e Kubernetes.\n3. Check \"Enable Kubernetes\" and apply changes.\n\n### **VIII. II. Creating Kubernetes Configuration files**\n\n1. Navigate to your project directory and create a subdirectory named k8s.\n2. Inside this directory, create files such as deployment.yaml, service.yaml, and ingress.yaml.\n\n```\n# Terraform Folder Structure\nHello CICD\n└─ k8s\n    ├── deployment.yaml\n    ├── ingress.yaml\n    └── service.yaml\n```\n\n**deployment.yaml** file\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: hello-cicd\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: hello-cicd\n  template:\n    metadata:\n      labels:\n        app: hello-cicd\n    spec:\n      containers:\n        - name: hello-cicd\n          image: {username}/hello-cicd:latest\n          ports:\n            - containerPort: 5000\n```\n\n**Explanations**:\n\n- `apiVersion: apps/v1` specifies the API version for the Kubernetes Deployment object. `apps/v1` is the stable API version for managing deployments.\n- `kind: Deployment` indicates that the resource being defined is a Deployment. A Deployment ensures that a specified number of pod replicas are running at any given time.\n- `metadata` provides metadata about the deployment.\n- `spec` describes the desired state of the Deployment.\n  - `replicas: 3` specifies that three replicas (pods) of the application should be running.\n  - `selector` defines how to identify the pods managed by this Deployment.\n    - `matchLabels` specifies that pods with the label app: hello-cicd are selected.\n  - `template` describes the pod that will be created by the Deployment.\n    - `containers` specifies the containers that will run in the pods.\n      - `name: hello-cicd` is the name of the container.\n      - `image: {username}/hello-cicd:latest` is the Docker image to use for the container.\n      - `ports` specifies the ports that the container will expose.\n        - `containerPort: 5000` mentions that the container will listen on port 5000.\n\n**service.yaml** file\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: hello-cicd-service\n  namespace: hello-cicd-namespace\nspec:\n  type: LoadBalancer\n  ports:\n    - port: 5000\n      targetPort: 5000\n      protocol: TCP\n  selector:\n    app: hello-cicd\n```\n\n**Explanations**:\n\n- `kind: Service` indicates that the resource being defined is a Service. A Service is an abstraction that defines a logical set of pods and a policy to access them.\n- `spec` describes the desired state of the Service.\n  - `type: LoadBalancer` specifies that the Service should be of type LoadBalancer, which exposes the Service externally using a cloud provider’s load balancer.\n  - `ports` defines the ports that the Service will expose.\n    - `port: 5000` si the port on which the Service will be exposed.\n    - `targetPort: 5000` is the port on the container that the traffic will be forwarded to.\n    - `protocol: TCP` is the protocol used by the Service. In this case, it’s TCP.\n  - `selector` defines how the Service will identify the pods it routes traffic to.\n\n### **VIII. III. Deploy and Access the Application**\n\n1. On the terminal, use `kubectl apply -f k8s/` to apply your Kubernetes configurations.\n2. Since you’re using Docker Desktop, your service type can be LoadBalancer.\n3. Run `kubectl get services -n hello-cicd-namespace` to find the IP and port to access your Flask application.\n\n## **IX. Setting Up Continuous Integration and Continuous Deployment (CI/CD) with Jenkins**\n\nFirst, you need to have Jenkins installed on your server or local machine.\n\n### **IX. I. Setting up Jenkins**\n\n1. Download [Jenkins](https://www.jenkins.io/download/) for your platform.\n2. Install Jenkins based on the instructions specific to the OS.\n3. After installation, navigate to `https://localhost:8080`.\n4. Follow the on-screen installation to complete the setup, which includes unlocking Jenkins using the initial admin password found in the installation logs or file.\n\n### **IX. II. Configure Jenkins for the Project**\n\nAfter installation, we need to set up the project in Jenkins.\n\n1. Create a New Job:\n   - On the Jenkin's dashboard, click New Item.\n   - Enter the name for the job. For Example, `hello-cicd`.\n   - Select `Pipeline` and click OK.\n2. Configure Source Code Management:\n   - In the job configurations, scroll to the \"`pipeline`\" section.\n   - You can choose either \"`Pipeline script`\" to write the Jenkinsfile script directly in the UI, or \"`Pipeline script from SCM`\" to load it from your source control. `Git` in my case.\n\n### **IX. III. Creating a Jenkinsfile**\n\nA `Jenkinsfile` defines the steps that Jenkins should follow as part of the CI pipeline.\n\n```groovy\npipeline {\n    agent any\n    environment {\n        // Update this with your Docker Hub username and the repository name\n        DOCKER_IMAGE = '{username}/hello-cicd'\n\n        // Specify the path to your kubeconfig file correctly for a Windows machine.\n        KUBECONFIG = 'C:\\\\Users\\\\user\\\\.kube\\\\config'\n    }\n    stages {\n        stage('Preparation') {\n            steps {\n                script {\n                    bat \"python -m pip install -r requirements.txt\"\n                }\n            }\n        }\n        stage('Test') {\n            steps {\n                script {\n                    bat \"python -m unittest discover -s tests\"\n                }\n            }\n        }\n        stage('Build') {\n            steps {\n                script {\n                    bat \"docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} .\"\n                    bat \"docker push ${DOCKER_IMAGE}:${BUILD_NUMBER}\"\n                }\n            }\n        }\n        stage('Deploy') {\n            steps {\n                script {\n                    // Now using 'hello-cicd' as the container name as per your deployment details\n                    bat \"kubectl set image deployment/hello-cicd hello-cicd=${DOCKER_IMAGE}:${BUILD_NUMBER} -n hello-cicd-namespace\"\n                }\n            }\n        }\n    }\n}\n```\n\n**Explanation**:\n\n- `pipeline` declares the beginning of the Jenkins pipeline.\n- `agent any` specifies that the pipeline can run on any available Jenkins agent.\n- `environment` defines the environment variables that are accessible throughout the pipeline.\n  - `DOCKER_IMAGE` is the docker image name.\n  - `KUBECONFIG` is the path to Kubernetes configuration file (`kubeconfig`)\n- `stages` consists of the following stages: `Preparation`, `Test`, `Build` and `Deploy`.\n  - `stage('Preparation')` declares a pipeline stage named \"Preparation\".\n    - `bat \"python -m pip install -r requirements.txt\"` is a batch command that runs updates pip (Python’s package installer) and then installs all the Python dependencies listed in the requirements.txt file.\n  - `stage('Test')` declares a new stage named \"Test\". \n    - `bat \"python -m unittest discover -s tests\"` command runs the Python unittest module's discovery feature, which automatically identifies and runs tests. The -s tests option tells unittest to look for test files in the tests directory. \n  - `stage(\"Build\")` defines a stage named \"Build\".\n    - `bat \"docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} .\"` uses the `bat` command to execute a batch script on the Windows machine. It builds a Docker image with a tag that includes the Docker image name and the Jenkins build number.\n    - `bat \"docker push ${DOCKER_IMAGE}:${BUILD_NUMBER}\"` pushes the docker image to the Docker Hub using the tag created in the previous step.\n  - `stage(\"Deploy\")` defines a stage named \"Deploy\".\n    - `bat \"kubectl set image deployment/hello-cicd hello-cicd=${DOCKER_IMAGE}:${BUILD_NUMBER} -n hello-cicd-namespace\"` uses the `bat` command to execute a batch script to update the Kubernetes deployment named `hello-cicd` in the `hello-cicd-namespace` to use the new Docker image tagged with the current build number.\n\n### **IX. IV. Build the Pipeline**\n\nConfigure the Jenkins pipeline to pull the code from the GitHub repository, build the Docker image, run tests, and deploy the application.\n\n1. On the Jenkins dashboard, Click on the job you just created.\n2. Click `Build Now` to start the pipeline.\n3. After clicking Build Now, you'll see a new build appear under the Build History.\n4. Click on the build number or the progress bar icon to open the build status page.\n5. Here you can monitor the progress and see the console output by clicking Console Output. This will show you real-time logs of the build process, including any tests run, Docker build steps, and deployment actions.\n\n### **IX. V. Viewing your application**\n\n1. If your Jenkins server and Flask app are deployed on the same network or machine, you can access your Flask application by navigating to `http://\u003cjenkins-host-ip\u003e:5000` in your web browser.\n2. If it’s running as a Docker container directly on the Jenkins host, and you've mapped the ports correctly as in the example, http://localhost:5000 should display your Flask app.\n\n## **X. Expose Local Development Environment with Ngrok**\n\nIf your Jenkins server is hosted locally and you want to expose it to the internet to handle incoming webhooks from services like GitHub, GitLab, or Bitbucket, you can use ngrok. Ngrok is a tool that creates a secure tunnel to your localhost, providing an externally accessible URL that forwards to your local development environment.\n\n### **X. I. Download and install ngrok**\n\n1. Go to the [ngrok](https://ngrok.com/download) website and download the version appropriate for your operating system.\n2. Extract the downloaded file. This will give you the ngrok executable.\n3. To use ngrok, you need to sign up for an account. Once signed up, log in to your dashboard on the ngrok website.\n4. In your ngrok dashboard, you will find your `auth token`. This token is used to authenticate with ngrok's servers.\n5. Open terminal/command prompt and navigate to the directory where you extracted ngrok.\n6. Connect your account using the auth token.\n   ```sh\n   ./ngrok authtoken \u003cYOUR_AUTH_TOKEN\u003e\n   ```\n\n### **X. II. Start ngrok Tunnel**\n\nYou need to know on which port your Jenkins server is running. By default, Jenkins typically runs on port 8080.\n\nIn the terminal, change to where ngrock executable is kept and run the following command to start an ngrok tunnel to the Jenkins port:\n\n```sh\n./ngrok http 8080\n```\n\nThis command tells ngrok to forward HTTP traffic from the ngrok URL to port 8080 on your localhost.\n\nOnce ngrok is running, it will display an URL in the terminal that looks something like `http://\u003crandom-id\u003e.ngrok-free.ap`. This URL is publicly accessible and routes to your local Jenkins server.\n\n**Note**: Your ngrok tunnel must be kept running as long as you need external access to your local Jenkins. If ngrok stops, the URL will no longer function.\n\n## **XI. Automate Actions with GitHub Webhooks**\n\nConfigure webhooks to automate actions such as triggering Jenkins jobs or updating the deployment status.\n\nUse the ngrok URL in your webhook configuration for GitHub, GitLab, or Bitbucket. Append any specific endpoint required by the plugin you are using (like /github-webhook/ for GitHub).\n\n```sh\nhttp://\u003crandom-id\u003e.ngrok.io/github-webhook/\n```\n\n### **XI. I. GitHub Setup**\n\n1. Go to your repository on GitHub.\n2. Click on `Settings` \u003e `Webhooks` \u003e `Add webhook`.\n   - Payload URL: Enter the Jenkins URL followed by /github-webhook/. For example: `http://\u003crandom-id\u003e.ngrok.io/github-webhook/`.\n   - Content type: Select `application/json`.\n   - Which events would you like to trigger this webhook?: Choose \"`Just the push event.`\"\n   - Ensure the `Active` box is checked to enable the webhook.\n3. Click Add webhook.\n\n### **XI. II. Jenkins Setup**\n\n1. Ensure that the \"GitHub plugin\" is installed in Jenkins. You can check this under `Manage Jenkins` \u003e `Manage Plugins` \u003e `Available/Installed`.\n2. In Jenkins, go to `Manage Jenkins` \u003e `Configure System`.\n3. Under the GitHub section, ensure your GitHub credentials are set up and add a new GitHub Server if necessary.\n4. Go to your job configuration page by clicking on the job and then clicking \"`Configure`\".\n5. Under \"`Build Triggers`\", check \"`GitHub hook trigger for GITScm polling`\".\n\n## **XII. Test the Full Workflow**\n\nTest the entire CI/CD pipeline from code commit to deployment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedwinrlambert%2Fhello-cicd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedwinrlambert%2Fhello-cicd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedwinrlambert%2Fhello-cicd/lists"}