28 Commits

Author SHA1 Message Date
24ec539932 release 1.0.3 2021-12-21 23:15:52 +02:00
2803046ac3 add awx 17 example 2021-12-21 22:57:45 +02:00
d1768c1d9d FIXES #377: down -v 2021-12-21 22:57:45 +02:00
820ea012c5 FIXES #: U mount propagation option 2021-12-21 22:57:45 +02:00
5ba96a1082 #365: 'Namespace' object has no attribute 'volumes' 2021-12-21 22:57:45 +02:00
49fe6e7e0f Update README.md 2021-12-18 23:34:32 +02:00
6c1ccfcefa Add missing arguments to the log (latest, names, since, until) 2021-12-14 11:35:30 +02:00
724d2fd18c Support viewing all logs 2021-12-14 11:35:30 +02:00
3e940579d9 Support for starting/stopping/restarting all services
Reverse services when stopping or restarting
2021-12-14 11:35:30 +02:00
af1697e9bf FIXES #288: extenal as dict 2021-12-13 03:25:17 +02:00
e62f1a54af FIXES #288: extenal as dict 2021-12-13 01:21:34 +02:00
179f9ab0e3 FIXES #288: do not create external network 2021-12-13 00:24:23 +02:00
dd6b1ee88c FIXES #288: do not create external network 2021-12-13 00:21:53 +02:00
9a8dc4ca17 release 1.0.2 2021-12-11 02:06:10 +02:00
6b5f62d693 Fixes #199: seccomp:unconfined 2021-12-11 01:50:40 +02:00
3782b4ab84 FIXES #371: respect COMPOSE_FILE env 2021-12-10 23:26:13 +02:00
95e07e27f0 FIXES #185: creates dirs 2021-12-10 22:46:22 +02:00
a3123ce480 #222: normalize basedir using os.path.realpath 2021-12-10 22:27:00 +02:00
02f78dc3d7 FIXES #333: when volumes are merged, remove duplicates 2021-12-10 02:06:43 +02:00
8cd97682d0 FIXES #370: bug-for-bug hanlding of .env 2021-12-10 01:01:45 +02:00
85244272ff FIXES #368: parse depends_on of type dict 2021-12-09 16:18:52 +02:00
30cfe2317c set version 2021-12-09 16:12:59 +02:00
7fda1cc835 fix AttributeError when running a one-off command
Without this, I get errors when running "podman-compose -p podname run".
2021-12-09 16:11:04 +02:00
5f40f4df31 Remove named volumes during "down -v"
Fixes containers#105

Signed-off-by: Luiz Carvalho <lucarval@redhat.com>
2021-12-09 16:09:59 +02:00
d38aeaa713 update README 2021-12-09 15:59:34 +02:00
17f9ca61bd test fixes for SELinux (Fedora) 2021-11-24 18:06:18 +02:00
80a47a13d5 add network-alias 2021-11-21 12:35:13 +02:00
872404c3a7 initial work on CNI podman network create 2021-11-21 01:23:29 +02:00
19 changed files with 250 additions and 219 deletions

View File

@ -1,13 +1,10 @@
# Podman Compose # Podman Compose
An implementation of `docker-compose` with [Podman](https://podman.io/) backend. An implementation of [Compose Spec](https://compose-spec.io/) with [Podman](https://podman.io/) backend.
The main objective of this project is to be able to run `docker-compose.yml` unmodified and rootless. This project focus on:
This project is aimed to provide drop-in replacement for `docker-compose`,
and it's very useful for certain cases because:
- can run rootless * rootless
- no daemon, no setup. * daemon-less process model, we directly execute podman, no running daemon.
- can be used by developers to run single-machine containerized stacks using single familiar YAML file
This project only depend on: This project only depend on:
@ -18,20 +15,27 @@ This project only depend on:
And it's formed as a single python file script that you can drop into your PATH and run. And it's formed as a single python file script that you can drop into your PATH and run.
## References:
* [spec.md](https://github.com/compose-spec/compose-spec/blob/master/spec.md)
* [docker-compose compose-file-v3](https://docs.docker.com/compose/compose-file/compose-file-v3/)
* [docker-compose compose-file-v2](https://docs.docker.com/compose/compose-file/compose-file-v2/)
## Alternatives
As in [this article](https://fedoramagazine.org/use-docker-compose-with-podman-to-orchestrate-containers-on-fedora/) you can setup a `podman.socket` and use unmodified `docker-compose` that talks to that socket but in this case you lose the process-model (ex. `docker-compose build` will send a possibly large context tarball to the daemon)
For production-like single-machine containerized environment consider For production-like single-machine containerized environment consider
- [k3s](https://k3s.io) | [k3s github](https://github.com/rancher/k3s) - [k3s](https://k3s.io) | [k3s github](https://github.com/rancher/k3s)
- [MiniKube](https://minikube.sigs.k8s.io/) - [MiniKube](https://minikube.sigs.k8s.io/)
- [MiniShift](https://www.okd.io/minishift/)
For the real thing (multi-node clusters) check any production For the real thing (multi-node clusters) check any production
OpenShift/Kubernetes distribution like [OKD](https://www.okd.io/minishift/). OpenShift/Kubernetes distribution like [OKD](https://www.okd.io/).
## Versions ## Versions
If you have legacy version of `podman` (before 3.x) you might need to stick with legacy `podman-compose` `0.1.x` branch. If you have legacy version of `podman` (before 3.1.0) you might need to stick with legacy `podman-compose` `0.1.x` branch.
The legacy branch 0.1.x uses mappings and workarounds to compensate for rootless limitations. The legacy branch 0.1.x uses mappings and workarounds to compensate for rootless limitations.
Modern podman versions (>=3.4) do not have those limitations and thus you can use latest and stable 1.x branch. Modern podman versions (>=3.4) do not have those limitations and thus you can use latest and stable 1.x branch.
@ -59,7 +63,7 @@ curl -o /usr/local/bin/podman-compose https://raw.githubusercontent.com/containe
chmod +x /usr/local/bin/podman-compose chmod +x /usr/local/bin/podman-compose
``` ```
or or inside your home
``` ```
curl -o ~/.local/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/devel/podman_compose.py curl -o ~/.local/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/devel/podman_compose.py
@ -96,18 +100,11 @@ which have
When testing the `AWX3` example, if you got errors just wait for db migrations to end. When testing the `AWX3` example, if you got errors just wait for db migrations to end.
There is also AWX 17.1.0
## Tests ## Tests
Inside `tests/` directory we have many useless docker-compose stacks Inside `tests/` directory we have many useless docker-compose stacks
that are meant to test as much cases as we can to make sure we are compatible that are meant to test as much cases as we can to make sure we are compatible
## How it works
The default mapping `1podfw` creates a single pod and attach all containers to
its network namespace so that all containers talk via localhost.
For more information see [docs/Mappings.md](docs/Mappings.md).
If you are running as root, you might use identity mapping.

View File

@ -35,7 +35,7 @@ except ImportError:
import yaml import yaml
from dotenv import dotenv_values from dotenv import dotenv_values
__version__ = '0.1.11' __version__ = '1.0.3'
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info[0] == 3
if PY3: if PY3:
@ -256,149 +256,21 @@ def norm_ulimit(inner_value):
# if int or string return as is # if int or string return as is
return inner_value return inner_value
#def tr_identity(project_name, given_containers):
# pod_name = f'pod_{project_name}'
# pod = dict(name=pod_name)
# containers = []
# for cnt in given_containers:
# containers.append(dict(cnt, pod=pod_name))
# return [pod], containers
# transformation helpers def tr_identity(project_name, given_containers):
def adj_hosts(services, cnt, dst="127.0.0.1"):
"""
adjust container cnt in-place to add hosts pointing to dst for services
"""
common_extra_hosts = []
for srv, cnts in services.items():
common_extra_hosts.append("{}:{}".format(srv, dst))
for cnt0 in cnts:
common_extra_hosts.append("{}:{}".format(cnt0, dst))
extra_hosts = list(cnt.get("extra_hosts", []))
extra_hosts.extend(common_extra_hosts)
# link aliases
for link in cnt.get("links", []):
a = link.strip().split(':', 1)
if len(a) == 2:
alias = a[1].strip()
extra_hosts.append("{}:{}".format(alias, dst))
cnt["extra_hosts"] = extra_hosts
def move_list(dst, containers, key):
"""
move key (like port forwarding) from containers to dst (a pod or a infra container)
"""
a = set(dst.get(key, None) or [])
for cnt in containers:
a0 = cnt.get(key, None)
if a0:
a.update(a0)
del cnt[key]
if a:
dst[key] = list(a)
def move_port_fw(dst, containers):
"""
move port forwarding from containers to dst (a pod or a infra container)
"""
move_list(dst, containers, "ports")
def move_extra_hosts(dst, containers):
"""
move port forwarding from containers to dst (a pod or a infra container)
"""
move_list(dst, containers, "extra_hosts")
# transformations
transformations = {}
def trans(func):
transformations[func.__name__.replace("tr_", "")] = func
return func
@trans
def tr_identity(project_name, services, given_containers):
containers = [] containers = []
for cnt in given_containers: for cnt in given_containers:
containers.append(dict(cnt)) containers.append(dict(cnt))
return [], containers return [], containers
@trans
def tr_publishall(project_name, services, given_containers):
containers = []
for cnt0 in given_containers:
cnt = dict(cnt0, publishall=True)
# adjust hosts to point to the gateway, TODO: adjust host env
adj_hosts(services, cnt, '10.0.2.2')
containers.append(cnt)
return [], containers
@trans
def tr_hostnet(project_name, services, given_containers):
containers = []
for cnt0 in given_containers:
cnt = dict(cnt0, network_mode="host")
# adjust hosts to point to localhost, TODO: adjust host env
adj_hosts(services, cnt, '127.0.0.1')
containers.append(cnt)
return [], containers
@trans
def tr_cntnet(project_name, services, given_containers):
containers = []
infra_name = project_name + "_infra"
infra = dict(
name=infra_name,
image="k8s.gcr.io/pause:3.1",
_service=None,
service_name=None
)
for cnt0 in given_containers:
cnt = dict(cnt0, network_mode="container:"+infra_name)
deps = cnt.get("depends_on", None) or []
deps.append(infra_name)
cnt["depends_on"] = deps
# adjust hosts to point to localhost, TODO: adjust host env
adj_hosts(services, cnt, '127.0.0.1')
if "hostname" in cnt:
del cnt["hostname"]
containers.append(cnt)
move_port_fw(infra, containers)
move_extra_hosts(infra, containers)
containers.insert(0, infra)
return [], containers
@trans
def tr_1pod(project_name, services, given_containers):
"""
project_name:
services: {service_name: ["container_name1", "..."]}, currently only one is supported
given_containers: [{}, ...]
"""
pod = dict(name=project_name)
containers = []
for cnt0 in given_containers:
cnt = dict(cnt0, pod=project_name)
# services can be accessed as localhost because they are on one pod
# adjust hosts to point to localhost, TODO: adjust host env
adj_hosts(services, cnt, '127.0.0.1')
containers.append(cnt)
return [pod], containers
@trans
def tr_1podfw(project_name, services, given_containers):
pods, containers = tr_1pod(project_name, services, given_containers)
pod = pods[0]
move_port_fw(pod, containers)
return pods, containers
def assert_volume(compose, mount_dict): def assert_volume(compose, mount_dict):
""" """
inspect volume to get directory inspect volume to get directory
@ -674,6 +546,56 @@ def norm_ports(ports_in):
ports_out.append(port) ports_out.append(port)
return ports_out return ports_out
def assert_cnt_nets(compose, cnt):
"""
create missing networks
"""
proj_name = compose.project_name
nets = compose.networks
default_net = compose.default_net
cnt_nets = norm_as_list(cnt.get("networks", None) or default_net)
for net in cnt_nets:
net_desc = nets[net] or {}
is_ext = net_desc.get("external", None)
ext_desc = is_ext if is_dict(is_ext) else {}
default_net_name = net if is_ext else f"{proj_name}_{net}"
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
try: compose.podman.output([], "network", ["exists", net_name])
except subprocess.CalledProcessError:
if is_ext:
raise RuntimeError(f"External network [{net_name}] does not exists")
args = [
"create",
"--label", "io.podman.compose.project={}".format(proj_name),
"--label", "com.docker.compose.project={}".format(proj_name),
]
# TODO: add more options here, like driver, internal, ..etc
labels = net_desc.get("labels", None) or []
for item in norm_as_list(labels):
args.extend(["--label", item])
if net_desc.get("internal", None):
args.append("--internal")
args.append(net_name)
compose.podman.output([], "network", args)
compose.podman.output([], "network", ["exists", net_name])
def get_net_args(compose, cnt):
service_name = cnt["service_name"]
proj_name = compose.project_name
default_net = compose.default_net
nets = compose.networks
cnt_nets = norm_as_list(cnt.get("networks", None) or default_net)
net_names = set()
for net in cnt_nets:
net_desc = nets[net] or {}
is_ext = net_desc.get("external", None)
ext_desc = is_ext if is_dict(is_ext) else {}
default_net_name = net if is_ext else f"{proj_name}_{net}"
net_name = ext_desc.get("name", None) or net_desc.get("name", None) or default_net_name
net_names.add(net_name)
net_names_str = ",".join(net_names)
return ["--net", net_names_str, "--network-alias", service_name]
def container_to_args(compose, cnt, detached=True): def container_to_args(compose, cnt, detached=True):
# TODO: double check -e , --add-host, -v, --read-only # TODO: double check -e , --add-host, -v, --read-only
dirname = compose.dirname dirname = compose.dirname
@ -720,6 +642,8 @@ def container_to_args(compose, cnt, detached=True):
podman_args.extend(['--tmpfs', i]) podman_args.extend(['--tmpfs', i])
for volume in cnt.get('volumes', []): for volume in cnt.get('volumes', []):
podman_args.extend(get_mount_args(compose, cnt, volume)) podman_args.extend(get_mount_args(compose, cnt, volume))
assert_cnt_nets(compose, cnt)
podman_args.extend(get_net_args(compose, cnt))
log = cnt.get('logging') log = cnt.get('logging')
if log is not None: if log is not None:
podman_args.append(f'--log-driver={log.get("driver", "k8s-file")}') podman_args.append(f'--log-driver={log.get("driver", "k8s-file")}')
@ -742,7 +666,7 @@ def container_to_args(compose, cnt, detached=True):
elif not isinstance(port, str): elif not isinstance(port, str):
raise TypeError("port should be either string or dict") raise TypeError("port should be either string or dict")
podman_args.extend(['-p', port]) podman_args.extend(['-p', port])
user = cnt.get('user', None) user = cnt.get('user', None)
if user is not None: if user is not None:
podman_args.extend(['-u', user]) podman_args.extend(['-u', user])
@ -1053,6 +977,8 @@ class PodmanCompose:
self.pods = None self.pods = None
self.containers = None self.containers = None
self.vols = None self.vols = None
self.networks = {}
self.default_net = "default"
self.declared_secrets = None self.declared_secrets = None
self.container_names_by_service = None self.container_names_by_service = None
self.container_by_name = None self.container_by_name = None
@ -1125,7 +1051,6 @@ class PodmanCompose:
no_ansi = args.no_ansi no_ansi = args.no_ansi
no_cleanup = args.no_cleanup no_cleanup = args.no_cleanup
dry_run = args.dry_run dry_run = args.dry_run
transform_policy = args.transform_policy
host_env = None host_env = None
dirname = os.path.realpath(os.path.dirname(filename)) dirname = os.path.realpath(os.path.dirname(filename))
dir_basename = os.path.basename(dirname) dir_basename = os.path.basename(dirname)
@ -1181,6 +1106,26 @@ class PodmanCompose:
flat_deps(services) flat_deps(services)
service_names = sorted([ (len(srv["_deps"]), name) for name, srv in services.items() ]) service_names = sorted([ (len(srv["_deps"]), name) for name, srv in services.items() ])
service_names = [ name for _, name in service_names] service_names = [ name for _, name in service_names]
nets = compose.get("networks", None) or {}
if not nets:
nets["default"] = None
self.networks = nets
if len(self.networks)==1:
self.default_net = list(nets.keys())[0]
elif "default" in nets:
self.default_net = "default"
else:
self.default_net = None
default_net = self.default_net
allnets = set()
for name, srv in services.items():
srv_nets = norm_as_list(srv.get("networks", None) or default_net)
allnets.update(srv_nets)
given_nets = set(nets.keys())
missing_nets = given_nets - allnets
if len(missing_nets):
missing_nets_str= ",".join(missing_nets)
raise RuntimeError(f"missing networks: {missing_nets_str}")
# volumes: [...] # volumes: [...]
self.vols = compose.get('volumes', {}) self.vols = compose.get('volumes', {})
podman_compose_labels = [ podman_compose_labels = [
@ -1237,14 +1182,11 @@ class PodmanCompose:
given_containers = list(container_by_name.values()) given_containers = list(container_by_name.values())
given_containers.sort(key=lambda c: len(c.get('_deps', None) or [])) given_containers.sort(key=lambda c: len(c.get('_deps', None) or []))
#print("sorted:", [c["name"] for c in given_containers]) #print("sorted:", [c["name"] for c in given_containers])
tr = transformations[transform_policy] pods, containers = tr_identity(project_name, given_containers)
pods, containers = tr(
project_name, container_names_by_service, given_containers)
self.pods = pods self.pods = pods
self.containers = containers self.containers = containers
self.container_by_name = dict([ (c["name"], c) for c in containers]) self.container_by_name = dict([ (c["name"], c) for c in containers])
def _parse_args(self): def _parse_args(self):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter formatter_class=argparse.RawTextHelpFormatter
@ -1289,17 +1231,6 @@ class PodmanCompose:
help="Do not stop and remove existing pod & containers", action='store_true') help="Do not stop and remove existing pod & containers", action='store_true')
parser.add_argument("--dry-run", parser.add_argument("--dry-run",
help="No action; perform a simulation of commands", action='store_true') help="No action; perform a simulation of commands", action='store_true')
parser.add_argument("-t", "--transform_policy",
help=textwrap.dedent("""\
how to translate docker compose to podman (default: 1podfw)
1podfw - create all containers in one pod (inter-container communication is done via localhost), doing port mapping in that pod
1pod - create all containers in one pod, doing port mapping in each container (does not work)
identity - no mapping
hostnet - use host network, and inter-container communication is done via host gateway and published ports
cntnet - create a container and use it via --network container:name (inter-container communication via localhost)
publishall - publish all ports to host (using -P) and communicate via gateway
"""),
choices=['1pod', '1podfw', 'hostnet', 'cntnet', 'publishall', 'identity'], default='1podfw')
podman_compose = PodmanCompose() podman_compose = PodmanCompose()
@ -1428,10 +1359,9 @@ def create_pods(compose, args):
podman_args = [ podman_args = [
"create", "create",
"--name={}".format(pod["name"]), "--name={}".format(pod["name"]),
"--share", "net",
] ]
if compose.podman_version and not strverscmp_lt(compose.podman_version, "3.4.0"): #if compose.podman_version and not strverscmp_lt(compose.podman_version, "3.4.0"):
podman_args.append("--infra-name={}_infra".format(pod["name"])) # podman_args.append("--infra-name={}_infra".format(pod["name"]))
ports = pod.get("ports", None) or [] ports = pod.get("ports", None) or []
if isinstance(ports, str): if isinstance(ports, str):
ports = [ports] ports = [ports]
@ -1622,11 +1552,15 @@ def compose_exec(compose, args):
def transfer_service_status(compose, args, action): def transfer_service_status(compose, args, action):
# TODO: handle dependencies, handle creations # TODO: handle dependencies, handle creations
container_names_by_service = compose.container_names_by_service container_names_by_service = compose.container_names_by_service
if not args.services:
args.services = container_names_by_service.keys()
targets = [] targets = []
for service in args.services: for service in args.services:
if service not in container_names_by_service: if service not in container_names_by_service:
raise ValueError("unknown service: " + service) raise ValueError("unknown service: " + service)
targets.extend(container_names_by_service[service]) targets.extend(container_names_by_service[service])
if action in ['stop', 'restart']:
targets = list(reversed(targets))
podman_args=[] podman_args=[]
timeout=getattr(args, 'timeout', None) timeout=getattr(args, 'timeout', None)
if timeout is not None: if timeout is not None:
@ -1649,20 +1583,33 @@ def compose_restart(compose, args):
@cmd_run(podman_compose, 'logs', 'show logs from services') @cmd_run(podman_compose, 'logs', 'show logs from services')
def compose_logs(compose, args): def compose_logs(compose, args):
container_names_by_service = compose.container_names_by_service container_names_by_service = compose.container_names_by_service
target = None if not args.services and not args.latest:
if args.service not in container_names_by_service: args.services = container_names_by_service.keys()
raise ValueError("unknown service: " + args.service) targets = []
target = container_names_by_service[args.service] for service in args.services:
if service not in container_names_by_service:
raise ValueError("unknown service: " + service)
targets.extend(container_names_by_service[service])
podman_args = [] podman_args = []
if args.follow: if args.follow:
podman_args.append('-f') podman_args.append('-f')
if args.latest:
podman_args.append("-l")
if args.names:
podman_args.append('-n')
if args.since:
podman_args.extend(['--since', args.since])
# the default value is to print all logs which is in podman = 0 and not # the default value is to print all logs which is in podman = 0 and not
# needed to be passed # needed to be passed
if args.tail and args.tail != 'all': if args.tail and args.tail != 'all':
podman_args.extend(['--tail', args.tail]) podman_args.extend(['--tail', args.tail])
if args.timestamps: if args.timestamps:
podman_args.append('-t') podman_args.append('-t')
compose.podman.run([], 'logs', podman_args+target) if args.until:
podman_args.extend(['--until', args.until])
for target in targets:
podman_args.append(target)
compose.podman.run([], 'logs', podman_args)
################### ###################
# command arguments parsing # command arguments parsing
@ -1770,23 +1717,26 @@ def compose_parse_timeout(parser):
help="Specify a shutdown timeout in seconds. ", help="Specify a shutdown timeout in seconds. ",
type=int, default=10) type=int, default=10)
@cmd_parse(podman_compose, ['start', 'stop', 'restart'])
def compose_parse_services(parser):
parser.add_argument('services', metavar='services', nargs='+',
help='affected services')
@cmd_parse(podman_compose, ['logs']) @cmd_parse(podman_compose, ['logs'])
def compose_logs_parse(parser): def compose_logs_parse(parser):
parser.add_argument("-f", "--follow", action='store_true', parser.add_argument("-f", "--follow", action='store_true',
help="Follow log output.") help="Follow log output. The default is false")
parser.add_argument("-l", "--latest", action='store_true',
help="Act on the latest container podman is aware of")
parser.add_argument("-n", "--names", action='store_true',
help="Output the container name in the log")
parser.add_argument("--since", help="Show logs since TIMESTAMP",
type=str, default=None)
parser.add_argument("-t", "--timestamps", action='store_true', parser.add_argument("-t", "--timestamps", action='store_true',
help="Show timestamps.") help="Show timestamps.")
parser.add_argument("--tail", parser.add_argument("--tail",
help="Number of lines to show from the end of the logs for each " help="Number of lines to show from the end of the logs for each "
"container.", "container.",
type=str, default="all") type=str, default="all")
parser.add_argument('service', metavar='service', nargs=None, parser.add_argument("--until", help="Show logs until TIMESTAMP",
help='service name') type=str, default=None)
parser.add_argument('services', metavar='services', nargs='*', default=None,
help='service names')
@cmd_parse(podman_compose, 'pull') @cmd_parse(podman_compose, 'pull')
def compose_pull_parse(parser): def compose_pull_parse(parser):
@ -1816,7 +1766,7 @@ def compose_build_parse(parser):
parser.add_argument("--no-cache", parser.add_argument("--no-cache",
help="Do not use cache when building the image.", action='store_true') help="Do not use cache when building the image.", action='store_true')
@cmd_parse(podman_compose, ['build', 'up', 'down']) @cmd_parse(podman_compose, ['build', 'up', 'down', 'start', 'stop', 'restart'])
def compose_build_parse(parser): def compose_build_parse(parser):
parser.add_argument('services', metavar='services', nargs='*',default=None, parser.add_argument('services', metavar='services', nargs='*',default=None,
help='affected services') help='affected services')

View File

@ -4,7 +4,7 @@ services:
image: busybox image: busybox
command: busybox httpd -h /var/www/html/ -f -p 8001 command: busybox httpd -h /var/www/html/ -f -p 8001
volumes: volumes:
- ./1.env:/var/www/html/index.txt - ./1.env:/var/www/html/index.txt:z
env_file: ./1.env env_file: ./1.env
labels: labels:
l1: v1 l1: v1

View File

@ -1,10 +1,11 @@
version: '3' version: '3'
services: services:
web1: web1:
image: busybox
env_file: ./12.env env_file: ./12.env
labels: labels:
- l1=v2 - l1=v2
- l2=v2 - l2=v2
environment: environment:
mykey1: myval2 mykey1: myval2
mykey2: myval2 mykey2: myval2
@ -13,6 +14,6 @@ services:
image: busybox image: busybox
command: busybox httpd -h /var/www/html/ -f -p 8002 command: busybox httpd -h /var/www/html/ -f -p 8002
volumes: volumes:
- ./2.env:/var/www/html/index.txt - ./2.env:/var/www/html/index.txt:z
env_file: ./2.env env_file: ./2.env

View File

@ -0,0 +1,21 @@
version: "3"
services:
web1:
image: busybox
hostname: web1
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
ports:
- 8001:8001
volumes:
- ./test1.txt:/var/www/html/index.txt:ro,z
web2:
image: busybox
hostname: web1
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
ports:
- 8002:8001
volumes:
- ./test2.txt:/var/www/html/index.txt:ro,z

View File

@ -0,0 +1 @@
test1

View File

@ -0,0 +1 @@
test2

View File

@ -0,0 +1,23 @@
version: "3"
networks:
mystack:
services:
web1:
image: busybox
hostname: web1
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
ports:
- 8001:8001
volumes:
- ./test1.txt:/var/www/html/index.txt:ro,z
web2:
image: busybox
hostname: web2
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
ports:
- 8002:8001
volumes:
- ./test2.txt:/var/www/html/index.txt:ro,z

View File

@ -0,0 +1 @@
test1

View File

@ -0,0 +1 @@
test2

View File

@ -0,0 +1,31 @@
version: "3"
networks:
net1:
net2:
services:
web1:
image: busybox
#container_name: web1
hostname: web1
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
networks:
- net1
ports:
- 8001:8001
volumes:
- ./test1.txt:/var/www/html/index.txt:ro,z
web2:
image: busybox
#container_name: web2
hostname: web2
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html
networks:
- net1
- net2
ports:
- 8002:8001
volumes:
- ./test2.txt:/var/www/html/index.txt:ro,z

View File

@ -0,0 +1 @@
test1

View File

@ -0,0 +1 @@
test2

View File

@ -2,32 +2,34 @@ version: "3"
services: services:
web1: web1:
image: busybox image: busybox
hostname: web1
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html working_dir: /var/www/html
ports: ports:
- 8001:8001 - 8001:8001
volumes: volumes:
- ./test1.txt:/var/www/html/index.txt:ro - ./test1.txt:/var/www/html/index.txt:ro,z
web2: web2:
image: busybox image: busybox
hostname: web2
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"]
working_dir: /var/www/html working_dir: /var/www/html
ports: ports:
- 8002:8002 - 8002:8002
- target: 8003 - target: 8003
host_ip: 127.0.0.1 host_ip: 127.0.0.1
published: 8003 published: 8003
protocol: udp protocol: udp
- target: 8004 - target: 8004
host_ip: 127.0.0.1 host_ip: 127.0.0.1
published: 8004 published: 8004
protocol: tcp protocol: tcp
- target: 8005 - target: 8005
published: 8005 published: 8005
- target: 8006 - target: 8006
protocol: udp protocol: udp
- target: 8007 - target: 8007
host_ip: 127.0.0.1 host_ip: 127.0.0.1
volumes: volumes:
- ./test2.txt:/var/www/html/index.txt:ro - ./test2.txt:/var/www/html/index.txt:ro,z

View File

@ -8,7 +8,7 @@ services:
- /run - /run
- /tmp - /tmp
volumes: volumes:
- ./print_secrets.sh:/tmp/print_secrets.sh - ./print_secrets.sh:/tmp/print_secrets.sh:z
secrets: secrets:
- my_secret - my_secret
- my_secret_2 - my_secret_2

View File

View File

View File

@ -4,7 +4,7 @@ services:
image: redis:alpine image: redis:alpine
command: ["redis-server", "--appendonly yes", "--notify-keyspace-events", "Ex"] command: ["redis-server", "--appendonly yes", "--notify-keyspace-events", "Ex"]
volumes: volumes:
- ./data/redis:/data - ./data/redis:/data:z
tmpfs: /run1 tmpfs: /run1
ports: ports:
- "6379" - "6379"
@ -25,16 +25,16 @@ services:
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html working_dir: /var/www/html
volumes: volumes:
- ./data/web:/var/www/html:ro - ./data/web:/var/www/html:ro,z
web2: web2:
image: busybox image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"]
working_dir: /var/www/html working_dir: /var/www/html
volumes: volumes:
- ~/Downloads/www:/var/www/html:ro - ~/Downloads/www:/var/www/html:ro,z
web3: web3:
image: busybox image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8003"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8003"]
working_dir: /var/www/html working_dir: /var/www/html
volumes: volumes:
- /var/www/html:/var/www/html:ro - /var/www/html:/var/www/html:ro,z

View File

@ -14,7 +14,7 @@ services:
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
working_dir: /var/www/html working_dir: /var/www/html
volumes: volumes:
- myvol1:/var/www/html:ro - myvol1:/var/www/html:ro,z
web2: web2:
image: busybox image: busybox
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"] command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"]