Skip to content

Commit 8e6cc16

Browse files
committed
updates to help people understand how to run background processes in Docker
1 parent d0dd362 commit 8e6cc16

File tree

5 files changed

+170
-81
lines changed

5 files changed

+170
-81
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ RUN set -x \
3232
COPY monit.d/ /etc/monit.d/
3333
COPY docker-entrypoint.sh /docker-entrypoint.sh
3434
COPY rclone.sh /rclone.sh
35+
COPY rcron.sh /usr/bin/rcron
3536
COPY env_secrets.sh /env_secrets.sh
37+
RUN chmod +x /docker-entrypoint.sh /usr/bin/rcron
3638

3739
ENTRYPOINT ["/docker-entrypoint.sh"]
3840
CMD [""]

README.md

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,30 @@ First, pull the latest docker image:
5454
```bash
5555
docker pull openbridge/ob_bulkstash
5656
```
57-
This will pull the latest version by default. However, as part of the `hooks/build` process we publish a number of older versions of rclone. If you want to see the available versions, check out Docker Hub [`openbridge/ob_bulkstash`](https://hub.docker.com/r/openbridge/ob_bulkstash/tags/). For example, if you wanted to run version `1.19`, then pull that version like this:
57+
This will pull the latest version by default. However, as part of the `hooks/build` process we publish a number of older versions of rclone. If you want to see the available versions, check out Docker Hub [`openbridge/ob_bulkstash`](https://hub.docker.com/r/openbridge/ob_bulkstash/tags/). For example, if you wanted to run version `1.47`, then pull that version like this:
5858

5959
```bash
60-
docker pull openbridge/ob_bulkstash:1.46
60+
docker pull openbridge/ob_bulkstash:1.47.0
6161
```
6262
Additional pre-built versions are tagged and available for use: https://hub.docker.com/r/openbridge/ob_bulkstash/tags/
6363

6464
If you want to build your own image, you need to pass the version you want to use:
6565
```bash
66-
docker build --build-arg RCLONE_VERSION=1.46 -t openbridge/ob_bulkstash:1.46 .
67-
docker build --build-arg RCLONE_VERSION=1.46 -t openbridge/ob_bulkstash:latest .
66+
docker build --build-arg RCLONE_VERSION=1.47.0 -t openbridge/ob_bulkstash:1.46 .
67+
docker build --build-arg RCLONE_VERSION=1.47.0 -t openbridge/ob_bulkstash:latest .
6868
```
6969
Got your version setup? Great. Next, we need to define a configuration for remote storage locations. The following demonstrates how to sync Amazon and Google cloud storages.
7070

71+
### Testing Your Build
72+
The validate that your build worked correctly, you can run a simple check which will have rclone echo the version back to you.
73+
74+
If you run this `docker run openbridge/ob_bulkstash rclone -V` you should see the version displayed like this:
75+
```Bash
76+
rclone v1.47.0
77+
- os/arch: linux/amd64
78+
- go version: go1.12.4
79+
```
80+
If you see this, success! Your image is ready to go!
7181

7282
## Amazon and Google Examples
7383
In our example we have a source of files at Amazon S3 and destination for those files at Google Cloud Storage location. This means we will need to set the configuration ENV variables for source and destination.
@@ -124,7 +134,7 @@ With your config setup, now you can run `rclone`!
124134

125135
This is an example Docker `RUN` command
126136
```bash
127-
docker run \
137+
docker run openbridge/ob_bulkstash \
128138
--env-file env/sample.env \
129139
rclone copy MYS3:myawsbucket/path/to/file/ MYGS:mygooglebucket/path/to/files/
130140
```
@@ -152,6 +162,10 @@ for i in ./env/*.env; do
152162
docker run -v /my/volume:/data -it --env-file ${i} openbridge/ob_bulkstash rclone copy MYS3:myawsbucket/path/to/file/ MYGS:mygooglebucket/path/to/files/
153163
done
154164
```
165+
Earlier we showed a simple rclone command to echo the version number:
166+
```bash
167+
docker run openbridge/ob_bulkstash rclone -V
168+
```
155169

156170
### Using the Google AUTH file
157171
Here is an example that mounts the Google auth file needed for service level accounts:
@@ -165,7 +179,17 @@ done
165179
# Using `crond` Inside Docker
166180
If you want to persist your container you can set it up to always be running with `crond` as a background process. While most everything is automated there are a few configuration items you need to set.
167181

168-
## Bring Your Own Crontab Configuration
182+
**IMPORTANT**: This assumes you have a basic understanding of Docker and background processes. If you do not know what `--detach , -d` means then please review the Docker docs about running in detached mode (hint: this is how you run things in the background)
183+
184+
## Runtime Environment
185+
Depending on your use of `CROND`, it may not have access to the OS defined `ENV` variables. As a convenience, the image will output these to a file:
186+
187+
```bash
188+
printenv | sed 's/^\([a-zA-Z0-9_]*\)=\(.*\)$/export \1="\2"/g' | grep -E "^export RCLONE" > /cron/rclone.env
189+
```
190+
If needed, you can then import these variables into any scripts that you want to run in the container such as using something like `source /cron/rclone.env`.
191+
192+
## Option 1: Bring Your Own Crontab Configuration
169193

170194
### Step 1: Setup your `crontab.conf` config
171195
Running `crond` requires a proper configuration file. You can easily add a crontab config file and have the container use it. A `crontab.conf` should look something like this:
@@ -209,12 +233,84 @@ RCLONE_CRONFILE=/cron/crontab.conf
209233
**This is the location in your container, not the host**
210234

211235

212-
## Using `/rclone.sh` and `crond` Inside Docker
236+
## Option 2: Automatic Generation `RCLONE_CRONFILE`
237+
238+
You can let the image generate and run a command for you under `CROND`.
239+
This is geared to running a single `CROND` task. If you want to run multiple tasks, it is best to choose **Option 1** which allows you more control over the number of tasks run.
213240

214-
Included in the image is a utility script that will run `rclone copy` and `rclone move`. It will also has a `foldersize` check in the event you want to trigger a `rclone move`. How is this helpful? If your disk is getting full this can trigger what amounts to be a cleanup task.
241+
### Setting your `CROND` command
242+
In your ENV, you need to set the desired command via `RCLONE_SYNC_COMMAND`. Here is an example command:
243+
244+
```bash
245+
docker run -d -e RCLONE_SYNC_COMMAND="*/15 * * * * /usr/bin/env bash -c /foo run" openbridge/ob_bulkstash crond -f
246+
```
247+
This will result in your container running in the detached mode (in the background) with a `CROND` entry like this:
248+
```bash
249+
SHELL=/bin/bash
250+
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
251+
*/15 * * * * /usr/bin/env bash -c /foo run
252+
```
253+
This is just an example command, it will likely vary according to what you are looking too run.
254+
255+
256+
## IMPORTANT `crontab.conf` NOTE
257+
Please note that if you set your own crontab config file via `RCLONE_CRONFILE=/cron/crontab.conf` it will take precedent over anything you pass via `-e` or set other environment variables.
258+
259+
## Understanding How To Run Docker and `CROND`
260+
Here are a few examples of running Docker and `CROND` in the background. You can accomplish the same using `docker-compose`
261+
262+
Running in detached mode:
263+
```bash
264+
docker run -d -e RCLONE_SYNC_COMMAND="*/15 * * * * /usr/bin/env bash -c /foo run" openbridge/ob_bulkstash crond -f
265+
```
266+
You can see the process running in the background:
267+
```bash
268+
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
269+
31 0 root R 1528 0% 3 0% top
270+
1 0 root S 1516 0% 1 0% crond -f
271+
```
272+
Running in detached mode using the `rcron.sh` helper script. This will use Monit has the background process monitor to make sure `CROND` is always running:
273+
```bash
274+
docker run -d -e RCLONE_SYNC_COMMAND="*/15 * * * * /usr/bin/env bash -c /foo run" openbridge/ob_bulkstash rcron start
275+
```
276+
You can see the process running in the background:
277+
```bash
278+
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
279+
21 1 root S 4788 0% 2 0% monit -Iv -c /etc/monitrc -l /dev/null
280+
1 0 root S 2172 0% 3 0% bash /usr/bin/rcron start
281+
23 0 root R 1524 0% 2 0% top
282+
20 1 root S 1516 0% 0 0% crond -b
283+
```
284+
Here is another example running Monit as the controlling process:
285+
```bash
286+
docker run -d -e RCLONE_SYNC_COMMAND="*/15 * * * * /usr/bin/env bash -c /foo" openbridge/ob_bulkstash monit -Iv -c /etc/monitrc -l /dev/null
287+
```
288+
You can see the process running in the background:
289+
```bash
290+
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
291+
1 0 root S 4788 0% 2 0% monit -Iv -c /etc/monitrc -l /dev/null
292+
22 0 root R 1528 0% 1 0% top
293+
21 1 root S 1516 0% 3 0% crond -b
294+
```
295+
296+
Hopefully you get the point on how to do this. You have options, just make sure you understand the basics on how to run Docker in various contexts.
297+
298+
# Creating Your Own Scripts: The `/rclone.sh` Example
299+
300+
Included in the image is a utility script. You can use this as a robust example for creating your own. It highlights the potential to mount scripts like this into your container to run different types of operations.
301+
302+
**Note**: `rclone.sh` is provided as-is and has not been fully tested. Think of it as a proof-of-concept, not something you should blindly use.
303+
304+
## Overview
305+
The script shows an example of how to run `rclone copy` and `rclone move`. It will also has a `foldersize` check in the event you want to trigger a `rclone move`. How is this helpful? If your disk is getting full this can trigger what amounts to be a cleanup task.
215306

216307
### Getting Started with `/rclone.sh`
217-
If you want the image to use `/rclone.sh` make sure the required environment variables are set correctly. In your `ENV`, you need to set the following:
308+
While the image contains `rclone.sh`, you will likely want to mount your own version of the script. For example;
309+
```
310+
-v /path/to/the/file/on/your/host/script.sh:/path/in/container/script.sh
311+
```
312+
313+
Also, you need to make sure the image can use `/rclone.sh`. This means you need to make sure any required environment variables are set correctly. For example, in your `ENV`, you need to set the following for `rclone.sh`:
218314

219315
* `RCLONE_CROND_SCHEDULE` crontab schedule `* * * * *` to perform sync every midnight
220316
* `RCLONE_CROND_SOURCE_PATH` source location for `rclone copy` command
@@ -262,15 +358,6 @@ RCLONE_CROND_DESTINATION_PATH="MYS3:ob-testing/ebs"
262358
RCLONE_CROND_SOURCE_SIZE="1000"
263359
```
264360

265-
## IMPORTANT
266-
Please note that if you set your own crontab config file via `RCLONE_CRONFILE=/cron/crontab.conf` it will take precedent over anything you set in these environment variables. The answer is simply to include all of these in your own config
267-
268-
```bash
269-
SHELL=/bin/bash
270-
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
271-
*/5 * * * * /usr/bin/env bash -c "/rclone.sh run" && curl -fsS --retry 3
272-
https://hchk.io/asads-aa12-ee23-qqw1-543e4c2ddv54385 > /dev/null
273-
```
274361

275362
# Setting Up SFTP Remotes
276363
You can setup SFTP remotes. This allows you to upload or download files from an SFTP server. You can also do server to server transfers between two remotes.
@@ -300,9 +387,14 @@ List a remote drive like this: `rclone lsd {remote name}:`
300387
Replace `{remote name}` with your actual remote name. Using our Amazon example it would look like this
301388
`rclone lsd MYS3:`
302389

390+
Using Docker this is a possible way to run the command:
391+
```bash
392+
docker run -env-file /env/my.env openbridge/ob_bulkstash rclone lsd MYS3:
393+
```
394+
303395
This will output your remote buckets like this:
304396

305-
```
397+
```bash
306398
-1 2017-04-11 16:38:05 -1 athena-lambda
307399
-1 2016-12-04 14:32:54 -1 aws-athena-query
308400
-1 2016-12-17 14:19:23 -1 aws-logs

docker-entrypoint.sh

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,58 +12,38 @@ set -o pipefail
1212

1313
function crond() {
1414

15-
if [[ -f ${RCLONE_CRONFILE} ]]; then
15+
# Create the environment file for crond
16+
if [[ -n "${RCLONE_CRONFILE:-}" ]] || [[ -n "${RCLONE_SYNC_COMMAND:-}" ]]; then
1617

17-
# If using your own cron config, use that now else we create one for you
18-
RCLONE_CRONFILE=/cron/crontab.conf
19-
export RCLONE_CRONFILE
18+
echo "OK CRON It is"
19+
if [[ ! -d /cron ]]; then mkdir -p /cron;fi
2020

21-
else
21+
RCLONE_CRONFILE=/cron/crontab.conf
22+
# If using your own cron config, use that now else we create one for you
23+
export RCLONE_CRONFILE
2224

23-
# For the use of /rclone.sh and crond
25+
printenv | sed 's/^\([a-zA-Z0-9_]*\)=\(.*\)$/export \1="\2"/g' | grep -E "^export RCLONE" > /cron/rclone.env
2426

25-
if [[ -n "${RCLONE_CROND_SOURCE_PATH:-}" ]] || [[ -n "${RCLONE_CROND_DESTINATION_PATH:-}" ]]; then
27+
if [[ -f /cron/rclone.env ]]; then echo "OK: The you set CROND to run. A ENV file was created here /cron/rclone.env. Continuing..."; else echo "ERROR: The CROND ENV is missing even though you want to run CROND. Please check your config file"; fi
2628

27-
#if [[ ! -f /cron/rclone.env ]]; then exit 1; fi
28-
if [[ ! -d /cron ]]; then mkdir -p /cron; fi
29-
30-
# Set a default if a schedule is not present
31-
if [[ -z "${RCLONE_CROND_SCHEDULE:-}" ]]; then RCLONE_CROND_SCHEDULE="0 0 * * *" && export RCLONE_CROND_SCHEDULE; fi
32-
33-
if [[ ! -z ${RCLONE_CROND_SOURCE_SIZE} ]]; then
34-
{
35-
echo 'check program foldersize with path "/bin/bash -c '/rclone.sh foldersize'"'
36-
echo ' if status != 0 for 2 cycles then exec "/usr/bin/env bash -c '/rclone.sh run'"'
37-
} | tee /etc/monit.d/check_foldersize
38-
fi
29+
fi
3930

40-
if [[ -z ${RCLONE_CROND_HEALTHCHECK_URL:-} ]]; then
41-
{
42-
echo 'SHELL=/bin/bash'
43-
echo 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
44-
echo '{{RCLONE_CROND_SCHEDULE}} /usr/bin/env bash -c "/rclone.sh run" 2>&1'
45-
} | tee ${RCLONE_CRONFILE}
4631

47-
sed -i 's|{{RCLONE_CROND_SCHEDULE}}|'"${RCLONE_CROND_SCHEDULE}"'|g' ${RCLONE_CRONFILE}
48-
else
32+
if [[ -n "${RCLONE_SYNC_COMMAND:-}" ]]; then
33+
echo "INFO: RCLONE_CROND_SCHEDULE variable is present. Generating ${RCLONE_CRONFILE}..."
4934
{
5035
echo 'SHELL=/bin/bash'
5136
echo 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
52-
echo '{{RCLONE_CROND_SCHEDULE}} /usr/bin/env bash -c "/rclone.sh run" && curl -fsS --retry 3'
53-
echo '{{RCLONE_CROND_HEALTHCHECK_URL}} > /dev/null'
37+
echo '{{RCLONE_SYNC_COMMAND}}'
5438
} | tee ${RCLONE_CRONFILE}
5539

56-
sed -i 's|{{RCLONE_CROND_SCHEDULE}}|'"${RCLONE_CROND_SCHEDULE}"'|g' ${RCLONE_CRONFILE}
57-
sed -i 's|{{RCLONE_CROND_HEALTHCHECK_URL}}|'"${RCLONE_CROND_HEALTHCHECK_URL}"'|g' ${RCLONE_CRONFILE}
58-
fi
59-
60-
if [[ ! -f ${RCLONE_CRONFILE} ]]; then exit 1; fi
40+
sed -i 's|{{RCLONE_SYNC_COMMAND}}|'"${RCLONE_SYNC_COMMAND}"'|g' ${RCLONE_CRONFILE}
6141

62-
fi
42+
if [[ ! -f ${RCLONE_CRONFILE} ]]; then exit 1; fi
6343
fi
6444

65-
# Load crontab config and start RCLONE_CROND_DESTINATION_PATH
66-
if [[ -f ${RCLONE_CRONFILE} ]]; then cat ${RCLONE_CRONFILE} | crontab - && crontab -l && runcrond="crond -b" && bash -c "${runcrond}"; fi
45+
# Add the crond config
46+
cat /cron/crontab.conf | crontab - && crontab -l
6747

6848
}
6949

@@ -73,6 +53,13 @@ if [[ -f ${RCLONE_CRONFILE} ]]; then cat ${RCLONE_CRONFILE} | crontab - && cront
7353

7454
function monit() {
7555

56+
if [[ ! -z ${RCLONE_CROND_SOURCE_SIZE:-} ]]; then
57+
{
58+
echo 'check program foldersize with path "/bin/bash -c '/rclone.sh foldersize'"'
59+
echo ' if status != 0 for 2 cycles then exec "/usr/bin/env bash -c '/rclone.sh run'"'
60+
} | tee /etc/monit.d/check_foldersize
61+
fi
62+
7663
# Create monit config
7764
{
7865
echo 'set daemon 10'
@@ -90,7 +77,6 @@ function monit() {
9077
} | tee /etc/monitrc
9178

9279
chmod 700 /etc/monitrc
93-
run="monit -c /etc/monitrc" && bash -c "${run}"
9480

9581
}
9682

@@ -100,7 +86,7 @@ run="monit -c /etc/monitrc" && bash -c "${run}"
10086

10187
function run() {
10288

103-
if [[ ! -z "${RCLONE_CROND_SCHEDULE:-}" ]] || [[ ! -z "${RCLONE_CRONFILE:-}" ]]; then crond && monit; fi
89+
if [[ ! -z "${RCLONE_SYNC_COMMAND:-}" ]] || [[ ! -z "${RCLONE_CRONFILE:-}" ]]; then crond && monit; fi
10490

10591
}
10692

rclone.sh

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,33 @@ source /cron/rclone.env
1010
# Possibly convert docker secrets marked variables.
1111
#source /env_secrets.sh
1212

13-
1413
function check (){
15-
"$@"
16-
local status=$?
17-
if [ $status -ne 0 ]; then
18-
echo "An rclone error with $1 occured" >&2
19-
else
2014
# Send the payload to the API
2115
if [[ -z $RCLONE_CROND_HEALTHCHECK_URL ]]; then
2216
echo "INFO: A health check has not been set. Not using health check services"
2317
else
24-
echo "OK: All tests passed, sending message to API..."
25-
POST=$(curl -s -S "$RCLONE_CROND_HEALTHCHECK_URL");
18+
echo "OK: sending message to Healthcheck API..."
19+
POST=$(curl -s -S "${RCLONE_CROND_HEALTHCHECK_URL}");
2620
# Check if the message posted to the API. It should return "ok". Anything other than "ok" indicates an issue
2721
if test "${POST}" != OK; then echo "ERROR: The check to the API failed (${POST})" && return 1; else echo "OK: Message successfully sent to the health check"; fi
2822
fi
29-
fi
30-
return $status
3123
}
3224

3325
function rclone_copy () {
3426

3527
(
3628
flock -n 200 || exit 1
3729
sync_command="rclone copy ${RCLONE_CROND_SOURCE_PATH} ${RCLONE_CROND_DESTINATION_PATH}"
38-
if [ "$RCLONE_SYNC_COMMAND" ]; then
39-
sync_command="$RCLONE_SYNC_COMMAND"
30+
if [ "${RCLONE_SYNC_COMMAND}" ]; then
31+
sync_command="${RCLONE_SYNC_COMMAND}"
4032
else
41-
if [[ -z "$RCLONE_CROND_SOURCE_PATH" ]] || [[ -z "$RCLONE_CROND_DESTINATION_PATH" ]]; then
33+
if [[ -z "${RCLONE_CROND_SOURCE_PATH}" ]] || [[ -z "${RCLONE_CROND_DESTINATION_PATH}" ]]; then
4234
echo "Error: A RCLONE PATH environment variable was not set or passed to the container. Please review your RCLONE source/destination paths."
4335
exit 1
4436
fi
4537
fi
46-
echo "Executing => $sync_command"
47-
eval "$sync_command" || send
38+
echo "Executing => ${sync_command}"
39+
eval "${sync_command}" || send
4840
) 200>/run/rclone.lock
4941

5042
}
@@ -53,27 +45,27 @@ function rclone_move () {
5345
(
5446
flock -n 200 || exit 1
5547
sync_command="rclone move ${RCLONE_CROND_SOURCE_PATH} ${RCLONE_CROND_DESTINATION_PATH}"
56-
if [ "$RCLONE_SYNC_COMMAND" ]; then
57-
sync_command="$RCLONE_SYNC_COMMAND"
48+
if [ "${RCLONE_SYNC_COMMAND}" ]; then
49+
sync_command="${RCLONE_SYNC_COMMAND}"
5850
else
59-
if [[ -z "$RCLONE_CROND_SOURCE_PATH" ]] || [[ -z "$RCLONE_CROND_DESTINATION_PATH" ]]; then
51+
if [[ -z "${RCLONE_CROND_SOURCE_PATH}" ]] || [[ -z "${RCLONE_CROND_DESTINATION_PATH}" ]]; then
6052
echo "Error: A RCLONE PATH environment variable was not set or passed to the container. Please review your RCLONE source/destination paths."
6153
exit 1
6254
fi
6355
fi
64-
echo "Executing => $sync_command"
65-
eval "$sync_command" || send
56+
echo "Executing => ${sync_command}"
57+
eval "${sync_command}" || send
6658
) 200>/run/rclone.lock
6759
}
6860

6961
function foldersize {
70-
if [[ -z $RCLONE_CROND_SOURCE_PATH ]] || [[ -z $RCLONE_CROND_SOURCE_SIZE ]]; then
62+
if [[ -z "${RCLONE_CROND_SOURCE_PATH}" ]] || [[ -z "${RCLONE_CROND_SOURCE_SIZE}" ]]; then
7163
echo "INFO: A local folder path and/or size has not been set. Not using folder monitor"
7264
else
7365
SIZE=$(/usr/bin/du -s ${RCLONE_CROND_SOURCE_PATH} | /usr/bin/awk '{print $1}')
7466
MBSIZE=$((SIZE / 1024))
75-
echo "$RCLONE_CROND_SOURCE_PATH is $MBSIZE MB"
76-
if [[ $MBSIZE -gt $(( ${RCLONE_CROND_SOURCE_SIZE} )) ]]; then
67+
echo "${RCLONE_CROND_SOURCE_PATH} is $MBSIZE MB"
68+
if [[ ${MBSIZE} -gt $(( ${RCLONE_CROND_SOURCE_SIZE} )) ]]; then
7769
rclone_move
7870
fi
7971
fi

0 commit comments

Comments
 (0)