Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8fb6a9f
spelling
kbehrman Jul 6, 2019
e7c8e4e
Merge branch 'master' of github.com:udacity/FSND_Deploy_Flask_App_to_…
kbehrman Jul 7, 2019
322c852
Update README.md
swwelch Jul 15, 2019
54b3d47
Update README.md
swwelch Jul 15, 2019
77b3325
Update README.md
kbehrman Jul 29, 2019
f29194c
Update README.md
kbehrman Jul 29, 2019
7c49dd1
Update README.md
kbehrman Jul 29, 2019
da52c1f
Updated README to move detailed instruction into the classroom.
swwelch Aug 1, 2019
47dd77e
Merge pull request #1 from udacity/stephen/README_updates
kbehrman Aug 1, 2019
63433f4
Update buildspec.yml
swwelch Nov 22, 2019
e5e4093
Updates to work with current systems
kbehrman Jun 28, 2020
d97e681
Update .gitignore
kbehrman Jul 8, 2020
6f81589
Add SheBangLine to main.py
SudKul Feb 25, 2021
be4cce8
Create role and add role to configmap
SudKul Feb 26, 2021
b717b73
Create role and add role to configmap
SudKul Feb 26, 2021
fb856ff
Fix the template to allow creating the KubectlAssumeRoleCustomResource
SudKul Feb 26, 2021
37f943a
Do not push your Github token
SudKul Feb 26, 2021
fa3f3e6
Add trust.json
SudKul Mar 7, 2021
185056a
Delete Dockerfile
SudKul Mar 7, 2021
835f6e5
Remove the iam-role-policy.json and trust.json files
SudKul Mar 7, 2021
5a0a9d8
Add a sample aws-auth-patch.yml file
SudKul Mar 7, 2021
67925eb
Update requirements.txt
SudKul Mar 15, 2021
2cc57ca
Update requirements.txt
SudKul Mar 15, 2021
81abf16
Create manual.yml
SudKul May 3, 2021
eebc1e7
Update .gitignore
SudKul May 3, 2021
4874da4
Update manual.yml
SudKul May 5, 2021
f7c665e
Add exercise resource
SudKul May 10, 2021
e2a21a5
Add Manual Deployment exercise
SudKul May 10, 2021
aa81775
Update manual.yml
SudKul Jul 29, 2021
322f6d8
Update README.md
rcudacity Aug 18, 2021
4b27e80
Update manual.yml
rcudacity Aug 18, 2021
8b8cc1d
Create CODEOWNERS
SudKul Oct 21, 2021
40b4a98
Update ci-cd-codepipeline.cfn.yml
tasawar-hussain Nov 14, 2021
c97f0fd
Update dependencies
SudKul May 11, 2022
9901753
Add starter files
SudKul May 11, 2022
c2cd2b2
Update the repo name
SudKul May 11, 2022
6b38e76
Update the Image path
SudKul May 11, 2022
d0d941c
Update README.md
SudKul May 11, 2022
2b00b3a
Update aws-auth-patch.yml
SudKul May 11, 2022
f62a9d9
Update kubectl version
SudKul May 11, 2022
22a1de6
Update kubectl version
SudKul May 11, 2022
94c8420
Use kubectl v1.23.6
SudKul May 11, 2022
788446b
Use kubectl v1.23.6
SudKul May 11, 2022
29f3927
Try again
SudKul May 11, 2022
df66f69
Try again
SudKul May 11, 2022
cea13fb
Fix the path
SudKul May 11, 2022
da7d2f6
Update README.md
SudKul May 11, 2022
dc16a86
Add the IAM username
SudKul May 11, 2022
53b5aee
Add the IAM username
SudKul May 11, 2022
a2a8fb2
Update the starter code (#41)
SudKul May 11, 2022
bbf099c
Add CFN templates
SudKul May 14, 2022
997a581
Update manual.yml
SudKul May 14, 2022
e066046
Merge pull request #42 from SudKul/master
SudKul May 14, 2022
35017a7
Replace requests lib from botocore with urllib3
jungleBadger Mar 31, 2024
0b62832
Replace dl.k8s.io with AWS image source
jungleBadger Mar 31, 2024
ce316b5
Update google signing key
jungleBadger Mar 31, 2024
2ab472d
Execute pip install to avoid issues within testing
jungleBadger Mar 31, 2024
a996e81
Update Dockerfile base image
jungleBadger Mar 31, 2024
1d54c4b
Merge pull request #82 from jungleBadger/boilerplate-adjustments
SudKul Apr 1, 2024
7a0a7bf
update code
ChinhQuoc Dec 23, 2024
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
46 changes: 46 additions & 0 deletions .github/workflows/manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Workflow to ensure whenever a Github PR is submitted,
# a JIRA ticket gets created automatically.
name: Manual Workflow

# Controls when the action will run.
on:
# Triggers the workflow on pull request events but only for the master branch
pull_request_target:
types: [assigned, opened, reopened]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
test-transition-issue:
name: Convert Github Issue to Jira Issue
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master

- name: Login
uses: atlassian/gajira-login@master
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}

- name: Create NEW JIRA ticket
id: create
uses: atlassian/gajira-create@master
with:
project: CONUPDATE
issuetype: Task
summary: |
Github PR - nd0044 - Full Stack Nanodegree C4 | Repo: ${{ github.repository }} | PR# ${{github.event.number}}
description: |
Repo link: https://github.com/${{ github.repository }}
PR no. ${{ github.event.pull_request.number }}
PR title: ${{ github.event.pull_request.title }}
PR description: ${{ github.event.pull_request.description }}
In addition, please resolve other issues, if any.
fields: '{"components": [{"name":"nd0044 - Full Stack Nanodegree"}], "customfield_16449":"https://classroom.udacity.com/nanodegrees/nd0044/dashboard/overview", "customfield_16450":"Resolve the PR", "labels": ["github"], "priority":{"id": "4"}}'

- name: Log created issue
run: echo "Issue ${{ steps.create.outputs.issue }} was created"
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.DS_Store
.env
.env*
.flaskenv
*.pyc
*.pyo
Expand All @@ -16,6 +16,7 @@ _mailinglist
.idea/
docs/_build/
__pycache__
.env_file

# Coverage reports
htmlcov/
Expand All @@ -26,3 +27,5 @@ htmlcov/
# Direnv
.envrc
.direnv

.github/**
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @udacity/active-public-content
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Use the `python:3.9` as a source image from the Amazon ECR Public Gallery
# We are not using `python:3.7.2-slim` from Dockerhub because it has put a pull rate limit.
FROM public.ecr.aws/sam/build-python3.9:latest

# Set up an app directory for your code
COPY . /app
WORKDIR /app

# Install `pip` and needed Python packages from `requirements.txt`
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

# Define an entrypoint which will run the main app using the Gunicorn WSGI server.
ENTRYPOINT ["gunicorn", "-b", ":8080", "main:APP"]
285 changes: 66 additions & 219 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,244 +1,91 @@
# Deploying Flask API
# Deploying a Flask API

## Initial setup
1. Fork this project by pressing the fork buton
2. Locally clone your Forked version. You can now begin modifying it.

## Containarizing and Running Locally
The supplied flask app is a very simple api with three endpoints.
GET '/': This is a simple health check, which returns the response 'Healthy'.
POST '/auth': This takes a email and password as json arguments and returns a jwt token base on a custom secret.
GET '/contents': This requires a valid jwt token, and returns the un-encrpyted contents of that token.

### Run the Api using Flask Server
1. Install python dependencies. These dependencies are kept in a requirements.txt file. To install them, use pip:

```bash
pip install -r requirements.txt
```

1. Setting up environment

The following environment variable is required:

**JWT_SECRET** - The secret used to make the JWT token, for the purpose of this course it can be any string.

The following environment variable is optional:

**LOG_LEVEL** - The level of logging. Will default to 'INFO', but when debugging an app locally, you may want to set it to 'DEBUG'
This is the project starter repo for the course Server Deployment, Containerization, and Testing.

```bash
export JWT_SECRET=myjwtsecret
export LOG_LEVEL=DEBUG
```
In this project you will containerize and deploy a Flask API to a Kubernetes cluster using Docker, AWS EKS, CodePipeline, and CodeBuild.

3. Run the app using the Flask server, from the flask-app directory, run:
```bash
python app/main.py
```
The Flask app that will be used for this project consists of a simple API with three endpoints:

To try the api endpoints, open a new shell and run, replacing '\<EMAIL\>' and '\<PASSWORD\>' with and any values:
- `GET '/'`: This is a simple health check, which returns the response 'Healthy'.
- `POST '/auth'`: This takes a email and password as json arguments and returns a JWT based on a custom secret.
- `GET '/contents'`: This requires a valid JWT, and returns the un-encrpyted contents of that token.

```bash
export TOKEN=`curl -d '{"email":"<EMAIL>","password":"<PASSWORD>"}' -H "Content-Type: application/json" -X POST localhost:80/auth | jq -r '.token'`
```
The app relies on a secret set as the environment variable `JWT_SECRET` to produce a JWT. The built-in Flask server is adequate for local development, but not production, so you will be using the production-ready [Gunicorn](https://gunicorn.org/) server when deploying the app.

This calls the endpoint 'localhost:80/auth' with the '{"email":"<EMAIL>","password":"<PASSWORD>"}' as the message body. The return value is a jwt token based on the secret you supplied. We are assigning that secret to the environment variable 'TOKEN'. To see the jwt token, run:
## Prerequisites

```bash
echo $TOKEN
```
To call the 'contents' endpoint, which decrpyts the token and returns it content, run:
- Docker Desktop - Installation instructions for all OSes can be found <a href="https://docs.docker.com/install/" target="_blank">here</a>.
- Git: <a href="https://git-scm.com/downloads" target="_blank">Download and install Git</a> for your system.
- Code editor: You can <a href="https://code.visualstudio.com/download" target="_blank">download and install VS code</a> here.
- AWS Account
- Python version between 3.7 and 3.9. Check the current version using:

```bash
curl --request GET 'http://127.0.0.1:80/contents' -H "Authorization: Bearer ${TOKEN}" | jq .
```bash
# Mac/Linux/Windows
python --version
```
You should see the email that you passed in as one of the values.

### Dockerize and Run Locally

1. Install Docker: [installation instructions](https://docs.docker.com/install/)

2. Create a Docker file. A Docker file decribes how to build a Docker image. Create a file named 'Dockerfile' in the app repo. The contents of the file describe the steps in creating a Docker image. Your Dockerfile should:
- use the 'python:strech' image as a source image
- Setup an app directory for your code
- Install needed python requirements
- Define an entrypoint which will run the main app using the gunicorn WSGI server

gunicorn should be run with the arguments:

```
gunicorn -b :8080 main:APP
```


3. Create a file named 'env_file' and use it to set the environment variables which will be run locally in your container. Here we do not need the export command, just an equals sign:


\<VARIABLE-NAME\>=\<VARIABLE-VALUE\>

4. Build a Local Docker Image
To build a Docker image run:
```
docker build -t jwt-api-test .
```

5. Run the image locally, using the 'gunicorn' server:
```
docker run --env-file=env_file -p 80:8080 jwt-api-test
```

To use the endpoints use the same curl commands as before:

```bash
export TOKEN=`curl -d '{"email":"<EMAIL>","password":"<PASSWORD>"}' -H "Content-Type: application/json" -X POST localhost:80/auth | jq -r '.token'`
```
```bash
curl --request GET 'http://127.0.0.1:80/contents' -H "Authorization: Bearer ${TOKEN}" | jq .
```

## Deployment to Kubernetes using CodePipeline and CodeBuild

### Create a Kubernetes (EKS) Cluster

1. Install aws cli

```bash
pip install awscli --upgrade --user
```
You can download a specific release version from <a href="https://www.python.org/downloads/" target="_blank">here</a>.

Note: If you are using a Python virtual environment, the command will be:
- Python package manager - PIP 19.x or higher. PIP is already installed in Python 3 >=3.4 downloaded from python.org . However, you can upgrade to a specific version, say 20.2.3, using the command:

```bash
pip install awscli --upgrade
```bash
# Mac/Linux/Windows Check the current version
pip --version
# Mac/Linux
pip install --upgrade pip==20.2.3
# Windows
python -m pip install --upgrade pip==20.2.3
```

2.
[Generate a aws access key id and secret key](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys)
- Terminal
- Mac/Linux users can use the default terminal.
- Windows users can use either the GitBash terminal or WSL.
- Command line utilities:
- AWS CLI installed and configured using the `aws configure` command. Another important configuration is the region. Do not use the us-east-1 because the cluster creation may fails mostly in us-east-1. Let's change the default region to:
```bash
aws configure set region us-east-2
```
Ensure to create all your resources in a single region.
- EKSCTL installed in your system. Follow the instructions [available here](https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html#installing-eksctl) or <a href="https://eksctl.io/introduction/#installation" target="_blank">here</a> to download and install `eksctl` utility.
- The KUBECTL installed in your system. Installation instructions for kubectl can be found <a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" target="_blank">here</a>.

3. Setup your environment to use these keys:
If you not already have a aws 'credentials' file setup, run:

```bash
aws configure
```
And use the credentials you generated in step 2. Your aws commandline tools will now use these credentials.

4. Install the 'eksctl' tool.

The 'eksctl' tool allow interaction wth a EKS cluster from the command line. To install, follow the [directions for your platform](https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html)

5. Create a EKS cluster

```bash
eksctl create cluster --name simple-jwt-api --version 1.12 --nodegroup-name standard-workers --nodes 3 --nodes-min 1 --nodes-max 4 --node-ami auto
```

This will take some time to do. Progress can be checked by visiting the aws console and selecting EKS from the services.

6. Check the cluster is ready:

```bash
kubectl get nodes
```

If the nodes are up and healthy, the cluster should be ready.

### Create Pipeline
You will now create a pipeline which watches your Github. When changes are checked in, it will build a new image and deploy it to your cluster.


1. Create an IAM role that CodeBuild can use to interact with EKS:

```bash
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
TRUST="{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"arn:aws:iam::${ACCOUNT_ID}:root\" }, \"Action\": \"sts:AssumeRole\" } ] }"
echo '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "eks:Describe*", "ssm:GetParameters" ], "Resource": "*" } ] }' > /tmp/iam-role-policy
aws iam create-role --role-name UdacityFlaskDeployCBKubectlRole --assume-role-policy-document "$TRUST" --output text --query 'Role.Arn'
aws iam put-role-policy --role-name UdacityFlaskDeployCBKubectlRole --policy-name eks-describe --policy-document file:///tmp/iam-role-policy
```

You have now created a role named 'UdacityFlaskDeployCBKubectlRole'

1. Grant the role access to the cluster.
The 'aws-auth ConfigMap' is used to grant role based access control to your cluster.

```
ROLE=" - rolearn: arn:aws:iam::$ACCOUNT_ID:role/UdacityFlaskDeployCBKubectlRole\n username: build\n groups:\n - system:masters"
kubectl get -n kube-system configmap/aws-auth -o yaml | awk "/mapRoles: \|/{print;print \"$ROLE\";next}1" > /tmp/aws-auth-patch.yml
kubectl patch configmap/aws-auth -n kube-system --patch "$(cat /tmp/aws-auth-patch.yml)"
```

1. Generate a GitHub access token.
A Github acces token will allow CodePipeline to monitor when a repo is changed. A token can be generated [here](https://github.com/settings/tokens/=).
This token should be saved somewhere that is secure.

1. The file *buildspec.yml* instructs CodeBuild. We need a way to pass your jwt secret to the app in kubernetes securly. You will be using AWS parameter-store to do this. First add the following to your buildspec.yml file:

```yaml
env:
parameter-store:
JWT_SECRET: JWT_SECRET
```

This lets CodeBuild know to set an evironment variable based on a value in the parameter-store.

1. Put secret into AWS Parameter Store

```
aws ssm put-parameter --name JWT_SECRET --value "YourJWTSecret" --type SecureString
```

1. Modify CloudFormation template.

There is file named *ci-cd-codepipeline.cfn.yml*, this the the template file you will use to create your CodePipeline pipeline. Open this file and go to the 'Parameters' section. These are parameters that will accept values when you create a stack. Fill in the 'Default' value for the following:
- **EksClusterName** : use the name of the EKS cluster you created above
- **GitSourceRepo** : use the name of your project's github repo.
- **GitHubUser** : use your github user name
- **KubectlRoleName** : use the name of the role you created for kubectl above

Save this file.

1. Create a stack for CodePipeline
- Go the the [CloudFormation service](https://us-east-2.console.aws.amazon.com/cloudformation/) in the aws console.
- Press the 'Create Stack' button.
- Choose the 'Upload template to S3' option and upload the template file 'ci-cd-codepipeline.cfn.yml'
- Press 'Next'. Give the stack a name, fill in your GitHub login and the Github access token generated in step 9.
- Confirm the cluster name matches your cluster, the 'kubectl IAM role' matches the role you created above, and the repository matches the name of your forked repo.
- Create the stack.

You can check it's status in the [CloudFormation console](https://us-east-2.console.aws.amazon.com/cloudformation/).

1. Check the pipeline works. Once the stack is successfully created, commit a change to the master branch of your github repo. Then, in the aws console go to the [CodePipeline UI](https://us-east-2.console.aws.amazon.com/codesuite/codepipeline). You should see that the build is running.

16. To test your api endpoints, get the external ip for your service:
## Initial setup

1. Fork the <a href="https://github.com/udacity/cd0157-Server-Deployment-and-Containerization" target="_blank">Server and Deployment Containerization Github repo</a> to your Github account.
1. Locally clone your forked version to begin working on the project.

```
kubectl get services simple-jwt-api -o wide
```bash
git clone https://github.com/SudKul/cd0157-Server-Deployment-and-Containerization.git
cd cd0157-Server-Deployment-and-Containerization/
```

Now use the external ip url to test the app:
1. These are the files relevant for the current project:

```
export TOKEN=`curl -d '{"email":"<EMAIL>","password":"<PASSWORD>"}' -H "Content-Type: application/json" -X POST <EXTERNAL-IP URL>:80/auth | jq -r '.token'`
curl --request GET '<EXTERNAL-IP URL>:80/contents' -H "Authorization: Bearer ${TOKEN}" | jq
```bash
.
├── Dockerfile
├── README.md
├── aws-auth-patch.yml #ToDo
├── buildspec.yml #ToDo
├── ci-cd-codepipeline.cfn.yml #ToDo
├── iam-role-policy.json #ToDo
├── main.py
├── requirements.txt
├── simple_jwt_api.yml
├── test_main.py #ToDo
└── trust.json #ToDo
```

17. Paste the external id from above below this line for the reviewer to use:

**EXTERNAL IP**:

18. Add running tests as part of the build.
## Project Steps

To require the unit tests to pass before our build will deploy new code to your cluster, you will add the tests to the build stage. Remember you installed the requirements and ran the unit tests locally at the beginning of this project. You will add the same commands to the *buildspec.yml*:
- Open *buildspec.yml*
- In the prebuild section, add a line to install the requirements and a line to run the tests. You may need to refer to 'pip' as 'pip3' and 'python' as 'python3'
- save the file
Completing the project involves several steps:

19. You can check the tests prevent a bad deployment by breaking the tests on purpose:
- Open the *test_main.py* file
- Add `assert False` to any of the tests
- Commit your code and push it to Github
- Check that the build fails in [CodePipeline](https://us-east-2.console.aws.amazon.com/codesuite/codepipeline)
1. Write a Dockerfile for a simple Flask API
2. Build and test the container locally
3. Create an EKS cluster
4. Store a secret using AWS Parameter Store
5. Create a CodePipeline pipeline triggered by GitHub checkins
6. Create a CodeBuild stage which will build, test, and deploy your code

For more detail about each of these steps, see the project lesson.
Loading
Loading