mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-29 05:36:47 +00:00
docker: add support for docker:target with docker-compose
This commit is contained in:
parent
2c288d26fc
commit
f0a7445fb4
2
packages/addons/service/docker/config/docker.conf
Normal file
2
packages/addons/service/docker/config/docker.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DOCKER_DAEMON_OPTS="--data-root=/storage/docker"
|
||||||
|
DOCKER_STORAGE_OPTS="--storage-driver=overlay2"
|
161
packages/addons/service/docker/lib/dockermon.py
Executable file
161
packages/addons/service/docker/lib/dockermon.py
Executable file
@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""docker monitor using docker /events HTTP streaming API"""
|
||||||
|
"""https://github.com/CyberInt/dockermon"""
|
||||||
|
|
||||||
|
from contextlib import closing
|
||||||
|
from functools import partial
|
||||||
|
from socket import socket, AF_UNIX, timeout
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from sys import stdout, version_info
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
if version_info[:2] < (3, 0):
|
||||||
|
from httplib import OK as HTTP_OK
|
||||||
|
from urlparse import urlparse
|
||||||
|
else:
|
||||||
|
from http.client import OK as HTTP_OK
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
__version__ = '0.2.2'
|
||||||
|
# buffer size must be 256 or lower otherwise events won't show in realtime
|
||||||
|
bufsize = 256
|
||||||
|
default_sock_url = 'ipc:///var/run/docker.sock'
|
||||||
|
|
||||||
|
|
||||||
|
class DockermonError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def read_http_header(sock):
|
||||||
|
"""Read HTTP header from socket, return header and rest of data."""
|
||||||
|
buf = []
|
||||||
|
hdr_end = '\r\n\r\n'
|
||||||
|
|
||||||
|
while True:
|
||||||
|
buf.append(sock.recv(bufsize).decode('utf-8'))
|
||||||
|
data = ''.join(buf)
|
||||||
|
i = data.find(hdr_end)
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
return data[:i], data[i + len(hdr_end):]
|
||||||
|
|
||||||
|
|
||||||
|
def header_status(header):
|
||||||
|
"""Parse HTTP status line, return status (int) and reason."""
|
||||||
|
status_line = header[:header.find('\r')]
|
||||||
|
# 'HTTP/1.1 200 OK' -> (200, 'OK')
|
||||||
|
fields = status_line.split(None, 2)
|
||||||
|
return int(fields[1]), fields[2]
|
||||||
|
|
||||||
|
|
||||||
|
def connect(url):
|
||||||
|
"""Connect to UNIX or TCP socket.
|
||||||
|
|
||||||
|
url can be either tcp://<host>:port or ipc://<path>
|
||||||
|
"""
|
||||||
|
url = urlparse(url)
|
||||||
|
if url.scheme == 'tcp':
|
||||||
|
sock = socket()
|
||||||
|
netloc = tuple(url.netloc.rsplit(':', 1))
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
elif url.scheme == 'ipc':
|
||||||
|
sock = socket(AF_UNIX)
|
||||||
|
netloc = url.path
|
||||||
|
hostname = 'localhost'
|
||||||
|
else:
|
||||||
|
raise ValueError('unknown socket type: %s' % url.scheme)
|
||||||
|
|
||||||
|
sock.connect(netloc)
|
||||||
|
return sock, hostname
|
||||||
|
|
||||||
|
|
||||||
|
def watch(callback, url=default_sock_url, run=None):
|
||||||
|
"""Watch docker events. Will call callback with each new event (dict).
|
||||||
|
|
||||||
|
url can be either tcp://<host>:port or ipc://<path>
|
||||||
|
"""
|
||||||
|
sock, hostname = connect(url)
|
||||||
|
if run:
|
||||||
|
sock.settimeout(1.5)
|
||||||
|
request = 'GET /events HTTP/1.1\nHost: %s\n\n' % hostname
|
||||||
|
request = request.encode('utf-8')
|
||||||
|
|
||||||
|
with closing(sock):
|
||||||
|
sock.sendall(request)
|
||||||
|
header, payload = read_http_header(sock)
|
||||||
|
status, reason = header_status(header)
|
||||||
|
if status != HTTP_OK:
|
||||||
|
raise DockermonError('bad HTTP status: %s %s' % (status, reason))
|
||||||
|
|
||||||
|
# Messages are \r\n<size in hex><JSON payload>\r\n
|
||||||
|
buf = [payload]
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = sock.recv(bufsize)
|
||||||
|
except timeout:
|
||||||
|
if run():
|
||||||
|
continue
|
||||||
|
if run and not run():
|
||||||
|
raise DockermonError('stopped')
|
||||||
|
if not chunk:
|
||||||
|
raise EOFError('socket closed')
|
||||||
|
buf.append(chunk.decode('utf-8'))
|
||||||
|
data = ''.join(buf)
|
||||||
|
i = data.find('\r\n')
|
||||||
|
if i == -1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
size = int(data[:i], 16)
|
||||||
|
start = i + 2 # Skip initial \r\n
|
||||||
|
|
||||||
|
if len(data) < start + size + 2:
|
||||||
|
continue
|
||||||
|
payload = data[start:start+size]
|
||||||
|
callback(json.loads(payload))
|
||||||
|
buf = [data[start+size+2:]] # Skip \r\n suffix
|
||||||
|
|
||||||
|
|
||||||
|
def print_callback(msg):
|
||||||
|
"""Print callback, prints message to stdout as JSON in one line."""
|
||||||
|
json.dump(msg, stdout)
|
||||||
|
stdout.write('\n')
|
||||||
|
stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def prog_callback(prog, msg):
|
||||||
|
"""Program callback, calls prog with message in stdin"""
|
||||||
|
pipe = Popen(prog, stdin=PIPE)
|
||||||
|
data = json.dumps(msg)
|
||||||
|
pipe.stdin.write(data.encode('utf-8'))
|
||||||
|
pipe.stdin.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
parser = ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--prog', default=None,
|
||||||
|
help='program to call (e.g. "jq --unbuffered .")')
|
||||||
|
parser.add_argument(
|
||||||
|
'--socket-url', default=default_sock_url,
|
||||||
|
help='socket url (ipc:///path/to/sock or tcp:///host:port)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--version', help='print version and exit',
|
||||||
|
action='store_true', default=False)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
print('dockermon %s' % __version__)
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
if args.prog:
|
||||||
|
prog = shlex.split(args.prog)
|
||||||
|
callback = partial(prog_callback, prog)
|
||||||
|
else:
|
||||||
|
callback = print_callback
|
||||||
|
|
||||||
|
try:
|
||||||
|
watch(callback, args.socket_url)
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
pass
|
@ -6,7 +6,7 @@ PKG_REV="1"
|
|||||||
PKG_ARCH="any"
|
PKG_ARCH="any"
|
||||||
PKG_LICENSE="ASL"
|
PKG_LICENSE="ASL"
|
||||||
PKG_SITE="http://www.docker.com/"
|
PKG_SITE="http://www.docker.com/"
|
||||||
PKG_DEPENDS_TARGET="cli containerd ctop moby runc tini"
|
PKG_DEPENDS_TARGET="cli containerd ctop docker-compose moby runc tini"
|
||||||
PKG_SECTION="service/system"
|
PKG_SECTION="service/system"
|
||||||
PKG_SHORTDESC="Docker is an open-source engine that automates the deployment of any application as a lightweight, portable, self-sufficient container that will run virtually anywhere."
|
PKG_SHORTDESC="Docker is an open-source engine that automates the deployment of any application as a lightweight, portable, self-sufficient container that will run virtually anywhere."
|
||||||
PKG_LONGDESC="Docker containers can encapsulate any payload, and will run consistently on and between virtually any server. The same container that a developer builds and tests on a laptop will run at scale, in production*, on VMs, bare-metal servers, OpenStack clusters, public instances, or combinations of the above."
|
PKG_LONGDESC="Docker containers can encapsulate any payload, and will run consistently on and between virtually any server. The same container that a developer builds and tests on a laptop will run at scale, in production*, on VMs, bare-metal servers, OpenStack clusters, public instances, or combinations of the above."
|
||||||
@ -43,3 +43,40 @@ addon() {
|
|||||||
post_install_addon() {
|
post_install_addon() {
|
||||||
sed -e "s/@DISTRO_PKG_SETTINGS_ID@/${DISTRO_PKG_SETTINGS_ID}/g" -i "${INSTALL}/default.py"
|
sed -e "s/@DISTRO_PKG_SETTINGS_ID@/${DISTRO_PKG_SETTINGS_ID}/g" -i "${INSTALL}/default.py"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post_makeinstall_target() {
|
||||||
|
mkdir -p ${INSTALL}/etc
|
||||||
|
# docker.conf
|
||||||
|
cp -P ${PKG_DIR}/config/docker.conf ${INSTALL}/etc
|
||||||
|
|
||||||
|
mkdir -p ${INSTALL}/usr/bin
|
||||||
|
# dockermon.py
|
||||||
|
cp -P ${PKG_DIR}/lib/dockermon.py ${INSTALL}/usr/bin/dockermon
|
||||||
|
|
||||||
|
# cli
|
||||||
|
cp -P $(get_build_dir cli)/bin/docker ${INSTALL}/usr/bin
|
||||||
|
|
||||||
|
# moby
|
||||||
|
cp -P $(get_build_dir moby)/bin/dockerd ${INSTALL}/usr/bin
|
||||||
|
cp -P $(get_build_dir moby)/bin/docker-proxy ${INSTALL}/usr/bin/docker-proxy
|
||||||
|
|
||||||
|
# containerd
|
||||||
|
cp -P $(get_build_dir containerd)/bin/containerd ${INSTALL}/usr/bin/containerd
|
||||||
|
cp -P $(get_build_dir containerd)/bin/containerd-shim-runc-v2 ${INSTALL}/usr/bin/containerd-shim-runc-v2
|
||||||
|
|
||||||
|
# ctop
|
||||||
|
cp -P $(get_build_dir ctop)/bin/ctop ${INSTALL}/usr/bin/ctop
|
||||||
|
|
||||||
|
# docker-compose
|
||||||
|
cp -P $(get_build_dir docker-compose)/docker-compose ${INSTALL}/usr/bin/docker-compose
|
||||||
|
|
||||||
|
# runc
|
||||||
|
cp -P $(get_build_dir runc)/bin/runc ${INSTALL}/usr/bin/runc
|
||||||
|
|
||||||
|
# tini
|
||||||
|
cp -P $(get_build_dir tini)/.${TARGET_NAME}/tini-static ${INSTALL}/usr/bin/docker-init
|
||||||
|
}
|
||||||
|
|
||||||
|
post_install() {
|
||||||
|
enable_service docker.service
|
||||||
|
}
|
||||||
|
23
packages/addons/service/docker/system.d/docker.service
Normal file
23
packages/addons/service/docker/system.d/docker.service
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Docker Application Container Engine
|
||||||
|
Documentation=https://docs.docker.com
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=notify
|
||||||
|
EnvironmentFile=-/etc/docker.conf
|
||||||
|
ExecStart=/usr/bin/dockerd --exec-opt native.cgroupdriver=systemd \
|
||||||
|
--log-driver=journald \
|
||||||
|
--group=root \
|
||||||
|
$DOCKER_DAEMON_OPTS \
|
||||||
|
$DOCKER_STORAGE_OPTS
|
||||||
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
|
TasksMax=8192
|
||||||
|
LimitNOFILE=1048576
|
||||||
|
LimitNPROC=1048576
|
||||||
|
LimitCORE=infinity
|
||||||
|
TimeoutStartSec=0
|
||||||
|
Restart=on-abnormal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
@ -0,0 +1,4 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
# Copyright (C) 2025-present Team LibreELEC (https://libreelec.tv)
|
||||||
|
|
||||||
|
d /storage/docker 0755 root root - -
|
Loading…
x
Reference in New Issue
Block a user