Compare commits
48 Commits
2015-11-10
...
2016-03-22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a38919ff5b | ||
|
|
c2180cffad | ||
|
|
914c80dba8 | ||
|
|
fa2dc41176 | ||
|
|
f27b99cbc4 | ||
|
|
73b997faf7 | ||
|
|
948691a7d6 | ||
|
|
aa7e8bb1f9 | ||
|
|
88e558bd08 | ||
|
|
1174845c52 | ||
|
|
8a73d85beb | ||
|
|
bc1bb493d4 | ||
|
|
cc6543ce1c | ||
|
|
8c62220c8d | ||
|
|
332eb9cff9 | ||
|
|
2ad3625687 | ||
|
|
7e60d482f7 | ||
|
|
4d2e62ffee | ||
|
|
b4870b8ed6 | ||
|
|
29ae9a10f2 | ||
|
|
ec2b098c29 | ||
|
|
240a55a18a | ||
|
|
362c5e8a65 | ||
|
|
b47dccdb8d | ||
|
|
dca494c090 | ||
|
|
9286fdec28 | ||
|
|
c31e961e61 | ||
|
|
2b88a121ba | ||
|
|
84a521296d | ||
|
|
86271b6336 | ||
|
|
104e736c45 | ||
|
|
9dfb421eb1 | ||
|
|
d69719abc6 | ||
|
|
98bac6713d | ||
|
|
6c1958e244 | ||
|
|
936a80f86d | ||
|
|
2634230a4a | ||
|
|
4e96a2949c | ||
|
|
175d468718 | ||
|
|
eb38b7e2e6 | ||
|
|
9d5dfc9a40 | ||
|
|
676aad7edc | ||
|
|
43d574a164 | ||
|
|
661948d162 | ||
|
|
edf3aeb9c4 | ||
|
|
e16c391deb | ||
|
|
1a486a2f95 | ||
|
|
962737ffa2 |
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*.pyc
|
||||
*.swp
|
||||
*~
|
||||
ips.txt
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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()
|
||||
46
bin/reconfigure-load-balancers.py
Executable 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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,8 +28,8 @@ webui:
|
||||
redis: A.B.C.D
|
||||
ports:
|
||||
- "8000:80"
|
||||
#volumes:
|
||||
# - "./webui/files/:/files/"
|
||||
volumes:
|
||||
- "./webui/files/:/files/"
|
||||
|
||||
#redis:
|
||||
# image: redis
|
||||
|
||||
44
dockercoins/docker-compose.yml-logging
Normal 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
23
dockercoins/docker-compose.yml-v2
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 -> "
|
||||
"http://container.training/</p>"
|
||||
)
|
||||
|
||||
SETTINGS = SETTINGS_BASIC
|
||||
SETTINGS = SETTINGS_ADVANCED
|
||||
|
||||
globals().update(SETTINGS)
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
12
registry/docker-compose.yml
Normal 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
|
||||
BIN
www/htdocs/ambassadors/dockercoins-1.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
www/htdocs/ambassadors/dockercoins-2.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
www/htdocs/ambassadors/dockercoins-3.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
www/htdocs/ambassadors/dockercoins-4.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
www/htdocs/ambassadors/dockercoins-5.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
www/htdocs/ambassadors/dockercoins-6.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
www/htdocs/ambassadors/dockercoins-7.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
www/htdocs/ambassadors/principle-1.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
www/htdocs/ambassadors/principle-2.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
www/htdocs/ambassadors/scaling-1.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
www/htdocs/ambassadors/scaling-2.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
www/htdocs/ambassadors/scaling-3.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
www/htdocs/ambassadors/scaling-4.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
www/htdocs/ambassadors/simple-1.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
www/htdocs/ambassadors/simple-2.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
www/htdocs/ambassadors/simple-3.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
www/htdocs/ambassadors/simple-4.png
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
www/htdocs/ambassadors/simple-5.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
www/htdocs/ambassadors/simple-6.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
www/htdocs/dockercoins-multi-node.png
Normal file
|
After Width: | Height: | Size: 252 KiB |
BIN
www/htdocs/dockercoins-single-node.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
www/htdocs/dockercoins.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
www/htdocs/kibana.png
Normal file
|
After Width: | Height: | Size: 145 KiB |