-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdeploy_docker_containers.py
More file actions
150 lines (125 loc) · 5.14 KB
/
Copy pathdeploy_docker_containers.py
File metadata and controls
150 lines (125 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import gzip
import io
import tarfile
from pathlib import Path
import docker
from dotenv import dotenv_values # pip install python-dotenv
client = docker.from_env() # uses DOCKER_HOST / socket automatically
class DockerCommands():
def run(self, image, container_name, port):
container = client.containers.run(
image=image,
name=container_name,
ports={f"{port}/tcp": int(port)}, # host:container
restart_policy={"Name": "always"},
volumes={"/var/run/docker.sock": { # same as compose bind mount
"bind": "/var/run/docker.sock",
"mode": "rw"
}},
# environment=env_vars,
detach=True, # don't block; return Container object
command=[
"python", "/app/api-containers/app.py",
"--env-file", "/app/deploy_env.env",
f"--port", f"{port}"
]
)
print(container.id)
def pull_image(self, ref: str):
"""Pull image (e.g. 'edgefl:latest' or 'ghcr.io/org/img:1.0')."""
print(f"Pulling {ref} …")
return client.images.pull(ref)
def save_image(self, image_name: str, out_path: str, compress: bool = False):
"""Save image to a tar (or tar.gz) file."""
img = client.images.get(image_name)
data_iter = img.save(named=True) # stream of bytes
if compress:
with gzip.open(out_path, "wb") as f:
for chunk in data_iter:
f.write(chunk)
else:
with open(out_path, "wb") as f:
for chunk in data_iter:
f.write(chunk)
print(f"Saved {image_name} to {out_path}")
def load_image(self, image_fil: str, image_name: str = "overlay_base", tag: str="temp"):
"""Load image tar (or tar.gz) into local Docker."""
in_path = Path(image_fil)
opener = gzip.open if in_path.suffix == ".gz" else open
with opener(in_path, "rb") as f:
imgs = client.images.load(f.read()) # returns list of Image objects
# 2️⃣ pick the first image and tag it
# img = imgs[0]
imgs[0].tag(repository=image_name, tag=tag)
print(f"Loaded {len(imgs)} image(s) from {in_path}")
return imgs
def build_overlay(self,
base_ref: str,
req_path: str, # requirements.txt path
data_handler_path: str, # data_handler.py path
env_path: str, # env file path
new_tag: str,
workdir: str = "/app",
) -> tuple[str, str]:
"""
Build a new image on top of *base_ref* with a replacement requirements file.
Returns (image_id, digest)
"""
data_handler_file_name = data_handler_path.split("/")[-1]
env_file_name = env_path.split("/")[-1]
dockerfile = f"""
FROM {base_ref}
WORKDIR {workdir}
COPY requirements.txt requirements.txt
COPY {env_file_name} deploy_env.env
COPY {data_handler_file_name} /app/edgefl/platform_components/data_handlers/{data_handler_file_name}
RUN pip install --no-cache-dir -r requirements.txt
""".lstrip()
# in-memory build context
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w") as tar:
for name, data in {
"Dockerfile": dockerfile.encode(),
"requirements.txt": Path(req_path).read_bytes(),
f"{data_handler_file_name}": Path(data_handler_path).read_bytes(),
f"{env_file_name}": Path(env_path).read_bytes(),
}.items():
ti = tarfile.TarInfo(name)
ti.size = len(data)
tar.addfile(ti, io.BytesIO(data))
buf.seek(0)
img, logs = client.images.build(
fileobj=buf,
custom_context=True,
tag=new_tag,
rm=True,
pull=False,
labels={"rebuilt-by": "overlay_build.py"},
)
# Show build output (optional)
for l in logs:
if "stream" in l:
print(l["stream"], end="")
inspect = client.api.inspect_image(img.id)
size = inspect.get("Size") / 10**6
return img.id, f"Size {size} MB"
# Example end-to-end
if __name__ == "__main__":
dc = DockerCommands()
image_name = "edgefl:latest"
# dc.save_image(image_name, out_path="/Users/roy/edgefl_latest.tar")
#
# dc.load_image("/Users/roy/edgefl_latest.tar", "my_edgefl_new:latest")
#
new_image = "my-new-image:op3"
dc.build_overlay(base_ref=image_name, req_path="/Users/roy/requirements.txt", data_handler_path="/Users/roy/Github-Repos/EdgeFL/winniio_data_handler.py", env_path="/Users/roy/Github-Repos/EdgeFL/edgefl/env_files/winniio_docker/winniio3.env", new_tag=f'{new_image}')
container_name = new_image.split(":")[-1]
if container_name == 'agg':
port = 8080
elif container_name == 'op1':
port = 8081
elif container_name == 'op2':
port = 8082
elif container_name == 'op3':
port = 8083
dc.run(new_image, container_name, port=port)