From c3856292a6b9d480a4b498433f4703d9a8f1afe9 Mon Sep 17 00:00:00 2001 From: Cyril Chapellier Date: Fri, 31 May 2024 21:21:17 +0200 Subject: [PATCH] [Feature] Combined Docker image (#150) --- .github/workflows/main.yml | 38 +++++-- docker/Dockerfile-standalone | 105 ++++++++++++++++++ docker/configurations/Caddyfile | 28 +++++ .../configurations/{davis.conf => nginx.conf} | 0 docker/configurations/supervisord.conf | 31 ++++++ docker/docker-compose-postgresql.yml | 2 +- docker/docker-compose-sqlite.yml | 2 +- docker/docker-compose-standalone.yml | 48 ++++++++ docker/docker-compose.yml | 2 +- 9 files changed, 241 insertions(+), 15 deletions(-) create mode 100644 docker/Dockerfile-standalone create mode 100644 docker/configurations/Caddyfile rename docker/configurations/{davis.conf => nginx.conf} (100%) create mode 100644 docker/configurations/supervisord.conf create mode 100644 docker/docker-compose-standalone.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f53f94d..3589a0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ env: # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io # github.repository as / - IMAGE_NAME: ${{ github.repository }} + ACCOUNT: tchapi jobs: build: @@ -18,9 +18,17 @@ jobs: strategy: fail-fast: false matrix: + image: + - davis + - davis-standalone platform: - linux/amd64 - linux/arm64 + include: + - image: davis + dockerfile: docker/Dockerfile + - image: davis-standalone + dockerfile: docker/Dockerfile-standalone steps: - name: Prepare @@ -36,7 +44,7 @@ jobs: uses: docker/metadata-action@v5 with: context: git - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }} # "Push by digest" needs an untagged ref tags: | type=raw,value= @@ -63,23 +71,23 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: docker/Dockerfile + file: ${{ matrix.dockerfile }} platforms: ${{ matrix.platform }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} - outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + outputs: type=image,name=${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true - name: Export digest run: | - mkdir -p /tmp/digests + mkdir -p /tmp/digests/ digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest uses: actions/upload-artifact@v4 with: - name: digests-${{ env.PLATFORM_PAIR }} + name: digests-${{ matrix.image }}_${{ env.PLATFORM_PAIR }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -87,6 +95,12 @@ jobs: merge: name: Create merged manifest and push to Github Packages runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - davis + - davis-standalone needs: - build steps: @@ -98,8 +112,8 @@ jobs: name: Download digests uses: actions/download-artifact@v4 with: - path: /tmp/digests - pattern: digests-* + path: /tmp/digests/${{ matrix.image }}/ + pattern: digests-${{ matrix.image }}_* merge-multiple: true - name: Set up Docker Buildx @@ -115,7 +129,7 @@ jobs: uses: docker/metadata-action@v5 with: context: git - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }} tags: | type=semver,pattern={{version}} type=edge,branch=${{ github.ref_name }} @@ -128,7 +142,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Create manifest list and push - working-directory: /tmp/digests + working-directory: /tmp/digests/${{ matrix.image }}/ run: | docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ @@ -140,8 +154,8 @@ jobs: --annotation index:org.opencontainers.image.source="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.source'] }}" \ --annotation index:org.opencontainers.image.url="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.url'] }}" \ --annotation index:org.opencontainers.image.revision="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}" \ - $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + $(printf '${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} \ No newline at end of file + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.ACCOUNT }}/${{ matrix.image }}:${{ steps.meta.outputs.version }} \ No newline at end of file diff --git a/docker/Dockerfile-standalone b/docker/Dockerfile-standalone new file mode 100644 index 0000000..558d75c --- /dev/null +++ b/docker/Dockerfile-standalone @@ -0,0 +1,105 @@ +FROM php:8.2-fpm-alpine + +ENV PHP_OPCACHE_MEMORY_CONSUMPTION="256" \ + PHP_OPCACHE_MAX_WASTED_PERCENTAGE="10" + +LABEL org.opencontainers.image.authors="tchap@tchap.me" +LABEL org.opencontainers.image.url="https://github.com/tchapi/davis/pkgs/container/davis-standalone" +LABEL org.opencontainers.image.description="A simple, fully translatable admin interface for sabre/dav based on Symfony 5 and Bootstrap 4 (Standalone version with reverse-proxy)" + +# Run update, and gets basic packages and packages for runtime +RUN apk --no-progress --update add --no-cache \ + curl unzip \ + # These are for php-intl + icu-libs \ + # This one is for IMAP (to provide libc-client.so) + c-client \ + # This one for LDAP + libldap \ + # These are for GD (map image in mail) + freetype \ + libjpeg-turbo \ + libpng \ + # This is for PostgreSQL + libpq \ + # For the webserver and process manager + caddy supervisor + +# Intl support +RUN apk --update --virtual build-deps-intl add --no-cache icu-dev \ + && docker-php-ext-install intl \ + && apk del build-deps-intl \ + && rm -rf /tmp/* + +# PDO: MySQL +RUN docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \ + && docker-php-ext-install pdo_mysql + +# PDO: PostgreSQL +RUN apk --update --virtual build-deps-pg add --no-cache libpq-dev \ + && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ + && docker-php-ext-install pgsql pdo_pgsql \ + && apk del build-deps-pg \ + && rm -rf /tmp/* + +# GD (map image in mail) +RUN apk --update --virtual build-deps-gd add --no-cache freetype-dev libjpeg-turbo-dev libpng-dev \ + && docker-php-ext-configure gd --with-freetype \ + && docker-php-ext-install gd \ + && docker-php-ext-enable gd \ + && apk del build-deps-gd \ + && rm -rf /tmp/* + +# LDAP auth support +RUN apk --update --virtual build-deps-ldap add --no-cache openldap-dev \ + && docker-php-ext-configure ldap \ + && docker-php-ext-install ldap \ + && apk del build-deps-ldap \ + && rm -rf /tmp/* + +# IMAP auth support +RUN apk --update --virtual build-deps-imap add --no-cache imap-dev openssl-dev krb5-dev \ + && docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ + && docker-php-ext-install imap \ + && apk del build-deps-imap \ + && rm -rf /tmp/* + +# OPCache +RUN docker-php-ext-install opcache +COPY ./docker/configurations/opcache.ini /usr/local/etc/php/conf.d/opcache.ini + +# Davis source +# The app folder needs to be owned by www-data so PHP-fpm can execute files +ADD --chown=www-data:www-data . /var/www/davis +WORKDIR /var/www/davis + +# Install Composer 2, then dependencies +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +RUN APP_ENV=prod COMPOSER_ALLOW_SUPERUSER=1 composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader + +# Caddy: web server +RUN mkdir -p /var/log/caddy +ADD ./docker/configurations/Caddyfile /etc/caddy/Caddyfile + +# Supervisor: Process manager +RUN mkdir -p /var/log/supervisor && mkdir -p /var/log/php-fpm +ADD ./docker/configurations/supervisord.conf /etc/supervisord.conf + +# We want to use sockets inside the container between Caddy and PHP-fpm +RUN mkdir /var/run/php-fpm && chown -R www-data:www-data /var/run/php-fpm +RUN sed -i 's/listen = /;listen = /' /usr/local/etc/php-fpm.d/www.conf +RUN sed -i 's/listen = 9000/listen = \/var\/run\/php-fpm\/php-fpm.sock/' /usr/local/etc/php-fpm.d/zz-docker.conf + +RUN mkdir -p ./var/log ./var/cache && chown -R www-data:www-data ./var + +# Cleanup (only useful when using --squash) +RUN docker-php-source delete && \ + rm -rf /var/www/davis/docker + +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] + +HEALTHCHECK --interval=120s --timeout=10s --start-period=30s --retries=3 \ + CMD curl --fail http://localhost:9000 || exit 1 + +# It's the Caddy port, not the PHP-fpm one (as we use sockets) +EXPOSE 9000 \ No newline at end of file diff --git a/docker/configurations/Caddyfile b/docker/configurations/Caddyfile new file mode 100644 index 0000000..49e476a --- /dev/null +++ b/docker/configurations/Caddyfile @@ -0,0 +1,28 @@ +{ + auto_https off +} + +:9000 { + root * /var/www/davis/public + php_fastcgi unix//var/run/php-fpm/php-fpm.sock + file_server { + # Safety net, just in case + hide .git .gitignore + } + + # enable compression + encode zstd gzip + + # Remove leaky headers + header { + -Server + -X-Powered-By + + # keep referrer data off of HTTP connections + Referrer-Policy no-referrer-when-downgrade + + # disable clients from sniffing the media type + X-Content-Type-Options nosniff + } + +} \ No newline at end of file diff --git a/docker/configurations/davis.conf b/docker/configurations/nginx.conf similarity index 100% rename from docker/configurations/davis.conf rename to docker/configurations/nginx.conf diff --git a/docker/configurations/supervisord.conf b/docker/configurations/supervisord.conf new file mode 100644 index 0000000..21978f7 --- /dev/null +++ b/docker/configurations/supervisord.conf @@ -0,0 +1,31 @@ +[supervisord] +nodaemon=true +user=root +pidfile=/run/supervisord.pid +logfile=/dev/null +logfile_maxbytes=0 + +[unix_http_server] +file=/run/supervisord.sock ; the path to the socket file + +[supervisorctl] +serverurl=unix:///run/supervisord.sock ; use a unix:// URL for a unix socket + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[program:caddy] +command=/usr/sbin/caddy run -c /etc/caddy/Caddyfile +autostart=true +autorestart=true +redirect_stderr=true +stdout_logfile=/var/log/caddy/access.log +stdout_logfile_maxbytes = 0 + +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize +autostart=true +autorestart=true +redirect_stderr=true +stdout_logfile=/var/log/php-fpm/access.log +stdout_logfile_maxbytes = 0 \ No newline at end of file diff --git a/docker/docker-compose-postgresql.yml b/docker/docker-compose-postgresql.yml index c2359ee..c7c7c76 100644 --- a/docker/docker-compose-postgresql.yml +++ b/docker/docker-compose-postgresql.yml @@ -12,7 +12,7 @@ services: volumes: - davis_www:/var/www/davis - type: bind - source: ./configurations/davis.conf + source: ./configurations/nginx.conf target: /etc/nginx/conf.d/default.conf ports: - 9000:80 diff --git a/docker/docker-compose-sqlite.yml b/docker/docker-compose-sqlite.yml index 911912d..ab31298 100644 --- a/docker/docker-compose-sqlite.yml +++ b/docker/docker-compose-sqlite.yml @@ -12,7 +12,7 @@ services: volumes: - davis_www:/var/www/davis - type: bind - source: ./configurations/davis.conf + source: ./configurations/nginx.conf target: /etc/nginx/conf.d/default.conf ports: - 9000:80 diff --git a/docker/docker-compose-standalone.yml b/docker/docker-compose-standalone.yml new file mode 100644 index 0000000..a0261be --- /dev/null +++ b/docker/docker-compose-standalone.yml @@ -0,0 +1,48 @@ +version: "3.7" +name: "davis-docker" + +services: + + mysql: + image: mariadb:10.6.10 + container_name: mysql + environment: + - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USER=${DB_USER} + - MYSQL_PASSWORD=${DB_PASSWORD} + volumes: + - database:/var/lib/mysql + + davis: + build: + context: ../ + dockerfile: ./docker/Dockerfile-standalone + image: davis:latest + # If you want to use a prebuilt image from Github + # image: ghcr.io/tchapi/davis-standalone:edge + container_name: davis-standalone + environment: + - APP_ENV=prod + - DATABASE_DRIVER=mysql + - DATABASE_URL=mysql://${DB_USER}:${DB_PASSWORD}@mysql:3306/${DB_DATABASE}?serverVersion=mariadb-10.6.10&charset=utf8mb4 + - MAILER_DSN=smtp://${MAIL_USERNAME}:${MAIL_PASSWORD}@${MAIL_HOST}:${MAIL_PORT} + - ADMIN_LOGIN=${ADMIN_LOGIN} + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - AUTH_REALM=${AUTH_REALM} + - AUTH_METHOD=${AUTH_METHOD} + - CALDAV_ENABLED=${CALDAV_ENABLED} + - CARDDAV_ENABLED=${CARDDAV_ENABLED} + - WEBDAV_ENABLED=${WEBDAV_ENABLED} + - WEBDAV_TMP_DIR=${WEBDAV_TMP_DIR} + - WEBDAV_PUBLIC_DIR=${WEBDAV_PUBLIC_DIR} + - INVITE_FROM_ADDRESS=${INVITE_FROM_ADDRESS} + - APP_TIMEZONE=${TIMEZONE} + depends_on: + - mysql + ports: + - 9000:9000 + +volumes: + database: + name: database diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7c9ddc2..9c57899 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -12,7 +12,7 @@ services: volumes: - davis_www:/var/www/davis - type: bind - source: ./configurations/davis.conf + source: ./configurations/nginx.conf target: /etc/nginx/conf.d/default.conf ports: - 9000:80