diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..151121e7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +env +__pycache__ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..d46488d3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Use the official Python slim buster image as a base +FROM python:3.9-slim-buster + +# Set the working directory in the container +WORKDIR /app + +# Copy the requirements file into the container +COPY ./analytics/requirements.txt requirements.txt + +# Install system dependencies +RUN apt-get update && \ + apt-get install -y build-essential libpq-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Upgrade pip and install Python dependencies +RUN pip install --upgrade pip setuptools wheel && \ + pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code into the container +COPY ./analytics . + +# Expose the port the app runs on +EXPOSE 5153 + +# Command to run the application +CMD ["python", "app.py"] diff --git a/README.md b/README.md index d9a987b6..f5893d88 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,41 @@ -# Coworking Space Service Extension -The Coworking Space Service is a set of APIs that enables users to request one-time tokens and administrators to authorize access to a coworking space. This service follows a microservice pattern and the APIs are split into distinct services that can be deployed and managed independently of one another. +# Coworking Space Service Extension: Analytics API Deployment -For this project, you are a DevOps engineer who will be collaborating with a team that is building an API for business analysts. The API provides business analysts basic analytics data on user activity in the service. The application they provide you functions as expected locally and you are expected to help build a pipeline to deploy it in Kubernetes. +## Overview +The Coworking Space Service Analytics API provides business analysts with basic analytics data on user activity within the coworking space. The application is deployed within a Kubernetes environment managed via AWS EKS. -## Getting Started +## Technologies and Tools -### Dependencies -#### Local Environment -1. Python Environment - run Python 3.6+ applications and install Python dependencies via `pip` -2. Docker CLI - build and run Docker images locally -3. `kubectl` - run commands against a Kubernetes cluster -4. `helm` - apply Helm Charts to a Kubernetes cluster +- **Python 3.9+**: Core language for the analytics application. +- **Docker CLI**: To build, test, and run Docker images locally. +- **kubectl**: For managing Kubernetes clusters. +- **Helm**: For deploying and managing applications in Kubernetes. +- **AWS ECR**: Container Registry for storing Docker images. +- **AWS CodeBuild**: Automates image builds and pushes to ECR. +- **Kubernetes with AWS EKS**: The eks cluster should have the necessary permissions and policy for dynamic volumes creation +- **CloudWatch**: Monitoring and logging service to track application performance and health. -#### Remote Resources -1. AWS CodeBuild - build Docker images remotely -2. AWS ECR - host Docker images -3. Kubernetes Environment with AWS EKS - run applications in k8s -4. AWS CloudWatch - monitor activity and logs in EKS -5. GitHub - pull and clone code +## Deployment Process -### Setup -#### 1. Configure a Database -Set up a Postgres database using a Helm Chart. +**1. Database Setup:** We use the Bitnami Helm chart to deploy PostgreSQL in the Kubernetes cluster. +NB: After adding the Helm repository, modify the values.yaml file to add the database configuration, the storageclass for your eks cluster and the persistent volume configuration. Retrieve the password that will be used to connect to the database to run seed files. -1. Set up Bitnami Repo -```bash -helm repo add https://charts.bitnami.com/bitnami -``` +**2. Docker image Build:** Create the Dockerfile with the instructions to build the image. After building the image, push it to ECR -2. Install PostgreSQL Helm Chart -``` -helm install /postgresql -``` +**3. CodeBuild pipeline:** Create the CodeBuild project and define the buildspec.yml file with the necessary steps to automate the build process. Configure the project to trigger the pipeline with a push event in the GitHub repository. -This should set up a Postgre deployment at `-postgresql.default.svc.cluster.local` in your Kubernetes cluster. You can verify it by running `kubectl svc` +**4. Kubernetes deployment:** Define the manifests to be used to deploy the app (in the deployment folder). Deploy the configmap, the secret, the deployment and the service to the cluster. Make sure the deployment configuration file uses the latest version of the docker image stored in ECR. Verify that the deployment is successful and test the application endpoints. -By default, it will create a username `postgres`. The password can be retrieved with the following command: -```bash -export POSTGRES_PASSWORD=$(kubectl get secret --namespace default -postgresql -o jsonpath="{.data.postgres-password}" | base64 -d) +**5. Monitoring:** Install the amazon-cloudwatch-observability addon to be abble to monitor the application logs in CloudWatch -echo $POSTGRES_PASSWORD -``` +## Releasing New Builds +To release new builds, follow these steps: -* The instructions are adapted from [Bitnami's PostgreSQL Helm Chart](https://artifacthub.io/packages/helm/bitnami/postgresql). +1. **Make Changes**: Implement the necessary changes or features in the codebase. -3. Test Database Connection -The database is accessible within the cluster. This means that when you will have some issues connecting to it via your local environment. You can either connect to a pod that has access to the cluster _or_ connect remotely via [`Port Forwarding`](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/) +2. **Commit and Push**: Commit your changes and push them to the main branch. This action will trigger the CodeBuild pipeline. -* Connecting Via Port Forwarding -```bash -kubectl port-forward --namespace default svc/-postgresql 5432:5432 & - PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432 -``` +3. **Monitor Build Status**: Check the AWS CodeBuild console to monitor the build process. Ensure that the build completes successfully without errors. -* Connecting Via a Pod -```bash -kubectl exec -it bash -PGPASSWORD="" psql postgres://postgres@:5432/postgres -c -``` +4. **Modify the deployment manifest**: Use the latest image URI to deploy the app in kubernetes -4. Run Seed Files -We will need to run the seed files in `db/` in order to create the tables and populate them with data. - -```bash -kubectl port-forward --namespace default svc/-postgresql 5432:5432 & - PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432 < -``` - -### 2. Running the Analytics Application Locally -In the `analytics/` directory: - -1. Install dependencies -```bash -pip install -r requirements.txt -``` -2. Run the application (see below regarding environment variables) -```bash - python app.py -``` - -There are multiple ways to set environment variables in a command. They can be set per session by running `export KEY=VAL` in the command line or they can be prepended into your command. - -* `DB_USERNAME` -* `DB_PASSWORD` -* `DB_HOST` (defaults to `127.0.0.1`) -* `DB_PORT` (defaults to `5432`) -* `DB_NAME` (defaults to `postgres`) - -If we set the environment variables by prepending them, it would look like the following: -```bash -DB_USERNAME=username_here DB_PASSWORD=password_here python app.py -``` -The benefit here is that it's explicitly set. However, note that the `DB_PASSWORD` value is now recorded in the session's history in plaintext. There are several ways to work around this including setting environment variables in a file and sourcing them in a terminal session. - -3. Verifying The Application -* Generate report for check-ins grouped by dates -`curl /api/reports/daily_usage` - -* Generate report for check-ins grouped by users -`curl /api/reports/user_visits` - -## Project Instructions -1. Set up a Postgres database with a Helm Chart -2. Create a `Dockerfile` for the Python application. Use a base image that is Python-based. -3. Write a simple build pipeline with AWS CodeBuild to build and push a Docker image into AWS ECR -4. Create a service and deployment using Kubernetes configuration files to deploy the application -5. Check AWS CloudWatch for application logs - -### Deliverables -1. `Dockerfile` -2. Screenshot of AWS CodeBuild pipeline -3. Screenshot of AWS ECR repository for the application's repository -4. Screenshot of `kubectl get svc` -5. Screenshot of `kubectl get pods` -6. Screenshot of `kubectl describe svc ` -7. Screenshot of `kubectl describe deployment ` -8. All Kubernetes config files used for deployment (ie YAML files) -9. Screenshot of AWS CloudWatch logs for the application -10. `README.md` file in your solution that serves as documentation for your user to detail how your deployment process works and how the user can deploy changes. The details should not simply rehash what you have done on a step by step basis. Instead, it should help an experienced software developer understand the technologies and tools in the build and deploy process as well as provide them insight into how they would release new builds. - - -### Stand Out Suggestions -Please provide up to 3 sentences for each suggestion. Additional content in your submission from the standout suggestions do _not_ impact the length of your total submission. -1. Specify reasonable Memory and CPU allocation in the Kubernetes deployment configuration -2. In your README, specify what AWS instance type would be best used for the application? Why? -3. In your README, provide your thoughts on how we can save on costs? - -### Best Practices -* Dockerfile uses an appropriate base image for the application being deployed. Complex commands in the Dockerfile include a comment describing what it is doing. -* The Docker images use semantic versioning with three numbers separated by dots, e.g. `1.2.1` and versioning is visible in the screenshot. See [Semantic Versioning](https://semver.org/) for more details. \ No newline at end of file +5. **Verify Deployment**: After the deployment is complete, verify that the new version is running correctly by checking the application logs in CloudWatch. diff --git a/buildspec.yml b/buildspec.yml new file mode 100644 index 00000000..54659fab --- /dev/null +++ b/buildspec.yml @@ -0,0 +1,20 @@ +version: 0.2 + +phases: + pre_build: + commands: + - echo Logging into Docker Hub + - echo $DOCKER_HUB_PASSWORD | docker login --username $DOCKER_HUB_USERNAME --password-stdin + - echo Logging into ECR + - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com + build: + commands: + - echo Starting build at `date` + - echo Building the Docker image... + - docker build -t $IMAGE_REPO_NAME:$CODEBUILD_BUILD_NUMBER . + - docker tag $IMAGE_REPO_NAME:$CODEBUILD_BUILD_NUMBER $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$CODEBUILD_BUILD_NUMBER + post_build: + commands: + - echo Completed build at `date` + - echo Pushing the Docker image... + - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$CODEBUILD_BUILD_NUMBER diff --git a/deployment/configmap.yaml b/deployment/configmap.yaml index 7dcae113..2f733756 100644 --- a/deployment/configmap.yaml +++ b/deployment/configmap.yaml @@ -1,17 +1,9 @@ apiVersion: v1 kind: ConfigMap metadata: - name: + name: my-postgres-postgresql data: - DB_NAME: - DB_USER: - DB_HOST: - DB_PORT: ---- -apiVersion: v1 -kind: Secret -metadata: - name: -type: Opaque -data: - : \ No newline at end of file + DB_NAME: "mydatabase" + DB_USERNAME: "myuser" + DB_HOST: "my-postgres-postgresql.default.svc.cluster.local" + DB_PORT: "5432" diff --git a/deployment/coworking.yaml b/deployment/coworking.yaml index cf86a612..aa5c5c2b 100644 --- a/deployment/coworking.yaml +++ b/deployment/coworking.yaml @@ -30,7 +30,7 @@ spec: spec: containers: - name: coworking - image: + image: 597052192244.dkr.ecr.us-east-1.amazonaws.com/coworking-app:latest imagePullPolicy: IfNotPresent livenessProbe: httpGet: @@ -46,11 +46,11 @@ spec: timeoutSeconds: 5 envFrom: - configMapRef: - name: + name: my-postgres-postgresql env: - name: DB_PASSWORD valueFrom: secretKeyRef: - name: - key: - restartPolicy: Always \ No newline at end of file + name: my-postgres-postgresql + key: DB_PASSWORD + restartPolicy: Always diff --git a/deployment/secret.yaml b/deployment/secret.yaml new file mode 100644 index 00000000..a7b64837 --- /dev/null +++ b/deployment/secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: my-postgres-postgresql +type: Opaque +data: + DB_PASSWORD: bXlwYXNzd29yZA== diff --git a/screenshots/2-1 codebuild project.PNG b/screenshots/2-1 codebuild project.PNG new file mode 100644 index 00000000..cc9e7548 Binary files /dev/null and b/screenshots/2-1 codebuild project.PNG differ diff --git a/screenshots/2-2 codebuild trigger.PNG b/screenshots/2-2 codebuild trigger.PNG new file mode 100644 index 00000000..5f0f005d Binary files /dev/null and b/screenshots/2-2 codebuild trigger.PNG differ diff --git a/screenshots/2-3-codebuild pipeline.PNG b/screenshots/2-3-codebuild pipeline.PNG new file mode 100644 index 00000000..9705c52a Binary files /dev/null and b/screenshots/2-3-codebuild pipeline.PNG differ diff --git a/screenshots/3- ECR repository.PNG b/screenshots/3- ECR repository.PNG new file mode 100644 index 00000000..744f3893 Binary files /dev/null and b/screenshots/3- ECR repository.PNG differ diff --git a/screenshots/4- get services.PNG b/screenshots/4- get services.PNG new file mode 100644 index 00000000..3b6b3503 Binary files /dev/null and b/screenshots/4- get services.PNG differ diff --git a/screenshots/5- kubectl get pods.PNG b/screenshots/5- kubectl get pods.PNG new file mode 100644 index 00000000..cf65a8e1 Binary files /dev/null and b/screenshots/5- kubectl get pods.PNG differ diff --git a/screenshots/6- kubectl describe svc database.PNG b/screenshots/6- kubectl describe svc database.PNG new file mode 100644 index 00000000..8751c4e0 Binary files /dev/null and b/screenshots/6- kubectl describe svc database.PNG differ diff --git a/screenshots/7-1-kubectl describe deployment.PNG b/screenshots/7-1-kubectl describe deployment.PNG new file mode 100644 index 00000000..6fc2e5be Binary files /dev/null and b/screenshots/7-1-kubectl describe deployment.PNG differ diff --git a/screenshots/7-2-kubectl describe deployment.PNG b/screenshots/7-2-kubectl describe deployment.PNG new file mode 100644 index 00000000..29a77e5c Binary files /dev/null and b/screenshots/7-2-kubectl describe deployment.PNG differ diff --git a/screenshots/8-1-cloudwatch log-groups.PNG b/screenshots/8-1-cloudwatch log-groups.PNG new file mode 100644 index 00000000..fdda239e Binary files /dev/null and b/screenshots/8-1-cloudwatch log-groups.PNG differ diff --git a/screenshots/8-2-cloudwatch application logs.PNG b/screenshots/8-2-cloudwatch application logs.PNG new file mode 100644 index 00000000..db1ad708 Binary files /dev/null and b/screenshots/8-2-cloudwatch application logs.PNG differ