This repo has the docker code to:
- build a docker image for inaturalist (the Ruby app)
- build a docker image for the inaturalist API (the NodeJS app)
- run a docker-compose stack including all the other parts of the system: postgres, elasticsearch, etc
We developed this so we can easily spin up a dev environment for development of an app that communicates with iNat.
This is not production ready. It's not even complete. It works as a simple dev server but there is still a lot more of the configuration that needs to be configurable via env vars. Basically, it's done to the point that it served our purposes.
Think carefully before using this to run your own production instance of inat. Quote taken from the inat developers page:
If you're considering forking our web app code in order to build a narrower version of iNat, please talk to us first! While we welcome forks to the software, we don't want to fork our community. Social networks lose their value when they fragment, so if you're thinking of making "iNat for Country X" or "iNat for Lepidopterists" or something, let's discuss ways that we can incorporate your needs into our existing infrastructure. We have a mechanism for localization through our international iNaturalist Network.
This repo uses git submodules. The docker related code is stored in this repo but then we have the inat and API codebases as submodules so we can pin the commit and make building easy.
Here's how you clone the repo:
git clone ...
git submodule init # gets the submodules ready
git submodule update # clones the submodules and checks out the right commitThen in the future, whenever you do a git pull, keep an eye out for updates to
the submodules and run a git submodule update. If you're unsure, just run it.
It's idempotent.
- get yourself a host machine
- install Docker (tested with version 18.09.7, build 2d0083d)
- install docker-compose (tested with version 1.23.2, build 1110ad01)
- clone this repo onto the host
cp start-or-restart-stack.sh.example start-or-restart-stack.shvim start-or-restart-stack.shto add all the values it asks forchmod +x start-or-restart-stack.sh- for the first time only after cloning
git submodule init - everytime you pull new commits, make sure the submodules are up to date
git submodule update - start the stack
./start-or-restart-stack.sh - watch the logs
docker logs -f inat_appuntil Rails has started - if you need to run the "only on first run" tasks, you can do it with:
docker exec -it inat_app bash bash /srv/inat/docker/optional-first-run-items.sh # or for the impatient, you'll be able to create a user after running # rake es:rebuild
- point your browser to the URL you configured (both in the
start-or-restart-stack.shfile and in your DNS) for the iNat service - sign up as a new user
- optional, make yourself an admin:
- go back to your terminal on the docker host
./scripts/db-psql.sh
- run this SQL
insert into roles_users values (1,1); -- assumes your user ID = 1
- you can now visit <inat.hostname>/admin in your browser
- as an admin, you can also bypass the rules for a minimum number of observations before you can do certain things like create a traditional project <inat.hostname>/projects/new_traditional
- go back to your terminal on the docker host
Running the start script with --build still relies on a cache of docker images
to speed up rebuilds. If you see anything that looks like stale files in your
docker builds, you can cache bust by running docker-compose build --no-cache.
You can also supply a service name (just a positional param) to only build a
single service.
The container doesn't expose a post so you can't connect to it from outside but
you can exec into it and run psql. There is a script that will drop you
into a psql shell inside the docker container:
./scripts/db-psql.shWe have a script that will perform this for you:
# note, the output path is on the docker *host*, not inside the container
./scripts/db-dump.sh /tmp/inat-pg.backupWe have a script that will perform this for you:
# note, the input path is on the docker *host*, not inside the container
./scripts/db-restore.sh /tmp/inat-pg.backupIf you didn't want to connect to this DB, then you can use \l to list
the available databases and connect to them with \c <database name>.
Sometimes you'll get errors due to missing indexes in ES. You can get a list of
which indexes exist in the server by hitting the endpoint
http://<es-container-host-port>/_aliases?pretty=true.
- edit the
docker-compose.ymlfile to override the entrypoint forinat_app. We need our terminal to be attached to the shell that runs rails so we'll set a noop entrypoint:services: inat: ...existing stuff... entrypoint: sh -c 'sleep 9999999999' # add this line
- (re)start the docker stack:
./start-or-restart-stack.sh
- exec into the docker container:
docker exec -it inat_app bash - (optional) install a text editor:
apt-get update && apt-get -y install --no-install-recommends vim - edit any file (using vim) you want to add a breakpoint to and simply add a
line with
debugger. You can debug the iNat code but you can also debug gems. See where they live withgem envand the output will haveINSTALLATION DIRECTORY: /usr/local/bundle. You can edit any file in/usr/local/bundle/gems/to add a debugger statement. - start the server
bash docker/entrypoint.sh
- interact with the server to trigger the breakpoint, then jump back to your
terminal and you'll see the debugger waiting with a
(byebug)prompt. Thehcommand will print debugging help - apparently pry is an enhanced debugger that you could look at using too
Sometimes you want to get a token, so here's how.
These scripts use httpie, so make sure you have it installed.
- go to the /oauth/applications/{id} page in your iNat instance
- click the Authorize with PKCE link
- copy the
codefrom the redirected URL, it doesn't matter if there's no listening server - Create a bash script with the following:
# TODO YOU need to update all these values inatServerDomain='https://your.inat.instance' # the URL of your iNat instance (use HTTPS if needed) # get these from the /oauth/applications/<id> page in iNat clientId='REPLACE-ME-ed4be74c44dccb8efdc1e8cf0d408c50e7f6d88bfb3e0e3839825' redirectUri='http://localhost:8080/oauth-callback' # this should be the matching code_verifier from the link that the UI generates codeVerifier='some_terrible_challenge' # replace with the code you copied above code='REPLACE-ME-e91fa6a6e3d1854e1b508ae82f8147ff180e5a51199ed074920a6' http -v \ $inatServerDomain/oauth/token \ client_id=$clientId \ redirect_uri=$redirectUri \ code=$code \ grant_type=authorization_code \ code_verifier=$codeVerifier
- save and run the script
- the response will contain your token (the
access_tokenfield), it will look something like:{ "access_token": "5ca26e9aaaaaa6fa0116c13869ad8fd7a8e118d32b6a0a2aed37f4301cb32029", "created_at": 1581041101, "scope": "write login", "token_type": "Bearer" }
The iNat (Ruby on Rails) server uses a different authentication mechanism from the iNat API (built with NodeJS). Now you have the token from the iNat server (step above) you can use that to get a JWT that can be used for the iNat API.
- run the following (note: we still use some env vars from)
# TODO YOU need to update all these values inatServerDomain='https://your.inat.instance' # the URL of your iNat instance (use HTTPS if needed) inatToken='access_token-from-steps-above' http -v $inatServerDomain/users/api_token \ Authorization:"Bearer $inatToken"
- the response will be your JWT, something like:
{ "api_token": "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxLCJvYXV0aaaaaaasaWNhdGlvbl9pZCI6MiwiZXhwIjoxNTgxMTI3MDc4fQ.Ti4aSGEylor-p60MyQCyUAV2I6SO-nEYzmyVeo3sQ1gTNK0HNdQhieU4zqJpyABYtb_C8sjytA8fwFMG4KhTSQ" }