containrrr / watchtower

A process for automating Docker container base image updates.
https://containrrr.dev/watchtower/
Apache License 2.0
18.94k stars 847 forks source link

Update local base images built from local Dockerfile #1824

Closed jzazo closed 9 months ago

jzazo commented 11 months ago

Is your feature request related to a problem? Please describe.

I use caddy base image inside a Dockerfile and add plugins to it. My docker compose is similar to this:

services:
  caddy:
    build: .
    restart: unless-stopped

with a local Dockerfile:

FROM caddy:builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Describe the solution you'd like

I would like to track caddy:latest and rebuild my Dockerfile image when a new image is published.

Describe alternatives you've considered

Maybe a pre-hook script on watchtower or a cron-job on building this docker image once per day or per week.

Additional context

No response

github-actions[bot] commented 11 months ago

Hi there! ๐Ÿ‘‹๐Ÿผ As you're new to this repo, we'd like to suggest that you read our code of conduct as well as our contribution guidelines. Thanks a bunch for opening your first issue! ๐Ÿ™

snakwu commented 10 months ago

I also need this feature and hope it can be implemented. Thank you

cellulosa commented 10 months ago

Same here. This is my docker-compose.yml:

version: "3.8"

services:

  watchtower:
    container_name: watchtower
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  caddy:
    container_name: caddy
    image: caddy:latest
    depends_on:
      - cloudflared
    build:
      context: .
      dockerfile: ${DOCKER_CONFIG_DIR}/caddy/Dockerfile
    volumes:
      - ${DOCKER_CONFIG_DIR}/caddy/Caddyfile:/etc/caddy/Caddyfile
      - ${DOCKER_CONFIG_DIR}/caddy:/data
    environment:
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
    ports:
      - 80:80
      - 443:443
    restart: unless-stopped

and caddy uses the following to implement some plugins:

FROM caddy:builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

which is built when running docker-compose with docker-compose up --build.

I just run watchtower for the first time and realised it updated caddy without re-building it:

INFO[86413] Found new caddy:latest image (2cbdde935029)  
INFO[86416] Stopping /caddy (9a1bebc8e378) with SIGTERM  
INFO[86417] Creating /caddy                              
INFO[86418] Session done

Which meant caddy became unusable for how I set it up.

Is there a way around it? Or should we just set watchtower to exclude caddy for the time being?

Thanks

piksel commented 10 months ago

You could use https://github.com/crazy-max/diun to watch for changes and rebuild the local image whenever caddy is updated.

Then you can just put a no-pull label on the container, which would make watchtower compare the local image with the current one:

LABEL "com.centurylinklabs.watchtower.no-pull"="true"
jzazo commented 9 months ago

Thanks for the suggestion. This has been more complicated than I expected because I wanted to run Diun from docker, but I think I managed to get it working. For reference, I added the label as you suggest to the container to monitor.

This is my diun container:

version: "3"
services:
  diun:
    container_name: diun
    image: crazymax/diun:latest
    user: "990:990"  # optional
    command: serve
    volumes:
      - /var/lib/diun:/data
      - /my-path/Dockerfile:/my-path/Dockerfile:ro
      - /my-path/pipe:/my-path/pipe
      - /my-path/launch_build.sh:/my-path/launch_build.sh:ro
    environment:
      - TZ=Europe/London
      - LOG_LEVEL=info
      - LOG_JSON=false
      - DIUN_WATCH_WORKERS=20
      - DIUN_WATCH_SCHEDULE=0 */6 * * *
      - DIUN_WATCH_JITTER=30s
      - DIUN_PROVIDERS_DOCKERFILE_PATTERNS=/my-path/Dockerfile
      - DIUN_NOTIF_SCRIPT_CMD=/my-path/launch_build.sh
    restart: unless-stopped

Note that I am running the container as user 990.

Then, I created a named pipeline as described here. This involved creating a pipe file owned by user 990, which you would need to have created first, or just run as root.

mkfifo /my-path/pipe

I also created a docker buildx script owned by root execpipe.sh

#!/bin/sh
pipe=/my-path/pipe
[ -p "$pipe" ] || mkfifo -m 0600 "$pipe" || exit 1
while :; do
    while read -r cmd; do
        if [ "$cmd" ]; then
            printf 'Running %s ...\n' "$cmd"
            docker buildx build -t local/caddy:latest -f /my-path/Dockerfile /my-path  # add tag to build & compose yml
        fi
    done <"$pipe"
done

and the command that diun executes launch_build.sh with read-only access:

#!/bin/sh
echo "docker build" > /my-path/pipe

Finally, I added the root executable to crontab with sudo crontab -e and the line:

@reboot /my-path/execpipe.sh >> /my-path/output.txt 2>&1

I have tested the pipe, the executables and tested from the container and it seems to work. I will monitor the crontab and hope it will work next time there is an update.

If anyone has suggestions on how to improve this flow, don't be shy. I have aimed not to eval any command to minimize security risks. I will leave this thread open a while longer, and if there are no better suggestions I will close the ticket.

stanthewizzard commented 1 month ago

Very clear AND complicated for prod. What would be awesom would be to have a way to use the caddy docker image watchtower it and use post update script (watchtower can do that) to install a module inside the docker

xcaddy is not included inside the docker image ? could we use xcaddy to add a module when the container is running ? thanks

stanthewizzard commented 1 month ago

answering to myself https://caddyserver.com/docs/command-line#caddy-add-package

This could be a post watchtower update script

Inside docker compose

    labels:
      - com.centurylinklabs.watchtower.lifecycle.post-update="/somewhere/wordpress.sh"

Inside the sh (for cloudflaer and exemple)

caddy add-package github.com/caddy-dns/cloudflare

jzazo commented 1 month ago

If I understand your suggestion, you aim to add caddy to the running container? That means that the changes won't be persistent, and cloudflare would only be added if updated and launched via watchtower. I think this is flimsy and prone to error?

stanthewizzard commented 1 month ago

No Iโ€™m adding to caddy the cloudflare module

the process New image Watchtower update the container (losing the module) Then after update watchtower launches a script to readd the module

I have something similar for a year with wordpress and a php module not with the official container. Works flawlessly. And you donโ€™t have to have a special build

jzazo commented 1 month ago

Sorry, I made a typo in my comment. I meant you add cloudflare to the caddy container. But you make changes when the container is running, so these changes would not be permanent (if I understand correctly how this works). So if you stop and start the container manually, wouldn't you loose the post-update changes? I believe the correct way is to rebuild the image, or am I misunderstanding something?

stanthewizzard commented 1 month ago

If add package is permanent (and it is if I understand it correctly) the script post watchtower is "permanent"

In the case of wordpress The image doesn't contain php-something. Watchtower update to the latest wordpress Then the script reinstall php-something. You have the latest wordpress with the latest php-something. The process take 1 or 2 minutes longer because of the reinstall of php-something. Works like that for more than 2 years without any issues !