Remove example directory (#20)
Signed-off-by: Carlos Rodriguez Hernandez <carlosrh@vmware.com>
This commit is contained in:
parent
9ab0475621
commit
ef858e744d
|
|
@ -1,4 +0,0 @@
|
|||
kubernetes.yml
|
||||
README.md
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
FROM bitnami/python:3 as builder
|
||||
ENV PATH="/app/.venv/bin:${PATH}"
|
||||
WORKDIR /app
|
||||
|
||||
RUN python -m venv .venv
|
||||
|
||||
COPY requirements.txt /app
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY . /app
|
||||
RUN python manage.py migrate
|
||||
|
||||
# The production image is constructed with a smaller, production grade
|
||||
# base image, and your code built in the previous build stage.
|
||||
FROM bitnami/python:3-prod
|
||||
ENV PATH="/app/.venv/bin:${PATH}"
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /app /app
|
||||
|
||||
EXPOSE 8000
|
||||
CMD python manage.py runserver 0:8000
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
# Example Application
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
$ kubectl create -f https://raw.githubusercontent.com/bitnami/bitnami-docker-python/master/example/kubernetes.yml
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
This example demostrates the use of the `bitnami/python` image to create a production build of your python application.
|
||||
|
||||
For demonstration purposes we'll bootstrap a [Django](https://www.djangoproject.com/) application, build a image with the tag `bitnami/python-example` and deploy it on a [Kubernetes](https://kubernetes.io) cluster.
|
||||
|
||||
## Generate the application
|
||||
|
||||
The example application is a [Django](https://www.djangoproject.com/) application bootstrapped using the `django-admin` utility.
|
||||
|
||||
```bash
|
||||
$ django-admin startproject example
|
||||
```
|
||||
|
||||
## Build and Test
|
||||
|
||||
To build a production Docker image of our application we'll use the `bitnami/python:2-prod` image, which is a production build of the Bitnami Python Image optimized for size.
|
||||
|
||||
```dockerfile
|
||||
FROM bitnami/python:2 as builder
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN virtualenv . && \
|
||||
. bin/activate && \
|
||||
pip install django && \
|
||||
python manage.py migrate
|
||||
|
||||
FROM bitnami/python:2-prod
|
||||
COPY --from=builder /app /app
|
||||
WORKDIR /app
|
||||
EXPOSE 8000
|
||||
CMD bash -c "source bin/activate && python manage.py runserver 0:8000"
|
||||
```
|
||||
|
||||
The `Dockerfile` consists of two build stages. The first stage uses the development image, `bitnami/python:2`, to copy the application source, create a virtualenv and install the required application modules with `pip`.
|
||||
|
||||
The second stage uses the production image, `bitnami/python:2-prod`, and copies over the application source and the installed modules from the previous stage. This creates a minimal Docker image that only consists of the application source, python modules and the python runtime.
|
||||
|
||||
To build the Docker image, execute the command:
|
||||
|
||||
```bash
|
||||
$ docker build -t bitnami/python-example:0.0.1 example/
|
||||
```
|
||||
|
||||
Since the `bitnami/python:2-prod` image is optimized for production deployments it does not include any packages that would bloat the image.
|
||||
|
||||
```console
|
||||
$ docker image ls
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
bitnami/python-example 0.0.1 0d43bbca1cd2 22 seconds ago 193MB
|
||||
```
|
||||
|
||||
You can now launch and test the image locally.
|
||||
|
||||
```console
|
||||
$ docker run -it --rm -p 8000:8000 bitnami/python-example:0.0.1
|
||||
|
||||
Performing system checks...
|
||||
|
||||
System check identified no issues (0 silenced).
|
||||
November 09, 2017 - 11:25:27
|
||||
Django version 1.11.7, using settings 'example.settings'
|
||||
Starting development server at http://0:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
```
|
||||
|
||||
Finally, push the image to the Docker registry
|
||||
|
||||
```bash
|
||||
$ docker push bitnami/python-example:0.0.1
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
The `kubernetes.yml` file from the `example/` folder can be used to deploy our `bitnami/python-example:0.0.1` image to a Kubernetes cluster.
|
||||
|
||||
Simply download the Kubernetes manifest and create the Kubernetes resources described in the manifest using the command:
|
||||
|
||||
```console
|
||||
$ kubectl create -f kubernetes.yml
|
||||
ingress "example-ingress" created
|
||||
service "example-svc" created
|
||||
persistentvolumeclaim "example-data-pvc" created
|
||||
deployment "example-deployment" created
|
||||
```
|
||||
|
||||
From the output of the above command you will notice that we create the following resources:
|
||||
|
||||
- [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)
|
||||
- [Service](https://kubernetes.io/docs/concepts/services-networking/service/)
|
||||
- [Volume](https://kubernetes.io/docs/concepts/storage/volumes/)
|
||||
+ [PersistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/volumes/#persistentvolumeclaim)
|
||||
- [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Our example application is stateless and does not store any data or does not require any user configurations. As such we do not need to create the `PersistentVolumeClaim` resource. Our `kubernetes.yml` creates this resource strictly to demostrate how it is defined in the manifest.
|
||||
|
||||
## Accessing the application
|
||||
|
||||
Typically in production you would access the application via a Ingress controller. Our `kubernetes.yml` already defines a `Ingress` resource. Please refer to the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) documentation to learn how to deploy an ingress controller in your cluster.
|
||||
|
||||
> **Hint**
|
||||
>
|
||||
> https://kubeapps.com/charts/stable/nginx-ingress
|
||||
|
||||
The following are alternate ways of accessing the application, typically used during application development and testing.
|
||||
|
||||
Since the service `example-svc` is defined to be of type `NodePort`, we can set up port forwarding to access our web application like so:
|
||||
|
||||
```bash
|
||||
$ kubectl port-forward $(kubectl get pods -l app=example -o jsonpath="{ .items[0].metadata.name }") 8000:8000
|
||||
```
|
||||
|
||||
The command forwards the local port `8000` to port `8000` of the Pod container. You can access the application by visiting the `http://localhost:8000`.
|
||||
|
||||
> **Note:**
|
||||
>
|
||||
> If your using minikube, you can access the application by simply executing the following command:
|
||||
>
|
||||
> ```bash
|
||||
> $ minikube service example-svc
|
||||
> ```
|
||||
|
||||
## Health Checks
|
||||
|
||||
The `kubernetes.yml` manifest defines default probes to check the health of the application. For our application we are simply probing if the application is responsive to queries on the root resource.
|
||||
|
||||
You application can define a route, such as the commonly used `/healthz`, that reports the application status and use that route in the health probes.
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
"""
|
||||
Django settings for example project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.11.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.11/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.11/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '@lgvsj4kea_42o(aa6$dza_c2oc_7(7-79dth2gkjrrbxr$p3u'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'example.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'example.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
"""example URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/1.11/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
]
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
"""
|
||||
WSGI config for example project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: example-ingress
|
||||
labels:
|
||||
app: example
|
||||
annotations:
|
||||
spec:
|
||||
rules:
|
||||
- host: app.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: example-svc
|
||||
servicePort: 80
|
||||
tls:
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: example-svc
|
||||
labels:
|
||||
app: example
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
selector:
|
||||
app: example
|
||||
type: NodePort
|
||||
status:
|
||||
loadBalancer: {}
|
||||
- apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: example-data-pvc
|
||||
annotations: {}
|
||||
labels:
|
||||
app: example
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Mi
|
||||
status: {}
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: example-deployment
|
||||
labels:
|
||||
app: example
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: example
|
||||
spec:
|
||||
containers:
|
||||
- name: example
|
||||
image: bitnami/python-example:0.0.1
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: app.example.com
|
||||
initialDelaySeconds: 15
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
httpHeaders:
|
||||
- name: Host
|
||||
value: app.example.com
|
||||
initialDelaySeconds: 5
|
||||
resources: {}
|
||||
volumeMounts:
|
||||
- mountPath: /app/data
|
||||
name: example-data
|
||||
restartPolicy: Always
|
||||
volumes:
|
||||
- name: example-data
|
||||
persistentVolumeClaim:
|
||||
claimName: example-data-pvc
|
||||
status: {}
|
||||
kind: List
|
||||
metadata: {}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError:
|
||||
# The above import may fail for some other reason. Ensure that the
|
||||
# issue is really that Django is missing to avoid masking other
|
||||
# exceptions on Python 2.
|
||||
try:
|
||||
import django
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
)
|
||||
raise
|
||||
execute_from_command_line(sys.argv)
|
||||
|
|
@ -1 +0,0 @@
|
|||
django
|
||||
Loading…
Reference in New Issue