Skip to content

Commit 3c56cc1

Browse files
committed
Add Dockerfile, docs, and ci check.
1 parent 5b2a9c5 commit 3c56cc1

11 files changed

+229
-12
lines changed

.dockerignore

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Only include the lute/ folder.
2+
#
3+
# On mac, to find all directories and sizes:
4+
# du -hd 1
5+
#
6+
.git/
7+
.pytest_cache/
8+
.venv/
9+
__pycache__/
10+
dist/
11+
htmlcov/
12+
tests/
13+
utils/
14+
data/

.github/workflows/ci.yml

+25-2
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,35 @@ jobs:
100100
# Verify with utils script back in the workspace
101101
# (verify.py is not included in flit install)
102102
pushd ${{ github.workspace }}
103-
python -m utils.verify
103+
python -m utils.verify 5000
104104
popd
105105
106106
pkill -f "python -m lute.main" # Kill that process.
107107
108108
109+
# Test build docker container and try running.
110+
# Slightly wasteful re-setup of node.
111+
docker-build:
112+
runs-on: ubuntu-latest
113+
needs: base-ci
114+
steps:
115+
- uses: actions/checkout@v3
116+
- uses: actions/setup-python@v4
117+
with:
118+
python-version: 3.11
119+
cache: 'pip' # caching pip dependencies
120+
- run: pip install -r requirements.txt
121+
- name: Test docker build
122+
run: |
123+
docker build -f docker/Dockerfile --build-arg INSTALL_MECAB=false -t lute3 .
124+
# Run container in the background, and check.
125+
docker run -d -p 5000:5000 -v ./my_data:/lute_data -v ./my_backups:/lute_backup --name my-lute lute3:latest
126+
sleep 10 # Give it a moment to start.
127+
python -m utils.verify 5000
128+
docker stop my-lute
129+
docker rm my-lute
130+
131+
109132
# Generate a coverage badge, don't worry if it fails.
110133
# Uses https://github.com/Schneegans/dynamic-badges-action to update a secret gist
111134
# (ID a15001ec2ff889f7be0b553df9881566) and an API token, per notes at
@@ -235,4 +258,4 @@ jobs:
235258
- name: Verify flit install
236259
run: |
237260
cd ${{ github.workspace }}
238-
python -m utils.verify
261+
python -m utils.verify 5000

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ lute/config/config.yml
66
*.ini
77
*.ini.bkp
88

9+
docker/docker-compose.yml
10+
docker-compose.yml
11+
912
# user data
1013
data/
1114

docker/Dockerfile

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Official python base image.
2+
FROM python:3.11-slim-bookworm
3+
4+
# Define a build argument with a default value.
5+
ARG INSTALL_MECAB=false
6+
7+
# Install mecab for Japanese support if INSTALL_MECAB is true, e.g.
8+
# docker build --build-arg INSTALL_MECAB=true -t lute3 .
9+
RUN if [ "$INSTALL_MECAB" = "true" ]; then \
10+
apt-get update -y && \
11+
apt-get install -y mecab mecab-ipadic-utf8 && \
12+
apt-get clean && rm -rf /var/lib/apt/lists/*; \
13+
fi
14+
15+
# Lute code and config.
16+
COPY requirements.txt .
17+
ENV PIP_ROOT_USER_ACTION=ignore
18+
RUN pip install -r requirements.txt
19+
COPY lute /lute
20+
RUN mv /lute/config/config.yml.docker /lute/config/config.yml
21+
22+
EXPOSE 5000
23+
24+
# Start script.
25+
COPY docker/check_mounts_and_start.sh /lute/start.sh
26+
RUN chmod +x /lute/start.sh
27+
ENTRYPOINT ["/lute/start.sh"]

docker/README.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# The docker container
2+
3+
The Dockerfile can be used to create two variants: one with mecab and dictionary (800+ MB) , and one without (300 MB).
4+
5+
## Building
6+
7+
Sample build, run from the root folder. `INSTALL_MECAB` is `true` or `false`
8+
9+
```
10+
docker build -f docker/Dockerfile --build-arg INSTALL_MECAB=false -t lute3 .
11+
```
12+
13+
## Starting
14+
15+
The Docker image is configured to write to directories in the container which must be mounted from the host:
16+
17+
* `/lute_data`: the database, user images, etc.
18+
* `/lute_backup`: your backups
19+
20+
If these directories are not mounted, the container will not start.
21+
22+
### From the command line:
23+
24+
```
25+
docker run -p 5000:5000 -v ./my_data:/lute_data -v ./my_backups:/lute_backup --name my-lute lute3:latest
26+
```
27+
28+
The above:
29+
30+
* runs the container from the lute3:latest image
31+
* exposes port 5000 on the host (so localhost:5000 works)
32+
* mounts the necessary directories
33+
* names the container "my-lute".
34+
35+
### From a compose file
36+
37+
```
38+
# Sample docker-compose.yml file
39+
version: '3.9'
40+
services:
41+
lute:
42+
image: lute3:latest
43+
name: my-lute
44+
ports:
45+
- 5000:5000
46+
volumes:
47+
- ./my_data:/lute_data
48+
- ./my_backups:/lute_backup
49+
```
50+
51+
With that file in some location, you could do:
52+
53+
```
54+
docker compose up -d
55+
```
56+
57+
A `docker-compose.yml.example' is provided as a starting point in this directory -- copy that to a docker-compose.yml file in project root and use it:
58+
59+
```
60+
docker compose up -d
61+
62+
python -m utils.verify
63+
64+
docker compose stop lute
65+
```
66+
67+
## Stopping
68+
69+
If you started the container from the command line, you'll need to get the container ID, and then stop it.
70+
71+
```
72+
docker ps # lists all running IDs
73+
docker stop <containerID> # ID found in prev. command
74+
```
75+
76+
If you started it with `docker compose`, you can do
77+
78+
```
79+
docker compose stop
80+
```

docker/check_mounts_and_start.sh

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/sh
2+
#
3+
# Check that the folders given in the
4+
# lute/config/config.yml.docker are in fact mounted.
5+
6+
for d in /lute_data /lute_backup; do
7+
checkdir=`mount | grep "$d"`
8+
if [ -z "$checkdir" ]
9+
then
10+
echo ""
11+
echo "-------------------------------------------------------"
12+
echo "$d container directory is not mounted, quitting."
13+
echo ""
14+
echo "Lute (containerized) writes to /lute_data and lute_backup."
15+
echo "BOTH of these must be mounted."
16+
echo ""
17+
echo "If these folders are not mounted from host directories,"
18+
echo "then the writes will go to the container's writable layer,"
19+
echo "and would be destroyed if the container were deleted."
20+
echo "That would mean loss of data, so this check prevents it."
21+
echo ""
22+
echo "Please ensure you mount a host directory,"
23+
echo "either in your docker-compose.yml or in your docker run."
24+
echo "-------------------------------------------------------"
25+
echo ""
26+
exit
27+
fi
28+
done
29+
30+
python -m lute.main

docker/docker-compose.yml.example

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Sample docker-compose.yml file.
2+
#
3+
version: '3.9'
4+
services:
5+
lute:
6+
image: lute3:latest
7+
ports:
8+
- 5000:5000
9+
volumes:
10+
# Note: you should change these directories
11+
# to absolute paths (e.g. "/usr/yourname/etc/my_data")
12+
- ./my_data:/lute_data
13+
- ./my_backups:/lute_backup

docs/development.md

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ Todos are in the code as comments, e.g. `# TODO [<group name>:] detail`, `<!-- T
9999
`inv todos` collects all of these in a simple report.
100100

101101

102+
# Docker
103+
104+
Notes for building and running a Docker container are at ../docker/README.com.
105+
102106
# Misc dev notes
103107

104108
## read-only db during tests

lute/config/config.yml.docker

+13-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,18 @@ DATAPATH: /lute_data
1010
BACKUP_PATH: /lute_backup
1111

1212
# Value to set in environment MECAB_PATH.
13-
# _May_ be needed for docker, if natto-py
14-
# can't find it automatically in the base image.
15-
# MECAB_PATH: xyz
13+
# The current build of the container can't find mecab,
14+
# so we have to set it here.
15+
#
16+
# To find the correct path, first build and run the container,
17+
# then connect to it, and find libmecab.so.2 like this:
18+
#
19+
# $ docker exec -it lute_v3-lute-1 bash
20+
# root@cid/# which mecab
21+
# /usr/bin/mecab
22+
# root@cid:/# ldd /usr/bin/mecab
23+
# ...
24+
# libmecab.so.2 => /lib/aarch64-linux-gnu/libmecab.so.2 (0x0000ffff9b540000)
25+
MECAB_PATH: /lib/aarch64-linux-gnu/libmecab.so.2
1626

1727
IS_DOCKER: true

lute/main.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,20 @@ def start():
5858
Running at:
5959
6060
http://localhost:{port}
61+
"""
62+
)
6163

64+
close_msg = """
6265
When you're finished reading, stop this process
6366
with Ctrl-C or your system equivalent.
6467
"""
65-
)
68+
if app_config.is_docker:
69+
close_msg = """
70+
When you're finished reading, stop this container
71+
with Ctrl-C, docker compose stop, or docker stop <containerid>
72+
as appropriate.
73+
"""
74+
_print(close_msg)
6675

6776
serve(app, host="0.0.0.0", port=port)
6877

utils/verify.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55
66
Usage:
77
8-
python -m utils.verify
8+
python -m utils.verify [port]
99
"""
1010

11+
import sys
1112
import json
1213
import requests
13-
from lute.config.app_config import AppConfig
1414

1515

16-
def verify():
16+
def verify(port):
1717
"""
1818
Check the /info page.
1919
"""
2020

21-
app_config = AppConfig.create_from_config()
22-
port = app_config.port
2321
url = f"http://localhost:{port}/info"
2422
resp = requests.get(url, timeout=5)
2523
c = resp.status_code
@@ -32,4 +30,10 @@ def verify():
3230

3331

3432
if __name__ == "__main__":
35-
verify()
33+
useport = None
34+
if len(sys.argv) == 2:
35+
useport = sys.argv[1]
36+
else:
37+
print("Must supply port as argument")
38+
sys.exit(1)
39+
verify(useport)

0 commit comments

Comments
 (0)