├── .gitignore ├── LICENSE ├── README.md ├── chapter-4 └── website │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── app.py │ ├── docker-compose.yml │ └── requirements.txt ├── chapter-5 └── website │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Vagrantfile │ ├── app.py │ ├── docker-compose.yml │ └── requirements.txt ├── chapter-6 ├── deploy │ └── units │ │ ├── redis.service │ │ └── rediscounter.service └── website │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Vagrantfile │ ├── app.py │ ├── docker-compose.yml │ └── requirements.txt ├── chapter-7 ├── deploy │ ├── nginx │ │ ├── Dockerfile │ │ ├── certs │ │ │ ├── dhparam.pem │ │ │ ├── rediscounter.crt │ │ │ └── rediscounter.key │ │ ├── configs │ │ │ ├── default.conf │ │ │ └── nginx.conf │ │ └── docker-entrypoint │ └── units │ │ ├── nginx.service │ │ ├── redis.service │ │ └── rediscounter.service └── website │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Vagrantfile │ ├── app.py │ ├── docker-compose.yml │ └── requirements.txt ├── chapter-8 ├── deploy │ ├── git │ │ └── post-receive │ │ │ ├── nginx │ │ │ └── rediscounter │ ├── nginx │ │ ├── Dockerfile │ │ ├── certs │ │ │ ├── dhparam.pem │ │ │ ├── rediscounter.crt │ │ │ └── rediscounter.key │ │ ├── configs │ │ │ ├── default.conf │ │ │ └── nginx.conf │ │ └── docker-entrypoint │ └── units │ │ ├── nginx.service │ │ ├── redis.service │ │ └── rediscounter.service └── website │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Vagrantfile │ ├── app.py │ ├── docker-compose.yml │ └── requirements.txt └── chapter-9 ├── deploy ├── git │ └── post-receive │ │ ├── nginx │ │ └── rediscounter ├── nginx │ ├── Dockerfile │ ├── certs │ │ ├── dhparam.pem │ │ ├── rediscounter.crt │ │ └── rediscounter.key │ ├── configs │ │ ├── default.conf │ │ └── nginx.conf │ └── docker-entrypoint ├── production │ └── rules-save └── units │ ├── nginx.service │ ├── redis.service │ └── rediscounter.service └── website ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Vagrantfile ├── app.py ├── docker-compose.yml └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Nick Janetakis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploy web apps with Docker 2 | 3 | This repository contains progress points based on where you are in [Deploy web 4 | apps with Docker](https://leanpub.com/deploy-web-apps-with-docker), a book for 5 | deploying web applications with Docker by [Nick 6 | Janetakis](http://nickjanetakis.com/). 7 | 8 | ## License 9 | 10 | MIT 11 | 12 | ## Buy the Book 13 | 14 | You can buy [Deploy web apps with 15 | Docker](https://leanpub.com/deploy-web-apps-with-docker) on LeanPub. 16 | 17 | Just as a fair warning I maintained this book for years but it's no longer 18 | being updated. This repo is up to ensure folks who have the book can access the 19 | material. An up to date list of courses I've created around building and 20 | deploying web apps can be found here . 21 | -------------------------------------------------------------------------------- /chapter-4/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | -------------------------------------------------------------------------------- /chapter-4/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | -------------------------------------------------------------------------------- /chapter-4/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-4/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-4/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-4/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | -------------------------------------------------------------------------------- /chapter-5/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | .vagrant/ 6 | -------------------------------------------------------------------------------- /chapter-5/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | 19 | .vagrant/ 20 | -------------------------------------------------------------------------------- /chapter-5/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-5/website/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version '>= 1.6.0' 2 | 3 | # Configuration settings for the Virtual Machine. 4 | $update_channel = 'beta' 5 | $image_version = 'current' 6 | $vm_memory = 1024 7 | $vm_cpus = 1 8 | $forwarded_ports = { 9 | '8000' => '8000' 10 | } 11 | $vm_host = 'core-01' 12 | $vm_ip = '172.17.8.101' 13 | 14 | Vagrant.configure('2') do |config| 15 | config.ssh.insert_key = false 16 | config.ssh.forward_agent = true 17 | 18 | config.vm.box = 'coreos-%s' % [$update_channel] 19 | if $image_version != 'current' 20 | config.vm.box_version = $image_version 21 | end 22 | config.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json' % [$update_channel, $image_version] 23 | 24 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 25 | config.vm.provider vmware do |v, override| 26 | override.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json' % [$update_channel, $image_version] 27 | end 28 | end 29 | 30 | config.vm.provider :virtualbox do |v| 31 | v.check_guest_additions = false 32 | v.functional_vboxsf = false 33 | end 34 | 35 | if Vagrant.has_plugin?('vagrant-vbguest') then 36 | config.vbguest.auto_update = false 37 | end 38 | 39 | config.vm.define vm_name = $vm_host do |config| 40 | config.vm.hostname = vm_name 41 | 42 | config.vm.network :private_network, ip: $vm_ip 43 | 44 | $forwarded_ports.each do |guest, host| 45 | config.vm.network 'forwarded_port', guest: guest, host: host, auto_correct: true 46 | end 47 | 48 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 49 | config.vm.provider vmware do |v| 50 | v.gui = false 51 | v.vmx['memsize'] = $vm_memory 52 | v.vmx['numvcpus'] = $vm_cpus 53 | end 54 | end 55 | 56 | config.vm.provider :virtualbox do |vb| 57 | vb.gui = false 58 | vb.memory = $vm_memory 59 | vb.cpus = $vm_cpus 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /chapter-5/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-5/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-5/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | -------------------------------------------------------------------------------- /chapter-6/deploy/units/redis.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/mkdir -p /var/lib/%p/data 9 | ExecStartPre=-/usr/bin/docker kill %p 10 | ExecStartPre=-/usr/bin/docker rm -f %p 11 | ExecStart=/usr/bin/docker run --rm --name %p \ 12 | -v /var/lib/%p/data:/var/lib/%p/data -p 6379:6379 %p:2.8.21 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target rediscounter.service 17 | -------------------------------------------------------------------------------- /chapter-6/deploy/units/rediscounter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service redis.service 4 | After=docker.service redis.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | --link redis:redis -p 8000:8000 %p 12 | ExecStop=/usr/bin/docker stop %p 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /chapter-6/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | .vagrant/ 6 | -------------------------------------------------------------------------------- /chapter-6/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | 19 | .vagrant/ 20 | -------------------------------------------------------------------------------- /chapter-6/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-6/website/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version '>= 1.6.0' 2 | 3 | # Configuration settings for the Virtual Machine. 4 | $update_channel = 'beta' 5 | $image_version = 'current' 6 | $vm_memory = 1024 7 | $vm_cpus = 1 8 | $forwarded_ports = { 9 | '8000' => '8000' 10 | } 11 | $vm_host = 'core-01' 12 | $vm_ip = '172.17.8.101' 13 | 14 | Vagrant.configure('2') do |config| 15 | config.ssh.insert_key = false 16 | config.ssh.forward_agent = true 17 | 18 | config.vm.box = 'coreos-%s' % [$update_channel] 19 | if $image_version != 'current' 20 | config.vm.box_version = $image_version 21 | end 22 | config.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json' % [$update_channel, $image_version] 23 | 24 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 25 | config.vm.provider vmware do |v, override| 26 | override.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json' % [$update_channel, $image_version] 27 | end 28 | end 29 | 30 | config.vm.provider :virtualbox do |v| 31 | v.check_guest_additions = false 32 | v.functional_vboxsf = false 33 | end 34 | 35 | if Vagrant.has_plugin?('vagrant-vbguest') then 36 | config.vbguest.auto_update = false 37 | end 38 | 39 | config.vm.define vm_name = $vm_host do |config| 40 | config.vm.hostname = vm_name 41 | 42 | config.vm.network :private_network, ip: $vm_ip 43 | 44 | $forwarded_ports.each do |guest, host| 45 | config.vm.network 'forwarded_port', guest: guest, host: host, auto_correct: true 46 | end 47 | 48 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 49 | config.vm.provider vmware do |v| 50 | v.gui = false 51 | v.vmx['memsize'] = $vm_memory 52 | v.vmx['numvcpus'] = $vm_cpus 53 | end 54 | end 55 | 56 | config.vm.provider :virtualbox do |vb| 57 | vb.gui = false 58 | vb.memory = $vm_memory 59 | vb.cpus = $vm_cpus 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /chapter-6/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-6/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-6/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.9.3 2 | MAINTAINER Nick Janetakis 3 | 4 | # Delete default static pages. 5 | RUN rm /usr/share/nginx/html/* 6 | 7 | # Copy over the custom nginx and default configs. 8 | COPY configs/nginx.conf /etc/nginx/nginx.conf 9 | COPY configs/default.conf /etc/nginx/conf.d/default.conf 10 | 11 | # Copy over the self signed SSL certificates. 12 | COPY certs/rediscounter.crt /etc/ssl/certs/rediscounter.crt 13 | COPY certs/rediscounter.key /etc/ssl/private/rediscounter.key 14 | COPY certs/dhparam.pem /etc/ssl/private/dhparam.pem 15 | 16 | # Allow us to customize the entry point of the image. 17 | COPY docker-entrypoint / 18 | RUN chmod +x /docker-entrypoint 19 | ENTRYPOINT ["/docker-entrypoint"] 20 | 21 | # Start nginx in the foreground. 22 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/certs/dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA4ZvlxM3OiUA+WWjQolRqxLazGeM0sVwXUJLpPlVrjZH/9Slo7ovq 3 | VZhFgBTZToXZlO+rG77/7XvkLxOwa/Bxj9sEyieObFh5M3AYpqPliotsPDNvNl1l 4 | Ys09k+mYUqW/WToVHburvDAHLseHBiRPTkKMEgZ79wZE9SHiHXOJnNSb2HvgfnHz 5 | 4QM+e/z4Yx1q22TF+lACm/zIFrPhUQpu5RkF7R3jwurVdWa5XyVljGPlnQXbeBns 6 | fECKaU2wsQc2+t5gpkdxDlX7ZDQ9Nh+DkW0WCrGyLm2cBfP2qm5X4RL1ge3LZzYC 7 | CpHZ7NCtHFneOUtlZ+XMLxHbseosm2PNYwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/certs/rediscounter.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAm+gAwIBAgIJAJFkaQ5PfmT8MA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV 3 | BAYTAlVTMRAwDgYDVQQIDAdOZXdZb3JrMRAwDgYDVQQHDAdOZXdZb3JrMQswCQYD 4 | VQQKDAJJVDEaMBgGA1UEAwwRbG9jYWxyZWdpc3RyeS5jb20wHhcNMTUwODAzMTQx 5 | MDAyWhcNMjUwNzMxMTQxMDAyWjBaMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHTmV3 6 | WW9yazEQMA4GA1UEBwwHTmV3WW9yazELMAkGA1UECgwCSVQxGjAYBgNVBAMMEWxv 7 | Y2FscmVnaXN0cnkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 8 | 3QgNGc1Dpf4SMls0R/RroX7d6cniisI2XgspVHHYW8hDx/NO8NdZf3KCD9T+SAVg 9 | 9DddbGx7StxhMnB1lW7HmeOzGFR4NjOuh4fz0iYYoLRd4gzkit0D+OSBsnr4tLle 10 | yysEC0wYkLAHCHeZlZ9u54DTW9bzGW4KNFAkLagBQMGcg2bkXpqOr1xrO+6uc5IV 11 | 8VM6yoedXlDek6K0rsxRUmh/8jcEXMwFTqmnTexShYVB+Ji5XLPgikSOmQje+3JF 12 | 1tE8rIsPhF98mWz/wRRBaV+5LJtTM54TXmi4BrmMALKM0Lo1khvWitwIfPOJWGEX 13 | VJllkJARv2KFX87vkEv6dQIDAQABo1AwTjAdBgNVHQ4EFgQUnUGjY1oLqT00U7y2 14 | iGOSgpx6EZkwHwYDVR0jBBgwFoAUnUGjY1oLqT00U7y2iGOSgpx6EZkwDAYDVR0T 15 | BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeS06ifrHBKZ700o4bjEt2vFg1pcK 16 | abk8Bw7Vcn2RvDB09lJg3UFRs6CrJFqOnVF+AUA2goB8UKP7/LeYnN00DdZsLRi9 17 | sY93agdq95iv62+VdCnA+XleDS01wQFwzOlXb7p76y9a/v3SItMB9viXZ93wIo8E 18 | C0Owql0tvevvxqkxPru2QQO6JOzA70mQ19vQJsdBuviTeGMul7trZeOTmDRlR5vT 19 | u9h5Xk5bLV50H56MlZNOJvY+rnX+yk2Qgru2HDlOwb2vYYOZvSqiAHCuIB3VslpP 20 | iX0gAyV5WeSxmLQna6IxJIuNUUdq4UR6ZoACz69DQa1WO+dbs9jWseD5Cg== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/certs/rediscounter.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDdCA0ZzUOl/hIy 3 | WzRH9Guhft3pyeKKwjZeCylUcdhbyEPH807w11l/coIP1P5IBWD0N11sbHtK3GEy 4 | cHWVbseZ47MYVHg2M66Hh/PSJhigtF3iDOSK3QP45IGyevi0uV7LKwQLTBiQsAcI 5 | d5mVn27ngNNb1vMZbgo0UCQtqAFAwZyDZuRemo6vXGs77q5zkhXxUzrKh51eUN6T 6 | orSuzFFSaH/yNwRczAVOqadN7FKFhUH4mLlcs+CKRI6ZCN77ckXW0Tysiw+EX3yZ 7 | bP/BFEFpX7ksm1MznhNeaLgGuYwAsozQujWSG9aK3Ah884lYYRdUmWWQkBG/YoVf 8 | zu+QS/p1AgMBAAECggEBANG7sQq5rqZU5xlnV72rXXIZuyL7UX7PeN1WA/rAKEg3 9 | SLHz2wVHowH/OxEgz8SxbeVun7ShX4CSi5xcAAcy3i3VVX0RshvkgIjUZXUUdywO 10 | 2kMEbtyhigJjefpNG7AJcbyhba32oByzG4laS58hcRA1OtmbpoOL2hz3qsyz7bRt 11 | /vVaJHFTm6CSz9Wn/m2xWmyLJn3ObHE8AMp8FteztkcNceqGWpga99u6PSMFJ0G8 12 | JT/RDUUO2WA5hf0GmJbBTyqx6977ivFGeYhXrlpOxxVxkx0x+Gd7p/F/FNhGmr1l 13 | qIa+z+NcWOxKV30BG7Ra9A0doxVIojQCASLkPN3rXAECgYEA/QELsstfvSmdZvUJ 14 | 6Ox3g+Ozxxm5w0tiCEaeC0nlsrENPabSq9xEs2jbKhww5ox3sPs6OKusRD6YPE2t 15 | Eq/uEVyn+oGOr+J4Q4zQTDNrd2QAYGMZRNuZkTOFpe8KZBmTIo4mCtEGp2SUSJcC 16 | uL6cO6DbwSikrijHvkFu6YpMSQECgYEA36YVe9Uz0xLbmtxeYVSijeEfvfNRDjys 17 | cbjAI6eFmntR8XTmEWoZVRKmA9kDg9IlK82vZeEqMlEviagO6eAjl6rC9lxfBTUl 18 | BbQWaAFZGimQInk0cWiHc8AhdOK+H7LKaEMIycMNu2AUoWBN/Mc9CGy8DFrc3RRQ 19 | FuYrXWGpnXUCgYEAqpks6TfHa8cG0ujB8OSaRj2g+Mz4/J31EX2Ejjoa/53xPrQh 20 | dC9Hx+4ZclCmDJ+FCbqtbI8dzrqibm82F9a3Yc+nmPwJWcIMtAfcYLV/bnbo5hWM 21 | cWjeKRGjudrwl8TC+Nb/AeYmZXMlpbjl5erpcC+sXpfoS2NGJJz8i89sVwECgYBP 22 | ZYTG+390dYNkzMrsvsEeoUdFhfXGmh+WF8KOZdB2cUU79QYgNIxduUsanpYy3A26 23 | KUEVaAQ07MF1myYAPUQlecfQ8iYBkUZdaftyXNgnA45ZzrGheTxtCU5XUo+wbSaS 24 | MQoTpp1fYdKxH6FQFeNC9Gcl87PpAGcWWgwXEK7IaQKBgQDn7kAAlxOYNQye8320 25 | IfOoUyzFMLotlGclqKvkhLEDmg5aUEurdOAR01qSQt2OcJ5AlXJ2UJpsOCx7cXg2 26 | qt37wOz5F0L5jsHuPDLImQm2rdPuWZH2VwrYF71ejkmAw1sAOaPQCYpl8wGbKGRt 27 | y7ZDJzUMJ6e3JMLVYcOBIvRLGw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/configs/default.conf: -------------------------------------------------------------------------------- 1 | upstream rediscounter { 2 | # The Flask application. 3 | server rediscounter:8000; 4 | } 5 | 6 | # In case you want 'www' addresses to be automatically redirected without 'www'. 7 | # server { 8 | # listen 80; 9 | # listen 443; 10 | # server_name www.yourrealdomain.com; 11 | # return 301 https://yourrealdomain.com$request_uri; 12 | #} 13 | 14 | server { 15 | listen 80 default deferred; 16 | server_name 172.17.8.101; 17 | 18 | # All http traffic will get redirected to SSL. 19 | return 307 https://$host$request_uri; 20 | } 21 | 22 | server { 23 | # "deferred" reduces the number of formalities between the server and client. 24 | listen 443 default deferred; 25 | server_name 172.17.8.101; 26 | 27 | # Static asset path, which is read from the rediscounter's VOLUME. In this 28 | # case the example application has no assets, but this is how you would 29 | # configure assets to be served through nginx. 30 | root /rediscounter/build/public; 31 | 32 | # Ensure timeouts are equal across browsers and raise the max content-length size. 33 | keepalive_timeout 60; 34 | client_max_body_size 5m; 35 | 36 | # SSL goodness. 37 | ssl on; 38 | ssl_certificate /etc/ssl/certs/rediscounter.crt; 39 | ssl_certificate_key /etc/ssl/private/rediscounter.key; 40 | ssl_session_cache shared:SSL:50m; 41 | ssl_session_timeout 5m; 42 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 43 | ssl_prefer_server_ciphers on; 44 | ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; 45 | ssl_dhparam /etc/ssl/private/dhparam.pem; 46 | ssl_ecdh_curve secp384r1; 47 | add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains;' always; 48 | 49 | # Disallow access to hidden files and directories. 50 | location ~ /\. { 51 | return 404; 52 | access_log off; 53 | log_not_found off; 54 | } 55 | 56 | # Allow optionally writing an index.html file to take precedence over the upstream. 57 | try_files $uri $uri/index.html $uri.html @rediscounter; 58 | 59 | # Attempt to load the favicon or fall back to status code 204. 60 | location = /favicon.ico { 61 | try_files /favicon.ico = 204; 62 | access_log off; 63 | log_not_found off; 64 | } 65 | 66 | # Load the Flask app back end with proper headers. 67 | location @rediscounter { 68 | proxy_set_header X-Forwarded-Proto $scheme; 69 | proxy_set_header Host $http_host; 70 | proxy_set_header X-Real-IP $remote_addr; 71 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 72 | proxy_redirect off; 73 | proxy_pass http://rediscounter; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/configs/nginx.conf: -------------------------------------------------------------------------------- 1 | # Number of workers to run, usually equals number of CPU cores. 2 | worker_processes auto; 3 | 4 | # Maximum number of opened files per process. 5 | worker_rlimit_nofile 4096; 6 | 7 | events { 8 | # Maximum number of simultaneous connections that can be opened by a worker process. 9 | worker_connections 1024; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | # --------------------------------------------------------------------------- 17 | # Security settings from: 18 | # https://gist.github.com/plentz/6737338 19 | 20 | # Disable nginx version from being displayed on errors. 21 | server_tokens off; 22 | 23 | # config to don't allow the browser to render the page inside an frame or iframe 24 | # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking 25 | # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri 26 | # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options 27 | add_header X-Frame-Options SAMEORIGIN; 28 | 29 | # when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, 30 | # to disable content-type sniffing on some browsers. 31 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 32 | # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx 33 | # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx 34 | # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 35 | add_header X-Content-Type-Options nosniff; 36 | 37 | # This header enables the Cross-site scripting (XSS) filter built into most recent web browsers. 38 | # It's usually enabled by default anyway, so the role of this header is to re-enable the filter for 39 | # this particular website if it was disabled by the user. 40 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 41 | add_header X-XSS-Protection "1; mode=block"; 42 | # --------------------------------------------------------------------------- 43 | 44 | # Avoid situations where a hostname is too long when dealing with vhosts. 45 | server_names_hash_bucket_size 64; 46 | server_names_hash_max_size 512; 47 | 48 | # Performance optimizations. 49 | sendfile on; 50 | tcp_nopush on; 51 | 52 | # http://nginx.org/en/docs/hash.html 53 | types_hash_max_size 2048; 54 | 55 | # Enable gzip for everything but IE6. 56 | gzip on; 57 | gzip_disable "msie6"; 58 | 59 | # Default config for the app backend. 60 | include /etc/nginx/conf.d/default.conf; 61 | } 62 | -------------------------------------------------------------------------------- /chapter-7/deploy/nginx/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Overwrite a few variables, this allows us to use the same template 5 | # for both development, staging and production. 6 | CONFIG_PATH="/etc/nginx/conf.d/default.conf" 7 | STAGING_IP="172.17.8.101" 8 | STAGING_HOSTNAME="core-01" 9 | DOMAIN_NAME="yourrealdomain.com" 10 | 11 | if [[ $(hostname) != "${STAGING_HOSTNAME}" ]]; then 12 | sed -i "s/${STAGING_IP}/${DOMAIN_NAME}/g" "${CONFIG_PATH}" 13 | fi 14 | 15 | # Execute the CMD from the Dockerfile. 16 | exec "$@" 17 | -------------------------------------------------------------------------------- /chapter-7/deploy/units/nginx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service rediscounter.service 4 | After=docker.service rediscounter.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | -p 80:80 -p 443:443 \ 12 | --link rediscounter:rediscounter \ 13 | -v /etc/ssl/certs:/etc/ssl/certs \ 14 | -v /etc/ssl/private:/etc/ssl/private %p 15 | ExecStop=/usr/bin/docker stop %p 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /chapter-7/deploy/units/redis.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/mkdir -p /var/lib/%p/data 9 | ExecStartPre=-/usr/bin/docker kill %p 10 | ExecStartPre=-/usr/bin/docker rm -f %p 11 | ExecStart=/usr/bin/docker run --rm --name %p \ 12 | -v /var/lib/%p/data:/var/lib/%p/data -p 6379:6379 %p:2.8.21 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target rediscounter.service 17 | -------------------------------------------------------------------------------- /chapter-7/deploy/units/rediscounter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service redis.service 4 | After=docker.service redis.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | --link redis:redis -p 8000:8000 %p 12 | ExecStartPost=-/usr/bin/docker stop nginx 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target nginx.service 17 | -------------------------------------------------------------------------------- /chapter-7/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | .vagrant/ 6 | -------------------------------------------------------------------------------- /chapter-7/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | 19 | .vagrant/ 20 | -------------------------------------------------------------------------------- /chapter-7/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-7/website/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version '>= 1.6.0' 2 | 3 | # Configuration settings for the Virtual Machine. 4 | $update_channel = 'beta' 5 | $image_version = 'current' 6 | $vm_memory = 1024 7 | $vm_cpus = 1 8 | $forwarded_ports = { 9 | '8000' => '8000' 10 | } 11 | $vm_host = 'core-01' 12 | $vm_ip = '172.17.8.101' 13 | 14 | Vagrant.configure('2') do |config| 15 | config.ssh.insert_key = false 16 | config.ssh.forward_agent = true 17 | 18 | config.vm.box = 'coreos-%s' % [$update_channel] 19 | if $image_version != 'current' 20 | config.vm.box_version = $image_version 21 | end 22 | config.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json' % [$update_channel, $image_version] 23 | 24 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 25 | config.vm.provider vmware do |v, override| 26 | override.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json' % [$update_channel, $image_version] 27 | end 28 | end 29 | 30 | config.vm.provider :virtualbox do |v| 31 | v.check_guest_additions = false 32 | v.functional_vboxsf = false 33 | end 34 | 35 | if Vagrant.has_plugin?('vagrant-vbguest') then 36 | config.vbguest.auto_update = false 37 | end 38 | 39 | config.vm.define vm_name = $vm_host do |config| 40 | config.vm.hostname = vm_name 41 | 42 | config.vm.network :private_network, ip: $vm_ip 43 | 44 | $forwarded_ports.each do |guest, host| 45 | config.vm.network 'forwarded_port', guest: guest, host: host, auto_correct: true 46 | end 47 | 48 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 49 | config.vm.provider vmware do |v| 50 | v.gui = false 51 | v.vmx['memsize'] = $vm_memory 52 | v.vmx['numvcpus'] = $vm_cpus 53 | end 54 | end 55 | 56 | config.vm.provider :virtualbox do |vb| 57 | vb.gui = false 58 | vb.memory = $vm_memory 59 | vb.cpus = $vm_cpus 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /chapter-7/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-7/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-7/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | -------------------------------------------------------------------------------- /chapter-8/deploy/git/post-receive/nginx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Configuration. 4 | REPO_NAME="nginx" 5 | 6 | # Check out the newest version of the code. 7 | export GIT_WORK_TREE="/var/git/${REPO_NAME}" 8 | git checkout -f 9 | 10 | TAG="$(git log --pretty=format:'%h' -n 1)" 11 | FULL_COMMIT_TAG="${REPO_NAME}:${TAG}" 12 | FULL_LATEST_TAG="${REPO_NAME}:latest" 13 | 14 | # Build the image with the proper commit tag. 15 | docker build -t "${FULL_COMMIT_TAG}" "${GIT_WORK_TREE}" 16 | 17 | # Get the Docker ID of the last built image. 18 | DOCKER_ID="$(docker images -q $REPO_NAME | head -1)" 19 | 20 | # Tag a latest version based off the proper commit tag. 21 | docker tag -f "${DOCKER_ID}" "${FULL_LATEST_TAG}" 22 | 23 | echo "Restarting ${REPO_NAME}" 24 | docker stop "${REPO_NAME}" 25 | 26 | echo "Removing untagged Docker images (may take a while)" 27 | docker rmi $(docker images --quiet --filter "dangling=true") 28 | -------------------------------------------------------------------------------- /chapter-8/deploy/git/post-receive/rediscounter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Configuration. 4 | REPO_NAME="rediscounter" 5 | 6 | # Check out the newest version of the code. 7 | export GIT_WORK_TREE="/var/git/${REPO_NAME}" 8 | git checkout -f 9 | 10 | TAG="$(git log --pretty=format:'%h' -n 1)" 11 | FULL_COMMIT_TAG="${REPO_NAME}:${TAG}" 12 | FULL_LATEST_TAG="${REPO_NAME}:latest" 13 | 14 | # Build the image with the proper commit tag. 15 | docker build -t "${FULL_COMMIT_TAG}" "${GIT_WORK_TREE}" 16 | 17 | # Get the Docker ID of the last built image. 18 | DOCKER_ID="$(docker images -q $REPO_NAME | head -1)" 19 | 20 | # Tag a latest version based off the proper commit tag. 21 | docker tag -f "${DOCKER_ID}" "${FULL_LATEST_TAG}" 22 | 23 | echo "Restarting ${REPO_NAME}" 24 | docker stop "${REPO_NAME}" 25 | 26 | echo "Removing untagged Docker images (may take a while)" 27 | docker rmi $(docker images --quiet --filter "dangling=true") 28 | 29 | echo "Restarting nginx" 30 | docker stop "nginx" 31 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.9.3 2 | MAINTAINER Nick Janetakis 3 | 4 | # Delete default static pages. 5 | RUN rm /usr/share/nginx/html/* 6 | 7 | # Copy over the custom nginx and default configs. 8 | COPY configs/nginx.conf /etc/nginx/nginx.conf 9 | COPY configs/default.conf /etc/nginx/conf.d/default.conf 10 | 11 | # Copy over the self signed SSL certificates. 12 | COPY certs/rediscounter.crt /etc/ssl/certs/rediscounter.crt 13 | COPY certs/rediscounter.key /etc/ssl/private/rediscounter.key 14 | COPY certs/dhparam.pem /etc/ssl/private/dhparam.pem 15 | 16 | # Allow us to customize the entry point of the image. 17 | COPY docker-entrypoint / 18 | RUN chmod +x /docker-entrypoint 19 | ENTRYPOINT ["/docker-entrypoint"] 20 | 21 | # Start nginx in the foreground. 22 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/certs/dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA4ZvlxM3OiUA+WWjQolRqxLazGeM0sVwXUJLpPlVrjZH/9Slo7ovq 3 | VZhFgBTZToXZlO+rG77/7XvkLxOwa/Bxj9sEyieObFh5M3AYpqPliotsPDNvNl1l 4 | Ys09k+mYUqW/WToVHburvDAHLseHBiRPTkKMEgZ79wZE9SHiHXOJnNSb2HvgfnHz 5 | 4QM+e/z4Yx1q22TF+lACm/zIFrPhUQpu5RkF7R3jwurVdWa5XyVljGPlnQXbeBns 6 | fECKaU2wsQc2+t5gpkdxDlX7ZDQ9Nh+DkW0WCrGyLm2cBfP2qm5X4RL1ge3LZzYC 7 | CpHZ7NCtHFneOUtlZ+XMLxHbseosm2PNYwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/certs/rediscounter.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAm+gAwIBAgIJAJFkaQ5PfmT8MA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV 3 | BAYTAlVTMRAwDgYDVQQIDAdOZXdZb3JrMRAwDgYDVQQHDAdOZXdZb3JrMQswCQYD 4 | VQQKDAJJVDEaMBgGA1UEAwwRbG9jYWxyZWdpc3RyeS5jb20wHhcNMTUwODAzMTQx 5 | MDAyWhcNMjUwNzMxMTQxMDAyWjBaMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHTmV3 6 | WW9yazEQMA4GA1UEBwwHTmV3WW9yazELMAkGA1UECgwCSVQxGjAYBgNVBAMMEWxv 7 | Y2FscmVnaXN0cnkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 8 | 3QgNGc1Dpf4SMls0R/RroX7d6cniisI2XgspVHHYW8hDx/NO8NdZf3KCD9T+SAVg 9 | 9DddbGx7StxhMnB1lW7HmeOzGFR4NjOuh4fz0iYYoLRd4gzkit0D+OSBsnr4tLle 10 | yysEC0wYkLAHCHeZlZ9u54DTW9bzGW4KNFAkLagBQMGcg2bkXpqOr1xrO+6uc5IV 11 | 8VM6yoedXlDek6K0rsxRUmh/8jcEXMwFTqmnTexShYVB+Ji5XLPgikSOmQje+3JF 12 | 1tE8rIsPhF98mWz/wRRBaV+5LJtTM54TXmi4BrmMALKM0Lo1khvWitwIfPOJWGEX 13 | VJllkJARv2KFX87vkEv6dQIDAQABo1AwTjAdBgNVHQ4EFgQUnUGjY1oLqT00U7y2 14 | iGOSgpx6EZkwHwYDVR0jBBgwFoAUnUGjY1oLqT00U7y2iGOSgpx6EZkwDAYDVR0T 15 | BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeS06ifrHBKZ700o4bjEt2vFg1pcK 16 | abk8Bw7Vcn2RvDB09lJg3UFRs6CrJFqOnVF+AUA2goB8UKP7/LeYnN00DdZsLRi9 17 | sY93agdq95iv62+VdCnA+XleDS01wQFwzOlXb7p76y9a/v3SItMB9viXZ93wIo8E 18 | C0Owql0tvevvxqkxPru2QQO6JOzA70mQ19vQJsdBuviTeGMul7trZeOTmDRlR5vT 19 | u9h5Xk5bLV50H56MlZNOJvY+rnX+yk2Qgru2HDlOwb2vYYOZvSqiAHCuIB3VslpP 20 | iX0gAyV5WeSxmLQna6IxJIuNUUdq4UR6ZoACz69DQa1WO+dbs9jWseD5Cg== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/certs/rediscounter.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDdCA0ZzUOl/hIy 3 | WzRH9Guhft3pyeKKwjZeCylUcdhbyEPH807w11l/coIP1P5IBWD0N11sbHtK3GEy 4 | cHWVbseZ47MYVHg2M66Hh/PSJhigtF3iDOSK3QP45IGyevi0uV7LKwQLTBiQsAcI 5 | d5mVn27ngNNb1vMZbgo0UCQtqAFAwZyDZuRemo6vXGs77q5zkhXxUzrKh51eUN6T 6 | orSuzFFSaH/yNwRczAVOqadN7FKFhUH4mLlcs+CKRI6ZCN77ckXW0Tysiw+EX3yZ 7 | bP/BFEFpX7ksm1MznhNeaLgGuYwAsozQujWSG9aK3Ah884lYYRdUmWWQkBG/YoVf 8 | zu+QS/p1AgMBAAECggEBANG7sQq5rqZU5xlnV72rXXIZuyL7UX7PeN1WA/rAKEg3 9 | SLHz2wVHowH/OxEgz8SxbeVun7ShX4CSi5xcAAcy3i3VVX0RshvkgIjUZXUUdywO 10 | 2kMEbtyhigJjefpNG7AJcbyhba32oByzG4laS58hcRA1OtmbpoOL2hz3qsyz7bRt 11 | /vVaJHFTm6CSz9Wn/m2xWmyLJn3ObHE8AMp8FteztkcNceqGWpga99u6PSMFJ0G8 12 | JT/RDUUO2WA5hf0GmJbBTyqx6977ivFGeYhXrlpOxxVxkx0x+Gd7p/F/FNhGmr1l 13 | qIa+z+NcWOxKV30BG7Ra9A0doxVIojQCASLkPN3rXAECgYEA/QELsstfvSmdZvUJ 14 | 6Ox3g+Ozxxm5w0tiCEaeC0nlsrENPabSq9xEs2jbKhww5ox3sPs6OKusRD6YPE2t 15 | Eq/uEVyn+oGOr+J4Q4zQTDNrd2QAYGMZRNuZkTOFpe8KZBmTIo4mCtEGp2SUSJcC 16 | uL6cO6DbwSikrijHvkFu6YpMSQECgYEA36YVe9Uz0xLbmtxeYVSijeEfvfNRDjys 17 | cbjAI6eFmntR8XTmEWoZVRKmA9kDg9IlK82vZeEqMlEviagO6eAjl6rC9lxfBTUl 18 | BbQWaAFZGimQInk0cWiHc8AhdOK+H7LKaEMIycMNu2AUoWBN/Mc9CGy8DFrc3RRQ 19 | FuYrXWGpnXUCgYEAqpks6TfHa8cG0ujB8OSaRj2g+Mz4/J31EX2Ejjoa/53xPrQh 20 | dC9Hx+4ZclCmDJ+FCbqtbI8dzrqibm82F9a3Yc+nmPwJWcIMtAfcYLV/bnbo5hWM 21 | cWjeKRGjudrwl8TC+Nb/AeYmZXMlpbjl5erpcC+sXpfoS2NGJJz8i89sVwECgYBP 22 | ZYTG+390dYNkzMrsvsEeoUdFhfXGmh+WF8KOZdB2cUU79QYgNIxduUsanpYy3A26 23 | KUEVaAQ07MF1myYAPUQlecfQ8iYBkUZdaftyXNgnA45ZzrGheTxtCU5XUo+wbSaS 24 | MQoTpp1fYdKxH6FQFeNC9Gcl87PpAGcWWgwXEK7IaQKBgQDn7kAAlxOYNQye8320 25 | IfOoUyzFMLotlGclqKvkhLEDmg5aUEurdOAR01qSQt2OcJ5AlXJ2UJpsOCx7cXg2 26 | qt37wOz5F0L5jsHuPDLImQm2rdPuWZH2VwrYF71ejkmAw1sAOaPQCYpl8wGbKGRt 27 | y7ZDJzUMJ6e3JMLVYcOBIvRLGw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/configs/default.conf: -------------------------------------------------------------------------------- 1 | upstream rediscounter { 2 | # The Flask application. 3 | server rediscounter:8000; 4 | } 5 | 6 | # In case you want 'www' addresses to be automatically redirected without 'www'. 7 | # server { 8 | # listen 80; 9 | # listen 443; 10 | # server_name www.yourrealdomain.com; 11 | # return 301 https://yourrealdomain.com$request_uri; 12 | #} 13 | 14 | server { 15 | listen 80 default deferred; 16 | server_name 172.17.8.101; 17 | 18 | # All http traffic will get redirected to SSL. 19 | return 307 https://$host$request_uri; 20 | } 21 | 22 | server { 23 | # "deferred" reduces the number of formalities between the server and client. 24 | listen 443 default deferred; 25 | server_name 172.17.8.101; 26 | 27 | # Static asset path, which is read from the rediscounter's VOLUME. In this 28 | # case the example application has no assets, but this is how you would 29 | # configure assets to be served through nginx. 30 | root /rediscounter/build/public; 31 | 32 | # Ensure timeouts are equal across browsers and raise the max content-length size. 33 | keepalive_timeout 60; 34 | client_max_body_size 5m; 35 | 36 | # SSL goodness. 37 | ssl on; 38 | ssl_certificate /etc/ssl/certs/rediscounter.crt; 39 | ssl_certificate_key /etc/ssl/private/rediscounter.key; 40 | ssl_session_cache shared:SSL:50m; 41 | ssl_session_timeout 5m; 42 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 43 | ssl_prefer_server_ciphers on; 44 | ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; 45 | ssl_dhparam /etc/ssl/private/dhparam.pem; 46 | ssl_ecdh_curve secp384r1; 47 | add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains;' always; 48 | 49 | # Disallow access to hidden files and directories. 50 | location ~ /\. { 51 | return 404; 52 | access_log off; 53 | log_not_found off; 54 | } 55 | 56 | # Allow optionally writing an index.html file to take precedence over the upstream. 57 | try_files $uri $uri/index.html $uri.html @rediscounter; 58 | 59 | # Attempt to load the favicon or fall back to status code 204. 60 | location = /favicon.ico { 61 | try_files /favicon.ico = 204; 62 | access_log off; 63 | log_not_found off; 64 | } 65 | 66 | # Load the Flask app back end with proper headers. 67 | location @rediscounter { 68 | proxy_set_header X-Forwarded-Proto $scheme; 69 | proxy_set_header Host $http_host; 70 | proxy_set_header X-Real-IP $remote_addr; 71 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 72 | proxy_redirect off; 73 | proxy_pass http://rediscounter; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/configs/nginx.conf: -------------------------------------------------------------------------------- 1 | # Number of workers to run, usually equals number of CPU cores. 2 | worker_processes auto; 3 | 4 | # Maximum number of opened files per process. 5 | worker_rlimit_nofile 4096; 6 | 7 | events { 8 | # Maximum number of simultaneous connections that can be opened by a worker process. 9 | worker_connections 1024; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | # --------------------------------------------------------------------------- 17 | # Security settings from: 18 | # https://gist.github.com/plentz/6737338 19 | 20 | # Disable nginx version from being displayed on errors. 21 | server_tokens off; 22 | 23 | # config to don't allow the browser to render the page inside an frame or iframe 24 | # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking 25 | # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri 26 | # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options 27 | add_header X-Frame-Options SAMEORIGIN; 28 | 29 | # when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, 30 | # to disable content-type sniffing on some browsers. 31 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 32 | # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx 33 | # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx 34 | # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 35 | add_header X-Content-Type-Options nosniff; 36 | 37 | # This header enables the Cross-site scripting (XSS) filter built into most recent web browsers. 38 | # It's usually enabled by default anyway, so the role of this header is to re-enable the filter for 39 | # this particular website if it was disabled by the user. 40 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 41 | add_header X-XSS-Protection "1; mode=block"; 42 | # --------------------------------------------------------------------------- 43 | 44 | # Avoid situations where a hostname is too long when dealing with vhosts. 45 | server_names_hash_bucket_size 64; 46 | server_names_hash_max_size 512; 47 | 48 | # Performance optimizations. 49 | sendfile on; 50 | tcp_nopush on; 51 | 52 | # http://nginx.org/en/docs/hash.html 53 | types_hash_max_size 2048; 54 | 55 | # Enable gzip for everything but IE6. 56 | gzip on; 57 | gzip_disable "msie6"; 58 | 59 | # Default config for the app backend. 60 | include /etc/nginx/conf.d/default.conf; 61 | } 62 | -------------------------------------------------------------------------------- /chapter-8/deploy/nginx/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Overwrite a few variables, this allows us to use the same template 5 | # for both development, staging and production. 6 | CONFIG_PATH="/etc/nginx/conf.d/default.conf" 7 | STAGING_IP="172.17.8.101" 8 | STAGING_HOSTNAME="core-01" 9 | DOMAIN_NAME="yourrealdomain.com" 10 | 11 | if [[ $(hostname) != "${STAGING_HOSTNAME}" ]]; then 12 | sed -i "s/${STAGING_IP}/${DOMAIN_NAME}/g" "${CONFIG_PATH}" 13 | fi 14 | 15 | # Execute the CMD from the Dockerfile. 16 | exec "$@" 17 | -------------------------------------------------------------------------------- /chapter-8/deploy/units/nginx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service rediscounter.service 4 | After=docker.service rediscounter.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | -p 80:80 -p 443:443 \ 12 | --link rediscounter:rediscounter \ 13 | -v /etc/ssl/certs:/etc/ssl/certs \ 14 | -v /etc/ssl/private:/etc/ssl/private %p 15 | ExecStop=/usr/bin/docker stop %p 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /chapter-8/deploy/units/redis.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/mkdir -p /var/lib/%p/data 9 | ExecStartPre=-/usr/bin/docker kill %p 10 | ExecStartPre=-/usr/bin/docker rm -f %p 11 | ExecStart=/usr/bin/docker run --rm --name %p \ 12 | -v /var/lib/%p/data:/var/lib/%p/data -p 6379:6379 %p:2.8.21 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target rediscounter.service 17 | -------------------------------------------------------------------------------- /chapter-8/deploy/units/rediscounter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service redis.service 4 | After=docker.service redis.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | --link redis:redis -p 8000:8000 %p 12 | ExecStartPost=-/usr/bin/docker stop nginx 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target nginx.service 17 | -------------------------------------------------------------------------------- /chapter-8/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | .vagrant/ 6 | -------------------------------------------------------------------------------- /chapter-8/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | 19 | .vagrant/ 20 | -------------------------------------------------------------------------------- /chapter-8/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-8/website/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version '>= 1.6.0' 2 | 3 | # Configuration settings for the Virtual Machine. 4 | $update_channel = 'beta' 5 | $image_version = 'current' 6 | $vm_memory = 1024 7 | $vm_cpus = 1 8 | $forwarded_ports = { 9 | '80' => '8080', 10 | '443' => '8081' 11 | } 12 | $vm_host = 'core-01' 13 | $vm_ip = '172.17.8.101' 14 | 15 | Vagrant.configure('2') do |config| 16 | config.ssh.insert_key = false 17 | config.ssh.forward_agent = true 18 | 19 | config.vm.box = 'coreos-%s' % [$update_channel] 20 | if $image_version != 'current' 21 | config.vm.box_version = $image_version 22 | end 23 | config.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json' % [$update_channel, $image_version] 24 | 25 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 26 | config.vm.provider vmware do |v, override| 27 | override.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json' % [$update_channel, $image_version] 28 | end 29 | end 30 | 31 | config.vm.provider :virtualbox do |v| 32 | v.check_guest_additions = false 33 | v.functional_vboxsf = false 34 | end 35 | 36 | if Vagrant.has_plugin?('vagrant-vbguest') then 37 | config.vbguest.auto_update = false 38 | end 39 | 40 | config.vm.define vm_name = $vm_host do |config| 41 | config.vm.hostname = vm_name 42 | 43 | config.vm.network :private_network, ip: $vm_ip 44 | 45 | $forwarded_ports.each do |guest, host| 46 | config.vm.network 'forwarded_port', guest: guest, host: host, auto_correct: true 47 | end 48 | 49 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 50 | config.vm.provider vmware do |v| 51 | v.gui = false 52 | v.vmx['memsize'] = $vm_memory 53 | v.vmx['numvcpus'] = $vm_cpus 54 | end 55 | end 56 | 57 | config.vm.provider :virtualbox do |vb| 58 | vb.gui = false 59 | vb.memory = $vm_memory 60 | vb.cpus = $vm_cpus 61 | end 62 | 63 | # Create bare git repos. 64 | config.vm.provision 'shell', 65 | inline: 'sudo mkdir -p /var/git/nginx.git /var/git/nginx /var/git/rediscounter.git /var/git/rediscounter' 66 | 67 | config.vm.provision 'shell', 68 | inline: 'sudo su && cd /var/git/nginx.git && git --bare init && chown -R core:core /var/git/nginx.git && chown -R core:core /var/git/nginx' 69 | 70 | config.vm.provision 'shell', 71 | inline: 'sudo su && cd /var/git/rediscounter.git && git --bare init && chown -R core:core /var/git/rediscounter.git && chown -R core:core /var/git/rediscounter' 72 | 73 | 74 | # Copy files into the VM instance upon provision. 75 | config.vm.provision 'file', source: '../deploy/nginx/certs', 76 | destination: '/tmp' 77 | 78 | config.vm.provision 'file', source: '../deploy/units', 79 | destination: '/tmp' 80 | 81 | config.vm.provision 'file', source: '../deploy/git/post-receive', 82 | destination: '/tmp' 83 | 84 | # Move the files with sudo into the correct remote location. 85 | config.vm.provision 'shell', 86 | inline: 'sudo mv /tmp/certs/rediscounter.crt /etc/ssl/certs' 87 | 88 | config.vm.provision 'shell', 89 | inline: 'sudo mv /tmp/certs/rediscounter.key /etc/ssl/private' 90 | 91 | config.vm.provision 'shell', 92 | inline: 'sudo mv /tmp/certs/dhparam.pem /etc/ssl/private' 93 | 94 | config.vm.provision 'shell', 95 | inline: 'sudo mv /tmp/units/redis.service /etc/systemd/system' 96 | 97 | config.vm.provision 'shell', 98 | inline: 'sudo mv /tmp/units/nginx.service /etc/systemd/system' 99 | 100 | config.vm.provision 'shell', 101 | inline: 'sudo mv /tmp/units/rediscounter.service /etc/systemd/system' 102 | 103 | config.vm.provision 'shell', 104 | inline: 'mv /tmp/post-receive/nginx /var/git/nginx.git/hooks/post-receive' 105 | 106 | config.vm.provision 'shell', 107 | inline: 'mv /tmp/post-receive/rediscounter /var/git/rediscounter.git/hooks/post-receive' 108 | 109 | # Set proper permissions. 110 | config.vm.provision 'shell', 111 | inline: 'chmod +x /var/git/nginx.git/hooks/post-receive /var/git/rediscounter.git/hooks/post-receive' 112 | 113 | 114 | # Pull in any Docker images we need. 115 | config.vm.provision 'shell', 116 | inline: 'docker pull redis:2.8.21' 117 | 118 | # Enable and start Redis through systemd, this ensures it loads on bootup. 119 | config.vm.provision 'shell', 120 | inline: 'sudo systemctl enable redis.service && sudo systemctl start redis.service' 121 | 122 | # ************************************************************************** 123 | # A few commands need to be ran on your `workstation` before proceeding. 124 | # -------------------------------------------------------------------------- 125 | # Consult with Chapter 8 of the book to see the details. 126 | # ************************************************************************** 127 | 128 | # ************************************************************************** 129 | # A few commands need to be ran on the `CoreOS host` before proceeding. 130 | # -------------------------------------------------------------------------- 131 | # Consult with Chapter 8 of the book to see the details. 132 | # ************************************************************************** 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /chapter-8/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-8/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-8/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | -------------------------------------------------------------------------------- /chapter-9/deploy/git/post-receive/nginx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Configuration. 4 | REPO_NAME="nginx" 5 | 6 | # Check out the newest version of the code. 7 | export GIT_WORK_TREE="/var/git/${REPO_NAME}" 8 | git checkout -f 9 | 10 | TAG="$(git log --pretty=format:'%h' -n 1)" 11 | FULL_COMMIT_TAG="${REPO_NAME}:${TAG}" 12 | FULL_LATEST_TAG="${REPO_NAME}:latest" 13 | 14 | # Build the image with the proper commit tag. 15 | docker build -t "${FULL_COMMIT_TAG}" "${GIT_WORK_TREE}" 16 | 17 | # Get the Docker ID of the last built image. 18 | DOCKER_ID="$(docker images -q $REPO_NAME | head -1)" 19 | 20 | # Tag a latest version based off the proper commit tag. 21 | docker tag -f "${DOCKER_ID}" "${FULL_LATEST_TAG}" 22 | 23 | echo "Restarting ${REPO_NAME}" 24 | docker stop "${REPO_NAME}" 25 | 26 | echo "Removing untagged Docker images (may take a while)" 27 | docker rmi $(docker images --quiet --filter "dangling=true") 28 | -------------------------------------------------------------------------------- /chapter-9/deploy/git/post-receive/rediscounter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Configuration. 4 | REPO_NAME="rediscounter" 5 | 6 | # Check out the newest version of the code. 7 | export GIT_WORK_TREE="/var/git/${REPO_NAME}" 8 | git checkout -f 9 | 10 | TAG="$(git log --pretty=format:'%h' -n 1)" 11 | FULL_COMMIT_TAG="${REPO_NAME}:${TAG}" 12 | FULL_LATEST_TAG="${REPO_NAME}:latest" 13 | 14 | # Build the image with the proper commit tag. 15 | docker build -t "${FULL_COMMIT_TAG}" "${GIT_WORK_TREE}" 16 | 17 | # Get the Docker ID of the last built image. 18 | DOCKER_ID="$(docker images -q $REPO_NAME | head -1)" 19 | 20 | # Tag a latest version based off the proper commit tag. 21 | docker tag -f "${DOCKER_ID}" "${FULL_LATEST_TAG}" 22 | 23 | echo "Restarting ${REPO_NAME}" 24 | docker stop "${REPO_NAME}" 25 | 26 | echo "Removing untagged Docker images (may take a while)" 27 | docker rmi $(docker images --quiet --filter "dangling=true") 28 | 29 | echo "Restarting nginx" 30 | docker stop "nginx" 31 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.9.3 2 | MAINTAINER Nick Janetakis 3 | 4 | # Delete default static pages. 5 | RUN rm /usr/share/nginx/html/* 6 | 7 | # Copy over the custom nginx and default configs. 8 | COPY configs/nginx.conf /etc/nginx/nginx.conf 9 | COPY configs/default.conf /etc/nginx/conf.d/default.conf 10 | 11 | # Copy over the self signed SSL certificates. 12 | COPY certs/rediscounter.crt /etc/ssl/certs/rediscounter.crt 13 | COPY certs/rediscounter.key /etc/ssl/private/rediscounter.key 14 | COPY certs/dhparam.pem /etc/ssl/private/dhparam.pem 15 | 16 | # Allow us to customize the entry point of the image. 17 | COPY docker-entrypoint / 18 | RUN chmod +x /docker-entrypoint 19 | ENTRYPOINT ["/docker-entrypoint"] 20 | 21 | # Start nginx in the foreground. 22 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/certs/dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA4ZvlxM3OiUA+WWjQolRqxLazGeM0sVwXUJLpPlVrjZH/9Slo7ovq 3 | VZhFgBTZToXZlO+rG77/7XvkLxOwa/Bxj9sEyieObFh5M3AYpqPliotsPDNvNl1l 4 | Ys09k+mYUqW/WToVHburvDAHLseHBiRPTkKMEgZ79wZE9SHiHXOJnNSb2HvgfnHz 5 | 4QM+e/z4Yx1q22TF+lACm/zIFrPhUQpu5RkF7R3jwurVdWa5XyVljGPlnQXbeBns 6 | fECKaU2wsQc2+t5gpkdxDlX7ZDQ9Nh+DkW0WCrGyLm2cBfP2qm5X4RL1ge3LZzYC 7 | CpHZ7NCtHFneOUtlZ+XMLxHbseosm2PNYwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/certs/rediscounter.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAm+gAwIBAgIJAJFkaQ5PfmT8MA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV 3 | BAYTAlVTMRAwDgYDVQQIDAdOZXdZb3JrMRAwDgYDVQQHDAdOZXdZb3JrMQswCQYD 4 | VQQKDAJJVDEaMBgGA1UEAwwRbG9jYWxyZWdpc3RyeS5jb20wHhcNMTUwODAzMTQx 5 | MDAyWhcNMjUwNzMxMTQxMDAyWjBaMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHTmV3 6 | WW9yazEQMA4GA1UEBwwHTmV3WW9yazELMAkGA1UECgwCSVQxGjAYBgNVBAMMEWxv 7 | Y2FscmVnaXN0cnkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 8 | 3QgNGc1Dpf4SMls0R/RroX7d6cniisI2XgspVHHYW8hDx/NO8NdZf3KCD9T+SAVg 9 | 9DddbGx7StxhMnB1lW7HmeOzGFR4NjOuh4fz0iYYoLRd4gzkit0D+OSBsnr4tLle 10 | yysEC0wYkLAHCHeZlZ9u54DTW9bzGW4KNFAkLagBQMGcg2bkXpqOr1xrO+6uc5IV 11 | 8VM6yoedXlDek6K0rsxRUmh/8jcEXMwFTqmnTexShYVB+Ji5XLPgikSOmQje+3JF 12 | 1tE8rIsPhF98mWz/wRRBaV+5LJtTM54TXmi4BrmMALKM0Lo1khvWitwIfPOJWGEX 13 | VJllkJARv2KFX87vkEv6dQIDAQABo1AwTjAdBgNVHQ4EFgQUnUGjY1oLqT00U7y2 14 | iGOSgpx6EZkwHwYDVR0jBBgwFoAUnUGjY1oLqT00U7y2iGOSgpx6EZkwDAYDVR0T 15 | BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeS06ifrHBKZ700o4bjEt2vFg1pcK 16 | abk8Bw7Vcn2RvDB09lJg3UFRs6CrJFqOnVF+AUA2goB8UKP7/LeYnN00DdZsLRi9 17 | sY93agdq95iv62+VdCnA+XleDS01wQFwzOlXb7p76y9a/v3SItMB9viXZ93wIo8E 18 | C0Owql0tvevvxqkxPru2QQO6JOzA70mQ19vQJsdBuviTeGMul7trZeOTmDRlR5vT 19 | u9h5Xk5bLV50H56MlZNOJvY+rnX+yk2Qgru2HDlOwb2vYYOZvSqiAHCuIB3VslpP 20 | iX0gAyV5WeSxmLQna6IxJIuNUUdq4UR6ZoACz69DQa1WO+dbs9jWseD5Cg== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/certs/rediscounter.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDdCA0ZzUOl/hIy 3 | WzRH9Guhft3pyeKKwjZeCylUcdhbyEPH807w11l/coIP1P5IBWD0N11sbHtK3GEy 4 | cHWVbseZ47MYVHg2M66Hh/PSJhigtF3iDOSK3QP45IGyevi0uV7LKwQLTBiQsAcI 5 | d5mVn27ngNNb1vMZbgo0UCQtqAFAwZyDZuRemo6vXGs77q5zkhXxUzrKh51eUN6T 6 | orSuzFFSaH/yNwRczAVOqadN7FKFhUH4mLlcs+CKRI6ZCN77ckXW0Tysiw+EX3yZ 7 | bP/BFEFpX7ksm1MznhNeaLgGuYwAsozQujWSG9aK3Ah884lYYRdUmWWQkBG/YoVf 8 | zu+QS/p1AgMBAAECggEBANG7sQq5rqZU5xlnV72rXXIZuyL7UX7PeN1WA/rAKEg3 9 | SLHz2wVHowH/OxEgz8SxbeVun7ShX4CSi5xcAAcy3i3VVX0RshvkgIjUZXUUdywO 10 | 2kMEbtyhigJjefpNG7AJcbyhba32oByzG4laS58hcRA1OtmbpoOL2hz3qsyz7bRt 11 | /vVaJHFTm6CSz9Wn/m2xWmyLJn3ObHE8AMp8FteztkcNceqGWpga99u6PSMFJ0G8 12 | JT/RDUUO2WA5hf0GmJbBTyqx6977ivFGeYhXrlpOxxVxkx0x+Gd7p/F/FNhGmr1l 13 | qIa+z+NcWOxKV30BG7Ra9A0doxVIojQCASLkPN3rXAECgYEA/QELsstfvSmdZvUJ 14 | 6Ox3g+Ozxxm5w0tiCEaeC0nlsrENPabSq9xEs2jbKhww5ox3sPs6OKusRD6YPE2t 15 | Eq/uEVyn+oGOr+J4Q4zQTDNrd2QAYGMZRNuZkTOFpe8KZBmTIo4mCtEGp2SUSJcC 16 | uL6cO6DbwSikrijHvkFu6YpMSQECgYEA36YVe9Uz0xLbmtxeYVSijeEfvfNRDjys 17 | cbjAI6eFmntR8XTmEWoZVRKmA9kDg9IlK82vZeEqMlEviagO6eAjl6rC9lxfBTUl 18 | BbQWaAFZGimQInk0cWiHc8AhdOK+H7LKaEMIycMNu2AUoWBN/Mc9CGy8DFrc3RRQ 19 | FuYrXWGpnXUCgYEAqpks6TfHa8cG0ujB8OSaRj2g+Mz4/J31EX2Ejjoa/53xPrQh 20 | dC9Hx+4ZclCmDJ+FCbqtbI8dzrqibm82F9a3Yc+nmPwJWcIMtAfcYLV/bnbo5hWM 21 | cWjeKRGjudrwl8TC+Nb/AeYmZXMlpbjl5erpcC+sXpfoS2NGJJz8i89sVwECgYBP 22 | ZYTG+390dYNkzMrsvsEeoUdFhfXGmh+WF8KOZdB2cUU79QYgNIxduUsanpYy3A26 23 | KUEVaAQ07MF1myYAPUQlecfQ8iYBkUZdaftyXNgnA45ZzrGheTxtCU5XUo+wbSaS 24 | MQoTpp1fYdKxH6FQFeNC9Gcl87PpAGcWWgwXEK7IaQKBgQDn7kAAlxOYNQye8320 25 | IfOoUyzFMLotlGclqKvkhLEDmg5aUEurdOAR01qSQt2OcJ5AlXJ2UJpsOCx7cXg2 26 | qt37wOz5F0L5jsHuPDLImQm2rdPuWZH2VwrYF71ejkmAw1sAOaPQCYpl8wGbKGRt 27 | y7ZDJzUMJ6e3JMLVYcOBIvRLGw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/configs/default.conf: -------------------------------------------------------------------------------- 1 | upstream rediscounter { 2 | # The Flask application. 3 | server rediscounter:8000; 4 | } 5 | 6 | # In case you want 'www' addresses to be automatically redirected without 'www'. 7 | # server { 8 | # listen 80; 9 | # listen 443; 10 | # server_name www.yourrealdomain.com; 11 | # return 301 https://yourrealdomain.com$request_uri; 12 | #} 13 | 14 | server { 15 | listen 80 default deferred; 16 | server_name 172.17.8.101; 17 | 18 | # All http traffic will get redirected to SSL. 19 | return 307 https://$host$request_uri; 20 | } 21 | 22 | server { 23 | # "deferred" reduces the number of formalities between the server and client. 24 | listen 443 default deferred; 25 | server_name 172.17.8.101; 26 | 27 | # Static asset path, which is read from the rediscounter's VOLUME. In this 28 | # case the example application has no assets, but this is how you would 29 | # configure assets to be served through nginx. 30 | root /rediscounter/build/public; 31 | 32 | # Ensure timeouts are equal across browsers and raise the max content-length size. 33 | keepalive_timeout 60; 34 | client_max_body_size 5m; 35 | 36 | # SSL goodness. 37 | ssl on; 38 | ssl_certificate /etc/ssl/certs/rediscounter.crt; 39 | ssl_certificate_key /etc/ssl/private/rediscounter.key; 40 | ssl_session_cache shared:SSL:50m; 41 | ssl_session_timeout 5m; 42 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 43 | ssl_prefer_server_ciphers on; 44 | ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; 45 | ssl_dhparam /etc/ssl/private/dhparam.pem; 46 | ssl_ecdh_curve secp384r1; 47 | add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains;' always; 48 | 49 | # Disallow access to hidden files and directories. 50 | location ~ /\. { 51 | return 404; 52 | access_log off; 53 | log_not_found off; 54 | } 55 | 56 | # Allow optionally writing an index.html file to take precedence over the upstream. 57 | try_files $uri $uri/index.html $uri.html @rediscounter; 58 | 59 | # Attempt to load the favicon or fall back to status code 204. 60 | location = /favicon.ico { 61 | try_files /favicon.ico = 204; 62 | access_log off; 63 | log_not_found off; 64 | } 65 | 66 | # Load the Flask app back end with proper headers. 67 | location @rediscounter { 68 | proxy_set_header X-Forwarded-Proto $scheme; 69 | proxy_set_header Host $http_host; 70 | proxy_set_header X-Real-IP $remote_addr; 71 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 72 | proxy_redirect off; 73 | proxy_pass http://rediscounter; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/configs/nginx.conf: -------------------------------------------------------------------------------- 1 | # Number of workers to run, usually equals number of CPU cores. 2 | worker_processes auto; 3 | 4 | # Maximum number of opened files per process. 5 | worker_rlimit_nofile 4096; 6 | 7 | events { 8 | # Maximum number of simultaneous connections that can be opened by a worker process. 9 | worker_connections 1024; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | # --------------------------------------------------------------------------- 17 | # Security settings from: 18 | # https://gist.github.com/plentz/6737338 19 | 20 | # Disable nginx version from being displayed on errors. 21 | server_tokens off; 22 | 23 | # config to don't allow the browser to render the page inside an frame or iframe 24 | # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking 25 | # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri 26 | # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options 27 | add_header X-Frame-Options SAMEORIGIN; 28 | 29 | # when serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, 30 | # to disable content-type sniffing on some browsers. 31 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 32 | # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx 33 | # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx 34 | # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 35 | add_header X-Content-Type-Options nosniff; 36 | 37 | # This header enables the Cross-site scripting (XSS) filter built into most recent web browsers. 38 | # It's usually enabled by default anyway, so the role of this header is to re-enable the filter for 39 | # this particular website if it was disabled by the user. 40 | # https://www.owasp.org/index.php/List_of_useful_HTTP_headers 41 | add_header X-XSS-Protection "1; mode=block"; 42 | # --------------------------------------------------------------------------- 43 | 44 | # Avoid situations where a hostname is too long when dealing with vhosts. 45 | server_names_hash_bucket_size 64; 46 | server_names_hash_max_size 512; 47 | 48 | # Performance optimizations. 49 | sendfile on; 50 | tcp_nopush on; 51 | 52 | # http://nginx.org/en/docs/hash.html 53 | types_hash_max_size 2048; 54 | 55 | # Enable gzip for everything but IE6. 56 | gzip on; 57 | gzip_disable "msie6"; 58 | 59 | # Default config for the app backend. 60 | include /etc/nginx/conf.d/default.conf; 61 | } 62 | -------------------------------------------------------------------------------- /chapter-9/deploy/nginx/docker-entrypoint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Overwrite a few variables, this allows us to use the same template 5 | # for both development, staging and production. 6 | CONFIG_PATH="/etc/nginx/conf.d/default.conf" 7 | STAGING_IP="172.17.8.101" 8 | STAGING_HOSTNAME="core-01" 9 | DOMAIN_NAME="yourrealdomain.com" 10 | 11 | if [[ $(hostname) != "${STAGING_HOSTNAME}" ]]; then 12 | sed -i "s/${STAGING_IP}/${DOMAIN_NAME}/g" "${CONFIG_PATH}" 13 | fi 14 | 15 | # Execute the CMD from the Dockerfile. 16 | exec "$@" 17 | -------------------------------------------------------------------------------- /chapter-9/deploy/production/rules-save: -------------------------------------------------------------------------------- 1 | *filter 2 | 3 | :INPUT DROP [0:0] 4 | :FORWARD DROP [0:0] 5 | :OUTPUT ACCEPT [0:0] 6 | 7 | -A INPUT -i lo -j ACCEPT 8 | -A INPUT -i eth1 -j ACCEPT 9 | -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 10 | -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT 11 | -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT 12 | -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT 13 | -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT 14 | -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT 15 | -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT 16 | 17 | COMMIT 18 | -------------------------------------------------------------------------------- /chapter-9/deploy/units/nginx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service rediscounter.service 4 | After=docker.service rediscounter.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | -p 80:80 -p 443:443 \ 12 | --link rediscounter:rediscounter \ 13 | -v /etc/ssl/certs:/etc/ssl/certs \ 14 | -v /etc/ssl/private:/etc/ssl/private %p 15 | ExecStop=/usr/bin/docker stop %p 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /chapter-9/deploy/units/redis.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/mkdir -p /var/lib/%p/data 9 | ExecStartPre=-/usr/bin/docker kill %p 10 | ExecStartPre=-/usr/bin/docker rm -f %p 11 | ExecStart=/usr/bin/docker run --rm --name %p \ 12 | -v /var/lib/%p/data:/var/lib/%p/data -p 6379:6379 %p:2.8.21 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target rediscounter.service 17 | -------------------------------------------------------------------------------- /chapter-9/deploy/units/rediscounter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run %p 3 | Requires=docker.service redis.service 4 | After=docker.service redis.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker kill %p 9 | ExecStartPre=-/usr/bin/docker rm -f %p 10 | ExecStart=/usr/bin/docker run -t --rm --name %p \ 11 | --link redis:redis -p 8000:8000 %p 12 | ExecStartPost=-/usr/bin/docker stop nginx 13 | ExecStop=/usr/bin/docker stop %p 14 | 15 | [Install] 16 | WantedBy=multi-user.target nginx.service 17 | -------------------------------------------------------------------------------- /chapter-9/website/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp/* 3 | log/* 4 | .dockerignore 5 | .vagrant/ 6 | -------------------------------------------------------------------------------- /chapter-9/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # OS and editor files 6 | .DS_Store 7 | */**.DS_Store 8 | ._* 9 | .*.sw* 10 | *~ 11 | .idea/ 12 | .mr.developer.cfg 13 | .project 14 | .pydevproject 15 | *.tmproj 16 | *.tmproject 17 | tmtags 18 | 19 | .vagrant/ 20 | -------------------------------------------------------------------------------- /chapter-9/website/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the barebones version of Python 2.7.10. 2 | FROM python:2.7.10-slim 3 | MAINTAINER Nick Janetakis 4 | 5 | # Install any packages that must be installed. 6 | RUN apt-get update && apt-get install -qq -y build-essential --fix-missing --no-install-recommends 7 | 8 | # Set up the install path for this service. 9 | ENV INSTALL_PATH /rediscounter 10 | RUN mkdir -p $INSTALL_PATH 11 | 12 | # Update the workdir to be where our app is installed. 13 | WORKDIR $INSTALL_PATH 14 | 15 | # Ensure packages are cached and only get updated when necessary. If we didn’t do this step then every time we pushed an app change it would also re-run pip install, even if no packages changed. 16 | COPY requirements.txt requirements.txt 17 | RUN pip install -r requirements.txt 18 | 19 | # Copy the source from your workstation to the image at the WORKDIR path. 20 | COPY . . 21 | 22 | # Create a volume so that nginx can read from it. 23 | VOLUME ["$INSTALL_PATH/build/public"] 24 | 25 | # The default command to run if no command is specified. 26 | CMD python app.py 27 | -------------------------------------------------------------------------------- /chapter-9/website/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version '>= 1.6.0' 2 | 3 | # Configuration settings for the Virtual Machine. 4 | $update_channel = 'beta' 5 | $image_version = 'current' 6 | $vm_memory = 1024 7 | $vm_cpus = 1 8 | $forwarded_ports = { 9 | '80' => '8080', 10 | '443' => '8081' 11 | } 12 | $vm_host = 'core-01' 13 | $vm_ip = '172.17.8.101' 14 | 15 | Vagrant.configure('2') do |config| 16 | config.ssh.insert_key = false 17 | config.ssh.forward_agent = true 18 | 19 | config.vm.box = 'coreos-%s' % [$update_channel] 20 | if $image_version != 'current' 21 | config.vm.box_version = $image_version 22 | end 23 | config.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant.json' % [$update_channel, $image_version] 24 | 25 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 26 | config.vm.provider vmware do |v, override| 27 | override.vm.box_url = 'http://%s.release.core-os.net/amd64-usr/%s/coreos_production_vagrant_vmware_fusion.json' % [$update_channel, $image_version] 28 | end 29 | end 30 | 31 | config.vm.provider :virtualbox do |v| 32 | v.check_guest_additions = false 33 | v.functional_vboxsf = false 34 | end 35 | 36 | if Vagrant.has_plugin?('vagrant-vbguest') then 37 | config.vbguest.auto_update = false 38 | end 39 | 40 | config.vm.define vm_name = $vm_host do |config| 41 | config.vm.hostname = vm_name 42 | 43 | config.vm.network :private_network, ip: $vm_ip 44 | 45 | $forwarded_ports.each do |guest, host| 46 | config.vm.network 'forwarded_port', guest: guest, host: host, auto_correct: true 47 | end 48 | 49 | ['vmware_fusion', 'vmware_workstation'].each do |vmware| 50 | config.vm.provider vmware do |v| 51 | v.gui = false 52 | v.vmx['memsize'] = $vm_memory 53 | v.vmx['numvcpus'] = $vm_cpus 54 | end 55 | end 56 | 57 | config.vm.provider :virtualbox do |vb| 58 | vb.gui = false 59 | vb.memory = $vm_memory 60 | vb.cpus = $vm_cpus 61 | end 62 | 63 | # Create bare git repos. 64 | config.vm.provision 'shell', 65 | inline: 'sudo mkdir -p /var/git/nginx.git /var/git/nginx /var/git/rediscounter.git /var/git/rediscounter' 66 | 67 | config.vm.provision 'shell', 68 | inline: 'sudo su && cd /var/git/nginx.git && git --bare init && chown -R core:core /var/git/nginx.git && chown -R core:core /var/git/nginx' 69 | 70 | config.vm.provision 'shell', 71 | inline: 'sudo su && cd /var/git/rediscounter.git && git --bare init && chown -R core:core /var/git/rediscounter.git && chown -R core:core /var/git/rediscounter' 72 | 73 | 74 | # Copy files into the VM instance upon provision. 75 | config.vm.provision 'file', source: '../deploy/nginx/certs', 76 | destination: '/tmp' 77 | 78 | config.vm.provision 'file', source: '../deploy/units', 79 | destination: '/tmp' 80 | 81 | config.vm.provision 'file', source: '../deploy/git/post-receive', 82 | destination: '/tmp' 83 | 84 | # Move the files with sudo into the correct remote location. 85 | config.vm.provision 'shell', 86 | inline: 'sudo mv /tmp/certs/rediscounter.crt /etc/ssl/certs' 87 | 88 | config.vm.provision 'shell', 89 | inline: 'sudo mv /tmp/certs/rediscounter.key /etc/ssl/private' 90 | 91 | config.vm.provision 'shell', 92 | inline: 'sudo mv /tmp/certs/dhparam.pem /etc/ssl/private' 93 | 94 | config.vm.provision 'shell', 95 | inline: 'sudo mv /tmp/units/redis.service /etc/systemd/system' 96 | 97 | config.vm.provision 'shell', 98 | inline: 'sudo mv /tmp/units/nginx.service /etc/systemd/system' 99 | 100 | config.vm.provision 'shell', 101 | inline: 'sudo mv /tmp/units/rediscounter.service /etc/systemd/system' 102 | 103 | config.vm.provision 'shell', 104 | inline: 'mv /tmp/post-receive/nginx /var/git/nginx.git/hooks/post-receive' 105 | 106 | config.vm.provision 'shell', 107 | inline: 'mv /tmp/post-receive/rediscounter /var/git/rediscounter.git/hooks/post-receive' 108 | 109 | # Set proper permissions. 110 | config.vm.provision 'shell', 111 | inline: 'chmod +x /var/git/nginx.git/hooks/post-receive /var/git/rediscounter.git/hooks/post-receive' 112 | 113 | 114 | # Pull in any Docker images we need. 115 | config.vm.provision 'shell', 116 | inline: 'docker pull redis:2.8.21' 117 | 118 | # Enable and start Redis through systemd, this ensures it loads on bootup. 119 | config.vm.provision 'shell', 120 | inline: 'sudo systemctl enable redis.service && sudo systemctl start redis.service' 121 | 122 | # ************************************************************************** 123 | # A few commands need to be ran on your `workstation` before proceeding. 124 | # -------------------------------------------------------------------------- 125 | # Consult with Chapter 8 of the book to see the details. 126 | # ************************************************************************** 127 | 128 | # ************************************************************************** 129 | # A few commands need to be ran on the `CoreOS host` before proceeding. 130 | # -------------------------------------------------------------------------- 131 | # Consult with Chapter 8 of the book to see the details. 132 | # ************************************************************************** 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /chapter-9/website/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from redis import StrictRedis 3 | 4 | app = Flask(__name__) 5 | redis = StrictRedis(host='redis') 6 | 7 | @app.route('/') 8 | def hello_world(): 9 | hits = redis.incr('hits') 10 | return 'You visited {0} times!'.format(hits) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', port=8000, debug=True) 14 | -------------------------------------------------------------------------------- /chapter-9/website/docker-compose.yml: -------------------------------------------------------------------------------- 1 | redis: 2 | image: redis:2.8.21 3 | ports: 4 | - 6379:6379 5 | volumes: 6 | - ~/.docker-volumes/rediscounter/redis/data:/var/lib/redis/data 7 | 8 | website: 9 | build: . 10 | links: 11 | - redis 12 | volumes: 13 | - .:/rediscounter 14 | ports: 15 | - 8000:8000 16 | 17 | cadvisor: 18 | image: google/cadvisor:latest 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | ports: 25 | - 8080:8080 26 | -------------------------------------------------------------------------------- /chapter-9/website/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | redis==2.10.3 3 | --------------------------------------------------------------------------------