diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml new file mode 100644 index 0000000..7ff5636 --- /dev/null +++ b/.github/workflows/ghcr.yml @@ -0,0 +1,39 @@ +name: Build and publish to GHCR + +on: + push: + branches: + - master + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: akaunting/docker/akaunting + +jobs: + build-and-push: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: | + ghcr.io/akaunting/docker/akaunting:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index da86e7e..8f7ace5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,10 +45,19 @@ RUN docker-php-ext-configure gd \ pdo_mysql \ zip +RUN pecl install redis \ + && docker-php-ext-enable redis + RUN mkdir -p /var/www/akaunting \ - && curl -Lo /tmp/akaunting.zip 'https://akaunting.com/download.php?version=latest&utm_source=docker&utm_campaign=developers' \ - && unzip /tmp/akaunting.zip -d /var/www/html \ - && rm -f /tmp/akaunting.zip + && VERSION=$(curl -fsSL https://api.github.com/repos/akaunting/akaunting/releases/latest \ + | grep '"tag_name"' \ + | head -1 \ + | cut -d '"' -f4) \ + && echo "Downloading Akaunting ${VERSION}" \ + && curl -fLo /tmp/akaunting.zip \ + "https://github.com/akaunting/akaunting/releases/download/${VERSION}/Akaunting_${VERSION#v}-Stable.zip" \ + && unzip /tmp/akaunting.zip -d /var/www/html \ + && rm -f /tmp/akaunting.zip COPY files/akaunting.sh /usr/local/bin/akaunting.sh COPY files/html /var/www/html diff --git a/README.md b/README.md index e291143..c07f6c1 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ AKAUNTING_SETUP=true docker-compose -f fpm-alpine-nginx-docker-compose.yml -f fp # Download Akaunting using git and install composer and npm and run Akaunting using FPM on Alpine and using Nginx as internal proxy and supervisor to manage the queues AKAUNTING_SETUP=true docker-compose -f fpm-alpine-nginx-docker-compose.yml -f fpm-alpine-nginx-composer-supervisor-docker-compose.yml up --build ``` +## Kubernetes +See kubernetes folder ## License diff --git a/docker-compose.yml b/docker-compose.yml index 5adb7c9..91bff1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,8 @@ services: akaunting: container_name: akaunting - image: docker.io/akaunting/akaunting:latest + #image: docker.io/akaunting/akaunting:latest + image: ghcr.io/akaunting/docker/akaunting:latest build: context: . ports: diff --git a/files/akaunting.sh b/files/akaunting.sh index 3711072..6595151 100755 --- a/files/akaunting.sh +++ b/files/akaunting.sh @@ -5,6 +5,7 @@ a2enmod rewrite do_start= do_shell= do_setup= +do_queue_worker= while [ $# -gt 0 ]; do case "$1" in @@ -19,14 +20,23 @@ while [ $# -gt 0 ]; do do_setup=true do_start=true ;; + --queue-worker) + do_start=false + do_queue_worker=true + ;; esac shift done +if [ "$do_queue_worker" ]; then + PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-256M}" + exec php -d memory_limit="$PHP_MEMORY_LIMIT" artisan queue:work redis --queue=imports,imports:notify,default --verbose --tries=3 --timeout=90 --sleep=3 +fi + mkdir -p storage/framework/{sessions,views,cache} mkdir -p storage/app/uploads -if [ "$do_setup" -o "$AKAUNTING_SETUP" == "true" ]; then +if [ -z "$do_queue_worker" ] && [ "$do_setup" -o "$AKAUNTING_SETUP" == "true" ]; then retry_for=30 retry_interval=5 while sleep $retry_interval; do diff --git a/kubernetes/README.md b/kubernetes/README.md new file mode 100644 index 0000000..13ea46d --- /dev/null +++ b/kubernetes/README.md @@ -0,0 +1,51 @@ +## Setup behavior +Running each deployment on different nodes can cause performance issues, best to pin akaunting, mariadb and redis to the same node. Get the database and redis running first. + +In the akaunting.yaml, set to install mode in both the akaunting and akaunting-worker containers: + +```yaml +- name: APP_INSTALLED + value: "false" +- name: AKAUNTING_SETUP + value: "true" +``` + +The setup can take a while, so watch the logs until it completes. This should take you to the the language and database forms. These should be filled with whatever you put in the yaml. At this point scale the deployment to 0 and edit the yaml... + +```yaml +- name: APP_INSTALLED + value: "true" +- name: AKAUNTING_SETUP + value: "false" +``` + +3. apply the manifest again with and scale back up to 1. + +After the app comes back up, you should land on the login screen. After logging in, Akaunting will prompt for the API key, remaining business details such as currency and address information. + +## TRAEFIK +If you get problems with the container creating HTTP links when HTTPS are needed... this should fix it... + +`sudo vim /var/lib/rancher/k3s/server/manifests/traefik-config.yaml` + +```yaml +apiVersion: helm.cattle.io/v1 +kind: HelmChartConfig +metadata: + name: traefik + namespace: kube-system +spec: + valuesContent: |- + ports: + web: + forwardedHeaders: + insecure: true + websecure: + forwardedHeaders: + insecure: true +``` +It's best to do this before running the yamls. + +## License + +This project is released under the GPLv3 license. See [LICENSE.txt](LICENSE.txt). diff --git a/kubernetes/akaunting.yaml b/kubernetes/akaunting.yaml new file mode 100644 index 0000000..69a246e --- /dev/null +++ b/kubernetes/akaunting.yaml @@ -0,0 +1,181 @@ +apiVersion: v1 +kind: Secret +metadata: + name: akaunting-secrets +type: Opaque +stringData: + ADMIN_PASSWORD: Change-me + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: akaunting +spec: + replicas: 1 + selector: + matchLabels: + app: akaunting + template: + metadata: + labels: + app: akaunting + spec: + nodeSelector: + kubernetes.io/hostname: node_1 + + containers: + +# Akaunting container + - name: akaunting + image: ghcr.io/akaunting/docker/akaunting:latest + env: + - name: APP_INSTALLED + value: "false" + - name: AKAUNTING_SETUP + value: "true" + - name: APP_URL + value: "https://akaunting.mydomain.com" + - name: LOCALE + value: "en-GB" + - name: DB_HOST + value: "mariadb" + - name: DB_PORT + value: "3306" + - name: DB_NAME + value: "akaunting" + - name: DB_DATABASE + value: "akaunting" + - name: DB_USERNAME + value: "akaunting" + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: mariadb-secret + key: akaunting-password + - name: DB_PREFIX + value: "asd_" + - name: PHP_MEMORY_LIMIT + value: "256M" + + - name: COMPANY_NAME + value: "Business Name" + - name: COMPANY_EMAIL + value: "contact@mydomain.com" + - name: ADMIN_EMAIL + value: "admin@mydomain.com" + - name: ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: akaunting-secrets + key: ADMIN_PASSWORD + + # Redis + - name: REDIS_HOST + value: "redis" + - name: REDIS_PORT + value: "6379" + - name: REDIS_DB + value: "1" + - name: REDIS_DATABASE + value: "1" + - name: CACHE_DRIVER + value: "redis" + - name: SESSION_DRIVER + value: "redis" + - name: QUEUE_CONNECTION + value: "redis" + + volumeMounts: + - name: storage + mountPath: /var/www/html/storage + +# Worker container + - name: akaunting-worker + image: ghcr.io/akaunting/docker/akaunting:latest + args: + - --queue-worker + env: + - name: APP_INSTALLED + value: "false" + - name: AKAUNTING_SETUP + value: "true" + - name: APP_URL + value: "https://akaunting.mydomain.com" + - name: LOCALE + value: "en-GB" + - name: DB_HOST + value: "mariadb" + - name: DB_PORT + value: "3306" + - name: DB_NAME + value: "akaunting" + - name: DB_DATABASE + value: "akaunting" + - name: DB_USERNAME + value: "akaunting" + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: mariadb-secret + key: akaunting-password + - name: DB_PREFIX + value: "asd_" + + # Redis + - name: REDIS_HOST + value: "redis" + - name: REDIS_PORT + value: "6379" + - name: REDIS_DB + value: "1" + - name: REDIS_DATABASE + value: "1" + - name: CACHE_DRIVER + value: "redis" + - name: SESSION_DRIVER + value: "redis" + - name: QUEUE_CONNECTION + value: "redis" + - name: PHP_MEMORY_LIMIT + value: "256M" + + volumeMounts: + - name: storage + mountPath: /var/www/html/storage + + volumes: + - name: storage + hostPath: + path: /path/to/akaunting-data/storage + type: DirectoryOrCreate + +--- + +apiVersion: v1 +kind: Service +metadata: + name: akaunting +spec: + selector: + app: akaunting + ports: + - port: 80 + targetPort: 80 + +--- + +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: akaunting +spec: + entryPoints: + - web + routes: + - match: Host(`akaunting.mydomain.com`) + kind: Rule + services: + - name: akaunting + port: 80 \ No newline at end of file diff --git a/kubernetes/mariadb.yaml b/kubernetes/mariadb.yaml new file mode 100644 index 0000000..e9db75d --- /dev/null +++ b/kubernetes/mariadb.yaml @@ -0,0 +1,85 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mariadb-secret +type: Opaque +stringData: + root-password: "change-me" + akaunting-password: "change-me" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mariadb +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: mariadb + template: + metadata: + labels: + app: mariadb + spec: + nodeSelector: + kubernetes.io/hostname: node_1 + + containers: + - name: mariadb + image: mariadb:11.4 + imagePullPolicy: IfNotPresent + + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mariadb-secret + key: root-password + + - name: MARIADB_DATABASE + value: akaunting + + - name: MARIADB_USER + value: akaunting + + - name: MARIADB_PASSWORD + valueFrom: + secretKeyRef: + name: mariadb-secret + key: akaunting-password + + ports: + - containerPort: 3306 + + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + + volumeMounts: + - name: mariadb-data + mountPath: /var/lib/mysql + + volumes: + - name: mariadb-data + hostPath: + path: /path/to/mariadb/akaunting + type: DirectoryOrCreate + +--- +apiVersion: v1 +kind: Service +metadata: + name: mariadb +spec: + selector: + app: mariadb + ports: + - port: 3306 + targetPort: 3306 \ No newline at end of file diff --git a/kubernetes/redis.yaml b/kubernetes/redis.yaml new file mode 100644 index 0000000..b314d50 --- /dev/null +++ b/kubernetes/redis.yaml @@ -0,0 +1,49 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + nodeSelector: + kubernetes.io/hostname: node_1 + + containers: + - name: redis + image: redis:8-alpine + imagePullPolicy: IfNotPresent + args: + - redis-server + - "--maxmemory" + - "256mb" + - "--maxmemory-policy" + - "allkeys-lru" + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi + ports: + - containerPort: 6379 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + selector: + app: redis + ports: + - port: 6379 + targetPort: 6379 \ No newline at end of file