Browse Source

Streamline setup process (#3)

Akaunting provides an [install](332f2cc1d8/app/Console/Commands/Install.php) artisan script that does non-interactive installation from environment variables.

This PR converts from manual setup to automated first-time setup of the first company using this script, and converts the podman scripts to use `pod`s, consolidating configuration between `docker` and `podman` to settle on always using `127.0.0.1` for the DB host. `localhost` attempts a socket connection instead of trying to resolve the hostname, and I don't think we need to mess with mounting the socket into both containers.

Documentation for initial setup has been streamlined, and the variables are more clearly documented supporting this less interactive installation/setup.
Co-Authored-By: James Harmison <jharmison@noreply.git.jharmison.com>
Co-Committed-By: James Harmison <jharmison@noreply.git.jharmison.com>
main
James Harmison 6 months ago
parent
commit
11cb6ed3bb
  1. 34
      README.md
  2. 4
      build.sh
  3. 13
      env/db.env.example
  4. 27
      env/run.env.example
  5. 33
      files/akaunting.sh
  6. 2
      files/html/setup.env
  7. 43
      podman/README.md
  8. 53
      podman/automatic.sh
  9. 16
      podman/clean.sh
  10. 14
      podman/common.sh
  11. 8
      podman/create.sh
  12. 8
      podman/rm.sh
  13. 7
      podman/start.sh
  14. 3
      podman/stop.sh

34
README.md

@ -43,9 +43,9 @@ vi env/run.env # and set things
docker-compose up -d -e AKAUNTING_SETUP=true
```
Then head to HTTP at port 8080 on the docker-compose host, configure the server through the interactive wizard. Your Database is running in a container named `akaunting-db` which you may use by DNS name in some docker configurations, though perhaps you can just use `localhost`.
Then head to HTTP at port 8080 on the docker-compose hostand finish configuring your Akaunting company through the interactive wizard.
When setup is complete, you should bounce the compose in order to drop the AKAUNTING_SETUP variables.
After setup is complete, bring the containers down before bringing them back up without the setup variable.
```shell
docker-compose down -v
@ -60,36 +60,44 @@ You can manually start the container images with `docker`, and customize them as
```shell
docker run -d -v akaunting-db:/var/lib/mysql \
-e MYSQL_DATABASE=akaunting_db \
-e MYSQL_USER=akaunting_admin \
-e MYSQL_DATABASE=akaunting \
-e MYSQL_USER=admin \
-e MYSQL_PASSWORD=akaunting_password \
-e MYSQL_RANDOM_ROOT_PASSWORD=yes \
--name akaunting-db mariadb:latest
docker run --rm -d -v akaunting-data:/var/www/html/storage \
-p 8080:80 \
-e APP_URL=https://akaunting.example.com \
-e LOCALE=en-US \
-e DB_HOST=127.0.0.1 \
-e DB_DATABASE=akaunting \
-e DB_USERNAME=admin \
-e DB_PASSWORD=akaunting_password \
-e DB_PREFIX=qwe_ \
-e COMPANY_NAME="My Company" \
-e COMPANY_EMAIL="my@company.com" \
-e ADMIN_EMAIL="me@company.com" \
-e ADMIN_PASSWORD="password" \
--name akaunting harbor.jharmison.com/akaunting/akaunting:latest --setup
```
At this point, open a browser and access the interface via HTTP at port 8080. Configure the server using the interactive wizard, possibly including database setup. It doesn't seem to follow from environment variables during setup, so I didn't bother listing them here (except the prefix, which you should choose randomly).
When setup is complete, stop the container and start a new one without the `--setup` parameter.
At this point, open a browser and access the interface via HTTP at port 8080 and finish configuring your Akaunting company through the interactive wizard. After setup is complete, bring the akaunting container down and start a new one without the `--setup` parameter.
```shell
docker stop akaunting
docker run --rm -d -v akaunting-data:/var/www/html/storage \
-p 8080:80 \
-e APP_URL=https://akaunting.example.com \
-e DB_PREFIX=qwe_ \
-e DB_HOST=localhost \
-e DB_DATABASE=akaunting_db \
-e DB_USERNAME=akaunting_admin \
-e LOCALE=en-US \
-e DB_HOST=127.0.0.1 \
-e DB_DATABASE=akaunting \
-e DB_USERNAME=admin \
-e DB_PASSWORD=akaunting_password \
-e DB_PREFIX=qwe_ \
--name akaunting harbor.jharmison.com/akaunting/akaunting:latest
```
Note that the DB_PREFIX variable should stay the same in between runs, as it is randomly generated if not provided and the table names will not align between runs if not provided causing errors.
Note that the DB_PREFIX variable should stay the same in between runs, as it is randomly generated if not provided and the table names will not align, causing errors. Also note that several variables have been left off for this run, because they are not needed for non-setup runs.
### podman
@ -97,6 +105,8 @@ Please refer to [podman/README.md](podman/README.md) for more information on pro
The use of the `podman-docker` package for your distribution or a simple `alias docker=podman` in your ~/.bashrc should enable you to line-for-line copy the `docker` instructions above, except that you must also provide an exposed port for mariadb and ensure that you configure the Akaunting server to appropriately reach the Host IP or hostname to hit the port forward. Please see [this blog post](https://www.redhat.com/sysadmin/container-networking-podman) for more information on rootless networking with podman.
Use of the scripts from the `podman` directory will create pods, instead of containers, and you will not have to expose the database and can refer to it by `127.0.0.1`.
## Building your own
If you don't want to pull my image, or you've made changes to the code and would like to build your own, `build.sh` is provided at the repository root. You can run it with the `-h` option to see the provided options. You could modify `docker-compose.yml` or `podman/common.sh` to point to your own image if you would like to use the rest of the automation provided here.

4
build.sh

@ -110,13 +110,14 @@ while [ $# -gt 0 ]; do
done
this_branch=$(git branch --show-current) || this_branch=none
this_branch_clean=$(echo "$this_branch" | tr '/' '_')
this_commit=$(git rev-parse HEAD) || this_commit=unknown
build_date=$(date -Iseconds)
registry="${registry:-$DEFAULT_REGISTRY}"
repository="${repository:-$DEFAULT_REPOSITORY}"
image="${image:-$DEFAULT_IMAGE}"
build_tag="${build_tag:-$this_branch}"
build_tag="${build_tag:-$this_branch_clean}"
if [ ${#tags[@]} -eq 0 ]; then
tags=(latest)
fi
@ -129,7 +130,6 @@ if [ "$do_push" ]; then
fi
$runtime build \
--build-arg BUILD_DATE="$build_date" \
--build-arg GIT_BRANCH="$this_branch" \
--build-arg GIT_COMMIT="$this_commit" \
. -t "$akaunting_build_tag"

13
env/db.env.example

@ -1,4 +1,9 @@
MYSQL_DATABASE=akaunting_db # This could be changed
MYSQL_USER=akaunting_admin # This could be changed
MYSQL_PASSWORD=akaunting_password # This should definitely be changed to something long and random
MYSQL_RANDOM_ROOT_PASSWORD=yes # You should probably leave this
# These could be changed
MYSQL_DATABASE=akaunting
MYSQL_USER=admin
# This should definitely be changed to something long and random
MYSQL_PASSWORD=akaunting_password
# You should probably leave this
MYSQL_RANDOM_ROOT_PASSWORD=yes

27
env/run.env.example

@ -1,7 +1,22 @@
APP_URL=https://akaunting.example.com # You should change this to match your reverse proxy DNS name and protocol
# You should change this to match your reverse proxy DNS name and protocol
APP_URL=https://akaunting.example.com
LOCALE=en-US
DB_HOST=akaunting-db # You should change this to the appropriate hostname, if needed
DB_DATABASE=akaunting_db # You should change this to match env/db.env
DB_USERNAME=akaunting_admin # You should change this to match env/db.env
DB_PASSWORD=akaunting_password # You should change this to match env/db.env
DB_PREFIX=asd_ # You should change this to a random string of three numbers or letters followed by an underscore
# Don't change this unless you are using rootless podman without pod networking
DB_HOST=127.0.0.1
# Change these to match env/db.env
DB_DATABASE=akaunting
DB_USERNAME=admin
DB_PASSWORD=akaunting_password
# You should change this to a random string of three numbers or letters followed by an underscore
DB_PREFIX=asd_
# These define the first company to exist on this instance. They are only used during setup.
COMPANY_NAME=My Company
COMPANY_EMAIL=my@company.com
# This will be the first administrative user created on setup.
ADMIN_EMAIL=me@company.com
ADMIN_PASSWORD=password

33
files/akaunting.sh

@ -23,15 +23,36 @@ while [ $# -gt 0 ]; do
shift
done
if [ "$do_setup" -o "$AKAUNTING_SETUP" == "true" ]; then
mv setup.env .env
unset APP_INSTALLED
unset APP_DEBUG
fi
mkdir -p storage/framework/{sessions,views,cache}
mkdir -p storage/app/uploads
if [ "$do_setup" -o "$AKAUNTING_SETUP" == "true" ]; then
retry_for=30
retry_interval=5
while sleep $retry_interval; do
if php artisan install \
--db-host=$DB_HOST \
--db-name=$DB_DATABASE \
--db-username=$DB_USERNAME \
"--db-password=$DB_PASSWORD" \
--db-prefix=$DB_PREFIX \
"--company-name=$COMPANY_NAME" \
"--company-email=$COMPANY_EMAIL" \
"--admin-email=$ADMIN_EMAIL" \
"--admin-password=$ADMIN_PASSWORD" \
"--locale=$LOCALE" --no-interaction; then break
else
if [ $retry_for -le 0 ]; then
echo "Unable to find database!" >&2
exit 1
fi
(( retry_for -= retry_interval ))
fi
done
else
unset COMPANY_NAME COMPANY_EMAIL ADMIN_EMAIL ADMIN_PASSWORD
fi
chmod -R u=rwX,g=rX,o=rX /var/www/html
chown -R www-data:root /var/www/html

2
files/html/setup.env

@ -1,2 +0,0 @@
LOG_CHANNEL=stderr
APP_LOCALE=en-US

43
podman/README.md

@ -11,6 +11,8 @@ git clone https://git.jharmison.com/jharmison/akaunting
cd akaunting
```
### Environment Prep
Much like the docker-compose method, env files are used for the DB and application to reduce command line clutter. You should copy the env file examples and update them according to their comments.
```shell
@ -20,53 +22,54 @@ cp env/run.env.example env/run.env
vi env/run.env # and set things
```
Other variables used in managing the Akaunting containers are shown in `podman/common.sh`. You can override the default behavior by exporting the appropriate variables. This might make sense if you want to host multiple instances or have some other conflict, for example with ports, on a given host. For example, to override the port for MariaDB, you could `export AKAUNTING_DB_PORT=3307` before execution of any of the following.
Other variables used in the scripts for managing the Akaunting containers are shown in `podman/common.sh`. You can override the default behavior by exporting the appropriate variables. This might make sense if you want to host multiple instances or have some other conflict, for example with ports, on a given host. For example, to override the port published on the host for the web application, you could `export AKAUNTING_PORT=8081` before execution of any of the following.
### Simple Use Case
### Initial Setup
If you've clone the project on the user you'd like podman to be run from, and aren't interested in automatic updates or systemd units, you can conduct initial setup of Akaunting like this:
Initial setup is required to populate the database and build the directory structure for the application out.
```shell
podman/start.sh --setup
```
Visit the host over HTTP at port 8080 and configure Akaunting through the interactive wizard. Be sure to _not_ select `localhost` as the Database hostname, because podman networking doesn't let the containers address each other as `localhost` by default, and I didn't put them in a pod together. Use the local network host name or IP address of the host you're running podman from. After setup is complete, bring the containers down and restart them again.
It will take around 5, but up to 30, seconds for the interface to be available. Visit the host over HTTP at port 8080 and finish configuring your Akaunting company through the interactive wizard. After setup is complete, bring the containers down.
```shell
podman/stop.sh
podman/start.sh
```
If you want to clean up a deployment, removing all containers and volumes, also provided is clean.sh.
### Use
#### Simple start
There are two ways to run Akaunting from these images with podman. The first is simple and will run the containers, but not update the images automatically, and not start automatically on a reboot.
```shell
podman/clean.sh
podman/start.sh
```
### Unprivileged and automatic use case
#### Automatic Updates
If you want to run Akaunting as an unprivileged user, you should ensure that you've set the environment files to a location that user can read (via the export overrides in common.sh), as well as overriding the AKAUNTING_AUTOMATIC_USER variable. If you just want to run the containers automatically, with updates, then you don't need to change those variables.
Both unprivileged user and current users will require some extra lines in the `run.env` file to get through setup. You can add them with the following (supposing the default location for environment files):
The second method will automatically update the containers, and restart them on boot. As a user who can sudo, you should run the following:
```shell
echo APP_INSTALLED=false >> env/run.env
echo APP_DEBUG=true >> env/run.env
sudo loginctl enable-linger $user # Where $user here is replaced with the username you expect to be running Akaunting as.
```
Obviously, adjust those paths if needed for unprivileged users. Then you can run the following, as a user with sudo privileges:
The user you run Akaunting with, and therefore specify on the line above, doesn't need to be the same user executing these commands, but they need to have read access to the env files and scripts. They don't need the privilege to `sudo` at all. That command simply lets them have systemd user sessions at system boot time, rather than login time.
After that, you can safely run:
```shell
podman/automatic.sh
```
You will be prompted for your sudo password, because this is necessary to enable `systemd` linger through `loginctl`. Linger is what enables systemd user units to start at system boot time, even without a login for that user. If you are running the script already as the unprivileged user you want to run the containers, then you can add the `--no-sudo` argument to prevent a sudo password prompt at all. Note that you still need to run `sudo loginctl enable-linger $user` where `$user` is the username of the unprivileged user, if you expect the services to come up at boot time.
This will create the same pods as `start.sh`, except it will also label them for automatic updating based on image tag updates, generate systemd units to start them (at boot if you enabled systemd linger), and enable the `podman-auto-update.timer` that will check for image tag updates for appropriately labelled containers at midnight.
### Cleanup
After setting Akaunting up at the wizard like in other configurations, you should edit the environment files to remove the `APP_INSTALLED` and `APP_DEBUG` lines before restarting the systemd services. You can restart the services with commands like the following:
If you want to clean up a deployment, removing all containers, pods, volumes, and systemd units, also provided is clean.sh. It will completely remove any data saved in your existing Akaunting installations, so be sure that's what you want to do.
```shell
sudo -iu $user # where $user is replaced with the actual username of the unprivileged user
XDG_RUNTIME_DIR=/run/user/$UID systemctl --user restart container-akaunting.service container-akaunting-db.service
podman/clean.sh
```
The `io.containers.autoupdate=image` label is applied to the containers and the systemd user units are configured to recreate the containers. The `podman-auto-update.timer` systemd unit is also enabled, which causes `podman` to check for updates to the images for running containers for that user, downloading the new images and recreating the containers automatically.

53
podman/automatic.sh

@ -1,52 +1,17 @@
#!/bin/bash -ex
cd "$(dirname "$(realpath "$0")")"
AKAUNTING_AUTO_UPDATE=true
. common.sh
user_do() {
if [ "$user_uid" ]; then
old_runtime_dir="$XDG_RUNTIME_DIR"
export XDG_RUNTIME_DIR=/run/user/$user_uid
fi
if [ "$NO_SUDO" ]; then
"${@}"
else
sudo -Eu $user "${@}"
fi
if [ "$old_runtime_dir" ]; then
export XDG_RUNTIME_DIR="$old_runtime_dir"
unset old_runtime_dir
fi
}
./rm.sh
./create.sh
# linger is required for user session to start w/ systemd
if echo "${*}" | grep -qF "--no-sudo"; then
export NO_SUDO=true
export XDG_RUNTIME_DIR=/run/user/$UID
user_systemd_dir="$HOME/.config/systemd/user"
else
export NO_SUDO=""
sudo loginctl enable-linger $user
user_systemd_dir=$(user_do /bin/bash -lic 'echo $HOME' 2>/dev/null)/.config/systemd/user
user_uid=$(grep "^$user:" /etc/passwd | cut -d: -f3)
fi
mkdir -p "$user_systemd_dir"
user_do podman volume create $db_vol ||:
user_do podman volume create $vol ||:
podman generate systemd --new --name --files $pod
mv "${systemd_units[@]}" "$user_systemd_dir/"
user_do podman pull $db_img
user_do podman pull $img
user_do podman rm -f $name ||:
user_do podman rm -f $db_name ||:
user_do podman create -v $db_vol:/var/lib/mysql:Z "${extra_labels[@]}" -p $db_port:3306 --env-file $db_env --name $db_name $db_img
user_do podman create -v $vol:/var/www/html/storage:Z "${extra_labels[@]}" -p $port:80 --env-file $run_env --name $name $img
user_do mkdir -p "$user_systemd_dir"
user_do podman generate systemd --new --name $db_name > "$user_systemd_dir/$db_name.service"
user_do podman generate systemd --new --name $name > "$user_systemd_dir/$name.service"
user_do systemctl --user daemon-reload
user_do systemctl --user enable $db_name.service $name.service --now
user_do systemctl --user enable podman-auto-update.timer
systemctl --user daemon-reload
systemctl --user enable "${systemd_units[@]}" --now
systemctl --user enable podman-auto-update.timer

16
podman/clean.sh

@ -3,10 +3,18 @@
cd "$(dirname "$(realpath "$0")")"
. common.sh
./stop.sh
podman rm $name
podman rm $db_name
units_changed=''
for unit in "${systemd_units[@]}"; do
if [ -f "$user_systemd_dir/$unit" ]; then
systemctl user disable --now $unit
rm -f "$user_systemd_dir/$unit"
units_changed=true
fi
done
if [ "$units_changed" ]; then
systemctl --user daemon-reload
fi
./rm.sh
podman volume rm $vol
podman volume rm $db_vol

14
podman/common.sh

@ -1,6 +1,6 @@
#!/bin/bash
pod=${AKAUNTING_POD:-akaunting-pod}
img=${AKAUNTING_IMAGE:-harbor.jharmison.com/akaunting/akaunting:latest}
db_img=${AKAUNTING_DB_IMAGE:-docker.io/library/mariadb:latest}
name=${AKAUNTING_NAME:-akaunting}
@ -8,13 +8,19 @@ db_name=${AKAUNTING_DB_NAME:-akaunting-db}
vol=${AKAUNTING_VOLUME:-akaunting-data}
db_vol=${AKAUNTING_DB_VOLUME:-akaunting-db}
port=${AKAUNTING_PORT:-8080}
db_port=${AKAUNTING_DB_PORT:-3306}
run_env=$(realpath ${AKAUNTING_RUN_ENV_FILE:-../env/run.env})
db_env=$(realpath ${AKAUNTING_DB_ENV_FILE:-../env/db.env})
if [ "${AKAUNTING_AUTO_UPDATE:-true}" ]; then
if [ "${AKAUNTING_AUTO_UPDATE}" ]; then
extra_labels=(--label io.containers.autoupdate=image)
else
extra_labels=()
fi
user=${AKAUNTING_AUTOMATIC_USER:-$USER}
# Systemd specific stuff for automatic.sh, ignored otherwise
user_systemd_dir="$HOME/.config/systemd/user"
export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/$UID}
systemd_units=(
pod-$pod.service
container-$name.service
container-$db_name.service
)

8
podman/create.sh

@ -0,0 +1,8 @@
#!/bin/bash -ex
cd "$(dirname "$(realpath "$0")")"
. common.sh
podman pod create --name $pod -p $port:80 --add-host=localhost:127.0.0.1
podman create --pod $pod -v $db_vol:/var/lib/mysql --env-file $db_env --name $db_name $db_img
podman create --pod $pod -v $vol:/var/www/html/storage --env-file $run_env --name $name $img "${@}"

8
podman/rm.sh

@ -0,0 +1,8 @@
#!/bin/bash -x
cd "$(dirname "$(realpath "$0")")"
. common.sh
./stop.sh
podman pod rm $pod

7
podman/start.sh

@ -3,5 +3,8 @@
cd "$(dirname "$(realpath "$0")")"
. common.sh
podman run -v $db_vol:/var/lib/mysql --env-file $db_env -p $db_port:3306 -d --rm --name $db_name $db_img
podman run -v $vol:/var/www/html/storage --env-file $run_env -p $port:80 -d --rm --name $name $img "${@}"
if [ ! "$(podman pod ls --filter 'name='$pod --format '{{.ID}}')" ]; then
./create.sh "${@}"
fi
podman pod start $pod

3
podman/stop.sh

@ -3,5 +3,4 @@
cd "$(dirname "$(realpath "$0")")"
. common.sh
podman stop $name
podman stop $db_name
podman pod stop $pod
Loading…
Cancel
Save