├── week-4 ├── second-sample │ ├── requirements.txt │ ├── app.py │ ├── Dockerfile.plain │ └── Dockerfile ├── first-sample │ ├── requirements.txt │ ├── app.py │ └── Dockerfile ├── third-sample │ └── Dockerfile ├── docker-slides-week-4.pdf └── README.md ├── week-1 ├── hello-world-flask │ ├── requirements.txt │ ├── app.py │ └── Dockerfile ├── hello-world-nginx │ ├── index.html │ └── Dockerfile ├── hello-world │ └── Dockerfile ├── docker-slides-week-1.pdf └── README.md ├── week-2 ├── voting-app │ ├── voting │ │ ├── requirements.txt │ │ ├── README.md │ │ ├── utils │ │ │ └── __init__.py │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── templates │ │ │ └── index.html │ │ └── static │ │ │ └── stylesheets │ │ │ └── style.css │ ├── worker │ │ ├── README.md │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── worker │ │ │ └── Worker.java │ ├── result │ │ ├── README.md │ │ ├── Dockerfile │ │ ├── package.json │ │ ├── views │ │ │ ├── app.js │ │ │ ├── index.html │ │ │ └── stylesheets │ │ │ │ └── style.css │ │ └── server.js │ ├── README.md │ └── docker-compose.yaml ├── docker-slides-week-2.pdf ├── volumes │ └── README.md ├── wordpress │ ├── README.md │ └── docker-compose.yaml └── README.md ├── week-5 └── README.md ├── week-3 ├── docker-slides-week-3.pdf └── README.md ├── .github └── ISSUE_TEMPLATE │ └── week-ii-assignment.md └── README.md /week-4/second-sample/requirements.txt: -------------------------------------------------------------------------------- 1 | FLASK -------------------------------------------------------------------------------- /week-4/first-sample/requirements.txt: -------------------------------------------------------------------------------- 1 | FLASK 2 | -------------------------------------------------------------------------------- /week-4/third-sample/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | -------------------------------------------------------------------------------- /week-1/hello-world-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | -------------------------------------------------------------------------------- /week-1/hello-world-nginx/index.html: -------------------------------------------------------------------------------- 1 | Hello from Docker! 2 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Redis 3 | -------------------------------------------------------------------------------- /week-1/hello-world/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian 2 | CMD echo hello world 3 | -------------------------------------------------------------------------------- /week-5/README.md: -------------------------------------------------------------------------------- 1 | # Week V 2 | ## Kubernetes Fundamentals 3 | 4 | TBD 5 | -------------------------------------------------------------------------------- /week-1/docker-slides-week-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/learningpath-docker/master/week-1/docker-slides-week-1.pdf -------------------------------------------------------------------------------- /week-2/docker-slides-week-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/learningpath-docker/master/week-2/docker-slides-week-2.pdf -------------------------------------------------------------------------------- /week-3/docker-slides-week-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/learningpath-docker/master/week-3/docker-slides-week-3.pdf -------------------------------------------------------------------------------- /week-4/docker-slides-week-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/learningpath-docker/master/week-4/docker-slides-week-4.pdf -------------------------------------------------------------------------------- /week-2/voting-app/worker/README.md: -------------------------------------------------------------------------------- 1 | # DLP Worker 2 | 3 | The Worker of the Demo Application, processes votes from the Redis Queue and stores them in Postgres -------------------------------------------------------------------------------- /week-4/first-sample/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_world(): 6 | return 'Hello, World!' 7 | -------------------------------------------------------------------------------- /week-4/second-sample/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_world(): 6 | return 'Hello, World!' 7 | -------------------------------------------------------------------------------- /week-1/hello-world-flask/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_world(): 6 | return 'Hello, World!' 7 | -------------------------------------------------------------------------------- /week-2/voting-app/result/README.md: -------------------------------------------------------------------------------- 1 | # DLP Result 2 | 3 | A frontend part of the Demo Application, shows results from the Postgres. 4 | 5 | Available on `port 5001`. -------------------------------------------------------------------------------- /week-4/first-sample/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | COPY . /app 3 | WORKDIR /app 4 | RUN pip install -r requirements.txt 5 | CMD ["flask", "run", "--host=0.0.0.0"] 6 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/README.md: -------------------------------------------------------------------------------- 1 | # DLP Voting 2 | 3 | Voting App, the voting frontend of the Demo Application. Gets users votes and puts them into a queue in Redis for better scalability. 4 | 5 | Available on `port 5000`. -------------------------------------------------------------------------------- /week-4/second-sample/Dockerfile.plain: -------------------------------------------------------------------------------- 1 | FROM python:3.6-slim-buster 2 | COPY requirements.txt . 3 | RUN pip install -r requirements.txt 4 | COPY app.py . 5 | ENV FLASK_APP=app.py FLASK_ENV=development 6 | ENTRYPOINT ["flask", "run", "--host=0.0.0.0"] 7 | -------------------------------------------------------------------------------- /week-1/hello-world-flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3-alpine 2 | WORKDIR /usr/src/app 3 | COPY . . 4 | RUN pip install --no-cache-dir -r requirements.txt 5 | ENV FLASK_APP=app.py FLASK_ENV=development 6 | CMD [ "flask", "run", "--host=0.0.0.0", "--port=80" ] 7 | -------------------------------------------------------------------------------- /week-2/voting-app/result/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:0.10 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | ADD package.json /app/package.json 7 | RUN npm install && npm ls 8 | RUN mv /app/node_modules /node_modules 9 | 10 | ADD . /app 11 | 12 | ENV PORT=80 DB_HOST=db 13 | EXPOSE 80 14 | 15 | CMD ["node", "server.js"] 16 | -------------------------------------------------------------------------------- /week-1/hello-world-nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update && apt-get install -y nginx 4 | 5 | RUN ln -sf /dev/stdout /var/log/nginx/access.log 6 | RUN ln -sf /dev/stderr /var/log/nginx/error.log 7 | 8 | COPY index.html /var/www/html/index.nginx-debian.html 9 | 10 | CMD ["nginx", "-g", "daemon off;"] 11 | -------------------------------------------------------------------------------- /week-2/voting-app/worker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3-openjdk-8 2 | 3 | ENV REDIS_HOST=redis DB_HOST=db 4 | 5 | WORKDIR /code 6 | 7 | ADD pom.xml /code/pom.xml 8 | RUN ["mvn", "dependency:resolve"] 9 | RUN ["mvn", "verify"] 10 | 11 | # Adding source, compile and package into a fat jar 12 | ADD src /code/src 13 | RUN ["mvn", "package"] 14 | 15 | CMD ["java", "-jar", "target/worker-jar-with-dependencies.jar"] 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/week-ii-assignment.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Week II Assignment 3 | about: Use this template to register your week II assignment 4 | title: 'Week II Assignment: [YOUR_NAME]' 5 | labels: assignment, week II 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please review my Week II assignment! 11 | 12 | My name: [YOUR_NAME] 13 | My GitHub repository: [LINK TO YOUR REPOSITORY WEEK II] 14 | Description: [BRIEF DESCRIPTION OF THE PROJECT] 15 | -------------------------------------------------------------------------------- /week-2/volumes/README.md: -------------------------------------------------------------------------------- 1 | # Commands 2 | 3 | ``` 4 | docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql 5 | docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=true -v $(pwd)/mysql-data:/var/lib/mysql mysql 6 | docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=true -v mysql-data:/var/lib/mysql mysql 7 | ``` 8 | 9 | ``` 10 | docker exec -it CONTAINER_ID mysql 11 | create database DB_NAME; show databases; exit 12 | ``` 13 | 14 | ``` 15 | docker stop CONTAINER_ID 16 | docker rm CONTAINER_ID 17 | ``` 18 | -------------------------------------------------------------------------------- /week-2/voting-app/result/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "result-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "body-parser": "^1.14.1", 13 | "cookie-parser": "^1.4.0", 14 | "express": "^4.13.3", 15 | "method-override": "^2.3.5", 16 | "async": "^1.5.0", 17 | "pg": "^4.4.3", 18 | "socket.io": "^1.3.7" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import time 2 | from redis import Redis, ConnectionError 3 | 4 | 5 | def connect_to_redis(host): 6 | time.sleep(2) 7 | print "Connecting to redis (" + host + ")..." 8 | 9 | while True: 10 | try: 11 | redis = Redis(host=host, db=0) 12 | redis.ping() 13 | print "Connected to " + host 14 | return redis 15 | except ConnectionError: 16 | print "Failed to connect to redis (" + host + ") - retrying" 17 | time.sleep(1) 18 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/Dockerfile: -------------------------------------------------------------------------------- 1 | # Using official python runtime base image 2 | FROM python:2.7 3 | 4 | # Set the application directory 5 | WORKDIR /app 6 | 7 | # Install our requirements.txt 8 | ADD requirements.txt /app/requirements.txt 9 | RUN pip install -r requirements.txt 10 | 11 | # Copy our code from the current folder to /app inside the container 12 | ADD . /app 13 | 14 | # Make port 80 available for links and/or publish 15 | EXPOSE 80 16 | 17 | # Configure default redis host 18 | ENV REDIS_HOST=redis 19 | 20 | # Define our command to be run when launching the container 21 | CMD ["python", "app.py"] 22 | -------------------------------------------------------------------------------- /week-4/second-sample/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | MAINTAINER "Maintainer Name " 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends wget 5 | # Dumb init 6 | RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 7 | RUN chmod +x /usr/local/bin/dumb-init 8 | RUN pip install --upgrade pip 9 | WORKDIR /usr/src/app 10 | COPY requirements.txt . 11 | RUN pip install -r requirements.txt 12 | COPY app.py . 13 | ENV FLASK_APP=app.py FLASK_ENV=development 14 | ENTRYPOINT ["/usr/local/bin/dumb-init", "flask", "run", "--host=0.0.0.0"] 15 | -------------------------------------------------------------------------------- /week-2/wordpress/README.md: -------------------------------------------------------------------------------- 1 | # Commands to start wordpress manually 2 | 3 | ``` 4 | docker network create wp --driver bridge 5 | docker run --detach -v mysql_data:/var/lib/mysql --network wp --name database -e MYSQL_ROOT_PASSWORD=secretpassword -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpress mysql:5 6 | docker run -p 80:80 -v wp_data:/var/www/html --network wp --detach -e WORDPRESS_DB_HOST=database:3306 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wordpress wordpress:latest 7 | ``` 8 | 9 | # Commands to start wordpress with docker-compose 10 | 11 | ``` 12 | docker-compose up -d 13 | ``` 14 | 15 | See [docker-compose.yaml](./docker-compose.yaml) for more details. -------------------------------------------------------------------------------- /week-2/voting-app/README.md: -------------------------------------------------------------------------------- 1 | # Voting Demo Application 2 | 3 | Despite the simple way to run (`docker-compose up -d`), it's a complex scalable distributed microservices-oriented application. 4 | 5 | It consists of three services: 6 | 7 | * [Voting Fronted](./voting) - python application to vote. Available on `port 5000` by default. Sends votes to a redis queue. 8 | * [Worker](./worker) - java worker processing votes in background. Gets tasks from redis queue and stores processed votes to PostgreSQL database. 9 | * [Result](./result) - NodeJS app to show vote results. Reads processed data from PostgreSQL. Available on `port 5001` by default. 10 | 11 | ![image](https://user-images.githubusercontent.com/1742301/95069792-7a1fc500-0707-11eb-9a8c-e806af5e0e4f.png) 12 | -------------------------------------------------------------------------------- /week-4/README.md: -------------------------------------------------------------------------------- 1 | # Week IV 2 | ## Best Practices 3 | 4 | ### Videos 5 | * [First Run Record](https://youtu.be/nDZ1mjRFCYE) 6 | * [Second Run Record](https://youtu.be/unA3PnjzeUg) 7 | 8 | ### Slides 9 | * [Slides Week IV](./docker-slides-week-4.pdf) 10 | 11 | ### Code Samples 12 | * [First Sample](./first-sample) 13 | * [Second Sample](./second-sample) 14 | 15 | ### Assignment 16 | 17 | * Optimize one of the dockerfiles of your choice. You can pick one of the suggested or find something on your own. Try to make it smaller, exclude redundant dependencies while keeping dockerfile self-sufficient. Think not only about size but caching and build time as well. For multi-process containers, you may need docker-compose to separate them. 18 | * [Java]() 19 | * [NodeJS]() 20 | * [Python]() 21 | * Implement automated build for a project from p.I using Travis CI, Github Actions or another Buld Server of your choice. Build Pipeline should include: 22 | * docker build 23 | * testing (at least smoke tests) 24 | * docker push to hub.docker.com 25 | 26 | Need help or an advice? Use our [Discord](https://discord.gg/va4vnsm) to communicate! 27 | 28 | ### Questions? 29 | Find us at our [Discord Server](https://discord.gg/va4vnsm)! 30 | -------------------------------------------------------------------------------- /week-2/voting-app/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.8" # docker-compose config version 2 | services: # list of services 3 | voting: # Vote Frontend 4 | image: hadesarchitect/dlp-voting # Uses image. To switch to local build, comment this line out and enable next line (build) 5 | # build: ./voting 6 | depends_on: # Needs redis ready to dispatch votes 7 | - redis 8 | ports: # publishes port 80 of the container as port 5000 of the host 9 | - "5000:80" 10 | worker: # Vote processor 11 | image: hadesarchitect/dlp-worker 12 | #build: ./worker 13 | depends_on: # wait for redis and db to start 14 | - redis 15 | - db 16 | result: # Results 17 | image: hadesarchitect/dlp-result 18 | #build: ./result 19 | depends_on: 20 | - db 21 | ports: # publishes port 80 of the container as port 5001 of the host 22 | - "5001:80" 23 | redis: # Queue 24 | image: redis 25 | db: # Database 26 | image: postgres:9.4 27 | environment: # Needs a root password or auth_method=trust 28 | POSTGRES_HOST_AUTH_METHOD: trust # DO NOT USE ON PRODUCTION! Set PASSWORD INSTEAD!!! 29 | -------------------------------------------------------------------------------- /week-2/voting-app/result/views/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('catsvsdogs', []); 2 | var socket = io.connect({transports:['polling']}); 3 | 4 | var bg1 = document.getElementById('background-stats-1'); 5 | var bg2 = document.getElementById('background-stats-2'); 6 | 7 | app.controller('statsCtrl', function($scope){ 8 | var animateStats = function(a,b){ 9 | if(a+b>0){ 10 | var percentA = a/(a+b)*100; 11 | var percentB = 100-percentA; 12 | bg1.style.width= percentA+"%"; 13 | bg2.style.width = percentB+"%"; 14 | } 15 | }; 16 | 17 | $scope.aPercent = 50; 18 | $scope.bPercent = 50; 19 | 20 | var updateScores = function(){ 21 | socket.on('scores', function (json) { 22 | data = JSON.parse(json); 23 | var a = parseInt(data.a || 0); 24 | var b = parseInt(data.b || 0); 25 | 26 | animateStats(a, b); 27 | 28 | $scope.$apply(function() { 29 | if(a + b > 0){ 30 | $scope.aPercent = a/(a+b) * 100; 31 | $scope.bPercent = b/(a+b) * 100; 32 | $scope.total = a + b 33 | } 34 | }); 35 | }); 36 | }; 37 | 38 | var init = function(){ 39 | document.body.style.opacity=1; 40 | updateScores(); 41 | }; 42 | socket.on('message',function(data){ 43 | init(); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import render_template 3 | from flask import request 4 | from flask import make_response 5 | from utils import connect_to_redis 6 | import os 7 | import socket 8 | import random 9 | import json 10 | 11 | option_a = os.getenv('OPTION_A', "Cats") 12 | option_b = os.getenv('OPTION_B', "Dogs") 13 | redis_host = os.getenv('REDIS_HOST', "redis") 14 | hostname = socket.gethostname() 15 | 16 | redis = connect_to_redis(redis_host) 17 | app = Flask(__name__) 18 | 19 | 20 | @app.route("/", methods=['POST','GET']) 21 | def hello(): 22 | voter_id = request.cookies.get('voter_id') 23 | if not voter_id: 24 | voter_id = hex(random.getrandbits(64))[2:-1] 25 | 26 | vote = None 27 | 28 | if request.method == 'POST': 29 | vote = request.form['vote'] 30 | data = json.dumps({'voter_id': voter_id, 'vote': vote}) 31 | redis.rpush('votes', data) 32 | 33 | resp = make_response(render_template( 34 | 'index.html', 35 | option_a=option_a, 36 | option_b=option_b, 37 | hostname=hostname, 38 | vote=vote, 39 | )) 40 | resp.set_cookie('voter_id', voter_id) 41 | return resp 42 | 43 | 44 | if __name__ == "__main__": 45 | app.run(host='0.0.0.0', port=80, debug=True) 46 | -------------------------------------------------------------------------------- /week-1/README.md: -------------------------------------------------------------------------------- 1 | # Week I 2 | ## Docker Fundamentals I 3 | 4 | ### Videos 5 | * [First Run Record](https://youtu.be/JvbNT0NqD2Q) 6 | * [Second Run Record](https://youtu.be/PVZhcGPGsCM) 7 | 8 | ### Slides 9 | * [Slides Week I](./docker-slides-week-1.pdf) 10 | 11 | ### Code Samples 12 | * [Hello World](./hello-world) 13 | * [Hello World Nginx](./hello-world-nginx) 14 | * [Hello World Flask](./hello-world-flask) 15 | 16 | ### Assignment 17 | 18 | 1. Create an account at hub.docker.com 19 | 2. Install Docker (recommended) OR make yourself familiar with labs.play-with-docker.com using your DockerID from the step I 20 | 3. Containerize a SIMPLE hello-world application in the language/framework of your choice. Don’t take a complex system but **prefer a simple one**, like the Flask example we did today. It’s the week I assignment after all, we will cover more advanced scenarios next week! Push the created image to your docker repository at the docker hub as shown in the video. 21 | 4. Ensure you used every command from today’s class - both for Dockerfile and command line. 22 | 5. Invite a friend or a colleague! If you are at this slide with us, it means you liked the class, don’t you? It’s week I so it’s still the perfect time to jump in! 23 | 24 | ### Questions? 25 | Find us at our [Discord Server](https://discord.gg/va4vnsm)! 26 | -------------------------------------------------------------------------------- /week-2/voting-app/result/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cats vs Dogs -- Result 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
Cats
24 |
{{aPercent | number:1}}%
25 |
26 |
27 |
28 |
Dogs
29 |
{{bPercent | number:1}}%
30 |
31 |
32 |
33 |
34 |
35 | {{total}} votes 36 |
37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /week-2/wordpress/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.3' # docker-compose config version 2 | volumes: 3 | wordpress-data: {} 4 | mysql-data: {} 5 | services: # let's define services 6 | wordpress: # First service is wordpress 7 | image: wordpress:5.5.1-php7.3 # image to use 8 | volumes: # attach volumes 9 | - wordpress-data:/var/www/html # Plugins and themes are managed by wordpres -> we prefer named volume instead of bind mount. 10 | depends_on: # wait for DB to start 11 | - database 12 | ports: # publish ports 13 | - "8000:80" 14 | restart: on-failure # restart in case of failure 15 | environment: # set environment variables 16 | WORDPRESS_DB_HOST: database:3306 17 | WORDPRESS_DB_USER: wordpress 18 | WORDPRESS_DB_PASSWORD: wordpress # Don't like passwords in source code? We too! Join the Week 3 to learn the right way to handle it! 19 | WORDPRESS_DB_NAME: wordpress 20 | database: # wordpress needs a database 21 | image: mysql:5.7 # Let's stick to MySQL v5.7 22 | volumes: # Again volume to store data 23 | - mysql-data:/var/lib/mysql # Notice, it's a named volume, not a bind mount. 24 | restart: on-failure # Restart `always` will works as well but it's hard to stop the database then :) 25 | environment: # Env vars do some configuration 26 | MYSQL_ROOT_PASSWORD: secretpassword 27 | MYSQL_DATABASE: wordpress 28 | MYSQL_USER: wordpress 29 | MYSQL_PASSWORD: wordpress 30 | -------------------------------------------------------------------------------- /week-3/README.md: -------------------------------------------------------------------------------- 1 | # Week III 2 | ## Application Development with Docker 3 | 4 | ### Videos 5 | * [First Run Record](https://youtu.be/zrMMVmDRwUg) 6 | * [Second Run Record](https://youtu.be/jLujqzhUVao) 7 | 8 | ### Slides 9 | * [Slides Week III](./docker-slides-week-3.pdf) 10 | 11 | ### Code Samples 12 | * [KillrVideo](https://github.com/killrVideo) 13 | * [KillrVideo/All-in-One](https://github.com/KillrVideo/killrvideo-all-in-one) 14 | * [KillrVideo/Java-Backend](https://github.com/KillrVideo/killrvideo-java) 15 | 16 | ### Assignment 17 | 18 | We proceed with the assignment of the Week II 19 | 20 | 1. Use the Week II Task I as a first step. Then improve it following this week steps: env variables, github actions or travis build, tests, etc. Please use our week II samples for the inspiration. 21 | 2. If possible and ig it's not done yet, publish your code from p.1 on github in your repository. Create a new issue at [github.com/datastaxdevs/docker-learning-path/issues](https://github.com/datastaxdevs/docker-learning-path/issues). It may not be an option if you containerised a proprietary project, but please proceed to step III anyway. 22 | 3. Choose an issue from the list, write a comment that you have "taken" it. Review the project and think on how would you improve it. Write down your suggestions in the issue. Feel free to review multiple projects, also feel free to review a taken one! Stay polite! 23 | 4. If you want us to review your assignment publicly, send an issue link to me! We will pick some projects to discuss during week IV. We cover both mistakes and good decisions. :) 24 | 25 | 26 | ### Questions? 27 | Find us at our [Discord Server](https://discord.gg/va4vnsm)! 28 | -------------------------------------------------------------------------------- /week-2/README.md: -------------------------------------------------------------------------------- 1 | # Week II 2 | ## Docker Fundamentals II 3 | 4 | ### Videos 5 | * [First Run Record](https://youtu.be/cyHqbGWlsKk) 6 | * [Second Run Record](https://youtu.be/Y_ocioMZgt4) 7 | 8 | ### Slides 9 | * [Slides Week II](./docker-slides-week-2.pdf) 10 | 11 | ### Code Samples 12 | * [Persistent Data](./volumes) 13 | * [Wordpress](./wordpress) 14 | * [Microservices System](./voting-app) 15 | 16 | ### Assignment 17 | 1. This time you have to do a more complex setup using docker-compose. It should include at least one predefined image from hub.docker.com (like a database) and one custom image you build on your own. It should use bind mounts or volumes so `docker-compose down` will not wipe out data. Please use our examples of [wordpress](./wordpress) and [voting application](./voting-app) for the inspiration. 18 | 2. If possible, publish your code from p.1 on github by creating a new issue at https://github.com/datastaxdevs/docker-learning-path/issues/new/choose. It may not be an option if you containerised a proprietary project, but please proceed to step III anyway. 19 | 3. Open the issues list at https://github.com/datastaxdevs/docker-learning-path/issues, pick one not taken project, write a comment that you have "taken" it. Review the project and think on how would you improve it. Write down your suggestions in the issue. Feel free to review multiple projects, also feel free to review a taken one - more opinions is better! Stay polite! 20 | 4. If you want us to review your assignment publicly, send an issue link to me as well! We will pick some projects to discuss during week III. We cover both mistakes and good decisions. :) 21 | 5. Add me on linkedin. We spend together over 4 hours already so let's celebrate it! [linkedin.com/in/volochnev/](https://linkedin.com/in/volochnev/) 22 | 23 | ### Questions? 24 | Find us at our [Discord Server](https://discord.gg/va4vnsm)! 25 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{option_a}} vs {{option_b}}! 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

{{option_a}} vs {{option_b}}!

17 |
18 | 19 | 20 |
21 |
22 | (Tip: you can change your vote) 23 |
24 |
25 | Vote processed by {{hostname}} 26 |
27 |
28 |
29 | 30 | 31 | 32 | {% if vote %} 33 | 47 | {% endif %} 48 | 49 | 50 | -------------------------------------------------------------------------------- /week-2/voting-app/result/views/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | @import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600); 2 | 3 | *{ 4 | box-sizing:border-box; 5 | } 6 | html,body{ 7 | margin:0; 8 | padding:0; 9 | height:100%; 10 | font-family: 'Open Sans'; 11 | } 12 | body{ 13 | opacity:0; 14 | transition: all 1s linear; 15 | } 16 | 17 | .divider{ 18 | height: 150px; 19 | width:2px; 20 | background-color: #C0C9CE; 21 | position: relative; 22 | top: 50%; 23 | float: left; 24 | transform: translateY(-50%); 25 | } 26 | 27 | #background-stats-1{ 28 | background-color: #2196f3; 29 | } 30 | 31 | #background-stats-2{ 32 | background-color: #00cbca; 33 | } 34 | 35 | #content-container{ 36 | z-index:2; 37 | position:relative; 38 | margin:0 auto; 39 | display:table; 40 | padding:10px; 41 | max-width:940px; 42 | height:100%; 43 | } 44 | #content-container-center{ 45 | display:table-cell; 46 | text-align:center; 47 | vertical-align:middle; 48 | } 49 | #result{ 50 | position: absolute; 51 | bottom: 40px; 52 | right: 20px; 53 | color: #fff; 54 | opacity: 0.5; 55 | font-size: 45px; 56 | font-weight: 600; 57 | } 58 | #choice{ 59 | transition: all 300ms linear; 60 | line-height:1.3em; 61 | background:#fff; 62 | box-shadow: 10px 0 0 #fff, -10px 0 0 #fff; 63 | vertical-align:middle; 64 | font-size:40px; 65 | font-weight: 600; 66 | width: 450px; 67 | height: 200px; 68 | } 69 | #choice a{ 70 | text-decoration:none; 71 | } 72 | #choice a:hover, #choice a:focus{ 73 | outline:0; 74 | text-decoration:underline; 75 | } 76 | 77 | #choice .choice{ 78 | width: 49%; 79 | position: relative; 80 | top: 50%; 81 | transform: translateY(-50%); 82 | text-align: left; 83 | padding-left: 50px; 84 | } 85 | 86 | #choice .choice .label{ 87 | text-transform: uppercase; 88 | } 89 | 90 | #choice .choice.dogs{ 91 | color: #00cbca; 92 | float: right; 93 | } 94 | 95 | #choice .choice.cats{ 96 | color: #2196f3; 97 | float: left; 98 | } 99 | #background-stats{ 100 | z-index:1; 101 | height:100%; 102 | width:100%; 103 | position:absolute; 104 | } 105 | #background-stats div{ 106 | transition: width 400ms ease-in-out; 107 | display:inline-block; 108 | margin-bottom:-4px; 109 | width:50%; 110 | height:100%; 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Learning Path 2 | ## Online docker course for developers 3 | 4 | ![image](https://user-images.githubusercontent.com/1742301/94458110-c756e080-01b5-11eb-9c01-e11773ee43b4.png) 5 | 6 | * [Registration Page](https://www.eventbrite.co.uk/e/docker-learning-path-containers-from-basics-to-best-practices-tickets-119787275967) 7 | * [Discord Chat](https://discord.gg/va4vnsm) #docker-training 8 | 9 | Welcome to our free online Docker course, the perfect place to master a powerful technology used all over the world. The course is designed for people with no prior docker experience and will be a great opportunity for every developer. Doesn’t matter if you prefer java, python, golang or javascript, containers are widely used in any part of the Software Development world: Docker skills aren’t the option anymore but the requirement! 10 | 11 | The Docker Learning Path consists of 4 weeks as explained in the schedule below. Each class is ~120 minutes plus you will need around 60 minutes to do the exercises: it takes some time but the best investment is the investment in yourself! Register for the event, attend the course, do the homework and fill the final submission form to get your own very well deserved “Docker Upgrade Complete” Achievement. 12 | 13 | ### What we cover 14 | * Containers, why you may need them and how they help 15 | * Docker Fundamentals: general principles and use-cases 16 | * Basic Operations: running a container, building an image etc. 17 | * Image Repositories: push, pull, etc. 18 | * Advanced Scenarios: docker setup for complex systems and applications 19 | * Application Development with Docker 20 | * Security in Containers: stay safe! 21 | * Tips on where to go next to continue your learning 22 | 23 | ### Content 24 | * [Week 1 Monday 28th September: Docker Fundamentals I](./week-1) 25 | * [Week 2 Monday 5th October: Docker Fundamentals II](./week-2) 26 | * [Week 3 Monday 12th October: Application Development with Docker](./week-3) 27 | * [Week 4 Monday 19th October: Docker Best Practices](./week-4) 28 | * [Week 5 Monday 26th October: Intro to Kubernetes](./week-5) 29 | 30 | Each week we will run 2 LIVE workshops so you can pick the one most convenient to you. The content is the same in both, so you only need to attend one. All workshops will be recorded. 31 | 32 | * Option 1 - 12:30pm UTC - Best for EMEA and APAC 33 | * Option 2 - 5:00pm UTC - Best for NAM/LATAM and EMEA 34 | -------------------------------------------------------------------------------- /week-2/voting-app/result/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | async = require('async'), 3 | pg = require("pg"), 4 | cookieParser = require('cookie-parser'), 5 | bodyParser = require('body-parser'), 6 | methodOverride = require('method-override'), 7 | app = express(), 8 | server = require('http').Server(app), 9 | io = require('socket.io')(server); 10 | 11 | io.set('transports', ['polling']); 12 | 13 | var port = process.env.PORT || 4000; 14 | var db_host = process.env.DB_HOST || db; 15 | 16 | io.sockets.on('connection', function (socket) { 17 | 18 | socket.emit('message', { text : 'Welcome!' }); 19 | 20 | socket.on('subscribe', function (data) { 21 | socket.join(data.channel); 22 | }); 23 | }); 24 | 25 | async.retry( 26 | {times: 1000, interval: 1000}, 27 | function(callback) { 28 | pg.connect('postgres://postgres@' + db_host + '/postgres', function(err, client, done) { 29 | if (err) { 30 | console.error("Failed to connect to db (" + db_host + ")"); 31 | } 32 | callback(err, client); 33 | }); 34 | }, 35 | function(err, client) { 36 | if (err) { 37 | return console.err("Giving up"); 38 | } 39 | console.log("Connected to db (" + db_host + ")"); 40 | getVotes(client); 41 | } 42 | ); 43 | 44 | function getVotes(client) { 45 | client.query('SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote', [], function(err, result) { 46 | if (err) { 47 | console.error("Error performing query: " + err); 48 | } else { 49 | var data = result.rows.reduce(function(obj, row) { 50 | obj[row.vote] = row.count; 51 | return obj; 52 | }, {}); 53 | io.sockets.emit("scores", JSON.stringify(data)); 54 | } 55 | 56 | setTimeout(function() {getVotes(client) }, 1000); 57 | }); 58 | } 59 | 60 | app.use(cookieParser()); 61 | app.use(bodyParser()); 62 | app.use(methodOverride('X-HTTP-Method-Override')); 63 | app.use(function(req, res, next) { 64 | res.header("Access-Control-Allow-Origin", "*"); 65 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 66 | res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS"); 67 | next(); 68 | }); 69 | 70 | app.use(express.static(__dirname + '/views')); 71 | 72 | app.get('/', function (req, res) { 73 | res.sendFile(path.resolve(__dirname + '/views/index.html')); 74 | }); 75 | 76 | server.listen(port, function () { 77 | var port = server.address().port; 78 | console.log('App running on port ' + port); 79 | }); 80 | -------------------------------------------------------------------------------- /week-2/voting-app/voting/static/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | @import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600); 2 | 3 | *{ 4 | box-sizing:border-box; 5 | } 6 | html,body{ 7 | margin: 0; 8 | padding: 0; 9 | background-color: #F7F8F9; 10 | height: 100vh; 11 | font-family: 'Open Sans'; 12 | } 13 | 14 | button{ 15 | border-radius: 0; 16 | width: 100%; 17 | height: 50%; 18 | } 19 | 20 | button[type="submit"] { 21 | -webkit-appearance:none; -webkit-border-radius:0; 22 | } 23 | 24 | button i{ 25 | float: right; 26 | padding-right: 30px; 27 | margin-top: 3px; 28 | } 29 | 30 | button.a{ 31 | background-color: #1aaaf8; 32 | } 33 | 34 | button.b{ 35 | background-color: #00cbca; 36 | } 37 | 38 | #tip{ 39 | text-align: left; 40 | color: #c0c9ce; 41 | font-size: 14px; 42 | } 43 | 44 | #hostname{ 45 | position: absolute; 46 | bottom: 100px; 47 | right: 0; 48 | left: 0; 49 | color: #8f9ea8; 50 | font-size: 14px; 51 | } 52 | 53 | #content-container{ 54 | z-index: 2; 55 | position: relative; 56 | margin: 0 auto; 57 | display: table; 58 | padding: 10px; 59 | max-width: 940px; 60 | height: 100%; 61 | } 62 | #content-container-center{ 63 | display: table-cell; 64 | text-align: center; 65 | } 66 | 67 | #content-container-center h3{ 68 | color: #254356; 69 | } 70 | 71 | #choice{ 72 | transition: all 300ms linear; 73 | line-height: 1.3em; 74 | display: inline; 75 | vertical-align: middle; 76 | font-size: 3em; 77 | } 78 | #choice a{ 79 | text-decoration:none; 80 | } 81 | #choice a:hover, #choice a:focus{ 82 | outline:0; 83 | text-decoration:underline; 84 | } 85 | 86 | #choice button{ 87 | display: block; 88 | height: 80px; 89 | width: 330px; 90 | border: none; 91 | color: white; 92 | text-transform: uppercase; 93 | font-size:18px; 94 | font-weight: 700; 95 | margin-top: 10px; 96 | margin-bottom: 10px; 97 | text-align: left; 98 | padding-left: 50px; 99 | } 100 | 101 | #choice button.a:hover{ 102 | background-color: #1488c6; 103 | } 104 | 105 | #choice button.b:hover{ 106 | background-color: #00a2a1; 107 | } 108 | 109 | #choice button.a:focus{ 110 | background-color: #1488c6; 111 | } 112 | 113 | #choice button.b:focus{ 114 | background-color: #00a2a1; 115 | } 116 | 117 | #background-stats{ 118 | z-index:1; 119 | height:100%; 120 | width:100%; 121 | position:absolute; 122 | } 123 | #background-stats div{ 124 | transition: width 400ms ease-in-out; 125 | display:inline-block; 126 | margin-bottom:-4px; 127 | width:50%; 128 | height:100%; 129 | } 130 | -------------------------------------------------------------------------------- /week-2/voting-app/worker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | worker 7 | worker 8 | 1.0-SNAPSHOT 9 | 10 | 11 | 12 | 13 | org.json 14 | json 15 | 20140107 16 | 17 | 18 | 19 | redis.clients 20 | jedis 21 | 2.7.2 22 | jar 23 | compile 24 | 25 | 26 | 27 | org.postgresql 28 | postgresql 29 | 9.4-1200-jdbc41 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-jar-plugin 38 | 2.4 39 | 40 | worker 41 | 42 | 43 | true 44 | worker.Worker 45 | dependency-jars/ 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-compiler-plugin 53 | 3.1 54 | 55 | 1.7 56 | 1.7 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-assembly-plugin 62 | 63 | 64 | 65 | attached 66 | 67 | package 68 | 69 | worker 70 | 71 | jar-with-dependencies 72 | 73 | 74 | 75 | worker.Worker 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /week-2/voting-app/worker/src/main/java/worker/Worker.java: -------------------------------------------------------------------------------- 1 | package worker; 2 | 3 | import redis.clients.jedis.Jedis; 4 | import redis.clients.jedis.exceptions.JedisConnectionException; 5 | import java.sql.*; 6 | import org.json.JSONObject; 7 | 8 | class Worker { 9 | public static void main(String[] args) { 10 | try { 11 | Jedis redis = connectToRedis(System.getenv("REDIS_HOST")); 12 | Connection dbConn = connectToDB(System.getenv("DB_HOST")); 13 | 14 | System.err.println("Watching vote queue"); 15 | 16 | while (true) { 17 | String voteJSON = redis.blpop(0, "votes").get(1); 18 | JSONObject voteData = new JSONObject(voteJSON); 19 | String voterID = voteData.getString("voter_id"); 20 | String vote = voteData.getString("vote"); 21 | 22 | System.err.printf("Processing vote for '%s' by '%s'\n", vote, voterID); 23 | updateVote(dbConn, voterID, vote); 24 | } 25 | } catch (SQLException e) { 26 | e.printStackTrace(); 27 | System.exit(1); 28 | } 29 | } 30 | 31 | static void updateVote(Connection dbConn, String voterID, String vote) throws SQLException { 32 | PreparedStatement insert = dbConn.prepareStatement( 33 | "INSERT INTO votes (id, vote) VALUES (?, ?)"); 34 | insert.setString(1, voterID); 35 | insert.setString(2, vote); 36 | 37 | try { 38 | insert.executeUpdate(); 39 | } catch (SQLException e) { 40 | PreparedStatement update = dbConn.prepareStatement( 41 | "UPDATE votes SET vote = ? WHERE id = ?"); 42 | update.setString(1, vote); 43 | update.setString(2, voterID); 44 | update.executeUpdate(); 45 | } 46 | } 47 | 48 | static Jedis connectToRedis(String host) { 49 | Jedis conn = new Jedis(host); 50 | 51 | System.err.println("Connecting to redis (" + host + ")..."); 52 | 53 | while (true) { 54 | try { 55 | conn.keys("*"); 56 | break; 57 | } catch (JedisConnectionException e) { 58 | System.err.println("Failed to connect to redis - retrying"); 59 | sleep(1000); 60 | } 61 | } 62 | 63 | System.err.println("Connected to redis (" + host + ")!"); 64 | return conn; 65 | } 66 | 67 | static Connection connectToDB(String host) throws SQLException { 68 | Connection conn = null; 69 | 70 | try { 71 | 72 | Class.forName("org.postgresql.Driver"); 73 | String url = "jdbc:postgresql://" + host + "/postgres"; 74 | 75 | System.out.println("Connecting to DB (" + host + ")..."); 76 | 77 | while (conn == null) { 78 | try { 79 | conn = DriverManager.getConnection(url, "postgres", ""); 80 | } catch (SQLException e) { 81 | System.err.println("Failed to connect to db - retrying"); 82 | sleep(1000); 83 | } 84 | } 85 | 86 | System.out.println("Connected to DB (" + host + ")!"); 87 | 88 | PreparedStatement st = conn.prepareStatement( 89 | "CREATE TABLE IF NOT EXISTS votes (id VARCHAR(255) NOT NULL UNIQUE, vote VARCHAR(255) NOT NULL)"); 90 | st.executeUpdate(); 91 | 92 | } catch (ClassNotFoundException e) { 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | 97 | return conn; 98 | } 99 | 100 | static void sleep(long duration) { 101 | try { 102 | Thread.sleep(duration); 103 | } catch (InterruptedException e) { 104 | System.exit(1); 105 | } 106 | } 107 | } 108 | --------------------------------------------------------------------------------