Skip to content
Closed

Dev #33

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.git/
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.dockerignore
*.swp
*.swo
*.bak
*.tmp
*.temp
.cache/
.idea/
.vscode/
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM python:3.12.4

RUN apt update -y && apt install -y build-essential libpq-dev
RUN pip install --upgrade pip setuptools wheel


WORKDIR /usr/src/app

COPY analytics .

RUN pip install -r requirements.txt

ENV DB_USERNAME=myuser
ENV DB_PASSWORD=mypassword
ENV DB_HOST=127.0.0.1
ENV DB_PORT=5433
ENV DB_NAME=mydatabase

EXPOSE 5153

CMD ["python", "app.py"]
139 changes: 37 additions & 102 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,131 +1,66 @@
# 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.

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.

## Getting Started

### 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

#### 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

### Setup
#### 1. Configure a Database
Set up a Postgres database using a Helm Chart.

1. Set up Bitnami Repo
# create cluster
```bash
helm repo add <REPO_NAME> https://charts.bitnami.com/bitnami
```

2. Install PostgreSQL Helm Chart
```
helm install <SERVICE_NAME> <REPO_NAME>/postgresql
eksctl create cluster --name my-cluster --region us-east-1 --nodegroup-name my-nodes --node-type t3.small --nodes 1 --nodes-min 1 --nodes-max 2 --profile udacity-cloud
```

This should set up a Postgre deployment at `<SERVICE_NAME>-postgresql.default.svc.cluster.local` in your Kubernetes cluster. You can verify it by running `kubectl svc`

By default, it will create a username `postgres`. The password can be retrieved with the following command:
# Update kubeconfig
```bash
export POSTGRES_PASSWORD=$(kubectl get secret --namespace default <SERVICE_NAME>-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d)

echo $POSTGRES_PASSWORD
aws eks --region us-east-1 update-kubeconfig --name my-cluster --profile udacity-cloud
kubectl config current-context
```

<sup><sub>* The instructions are adapted from [Bitnami's PostgreSQL Helm Chart](https://artifacthub.io/packages/helm/bitnami/postgresql).</sub></sup>

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/)

* Connecting Via Port Forwarding
# Create db service
```bash
kubectl port-forward --namespace default svc/<SERVICE_NAME>-postgresql 5432:5432 &
PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432
kubectl get storageclass
kubectl apply -f deployment/pvc.yaml
kubectl apply -f deployment/pv.yaml
kubectl apply -f deployment/postgresql-deployment.yaml
kubectl apply -f deployment/postgresql-service.yaml
```

* Connecting Via a Pod
# setup port-forwarding from local:5433 to postgresql-service:5432
```bash
kubectl exec -it <POD_NAME> bash
PGPASSWORD="<PASSWORD HERE>" psql postgres://postgres@<SERVICE_NAME>:5432/postgres -c <COMMAND_HERE>
kubectl port-forward service/postgresql-service 5433:5432 &
```

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.
# set DB env variables

```bash
kubectl port-forward --namespace default svc/<SERVICE_NAME>-postgresql 5432:5432 &
PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432 < <FILE_NAME.sql>
export DB_PASSWORD=mypassword
export DB_USERNAME=myuser
export DB_HOST="127.0.0.1"
export DB_NAME="mydatabase"
export DB_PORT=5433
```

### 2. Running the Analytics Application Locally
In the `analytics/` directory:

1. Install dependencies
# Create table and insert data
```bash
pip install -r requirements.txt
PGPASSWORD="$DB_PASSWORD" psql --host 127.0.0.1 -U myuser -d mydatabase -p 5433 < db/<files.sql>
```
2. Run the application (see below regarding environment variables)

# connect db using port forwarding
```bash
<ENV_VARS> python app.py
PGPASSWORD="$DB_PASSWORD" psql --host 127.0.0.1 -U myuser -d mydatabase -p 5433
```

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.
# build app locally
```bash
apt update -y && apt install build-essential libpq-dev -y
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt
python app.py

* `DB_USERNAME`
* `DB_PASSWORD`
* `DB_HOST` (defaults to `127.0.0.1`)
* `DB_PORT` (defaults to `5432`)
* `DB_NAME` (defaults to `postgres`)
curl 127.0.0.1:5153/api/reports/daily_usage
curl 127.0.0.1:5153/api/reports/user_visits
```

If we set the environment variables by prepending them, it would look like the following:
# build app with docker
```bash
DB_USERNAME=username_here DB_PASSWORD=password_here python app.py
docker build -t test-coworking-analytics .
docker run --network="host" test-coworking-analytics
```
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 <BASE_URL>/api/reports/daily_usage`

* Generate report for check-ins grouped by users
`curl <BASE_URL>/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 <DATABASE_SERVICE_NAME>`
7. Screenshot of `kubectl describe deployment <SERVICE_NAME>`
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?
# Set up Continuous Integration with CodeBuild

### 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.
19 changes: 19 additions & 0 deletions buildspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 0.2

phases:
pre_build:
commands:
- echo Logging into ECR
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin 741769714199.dkr.ecr.us-east-1.amazonaws.com
build:
commands:
- echo Starting build at `date`
- echo Building the Docker image...
- docker build -t coworking:$CODEBUILD_BUILD_NUMBER .
- docker tag coworking:$CODEBUILD_BUILD_NUMBER 741769714199.dkr.ecr.us-east-1.amazonaws.com/coworking:$CODEBUILD_BUILD_NUMBER
post_build:
commands:
- echo Completed build at `date`
- echo Pushing the Docker image...
- docker push 741769714199.dkr.ecr.us-east-1.amazonaws.com/coworking:$CODEBUILD_BUILD_NUMBER

34 changes: 34 additions & 0 deletions cmd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# create cluster
eksctl create cluster --name my-cluster --region us-east-1 --nodegroup-name my-nodes --node-type t3.small --nodes 1 --nodes-min 1 --nodes-max 2 --profile udacity-cloud

# Create db service
kubectl get storageclass
kubectl apply -f pvc.yaml
kubectl apply -f pv.yaml
kubectl apply -f postgresql-deployment.yaml
kubectl apply -f postgresql-service.yaml

# setup port-forwarding from local:5433 to postgresql-service:5432
kubectl port-forward service/postgresql-service 5433:5432 &

# set DB env variables
export DB_PASSWORD=mypassword
export DB_USERNAME=myuser
export DB_HOST="127.0.0.1"
export DB_PORT="5432"
export DB_NAME="mydatabase"

# Insert data
PGPASSWORD="$DB_PASSWORD" psql --host 127.0.0.1 -U myuser -d mydatabase -p 5433 < ./db/files.sql

# connect db
PGPASSWORD="$DB_PASSWORD" psql --host 127.0.0.1 -U myuser -d mydatabase -p 5433

# run local
python app.py
curl 127.0.0.1:5153/api/reports/daily_usage
curl 127.0.0.1:5153/api/reports/user_visits

# build local docker
docker build -t test-coworking-analytics .
docker run --network="host" test-coworking-analytics
Empty file added db/initdb.sh
Empty file.
18 changes: 5 additions & 13 deletions deployment/configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: <NAME OF THE ConfigMap>
name: configmap
data:
DB_NAME: <ENTER YOUR DB NAME HERE>
DB_USER: <ENTER YOUR USER NAME HERE>
DB_HOST: <ENTER YOUR DB HOST HERE>
DB_PORT: <ENTER YOUR DB PORT HERE>
---
apiVersion: v1
kind: Secret
metadata:
name: <NAME OF THE Secret>
type: Opaque
data:
<THE KEY FROM Secret WHICH has THE ENCODED PASSWORD>: <OUTPUT OF `echo -n 'the password' | base64`>
DB_NAME: "mydatabase"
DB_USER: "myuser"
DB_HOST: "postgresql-service.default.svc.cluster.local"
DB_PORT: "5432"
32 changes: 32 additions & 0 deletions deployment/postgresql-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgresql
spec:
selector:
matchLabels:
app: postgresql
template:
metadata:
labels:
app: postgresql
spec:
containers:
- name: postgresql
image: postgres:latest
env:
- name: POSTGRES_DB
value: mydatabase
- name: POSTGRES_USER
value: myuser
- name: POSTGRES_PASSWORD
value: mypassword
ports:
- containerPort: 5432
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgresql-storage
volumes:
- name: postgresql-storage
persistentVolumeClaim:
claimName: postgresql-pvc
10 changes: 10 additions & 0 deletions deployment/postgresql-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: postgresql-service
spec:
ports:
- port: 5432
targetPort: 5432
selector:
app: postgresql
13 changes: 13 additions & 0 deletions deployment/pv.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-manual-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: gp2
hostPath:
path: "/mnt/data"
11 changes: 11 additions & 0 deletions deployment/pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-pvc
spec:
storageClassName: gp2
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Loading