3

I have a deploy script that I am trying to use for my server for CD but I am running into issues writing the bash script to complete some of my required steps such as running npm and the migration commands.

How would I go about getting into a container bash, from this script, running the commands below and then exiting to finish pulling up the changes?

Here is the script I am trying to automate:

cd /Project
docker-compose -f docker-compose.prod.yml down
git pull
docker-compose -f docker-compose.prod.yml build

# all good until here because it opens bash and does not allow more commands to run
docker-compose -f docker-compose.prod.yml run --rm web bash

     npm install  # should be run inside of web bash
     python manage.py migrate_all # should be run inside of web bash
     exit # should be run inside of web bash

# back out of web bash
docker-compose -f docker-compose.prod.yml up -d
2
  • You can bash into the container easily enough (exec -it) but then you would have to have a bash shell in the container that you could continue to run scripts from. Why aren't you just executing the install from the Dockerfile? Commented Oct 9, 2020 at 15:47
  • 1
    You don't do this at all. Run npm install in your Dockerfile, so the image contains the correct library tree. (And if your docker-compose.yml mounts volumes over the image's source tree, delete that.) You can docker-compose run a separate container to execute the migrations. You can defer running docker-compose down until after docker-compose build finishes. Commented Oct 9, 2020 at 15:54

1 Answer 1

1

Typically a Docker image is self-contained, and knows how to start itself up without any user intervention. With some limited exceptions, you shouldn't ever need to docker-compose run interactive shells to do post-deploy setup, and docker exec should be reserved for emergency debugging.

You're doing two things in this script.

The first is to install Node packages. These should be encapsulated in your image; your Dockerfile will almost always look something like

FROM node
WORKDIR /app
COPY package*.json .
RUN npm ci              # <--- this line
COPY . .
CMD ["node", "index.js"]

Since the dependencies are in your image, you don't need to re-install them when the image starts up. Conversely, if you change your package.json file, re-running docker-compose build will re-run the npm install step and you'll get a clean package tree.

(There's a somewhat common setup that puts the node_modules directory into an anonymous volume, and overwrites the image's code with a bind mount. If you update your image, it will get the old node_modules directory from the anonymous volume and ignore the image updates. Delete these volumes: and use the code that's built into the image.)

Database migrations are a little trickier since you can't run them during the image build phase. There are two good approaches to this. One is to always have the container run migrations on startup. You can use an entrypoint script like:

#!/bin/sh
python manage.py migrate_all
exec "$@"

Make this script be executable and make it be the image's ENTRYPOINT, leaving the CMD be the command to actually start the application. On every container startup it will run migrations and then run the main container command, whatever it may be.

This approach doesn't necessarily work well if you have multiple replicas of the container (especially in a cluster environment like Docker Swarm or Kubernetes) or if you ever need to downgrade. In these cases it might make more sense to manually run migrations by hand. You can do that separately from the main container lifecycle with

docker-compose run web \
  python manage.py migrate_all

Finally, in terms of the lifecycle you describe, Docker images are immutable: this means that it's safe to rebuild new images while the old ones are running. A minimum-downtime approach to the upgrade sequence you describe might look like:

git pull

# Build new images (includes `npm install`)
docker-compose build

# Run migrations (if required)
docker-compose run web python manage.py migrate_all

# Restart all containers
docker-compose up --force-recreate
Sign up to request clarification or add additional context in comments.

1 Comment

Interestingly, because I use webpack, running npm run build altered my webpack-stats.json file so even though the container was built and swapped, there were 500 errors until the build was done... With that naturally your solution was forced so I could automate the builds correctly. Looking now this is what I should have always done, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.