4

I have a Python+MySQL webservice running on a Digital Ocean droplet in production. When I updated the code in the project on my local machine, I want the changes to take place in the droplet (in production) as well. So I do the following to deploy:

$ ssh root@12.34.56.78
# cd project
# git pull
# docker-compose down
# docker-compose build
# docker-compose up -d

After this, my database is empty. It has none of my tables in it. How can I update the code which is running, but not lose the data?

My Code

Project structure

.
├── db
├── docker-compose.yml
└── web
    ├── Dockerfile
    ├── project
    │   ├── app.py
    │   ├── __init__.py
    │   ├── blueprint1
    │   ├── blueprint2
    │   ├── models.py
    │   ├── static
    │   ├── templates
    │   └── _version.py
    ├── Makefile
    ├── migrations
    ├── README.md
    ├── setup.cfg
    ├── setup.py
    ├── start.sh
    ├── tests
    └── tox.ini

docker-compose.yml

version: "2"
services:
  db:
    image: mysql:5.7
    ports:
      - "32000:3306"
    environment:
      MYSQL_DATABASE: ***
      MYSQL_ROOT_PASSWORD: ***
    volumes:
      - ./db:/docker-entrypoint-initdb.d/:ro
  app:
    build: ./web
    links:
      - db
    ports:
      - "5000:5000"
    environment:
      MYSQL_PORT: 32000

Dockerfile

FROM python:3.7-slim

# Copy projects code
RUN apt-get update && apt-get install -y git
COPY . /opt/app
WORKDIR /opt/app
RUN pip install -e . --no-cache-dir

# Start app
ENV ENV=prod
EXPOSE 5000
ENTRYPOINT ["sh", "start.sh"]
CMD ["/opt/app/start.sh"]

What I've tried

# This killed my data:
$ docker-compose up -d --build app

# This did NOT update the code in app:
# 1
$ docker-compose stop app && docker-compose up -d app
# 2
$ docker-compose up -d --no-deps --build app
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • Might be directly related to https://stackoverflow.com/q/56486763/562769 – Martin Thoma Jun 07 '19 at 00:59
  • What's it actually doing to your database, you're using vague words like kill and nuke? If you're defined the volume correctly then it shouldn't loose any data when it comes back up – Shardj Jun 07 '19 at 16:31
  • I have no idea what it is doing to it. I just can tell that the tables don't exist anymore (the db does, but it's completely empty) – Martin Thoma Jun 07 '19 at 16:45
  • My best guess is that it completely re-initializes it. So it overwrites the volume with a new one. But that is pure guessing. – Martin Thoma Jun 07 '19 at 16:46
  • 1
    "ou're defined the volume correctly then it shouldn't loose any data when it comes back up" - how do I define the volume correctly? I've posted everything I did. – Martin Thoma Jun 07 '19 at 19:04
  • Well your volume declaration has two colons in it, which as far as I know isn't correct syntax but I could be wrong – Shardj Jun 08 '19 at 00:37
  • Can you post your start.sh file, maybe that's doing the overwriting. If you still have your db but not the tables then I don't think it's a volume issue. Some code of yours is probably wiping it. – Shardj Jun 10 '19 at 10:23
  • Hahahah, I found it. I was not using the MySQL database at all . I used a container-local sqlite database. Of course, this was gone then. I'll run over the comments / the question to make sure it is not confusing afterwards. – Martin Thoma Jun 10 '19 at 10:27

5 Answers5

12

You have to create a named volume for your database like so

version: '3'

services:    
  mysql:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

Now you can do safly run docker-compose stop or docker-compose down without losing your data on the mysql_data volume. Only when you run docker-compose down -v it will remove the volume as well.

You can see all your named volumes by running docker volume ls

Quick note: docker-compose is designed for development and not for production. You might want to consider a different strategy.

Chris
  • 3,795
  • 3
  • 17
  • 23
  • 1
    "You might want to consider a different strategy." Could you please add some examples here? – Martin Thoma Jun 13 '19 at 05:16
  • 1
    According to this page https://docs.docker.com/compose/production/ it seems ok to run it with `docker-compose`. Other solutions like docker swarm and kubernetes are more complex and probably bit overkill in your case – Chris Jun 13 '19 at 08:31
2
  1. Create folder on host-machine
mkdir /data/mysql
  1. Start your MySQL in docker
  2. Copy database from running MySQL container in to host-machine
docker ps | grep mysql
docker cp <container_id>:/var/lib/mysql /data/mysql
  1. Make change in your docker-compose.yml
    volumes:
      - ./db:/docker-entrypoint-initdb.d/:ro
      - /data/mysql:/var/lib/mysql
  1. Restart all.
Qteb
  • 483
  • 4
  • 14
  • 1
    This does not solve my problem. When I do those changes and then go to the workflow "down, build, up" I still lose my data. – Martin Thoma Jun 04 '19 at 21:10
  • To confirm: The MySQL DB is still part of docker-compose, right? So "start mysql in docker" means `docker-compose up db`? What does "restart all" mean as a command? – Martin Thoma Jun 07 '19 at 16:24
0

If you just want to update the app container without touching db container why not do

docker-compose down app
docker-compose up app

In this case you are just rebuilding your app container without touching db container.

And also its recommended to keep your containers stateless. Store your mysql data on local machine as Qteb suggests.

Thanks.

Himakar
  • 185
  • 12
  • Will this actually rebuild the app container? Or will it just shut it down / start the same container again? – Martin Thoma Jun 06 '19 at 16:51
  • It shuts down everything like container, image, volume and network and restarts again. It is a recommended way. Please follow this link for info https://stackoverflow.com/questions/41322541/rebuild-docker-container-on-file-changes – Himakar Jun 06 '19 at 16:55
  • So I guess in practice I will call this as `docker-compose down app && docker-compose up -d app` – Martin Thoma Jun 06 '19 at 17:13
  • This keeps the data, but it does not update the container. I just tried it. – Martin Thoma Jun 06 '19 at 17:15
  • it does not update the container? Are we still talking about app container? – Himakar Jun 06 '19 at 17:18
  • Well, it did not help. As I wrote, it did NOT update the code. Also, `docker-compose stop app && docker-compose up -d app` did not update the code. – Martin Thoma Jun 06 '19 at 17:19
  • Yes, I'm talking about the app container. – Martin Thoma Jun 06 '19 at 17:19
  • `docker-compose down app && docker-compose up -d app` should restart the container. `docker-compose stop app` wont change the code – Himakar Jun 06 '19 at 17:19
  • You guys forgetting docker-compose build? – Shardj Jun 07 '19 at 15:28
  • `docker-compose up` builds the image if it doesn't already exist. – Himakar Jun 08 '19 at 04:07
  • I found out that the problem was somewhere else. Could you please add some information about volumes (e.g. What is the difference between "volumes:" within a service and "volumes:" on the same level as "services:"? When are volumes created / destroyed?). If you add this, I'll give you the bounty and accept your answer. – Martin Thoma Jun 10 '19 at 10:31
  • Its simple. Outer Volumes are like globals. They are reusable with other services as well by name. If you dont want a reusable volume, dont name it. Just give the path. And In terms of `docker-compose`, When a container is initiated volumes get created if not already exits. They are not destroyed on `stop` command. They are destroyed on `down` command. – Himakar Jun 10 '19 at 13:56
0

First, you need to have a named volume for data persistence as mentioned by Chris.

Then, build the code image and start the container from the new image individually:

docker-compose up -d --no-deps --build app

The --no-deps flag doesn't start any linked services. Linked services are started by default. That would destroy your data (like when you did docker-compose up -d --build app).

References:

z11i
  • 951
  • 11
  • 26
0

To store mysql database or other database data make sure your docker-compose.yml will look like 1.if you want to use Dockerfile

version: '3.1'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:
  mysql-data:

2.if you want to use your image instead of Dockerfile than your docker-compose.yml will looks like

version: '3.1'   

services:
  php:
    image: php:7.4-apache
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html/
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - mysql-data:/var/lib/mysql

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
volumes:

if you want to store or preserve data of mysql then must remember to add two lines in your docker-compose.yml

volumes:
  - mysql-data:/var/lib/mysql

and

volumes:
  mysql-data:

after that use this command

docker-compose up -d

now your data will persistent and will not be deleted even after using this command

docker-compose down

extra:- but if you want to delete all data then you will use

docker-compose down -v

plus you can check your database data list by using this command

docker volume ls

DRIVER              VOLUME NAME
local               35c819179d883cf8a4355ae2ce391844fcaa534cb71dc9a3fd5c6a4ed862b0d4
local               133db2cc48919575fc35457d104cb126b1e7eb3792b8e69249c1cfd20826aac4
local               483d7b8fe09d9e96b483295c6e7e4a9d58443b2321e0862818159ba8cf0e1d39
local               725aa19ad0e864688788576c5f46e1f62dfc8cdf154f243d68fa186da04bc5ec
local               de265ce8fc271fc0ae49850650f9d3bf0492b6f58162698c26fce35694e6231c
local               phphelloworld_mysql-data
Hassan Saeed
  • 6,326
  • 1
  • 39
  • 37