Compare commits

...

48 Commits

Author SHA1 Message Date
Jérôme Petazzoni
a38919ff5b Minor fixes after Munich workshop 2016-03-24 14:12:00 +01:00
Jérôme Petazzoni
c2180cffad Minor updates for Munich workshop 2016-03-22 00:16:49 +01:00
Jérôme Petazzoni
914c80dba8 Merge pull request #12 from npalm/qcon
Qcon
2016-03-22 00:15:32 +01:00
Jérôme Petazzoni
fa2dc41176 Merge pull request #8 from svx/master
to -> do
2016-03-14 15:16:48 +01:00
Jérôme Petazzoni
f27b99cbc4 Merge pull request #9 from akafred/rng0-to-rng-in-ambassador
Change rng0 -> rng in dockercoins ambassador-file
2016-03-14 15:07:16 +01:00
Palm, Niek
73b997faf7 add sudo so docker can install consulfs 2016-03-11 15:59:16 +00:00
Palm, Niek
948691a7d6 Add missing commands and a hint 2016-03-11 15:25:16 +00:00
akafred
aa7e8bb1f9 Change rng0 -> rng in dockercoins ambassador-file 2016-03-11 10:55:51 +00:00
Jerome Petazzoni
88e558bd08 Last updates for QCON 2016-03-11 00:29:58 -08:00
Jerome Petazzoni
1174845c52 Add v2 load balancing technique 2016-03-09 15:39:23 -08:00
Jerome Petazzoni
8a73d85beb Fixed lcal registry setup thanks to @soulshake 2016-03-09 08:28:02 -08:00
Jerome Petazzoni
bc1bb493d4 First round of update for QCON 2016-03-09 06:37:21 -08:00
Jérôme Petazzoni
cc6543ce1c Add helper to remove stale endpoints 2016-03-05 14:33:37 +00:00
Jérôme Petazzoni
8c62220c8d Add missing EXPOSE in webui/Dockerfile 2016-03-02 12:12:00 +00:00
Jérôme Petazzoni
332eb9cff9 Add script to automate LB setup; add setup/teardown helpers 2016-03-02 12:11:22 +00:00
Jerome Petazzoni
2ad3625687 Add ConsulFS section 2016-03-01 06:30:53 -08:00
Jerome Petazzoni
7e60d482f7 Add cron strategies 2016-03-01 05:37:31 -08:00
Jerome Petazzoni
4d2e62ffee Adjust for QCON schedule; use local registry 2016-03-01 05:21:02 -08:00
Jérôme Petazzoni
b4870b8ed6 Hackish CEPH cluster on Docker 2016-03-01 01:01:54 +00:00
Jérôme Petazzoni
29ae9a10f2 Remove extraneous ambassadors 2016-02-29 22:03:25 +00:00
Jérôme Petazzoni
ec2b098c29 Add parallel_run function and use it to manage ambassadors 2016-02-29 21:24:32 +00:00
Jérôme Petazzoni
240a55a18a Update other scripts to use common.py 2016-02-29 19:08:01 +00:00
Jérôme Petazzoni
362c5e8a65 Add Compose file for self-hosted registry 2016-02-29 18:40:14 +00:00
Jérôme Petazzoni
b47dccdb8d Move scripts to bin directory; abstract Compose V1/V2 formats 2016-02-29 18:17:06 +00:00
sven
dca494c090 Revoke wording change 2016-02-24 20:39:20 +01:00
sven
9286fdec28 Assess -> Access 2016-02-19 11:46:14 +01:00
sven
c31e961e61 to -> do 2016-02-19 11:30:01 +01:00
Jerome Petazzoni
2b88a121ba Add DockerCoins image 2016-02-18 23:42:53 -08:00
Jerome Petazzoni
84a521296d Round of update for Amsterdam workshop 2016-02-18 15:09:58 -08:00
Jerome Petazzoni
86271b6336 Merge branch 'master' of github.com:jpetazzo/orchestration-workshop 2016-02-14 07:11:13 -08:00
Jerome Petazzoni
104e736c45 Add kibana screenshot 2016-02-14 07:10:52 -08:00
Jerome Petazzoni
9dfb421eb1 Last round of updates 2016-02-14 07:10:42 -08:00
Jerome Petazzoni
d69719abc6 Content rehaul before Paris workshop 2016-02-14 06:05:39 -08:00
Jérôme Petazzoni
98bac6713d Add v2 Compose file 2016-02-14 13:19:10 +00:00
Jerome Petazzoni
6c1958e244 Update Compose+Swarm section 2016-02-13 10:56:55 -08:00
Jerome Petazzoni
936a80f86d Conclusions + mention Docker Cloud and UCP 2016-02-13 10:23:10 -08:00
Jerome Petazzoni
2634230a4a Switch to node:4 image to work around AUFS issue 2016-02-12 09:11:00 -08:00
Jerome Petazzoni
4e96a2949c Add Swarm replication and rescheduling sections 2016-02-12 09:10:39 -08:00
Jerome Petazzoni
175d468718 Merge branch 'master' of github.com:jpetazzo/orchestration-workshop 2016-02-09 16:33:25 -08:00
Jerome Petazzoni
eb38b7e2e6 Rehaul logging section 2016-02-09 16:33:16 -08:00
Jérôme Petazzoni
9d5dfc9a40 Add sample Compose file with GELF logging 2016-02-10 00:32:00 +00:00
Jérôme Petazzoni
676aad7edc Revert to Compose v1 for simplicity 2016-02-09 23:39:30 +00:00
Jérôme Petazzoni
43d574a164 Add Compose file for ELK stack 2016-02-09 22:09:48 +00:00
Jerome Petazzoni
661948d162 Update versions, schedule, and twitter button 2016-02-08 07:24:50 -08:00
Jerome Petazzoni
edf3aeb9c4 Fix LISA reference 2016-01-26 10:34:07 -08:00
Jerome Petazzoni
e16c391deb Good to go! 2016-01-22 14:29:37 -08:00
Jérôme Petazzoni
1a486a2f95 Minor fixes 2016-01-21 18:06:30 -08:00
Jerome Petazzoni
962737ffa2 Update for SCALE; add support for multi-host networking 2016-01-21 16:44:34 -08:00
53 changed files with 4131 additions and 516 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*.pyc
*.swp
*~
ips.txt

View File

@@ -8,6 +8,11 @@ at multiple conferences and events like:
- KCDC, Kansas City (2015, June)
- JDEV, Bordeaux (2015, July)
- OSCON, Portland (2015, July)
- StrangeLoop, Saint Louis (2015, September)
- LISA, Washington D.C. (2015, November)
- SCALE, Pasadena (2016, January)
- Zenika, Paris (2016, February)
- Container Solutions, Amsterdam (2016, February)
## Slides

42
bin/add-load-balancer-v1.py Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python
import os
import sys
import yaml
# arg 1 = service name
# arg 2 = number of instances
service_name = sys.argv[1]
desired_instances = int(sys.argv[2])
compose_file = os.environ["COMPOSE_FILE"]
input_file, output_file = compose_file, compose_file
config = yaml.load(open(input_file))
# The ambassadors need to know the service port to use.
# Those ports must be declared here.
ports = yaml.load(open("ports.yml"))
port = str(ports[service_name])
command_line = port
depends_on = []
for n in range(1, 1+desired_instances):
config["services"]["{}{}".format(service_name, n)] = config["services"][service_name]
command_line += " {}{}:{}".format(service_name, n, port)
depends_on.append("{}{}".format(service_name, n))
config["services"][service_name] = {
"image": "jpetazzo/hamba",
"command": command_line,
"depends_on": depends_on,
}
if "networks" in config["services"]["{}1".format(service_name)]:
config["services"][service_name]["networks"] = config["services"]["{}1".format(service_name)]["networks"]
yaml.safe_dump(config, open(output_file, "w"), default_flow_style=False)

72
bin/add-load-balancer-v2.py Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python
import os
import sys
import yaml
def error(msg):
print("ERROR: {}".format(msg))
exit(1)
# arg 1 = service name
service_name = sys.argv[1]
compose_file = os.environ["COMPOSE_FILE"]
input_file, output_file = compose_file, compose_file
config = yaml.load(open(input_file))
version = config.get("version")
if version != "2":
error("Unsupported $COMPOSE_FILE version: {!r}".format(version))
# The load balancers need to know the service port to use.
# Those ports must be declared here.
ports = yaml.load(open("ports.yml"))
port = str(ports[service_name])
if service_name not in config["services"]:
error("service {} not found in $COMPOSE_FILE"
.format(service_name))
lb_name = "{}-lb".format(service_name)
be_name = "{}-be".format(service_name)
if lb_name in config["services"]:
error("load balancer {} already exists in $COMPOSE_FILE"
.format(service_name))
service = config["services"][service_name]
if "networks" in service:
error("service {} has custom networks"
.format(service_name))
# Put the service on its own network.
service["networks"] = {service_name: {"aliases": [ be_name ] } }
# Put a label indicating which load balancer is responsible for this service.
if "labels" not in service:
service["labels"] = {}
service["labels"]["loadbalancer"] = lb_name
# Add the load balancer.
config["services"][lb_name] = {
"image": "jpetazzo/hamba",
"command": "{} {} {}".format(port, be_name, port),
"depends_on": [ service_name ],
"networks": {
"default": {
"aliases": [ service_name ],
},
service_name: None,
},
}
if "networks" not in config:
config["networks"] = {}
if service_name not in config["networks"]:
config["networks"][service_name] = None
yaml.safe_dump(config, open(output_file, "w"), default_flow_style=False)

61
bin/build-tag-push.py Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python
from common import ComposeFile
import os
import subprocess
import time
registry = os.environ.get("DOCKER_REGISTRY")
if not registry:
print("Please set the DOCKER_REGISTRY variable, e.g.:")
print("export DOCKER_REGISTRY=jpetazzo # use the Docker Hub")
print("export DOCKER_REGISTRY=localhost:5000 # use a local registry")
exit(1)
# Get the name of the current directory.
project_name = os.path.basename(os.path.realpath("."))
# Generate a Docker image tag, using the UNIX timestamp.
# (i.e. number of seconds since January 1st, 1970)
version = str(int(time.time()))
# Execute "docker-compose build" and abort if it fails.
subprocess.check_call(["docker-compose", "-f", "docker-compose.yml", "build"])
# Load the services from the input docker-compose.yml file.
# TODO: run parallel builds.
compose_file = ComposeFile("docker-compose.yml")
# Iterate over all services that have a "build" definition.
# Tag them, and initiate a push in the background.
push_operations = dict()
for service_name, service in compose_file.services.items():
if "build" in service:
compose_image = "{}_{}".format(project_name, service_name)
registry_image = "{}/{}_{}:{}".format(registry, project_name, service_name, version)
# Re-tag the image so that it can be uploaded to the registry.
subprocess.check_call(["docker", "tag", compose_image, registry_image])
# Spawn "docker push" to upload the image.
push_operations[service_name] = subprocess.Popen(["docker", "push", registry_image])
# Replace the "build" definition by an "image" definition,
# using the name of the image on the registry.
del service["build"]
service["image"] = registry_image
# Wait for push operations to complete.
for service_name, popen_object in push_operations.items():
print("Waiting for {} push to complete...".format(service_name))
popen_object.wait()
print("Done.")
# Write the new docker-compose.yml file.
if "COMPOSE_FILE" not in os.environ:
os.environ["COMPOSE_FILE"] = "docker-compose.yml-{}".format(version)
print("Writing to new Compose file:")
else:
print("Writing to provided Compose file:")
print("COMPOSE_FILE={}".format(os.environ["COMPOSE_FILE"]))
compose_file.save()

76
bin/common.py Executable file
View File

@@ -0,0 +1,76 @@
import os
import subprocess
import sys
import time
import yaml
def COMPOSE_FILE():
if "COMPOSE_FILE" not in os.environ:
print("The $COMPOSE_FILE environment variable is not set. Aborting.")
exit(1)
return os.environ["COMPOSE_FILE"]
class ComposeFile(object):
def __init__(self, filename=None):
if filename is None:
filename = COMPOSE_FILE()
if not os.path.isfile(filename):
print("File {!r} does not exist. Aborting.".format(filename))
exit(1)
self.data = yaml.load(open(filename))
@property
def services(self):
if self.data.get("version") == "2":
return self.data["services"]
else:
return self.data
def save(self, filename=None):
if filename is None:
filename = COMPOSE_FILE()
with open(filename, "w") as f:
yaml.safe_dump(self.data, f, default_flow_style=False)
# Executes a bunch of commands in parallel, but no more than N at a time.
# This allows to execute concurrently a large number of tasks, without
# turning into a fork bomb.
# `parallelism` is the number of tasks to execute simultaneously.
# `commands` is a list of tasks to execute.
# Each task is itself a list, where the first element is a descriptive
# string, and the folloowing elements are the arguments to pass to Popen.
def parallel_run(commands, parallelism):
running = []
# While stuff is running, or we have stuff to run...
while commands or running:
# While there is stuff to run, and room in the pipe...
while commands and len(running)<parallelism:
command = commands.pop(0)
print("START {}".format(command[0]))
popen = subprocess.Popen(
command[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
popen._desc = command[0]
running.append(popen)
must_sleep = True
for popen in running:
status = popen.poll()
if status is not None:
must_sleep = False
running.remove(popen)
if status==0:
print("OK {}".format(popen._desc))
else:
print("ERROR {} [Exit status: {}]"
.format(popen._desc, status))
output = "\n" + popen.communicate()[0].strip()
output = output.replace("\n", "\n| ")
print(output)
else:
print("WAIT ({} running, {} more to run)"
.format(len(running), len(commands)))
if must_sleep:
time.sleep(1)

View File

@@ -1,16 +1,12 @@
#!/usr/bin/env python
from common import parallel_run
import os
import subprocess
import sys
import yaml
compose_file = os.environ.get("COMPOSE_FILE") or sys.argv[1]
stack = yaml.load(open(compose_file))
project_name = os.path.basename(os.path.realpath("."))
# Get all services and backends in our compose application
# Get all services and backends in our compose application.
containers_data = subprocess.check_output([
"docker", "ps",
"--filter", "label=com.docker.compose.project={}".format(project_name),
@@ -19,7 +15,7 @@ containers_data = subprocess.check_output([
'{{ .Ports }}',
])
# Build list of backends
# Build list of backends.
frontend_ports = dict()
backends = dict()
for container in containers_data.split('\n'):
@@ -40,7 +36,7 @@ for container in containers_data.split('\n'):
backends[service_name] = []
backends[service_name].append((backend_addr, backend_port))
# Get all existing ambassadors for this application
# Get all existing ambassadors for this application.
ambassadors_data = subprocess.check_output([
"docker", "ps",
"--filter", "label=ambassador.project={}".format(project_name),
@@ -49,7 +45,8 @@ ambassadors_data = subprocess.check_output([
'{{ .Label "ambassador.bindaddr" }}',
])
# Update ambassadors
# Update ambassadors.
operations = []
for ambassador in ambassadors_data.split('\n'):
if not ambassador:
continue
@@ -59,11 +56,14 @@ for ambassador in ambassadors_data.split('\n'):
bind_address, frontend_ports[service_name],
backends[service_name]))
command = [
ambassador_id,
"docker", "run", "--rm", "--volumes-from", ambassador_id,
"jpetazzo/hamba", "reconfigure",
"{}:{}".format(bind_address, frontend_ports[service_name])
]
for backend_addr, backend_port in backends[service_name]:
command.extend([backend_addr, backend_port])
print command
subprocess.check_call(command)
operations.append(command)
# Execute all commands in parallel.
parallel_run(operations, 10)

View File

@@ -1,23 +1,21 @@
#!/usr/bin/env python
from common import ComposeFile, parallel_run
import os
import subprocess
import sys
import yaml
compose_file = os.environ.get("COMPOSE_FILE") or sys.argv[1]
stack = yaml.load(open(compose_file))
config = ComposeFile()
project_name = os.path.basename(os.path.realpath("."))
# Get all services in our compose application
# Get all services in our compose application.
containers_data = subprocess.check_output([
"docker", "ps",
"--filter", "label=com.docker.compose.project={}".format(project_name),
"--format", '{{ .ID }} {{ .Label "com.docker.compose.service" }}',
])
# Get all existing ambassadors for this application
# Get all existing ambassadors for this application.
ambassadors_data = subprocess.check_output([
"docker", "ps",
"--filter", "label=ambassador.project={}".format(project_name),
@@ -26,7 +24,7 @@ ambassadors_data = subprocess.check_output([
'{{ .Label "ambassador.service" }}',
])
# Build a set of existing ambassadors
# Build a set of existing ambassadors.
ambassadors = dict()
for ambassador in ambassadors_data.split('\n'):
if not ambassador:
@@ -34,21 +32,24 @@ for ambassador in ambassadors_data.split('\n'):
ambassador_id, container_id, linked_service = ambassador.split()
ambassadors[container_id, linked_service] = ambassador_id
# Start the missing ambassadors
operations = []
# Start the missing ambassadors.
for container in containers_data.split('\n'):
if not container:
continue
container_id, service_name = container.split()
extra_hosts = stack[service_name].get("extra_hosts", {})
extra_hosts = config.services[service_name].get("extra_hosts", {})
for linked_service, bind_address in extra_hosts.items():
description = "Ambassador {}/{}/{}".format(
service_name, container_id, linked_service)
ambassador_id = ambassadors.get((container_id, linked_service))
ambassador_id = ambassadors.pop((container_id, linked_service), None)
if ambassador_id:
print("{} already exists: {}".format(description, ambassador_id))
else:
print("{} not found, creating it:".format(description))
subprocess.check_call([
print("{} not found, creating it.".format(description))
operations.append([
description,
"docker", "run", "-d",
"--net", "container:{}".format(container_id),
"--label", "ambassador.project={}".format(project_name),
@@ -58,3 +59,13 @@ for container in containers_data.split('\n'):
"jpetazzo/hamba", "run"
])
# Destroy extraneous ambassadors.
for ambassador_id in ambassadors.values():
print("{} is not useful anymore, destroying it.".format(ambassador_id))
operations.append([
"rm -f {}".format(ambassador_id),
"docker", "rm", "-f", ambassador_id,
])
# Execute all commands in parallel.
parallel_run(operations, 10)

38
bin/link-to-ambassadors.py Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python
from common import ComposeFile
import yaml
config = ComposeFile()
# The ambassadors need to know the service port to use.
# Those ports must be declared here.
ports = yaml.load(open("ports.yml"))
def generate_local_addr():
last_byte = 2
while last_byte<255:
yield "127.127.0.{}".format(last_byte)
last_byte += 1
for service_name, service in config.services.items():
if "links" in service:
for link, local_addr in zip(service["links"], generate_local_addr()):
if link not in ports:
print("Skipping link {} in service {} "
"(no port mapping defined). "
"Your code will probably break."
.format(link, service_name))
continue
if "extra_hosts" not in service:
service["extra_hosts"] = {}
service["extra_hosts"][link] = local_addr
del service["links"]
if "ports" in service:
del service["ports"]
if "volumes" in service:
del service["volumes"]
if service_name in ports:
service["ports"] = [ ports[service_name] ]
config.save()

View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python
# FIXME: hardcoded
PORT="80"
import os
import subprocess
project_name = os.path.basename(os.path.realpath("."))
# Get all existing services for this application.
containers_data = subprocess.check_output([
"docker", "ps",
"--filter", "label=com.docker.compose.project={}".format(project_name),
"--format", '{{ .Label "com.docker.compose.service" }} '
'{{ .Label "com.docker.compose.container-number" }} '
'{{ .Label "loadbalancer" }}',
])
load_balancers = dict()
for line in containers_data.split('\n'):
if not line:
continue
service_name, container_number, load_balancer = line.split(' ')
if load_balancer:
if load_balancer not in load_balancers:
load_balancers[load_balancer] = []
load_balancers[load_balancer].append((service_name, int(container_number)))
for load_balancer, backends in load_balancers.items():
# FIXME: iterate on all load balancers
container_name = "{}_{}_1".format(project_name, load_balancer)
command = [
"docker", "run", "--rm",
"--volumes-from", container_name,
"--net", "container:{}".format(container_name),
"jpetazzo/hamba", "reconfigure", PORT,
]
command.extend(
"{}_{}_{}:{}".format(project_name, backend_name, backend_number, PORT)
for (backend_name, backend_number) in sorted(backends)
)
print("Updating configuration for {} with {} backend(s)..."
.format(container_name, len(backends)))
subprocess.check_output(command)

141
bin/setup-all-the-things.sh Executable file
View File

@@ -0,0 +1,141 @@
#!/bin/sh
unset DOCKER_REGISTRY
unset DOCKER_HOST
unset COMPOSE_FILE
SWARM_IMAGE=jpetazzo/swarm:1.1.3-rc2-debug-experimental
check_ssh_keys () {
for N in $(seq 1 5); do
ssh node$N true
done
}
clean_1_containers () {
for N in $(seq 1 5); do
ssh node$N "docker ps -aq | xargs -r -n1 -P10 docker rm -f"
done
}
clean_2_volumes () {
for N in $(seq 1 5); do
ssh node$N "docker volume ls -q | xargs -r docker volume rm"
done
}
clean_3_images () {
for N in $(seq 1 5); do
ssh node$N "docker images | awk '/dockercoins|jpetazzo/ {print \$1\":\"\$2}' | xargs -r docker rmi -f"
done
}
clean_4_machines () {
rm -rf ~/.docker/machine/
}
clean_all () {
clean_1_containers
clean_2_volumes
clean_3_images
clean_4_machines
}
dm_swarm () {
eval $(docker-machine env node1 --swarm)
}
dm_node1 () {
eval $(docker-machine env node1)
}
setup_1_swarm () {
grep node[12345] /etc/hosts | grep -v ^127 |
while read IPADDR NODENAME; do
docker-machine create --driver generic \
--engine-opt cluster-store=consul://localhost:8500 \
--engine-opt cluster-advertise=eth0:2376 \
--swarm --swarm-master --swarm-image $SWARM_IMAGE \
--swarm-discovery consul://localhost:8500 \
--swarm-opt replication --swarm-opt advertise=$IPADDR:3376 \
--generic-ssh-user docker --generic-ip-address $IPADDR $NODENAME
done
}
setup_2_consul () {
ssh node1 docker run --name consul_node1 \
-d --restart=always --net host \
jpetazzo/consul agent -server -bootstrap
IPADDR=$(ssh node1 ip a ls dev eth0 |
sed -n 's,.*inet \(.*\)/.*,\1,p')
# Start other Consul nodes
for N in 2 3 4 5; do
ssh node$N docker run --name consul_node$N \
-d --restart=always --net host \
jpetazzo/consul agent -server -join $IPADDR
done
}
setup_3_wait () {
# Wait for a Swarm master
dm_swarm
while ! docker ps; do sleep 1; done
# Wait for all nodes to be there
while ! [ "$(docker info | grep "^Nodes:")" = "Nodes: 5" ]; do sleep 1; done
}
setup_4_registry () {
cd ~/orchestration-workshop/registry
dm_swarm
docker-compose up -d
for N in $(seq 2 5); do
docker-compose scale frontend=$N
done
}
setup_5_btp_dockercoins () {
cd ~/orchestration-workshop/dockercoins
dm_node1
export DOCKER_REGISTRY=localhost:5000
cp docker-compose.yml-v2 docker-compose.yml
~/orchestration-workshop/bin/build-tag-push.py | tee /tmp/btp.log
export $(tail -n 1 /tmp/btp.log)
}
setup_6_add_lbs () {
cd ~/orchestration-workshop/dockercoins
~/orchestration-workshop/bin/add-load-balancer-v2.py rng
~/orchestration-workshop/bin/add-load-balancer-v2.py hasher
}
setup_all () {
setup_1_swarm
setup_2_consul
setup_3_wait
setup_4_registry
setup_5_btp_dockercoins
setup_6_add_lbs
dm_swarm
}
force_remove_network () {
dm_swarm
NET="$1"
for CNAME in $(docker network inspect $NET | grep Name | grep -v \"$NET\" | cut -d\" -f4); do
echo $CNAME
docker network disconnect -f $NET $CNAME
done
docker network rm $NET
}
demo_1_compose_up () {
dm_swarm
cd ~/orchestration-workshop/dockercoins
docker-compose up -d
}
grep -qs -- MAGICMARKER "$0" && { # Don't display this line in the function lis
echo "You should source this file, then invoke the following functions:"
grep -- '^[a-z].*{$' "$0" | cut -d" " -f1
}

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env python
import os
import subprocess
import time
import yaml
user_name = os.environ.get("DOCKERHUB_USER")
if not user_name:
print("Please set the DOCKERHUB_USER to your user name, e.g.:")
print("export DOCKERHUB_USER=zoe")
exit(1)
# Get the name of the current directory.
project_name = os.path.basename(os.path.realpath("."))
# Generate a Docker image tag, using the UNIX timestamp.
# (i.e. number of seconds since January 1st, 1970)
version = str(int(time.time()))
input_file = os.environ.get(
"DOCKER_COMPOSE_YML", "docker-compose.yml")
output_file = os.environ.get(
"DOCKER_COMPOSE_YML", "docker-compose.yml-{}".format(version))
if input_file == output_file == "docker-compose.yml":
print("I will not clobber your docker-compose.yml file.")
print("Unset DOCKER_COMPOSE_YML or set it to something else.")
exit(1)
print("Input file: {}".format(input_file))
print("Output file: {}".format(output_file))
# Execute "docker-compose build" and abort if it fails.
subprocess.check_call(["docker-compose", "-f", input_file, "build"])
# Load the services from the input docker-compose.yml file.
# TODO: run parallel builds.
stack = yaml.load(open(input_file))
# Iterate over all services that have a "build" definition.
# Tag them, and initiate a push in the background.
push_operations = dict()
for service_name, service in stack.items():
if "build" in service:
compose_image = "{}_{}".format(project_name, service_name)
hub_image = "{}/{}_{}:{}".format(user_name, project_name, service_name, version)
# Re-tag the image so that it can be uploaded to the Docker Hub.
subprocess.check_call(["docker", "tag", compose_image, hub_image])
# Spawn "docker push" to upload the image.
push_operations[service_name] = subprocess.Popen(["docker", "push", hub_image])
# Replace the "build" definition by an "image" definition,
# using the name of the image on the Docker Hub.
del service["build"]
service["image"] = hub_image
# Wait for push operations to complete.
for service_name, popen_object in push_operations.items():
print("Waiting for {} push to complete...".format(service_name))
popen_object.wait()
print("Done.")
# Write the new docker-compose.yml file.
with open(output_file, "w") as f:
yaml.safe_dump(stack, f, default_flow_style=False)
print("Wrote new compose file.")
print("COMPOSE_FILE={}".format(output_file))

19
ceph/README.md Normal file
View File

@@ -0,0 +1,19 @@
# CEPH on Docker
Note: this doesn't quite work yet.
The OSD containers need to be started twice (the first time, they fail
initializing; second time is a champ).
Also, it looks like you need at least two OSD containers (or the OSD
container should have two disks/directories, whatever).
RadosGw is listening on port 8080.
The `admin` container will create a `docker` user using `radosgw-admin`.
If you run it multiple times, that's OK: further invocations are idempotent.
Last but not least: it looks like AWS CLI uses a new signature format
that doesn't work with RadosGW. After almost two hours trying to figure
out what was wrong, I tried the S3 credentials directly with boto and
it worked immediately (I was able to create a bucket).

53
ceph/docker-compose.yml Normal file
View File

@@ -0,0 +1,53 @@
version: "2"
services:
mon:
image: ceph/daemon
command: mon
environment:
CEPH_PUBLIC_NETWORK: 10.33.0.0/16
MON_IP: 10.33.0.2
osd:
image: ceph/daemon
command: osd_directory
depends_on:
- mon
volumes_from:
- mon
volumes:
- /var/lib/ceph/osd
mds:
image: ceph/daemon
command: mds
environment:
CEPHFS_CREATE: 1
depends_on:
- mon
volumes_from:
- mon
rgw:
image: ceph/daemon
command: rgw
depends_on:
- mon
volumes_from:
- mon
environment:
CEPH_OPTS: --verbose
admin:
image: ceph/daemon
entrypoint: radosgw-admin
depends_on:
- mon
volumes_from:
- mon
command: user create --uid=docker --display-name=docker
networks:
default:
ipam:
driver: default
config:
- subnet: 10.33.0.0/16
gateway: 10.33.0.1

View File

@@ -7,7 +7,7 @@ rng2:
rng3:
build: rng
rng0:
rng:
image: jpetazzo/hamba
links:
- rng1
@@ -28,8 +28,8 @@ webui:
- redis
ports:
- "8000:80"
#volumes:
# - "./webui/files/:/files/"
volumes:
- "./webui/files/:/files/"
redis:
image: jpetazzo/hamba
@@ -38,6 +38,6 @@ redis:
worker:
build: worker
links:
- rng0:rng
- hasher:hasher
- rng
- hasher
- redis

View File

@@ -28,8 +28,8 @@ webui:
redis: A.B.C.D
ports:
- "8000:80"
#volumes:
# - "./webui/files/:/files/"
volumes:
- "./webui/files/:/files/"
#redis:
# image: redis

View File

@@ -0,0 +1,44 @@
rng:
build: rng
ports:
- "8001:80"
log_driver: gelf
log_opt:
gelf-address: "udp://XX.XX.XX.XX:XXXXX"
hasher:
build: hasher
ports:
- "8002:80"
log_driver: gelf
log_opt:
gelf-address: "udp://XX.XX.XX.XX:XXXXX"
webui:
build: webui
links:
- redis
ports:
- "8000:80"
volumes:
- "./webui/files/:/files/"
log_driver: gelf
log_opt:
gelf-address: "udp://XX.XX.XX.XX:XXXXX"
redis:
image: redis
log_driver: gelf
log_opt:
gelf-address: "udp://XX.XX.XX.XX:XXXXX"
worker:
build: worker
links:
- rng
- hasher
- redis
log_driver: gelf
log_opt:
gelf-address: "udp://XX.XX.XX.XX:XXXXX"

View File

@@ -7,7 +7,7 @@ rng2:
rng3:
build: rng
rng0:
rng:
image: jpetazzo/hamba
links:
- rng1
@@ -37,7 +37,7 @@ redis:
worker:
build: worker
links:
- rng0:rng
- hasher:hasher
- rng
- hasher
- redis

View File

@@ -0,0 +1,23 @@
version: '2'
services:
rng:
build: rng
ports:
- 80
hasher:
build: hasher
ports:
- 80
webui:
build: webui
ports:
- 80
redis:
image: redis
worker:
build: worker

View File

@@ -1,6 +1,7 @@
FROM node
FROM node:4
RUN npm install express
RUN npm install redis
COPY files/ /files/
COPY webui.js /
CMD ["node", "webui.js"]
EXPOSE 80

View File

@@ -2,13 +2,16 @@
<html>
<head>
<title>DockerCoin Miner WebUI</title>
<link rel="stylesheet" type="text/css" href="rickshaw.min.css">
<link rel="stylesheet" type="text/css" href="rickshaw.min.css">
<style>
#graph {
background-color: #eee;
width: 800px;
height: 400px;
}
#tweet {
color: royalblue;
}
</style>
<script src="jquery.js"></script>
<script src="d3.min.js"></script>
@@ -38,13 +41,22 @@ function refresh () {
while (points.length > 0) {
points.pop();
}
var speed;
for (var i=0; i<series.length-1; i++) {
// Compute instantaneous speed
var s1 = series[i];
var s2 = series[i+1];
var speed = (s2.hashes-s1.hashes)/(s2.now-s1.now);
speed = (s2.hashes-s1.hashes)/(s2.now-s1.now);
points.push({ x: s2.now, y: speed });
}
$("#speed").text("~" + speed.toFixed(1) + " hashes/second");
var msg = ("I'm attending the @docker workshop at @Stylight, "
+ "and my #DockerCoins mining rig is crunching "
+ speed.toFixed(1) + " hashes/second! W00T!");
$("#tweet").attr(
"href",
"https://twitter.com/intent/tweet?text="+encodeURIComponent(msg)
);
if (graph == null) {
graph = new Rickshaw.Graph({
renderer: "area",
@@ -56,7 +68,7 @@ function refresh () {
series: [
{ name: "Coins",
color: "steelblue",
data: points
data: points
}
]
});
@@ -89,5 +101,11 @@ $(function () {
<div id="graph"></div>
<h2>
Current mining speed:
<span id="speed">-</span>
<a id="tweet" href="#">(Tweet this!)</a>
</h2>
</body>
</html>

56
elk/docker-compose.yml Normal file
View File

@@ -0,0 +1,56 @@
elasticsearch:
image: elasticsearch
# If you need to acces ES directly, just uncomment those lines.
#ports:
# - "9200:9200"
# - "9300:9300"
logstash:
image: logstash
command: |
-e '
input {
# Default port is 12201/udp
gelf { }
# This generates one test event per minute.
# It is great for debugging, but you might
# want to remove it in production.
heartbeat { }
}
# The following filter is a hack!
# The "de_dot" filter would be better, but it
# is not pre-installed with logstash by default.
filter {
ruby {
code => "
event.to_hash.keys.each { |k| event[ k.gsub('"'.'"','"'_'"') ] = event.remove(k) if k.include?'"'.'"' }
"
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
}
# This will output every message on stdout.
# It is great when testing your setup, but in
# production, it will probably cause problems;
# either by filling up your disks, or worse,
# by creating logging loops! BEWARE!
stdout {
codec => rubydebug
}
}'
ports:
- 12201/udp
links:
- elasticsearch
kibana:
image: kibana
ports:
- 5601
links:
- elasticsearch
environment:
ELASTICSEARCH_URL: http://elasticsearch:9200

View File

@@ -1,62 +0,0 @@
#!/usr/bin/env python
import os
import sys
import yaml
# You can specify up to 2 parameters:
# - with 0 parameter, we will look for the COMPOSE_FILE env var
# - with 1 parameter, the same file will be used for in and out
# - with 2 parameters, the 1st is the input, the 2nd the output
if len(sys.argv)==1:
if "COMPOSE_FILE" not in os.environ:
print("Please specify 1 or 2 file names, or set COMPOSE_FILE.")
sys.exit(1)
compose_file = os.environ["COMPOSE_FILE"]
if compose_file == "docker-compose.yml":
print("Refusing to operate directly on docker-compose.yml.")
print("Specify it on the command-line if that's what you want.")
sys.exit(1)
input_file, output_file = compose_file, compose_file
elif len(sys.argv)==2:
input_file, output_file = sys.argv[1], sys.argv[1]
elif len(sys.argv)==3:
input_file, output_file = sys.argv[1], sys.argv[2]
else:
print("Too many arguments. Please specify up to 2 file names.")
sys.exit(1)
stack = yaml.load(open(input_file))
# The ambassadors need to know the service port to use.
# Those ports must be declared here.
ports = yaml.load(open("ports.yml"))
def generate_local_addr():
last_byte = 2
while last_byte<255:
yield "127.127.0.{}".format(last_byte)
last_byte += 1
for service_name, service in stack.items():
if "links" in service:
for link, local_addr in zip(service["links"], generate_local_addr()):
if link not in ports:
print("Skipping link {} in service {} "
"(no port mapping defined). "
"Your code will probably break."
.format(link, service_name))
continue
if "extra_hosts" not in service:
service["extra_hosts"] = {}
service["extra_hosts"][link] = local_addr
del service["links"]
if "ports" in service:
del service["ports"]
if "volumes" in service:
del service["volumes"]
if service_name in ports:
service["ports"] = [ ports[service_name] ]
yaml.safe_dump(stack, open(output_file, "w"), default_flow_style=False)

View File

@@ -9,7 +9,7 @@ SETTINGS_BASIC = dict(
"<p>Your VM is reachable on the following address:</p>\n",
prettify=lambda x: x,
footer="<p>You can find the last version of the slides on "
"http://lisa.dckr.info/.</p>",
"http://view.dckr.info/.</p>",
)
SETTINGS_ADVANCED = dict(
@@ -21,11 +21,11 @@ SETTINGS_ADVANCED = dict(
"<p>Your machines are:<ul>\n",
prettify=lambda l: [ "node%d: %s"%(i+1, s)
for (i, s) in zip(range(len(l)), l) ],
footer="<p>You can find the last version of the slides on "
"http://lisa.dckr.info/.</p>"
footer="<p>You can find the last version of the slides on -&gt; "
"http://container.training/</p>"
)
SETTINGS = SETTINGS_BASIC
SETTINGS = SETTINGS_ADVANCED
globals().update(SETTINGS)

View File

@@ -1,5 +1,9 @@
pssh -I tee /tmp/postprep.py <<EOF
#!/usr/bin/env python
COMPOSE_VERSION = "1.6.2"
MACHINE_VERSION = "0.6.0"
SWARM_VERSION = "1.1.3"
import os
import sys
import urllib
@@ -25,18 +29,22 @@ while addresses:
if myaddr == cluster[0]:
os.system("[ -f .ssh/id_rsa ] || ssh-keygen -t rsa -f .ssh/id_rsa -P ''")
os.system("sudo apt-get remove -y --purge dnsmasq-base")
os.system("sudo apt-get -qy install python-setuptools pssh apache2-utils httping htop unzip")
os.system("sudo easy_install pip")
os.system("sudo pip install docker-compose==1.5.0")
os.system("docker pull swarm:1.0.0")
os.system("docker tag -f swarm:1.0.0 swarm")
os.system("sudo curl -L https://github.com/docker/machine/releases/download/v0.5.0/docker-machine_linux-amd64.zip -o /tmp/docker-machine.zip")
os.system("cd /usr/local/bin ; sudo unzip /tmp/docker-machine.zip")
os.system("sudo pip uninstall -y docker-compose")
#os.system("sudo pip install docker-compose=={}".format(COMPOSE_VERSION))
os.system("sudo curl -sSL -o /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/{}/docker-compose-$(uname -s)-$(uname -m)".format(COMPOSE_VERSION))
os.system("sudo chmod +x /usr/local/bin/docker-compose")
os.system("docker pull swarm:{}".format(SWARM_VERSION))
os.system("docker tag -f swarm:{} swarm".format(SWARM_VERSION))
#os.system("sudo curl -sSL https://github.com/docker/machine/releases/download/v{}/docker-machine_linux-amd64.zip -o /tmp/docker-machine.zip".format(MACHINE_VERSION))
#os.system("cd /usr/local/bin ; sudo unzip /tmp/docker-machine.zip")
os.system("sudo curl -sSL -o /usr/local/bin/docker-machine https://github.com/docker/machine/releases/download/v{}/docker-machine-$(uname -s)-$(uname -m)".format(MACHINE_VERSION))
os.system("sudo chmod +x /usr/local/bin/docker-machine*")
os.system("echo 1000000 | sudo tee /proc/sys/net/nf_conntrack_max")
os.system("""sudo sed -i 's,^DOCKER_OPTS=.*,DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:55555",' /etc/default/docker""")
os.system("sudo service docker restart")
#os.system("""sudo sed -i 's,^DOCKER_OPTS=.*,DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:55555",' /etc/default/docker""")
#os.system("sudo service docker restart")
EOF
pssh -t 300 -I "python /tmp/postprep.py >>/tmp/pp.out 2>>/tmp/pp.err" < ips.txt
pssh "[ -f .ssh/id_rsa ] || scp -o StrictHostKeyChecking=no node1:.ssh/id_rsa* .ssh"

37
registry/README.md Normal file
View File

@@ -0,0 +1,37 @@
# Docker Registry with Swarm superpowers
To start your registry, just do:
```
docker-compose up -d
```
You can then refer to the registry as `localhost:5000`.
If you are running on Swarm, do the following:
```
docker-compose up -d
docker-compose scale frontend=N
```
... where `N` is the number of nodes in your cluster.
This will make sure that a `frontend` container runs on every node,
so that `localhost:5000` always refers to your registry.
If you scale up your cluster, make sure to re-run `docker-compose scale`
accordingly.
If you supply a too large value for `N`, you will see errors
(since Swarm tries to schedule more frontends than there are
available hosts) but everything will work fine, don't worry.
Note: this will bind port 5000 on the loopoback interface on
all your machines. That port will therefore be unavailable if
you try e.g. `docker run -p 5000:...`.
Note: the registry will only be available from your cluster,
through the loopback interface. If you want to make it available
from outside, remove `127.0.0.1:` from the Compose file.

View File

@@ -0,0 +1,12 @@
version: "2"
services:
backend:
image: registry:2
frontend:
image: jpetazzo/hamba
command: 5000 backend:5000
ports:
- "127.0.0.1:5000:5000"
depends_on:
- backend

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
www/htdocs/dockercoins.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

File diff suppressed because it is too large Load Diff

BIN
www/htdocs/kibana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB