0) Student
1) starting A levels
3) gave a lightening talk last year
2) @lukespademan: [twitter, git, etc]
Deops is trying to automate stuff you would normaly have to do manually.
pip install pipenv --user
mkdir project
cd project
mkdir src
cd src
pipenv python --3.6 # creates the virtual environment
pipenv install django gunicorn psycopg2-binary # installs pakcages to venv
pipenv shell # enteres the virtual environment
django-admin startproject mysite .
django-admin startproject mysite .
# src/mysite/settings.py
# ...
try:
SECRET_KEY
except NameError:
SECRET_FILE = os.path.join(BASE_DIR, 'secret.txt')
try:
SECRET_KEY = open(SECRET_FILE).read().strip()
except IOError:
try:
import random
SECRET_KEY = ''.join([random.SystemRandom().choice(
'abcdefghijklmnopqrstuvxyz0123456789!@#$%^&*(-_=+)') for i in range(50)]
)
secret = open(SECRET_FILE, "w")
secret.write(SECRET_KEY)
secret.close()
except IOError:
Exception("Please create a %s file with random characters." % SECRET_FILE)
# ...
# src/mysite/settings.py
# ...
# SECURITY WARNING: don't run with debug turned on in production!
if os.environ.get('DJANGO_DEBUG'):
print("Debug is enabled.")
DEBUG = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
else:
DEBUG = False
ALLOWED_HOSTS = ["example.com", "localhost"]
# ...
# src/mysite/settings.py
# ...
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'HOST': 'db',
'PORT': 5432,
}
}
# ...
if DEBUG == True:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# src/mysite/settings.py
# ...
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
.
└── src
├── db.sqlite3
├── main.py
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
├── Pipfile.lock
└── secret.txt
cd src
pipenv shell
DJANGO_DEBUG=True python manage.py makemigrations
DJANGO_DEBUG=True python manage.py migrate
DJANGO_DEBUG=True python manage.py runserver
python manage.py startapp hello_world
# src/hello_world/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
# src/hello_world/views.py
from django.shortcuts import render
def index(request):
return render(request, 'hello_world/index.html')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, PyCon UK!</title>
</head>
<body>
<h1>Hello, PyCon UK!</h1>
</body>
</html>
# src/hello_world/templates/hello_world/index.html
# src/mysite/settings.py
...
INSTALLED_APPS = [
'hello_world',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
...
# src/mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('hello_world.urls')),
]
# Dockerfile
FORM python:3.6
RUN mkdir /src
ADD src/Pipfile /src
ADD src/Pipfile.lock /src
RUN pip install pipenv
WORKDIR /src
RUN pipenv install --system --deploy --ignore-pipfile
ADD src /src
# docker-compose.yml
version: '3'
services:
nginx:
image: registry.gitlab.com/lukespademan/mysite/nginx:latest
build:
context: .
dockerfile: Dockerfile.nginx
container_name: mysite_nginx
depends_on:
- web
ports:
- "80:8080"
web:
image: registry.gitlab.com/lukespademan/mysite:latest
build: .
container_name: mysite_django
command: bash start_django.sh
depends_on:
- db
db:
image: postgres:latest
container_name: mysite_postgres
volumes:
- "db:/var/lib/postgresql/data"
volumes:
db: {}
services:
nginx:
build: Dockerfile.nginx
container_name: mysite_nginx
depends_on:
- web
ports:
- "80:8080"
web:
build: .
container_name: mysite_django
command: bash start_django.sh
depends_on:
- db
db:
image: postgres:latest
container_name: mysite_postgres
volumes:
- "db:/var/lib/postgresql/data"
volumes:
db: {}
# src/start_django.sh
#!/bin/bash
ls
cat Pipfile
pip freeze
python manage.py migrate
python manage.py collectstatic --noinput
gunicorn mysite.wsgi -b 0.0.0.0:8000
FROM nginx:latest
ADD config/nginx /etc/nginx/conf.d
ADD src /src
EXPOSE 80
mkdir config
cd config
mkdir nginx
cd nginx
# config/nginx/my_django.conf
upstream web {
ip_hash;
server web:8000;
}
upstream web {
ip_hash;
server web:8000;
}
server {
location /robots.txt {alias /src/static/robots.txt;}
location /favicon.ico {alias /src/static/favicon.ico;}
location /static/ {
autoindex on;
alias /src/static/;
}
location / {
proxy_pass http://web/;
}
listen 8080;
}
.
└── config
│ └── nginx
│ └── my_django.conf
├── docker-compose.yml
├── Dockerfile
└── src
├── db.sqlite3
├── main.py
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── Pipfile
├── Pipfile.lock
├── secret.txt
└── start_django.sh
sudo docker-compose build
sudo docker-compose up
# .gitignore
__pycache__
db.sqlite3
secret.txt
# Devops Example
MIT License
Copyright (c) 2018 Luke Spademan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | \
sudo bash
sudo apt-get install gitlab-runner
sudo gitlab-runner register
# /etc/gitlab-runner/config.toml
...
volumes = ["cache", "/var/run/docker.sock:/var/run/docker.sock"]
...
sudo docker login registry.gitlab.com
sudo gitlab-runner start
sudo docker warm init --advertise-addr 123.123.123.123
image: tmaier/docker-compose:latest
stages:
- build
- test
- deploy
variables:
WEB_CONTAINER_IMAGE: registry.gitlab.com/lukespademan/mysite:latest
NGINX_CONTAINER_IMAGE: registry.gitlab.com/lukespademan/mysite/nginx:latest
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
test:
stage: test
script:
- docker-compose run web python manage.py test --no-input
build:
stage: build
script:
- docker-compose build
- docker-compose push
tags:
- docker
deploy:
stage: deploy
script:
- docker stack deploy --compose-file docker-compose.yml stack-name
tags:
- docker
only:
- master