mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 23:45:44 +07:00
[IMP] runbot: pull docker image only when needed
When a lot of Docker images are updated at the same time, all runbot hosts will try to pull them at the same moment. With this commit, only the images marked as `always_pull` will be pulled and if one of them takes too much time to be pulled, we let the host make another loop turn before continuing the images pull. Finally, if a host uses the Docker registry, it will pull the remote image when running a step, that way the image will be pulled automatically if needed.
This commit is contained in:
parent
b60d54d4ea
commit
3ae3f6dff3
@ -166,7 +166,7 @@ def _docker_pull(image_tag):
|
|||||||
"""Pull a docker image from a registry.
|
"""Pull a docker image from a registry.
|
||||||
:param image_tag: the full image tag, including the registry host
|
:param image_tag: the full image tag, including the registry host
|
||||||
e.g.: `dockerhub.runbot102.odoo.com/odoo:PureNobleTest`
|
e.g.: `dockerhub.runbot102.odoo.com/odoo:PureNobleTest`
|
||||||
:return: tuple(success, image) where success is a boolean and image a Docker image object or None in case of failure
|
:return: DockerMager.result dict
|
||||||
"""
|
"""
|
||||||
with DockerManager(image_tag) as dm:
|
with DockerManager(image_tag) as dm:
|
||||||
for chunk in dm.consume(dm.docker_client.api.pull(image_tag, stream=True)):
|
for chunk in dm.consume(dm.docker_client.api.pull(image_tag, stream=True)):
|
||||||
|
@ -57,6 +57,7 @@ class DockerManager:
|
|||||||
def __exit__(self, exception_type, exception_value, exception_traceback):
|
def __exit__(self, exception_type, exception_value, exception_traceback):
|
||||||
if self.log_progress:
|
if self.log_progress:
|
||||||
_logger.info('Finished in %.2fs', self.duration)
|
_logger.info('Finished in %.2fs', self.duration)
|
||||||
|
self.result['log_progress'] = self.log_progress
|
||||||
if exception_value:
|
if exception_value:
|
||||||
self.result['success'] = False
|
self.result['success'] = False
|
||||||
_logger.warning(exception_value)
|
_logger.warning(exception_value)
|
||||||
|
@ -16,7 +16,7 @@ from psycopg2 import sql
|
|||||||
from psycopg2.extensions import TransactionRollbackError
|
from psycopg2.extensions import TransactionRollbackError
|
||||||
|
|
||||||
from ..common import dt2time, now, grep, local_pgadmin_cursor, s2human, dest_reg, os, list_local_dbs, pseudo_markdown, RunbotException, findall, sanitize, markdown_escape
|
from ..common import dt2time, now, grep, local_pgadmin_cursor, s2human, dest_reg, os, list_local_dbs, pseudo_markdown, RunbotException, findall, sanitize, markdown_escape
|
||||||
from ..container import docker_stop, docker_state, Command, docker_run
|
from ..container import docker_stop, docker_state, Command, docker_run, docker_pull
|
||||||
from ..fields import JsonDictField
|
from ..fields import JsonDictField
|
||||||
|
|
||||||
from odoo import models, fields, api
|
from odoo import models, fields, api
|
||||||
@ -853,6 +853,15 @@ class BuildResult(models.Model):
|
|||||||
if 'image_tag' not in kwargs:
|
if 'image_tag' not in kwargs:
|
||||||
kwargs.update({'image_tag': self.params_id.dockerfile_id.image_tag})
|
kwargs.update({'image_tag': self.params_id.dockerfile_id.image_tag})
|
||||||
self._log('Preparing', 'Using Dockerfile Tag [%s](/runbot/dockerfile/tag/%s)', kwargs['image_tag'], kwargs['image_tag'], log_type='markdown')
|
self._log('Preparing', 'Using Dockerfile Tag [%s](/runbot/dockerfile/tag/%s)', kwargs['image_tag'], kwargs['image_tag'], log_type='markdown')
|
||||||
|
icp = self.env['ir.config_parameter']
|
||||||
|
docker_registry_host = self.env['runbot.host'].browse(int(icp.get_param('runbot.docker_registry_host_id', default=0)))
|
||||||
|
if docker_registry_host and self.host_id.use_remote_docker_registry:
|
||||||
|
result = docker_pull(f"dockerhub.{docker_registry_host.name}/{kwargs['image_tag']}")
|
||||||
|
if result['success']:
|
||||||
|
result['image'].tag(kwargs['image_tag'])
|
||||||
|
if result.get('log_progress'):
|
||||||
|
self._log('Docker Run', f'Docker image was pulled {"" if result["success"] else "with errors"}')
|
||||||
|
|
||||||
containers_memory_limit = self.env['ir.config_parameter'].sudo().get_param('runbot.runbot_containers_memory', 0)
|
containers_memory_limit = self.env['ir.config_parameter'].sudo().get_param('runbot.runbot_containers_memory', 0)
|
||||||
if containers_memory_limit and 'memory' not in kwargs:
|
if containers_memory_limit and 'memory' not in kwargs:
|
||||||
kwargs['memory'] = int(float(containers_memory_limit) * 1024 ** 3)
|
kwargs['memory'] = int(float(containers_memory_limit) * 1024 ** 3)
|
||||||
|
@ -128,6 +128,7 @@ class Dockerfile(models.Model):
|
|||||||
arch_base = fields.Text(related='template_id.arch_base', readonly=False, related_sudo=True)
|
arch_base = fields.Text(related='template_id.arch_base', readonly=False, related_sudo=True)
|
||||||
dockerfile = fields.Text(compute='_compute_dockerfile', tracking=True)
|
dockerfile = fields.Text(compute='_compute_dockerfile', tracking=True)
|
||||||
to_build = fields.Boolean('To Build', help='Build Dockerfile. Check this when the Dockerfile is ready.', default=False)
|
to_build = fields.Boolean('To Build', help='Build Dockerfile. Check this when the Dockerfile is ready.', default=False)
|
||||||
|
always_pull = fields.Boolean('Always pull', help='Always Pull on the hosts, not only at the use time', default=False, tracking=True)
|
||||||
version_ids = fields.One2many('runbot.version', 'dockerfile_id', string='Versions')
|
version_ids = fields.One2many('runbot.version', 'dockerfile_id', string='Versions')
|
||||||
description = fields.Text('Description')
|
description = fields.Text('Description')
|
||||||
view_ids = fields.Many2many('ir.ui.view', compute='_compute_view_ids', groups="runbot.group_runbot_admin")
|
view_ids = fields.Many2many('ir.ui.view', compute='_compute_view_ids', groups="runbot.group_runbot_admin")
|
||||||
|
@ -130,11 +130,16 @@ class Host(models.Model):
|
|||||||
all_tags = set(all_docker_files.mapped('image_tag'))
|
all_tags = set(all_docker_files.mapped('image_tag'))
|
||||||
if docker_registry_host and self.use_remote_docker_registry and not is_registry:
|
if docker_registry_host and self.use_remote_docker_registry and not is_registry:
|
||||||
_logger.info('Pulling docker images...')
|
_logger.info('Pulling docker images...')
|
||||||
for dockerfile in all_docker_files:
|
total_duration = 0
|
||||||
|
for dockerfile in all_docker_files.filtered('always_pull'):
|
||||||
remote_tag = f'dockerhub.{docker_registry_host.name}/{dockerfile.image_tag}'
|
remote_tag = f'dockerhub.{docker_registry_host.name}/{dockerfile.image_tag}'
|
||||||
result = docker_pull(remote_tag)
|
result = docker_pull(remote_tag)
|
||||||
if result['success']:
|
if result['success']:
|
||||||
result['image'].tag(dockerfile.image_tag)
|
result['image'].tag(dockerfile.image_tag)
|
||||||
|
total_duration += result['duration']
|
||||||
|
if total_duration > 60:
|
||||||
|
_logger.warning("Pulling images took more than 60 seconds... will continue later")
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
_logger.info('Building docker images...')
|
_logger.info('Building docker images...')
|
||||||
for dockerfile in self.env['runbot.dockerfile'].search([('to_build', '=', True)]):
|
for dockerfile in self.env['runbot.dockerfile'].search([('to_build', '=', True)]):
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="image_tag"/>
|
<field name="image_tag"/>
|
||||||
<field name="to_build"/>
|
<field name="to_build"/>
|
||||||
|
<field name="always_pull"/>
|
||||||
<field name="version_ids" widget="many2many_tags"/>
|
<field name="version_ids" widget="many2many_tags"/>
|
||||||
<field name="project_ids" widget="many2many_tags"/>
|
<field name="project_ids" widget="many2many_tags"/>
|
||||||
<field name="template_id"/>
|
<field name="template_id"/>
|
||||||
@ -104,6 +105,8 @@
|
|||||||
<field name="image_tag"/>
|
<field name="image_tag"/>
|
||||||
<field name="to_build" groups="!runbot.group_runbot_admin"/>
|
<field name="to_build" groups="!runbot.group_runbot_admin"/>
|
||||||
<field name="to_build" widget="boolean_toggle" groups="runbot.group_runbot_admin"/>
|
<field name="to_build" widget="boolean_toggle" groups="runbot.group_runbot_admin"/>
|
||||||
|
<field name="always_pull" groups="!runbot.group_runbot_admin"/>
|
||||||
|
<field name="always_pull" widget="boolean_toggle" groups="runbot.group_runbot_admin"/>
|
||||||
<field name="version_ids" widget="many2many_tags"/>
|
<field name="version_ids" widget="many2many_tags"/>
|
||||||
<field name="project_ids" widget="many2many_tags"/>
|
<field name="project_ids" widget="many2many_tags"/>
|
||||||
<field name="use_count"/>
|
<field name="use_count"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user