docker postgres with initial data is not persisted over commits - ruby-on-rails

I created a rails app in a docker environment and it links to a postgres instance. I edited the
postgres container to add initial data (by running rake db:setup from the rails app). Now I commited the postgres database, but it doesn't seem to remember my data when I create a new container (of the commited postgres image).
Isn't it possible to save data in a commit and then reuse it afterwards?
I used the postgres image: https://registry.hub.docker.com/_/postgres/

The problem is that the postgres Dockerfile declares "/var/lib/postgresql/data" as a volume. This is a just a normal directory that lives outside of the Union File System used by images. Volumes live until no containers link to them and they are explicitly deleted.
You have a few choices:
Use the --volumes-from command to share data with new containers. This will only work if there is only one running postgres image at a time, but it is the best solution.
Write your own Dockerfile which creates the data before declaring the volume. This data will then be copied into the volume when the container is created.
Write an entrypoint or cmd script which populates the database at run time.
All of these suggestions require you to use Volumes to manage the data once the container is running. Alternatively, you could write your own Dockerfile and simply not declare a volume. You could then use docker commit to create a new image after adding data. This will probably work in the short term, but is definitely not how you should work with containers - it isn't repeatable and you will eventually run out of layers in the Union File System.
Have a look at the official Docker docs on managing data in containers for more info.

Create a new Dockerfile and change PGDATA:
FROM postgres:9.2.10
RUN mkdir -p /var/lib/postgresql-static/data
ENV PGDATA /var/lib/postgresql-static/data

You should be all set with the following command. The most important part is the PGDATA location, which should be anything but the default.
docker run -e PGDATA=/var/lib/postgresql/pgdata -e POSTGRES_PASSWORD=YourPa$$W0rd -d postgres

It is not possible to save data during a commit since the data resides on a mount which is specific for that container and will get removed once you run docker rm <container ID> but you can use data volumes to share and reuse data between container and the changes made are directly on the volume.
You can use docker run -v /host/path:/Container/path to mount the volume to the new container.
Please refer to: https://docs.docker.com/userguide/dockervolumes/

For keeping permanent data such as databases, you should define these data volumes as external, therefore it will not be removed or created automatically every time you run docker-compose up or down commands, or redeploy your stack to the swarm.
...
volumes:
db-data:
external: true
...
then you should create this volume:
docker volume create db-data
and use it as data volume for your databse:
...
db:
image: postgres:latest
volumes:
- db-data:/var/lib/postgresql/data
ports:
- 5432:5432
...
In production, there are many factors to consider when using docker for keeping permanent data safely, specially in swarm mode, or in kubernetes cluster.

Related

Docker commit is not saving my changes to image

I'm new to docker world: I'm at a point where i can deploy docker containers and do some work.
Trying to get to the next level of saving my changes and moving my containers/images to another pc/server.
Currently, I'm using docker on windows 10, but I do have access to Ubuntu 16.04 server to test my work.
This is where I'm stuck: I have Wordpress and MariaDB images deployed on Docker.
My WP is running perfectly OK.I have installed few themes and created few pages with images.
At this point, I like to save my work and send it to my friend who will deploy my image and do further work on this same Wordpress.
What I have read online is: I should run docker commit command to save and create my docker image in .tar format and then send this image file (.tar) to my friend. He will run docker load -i on my file to load it as image into his docker and then create container from it which should give him all of my work on Wordpress.
Just to clarify, I'm committing both Wordpress and Mariadb containers.
I don't have any external volumes mounted so all the work is being saved in containers.
I do remember putting check mark on drive C and D in docker settings but i don't know if that has anything to to do with volumes.
I don't get any error in my commit and moving .tar files process. Once my friend create his containers from my committed images, he gets clean Wordpress (like new installation of Wordpress starting from wp setup pages).
Another thing I noticed is that the image I create has the same file size as original image i pulled. When I run docker images, I see my image is 420MB ,as well as Wordpress image is 420MB.
I think my image should be a little bit bigger since I have installed themes, plugins and uploaded images to Wordpress. At least it should add 3 to 5 MB more then original images. Please help. Thank you.
Running docker system df gives me this.
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 5 3 1.259GB 785.9MB (62%)
Containers 3 3 58.96kB 0B (0%)
Local Volumes 2 2 311.4MB 0B (0%)
Build Cache 0 0 0B 0B
Make sure, as shown here, to commit a running container (to avoid any data cleanup)
docker commit CONTAINER_ID yourImage
After the docker commit command, you can use docker save to save your image in a tar, and docker load to import it back, as shown here.
You should never run docker commit.
To answer your immediate question, containers that run databases generally store their data in volumes; they are set up so that the data is stored in an anonymous volume even if there was no docker run -v option given to explicitly store data in a named volume or host directory. That means that docker commit never persists the data in a database, and you need some other mechanism to copy the actual data around.
At a more practical level, your colleague can ask questions like "where did this 400 MB tarball come from, why should I trust it, and how can I recreate it if it gets damaged in transit?" There are also good questions like "the underlying database has a security fix I need, so how do I get the changes I made on top of a newer base image?" If you're diligent you can write down everything you do in a text file. If you then have a text file that says "I started from mysql:5.6, then I ran ..." that's very close to being a Dockerfile. The syntax is straightforward, and Docker has a good tutorial on building and running custom images.
When you need a custom image, you should always describe what goes into it using a Dockerfile, which can be checked into source control, and can rebuild an image using docker build.
For your use case it doesn't sound like you actually need a custom image. I would probably suggest setting up a Docker Compose YAML file that described your setup and actually stored the data in local directories. The database half of it might look like
version: '3'
services:
db:
image: 'mysql:8.0'
volumes:
- './mysql:/var/lib/mysql/data'
ports:
- '3306:3306'
The data will be stored on the host, in a mysql subdirectory. Now you can tar up this directory tree and send that tar file to your colleague, who can then untar it and recreate the same environment with its associated data.
Use docker build (Changes to the images should be stored in the Dockerfile).
Now if you have multiple services, just use docker's brother docker-compose. One extra step you have to do is create docker-compose.yml (don't be afraid yet my friend, it's nothing trivial). All you're doing in this file is listing out your images (along with defining where their Dockerfile is for that image, could be in some subfolder for each image). You can also define some other properties there if you'd like.
Notice that certain directories are considered volume directories by docker, meaning that they are container specific and therefore never saved in the image. The \data directory is such an example. When docker commit my_container my_image:my_tag is executed, all of the containers filesystem is saved, except for /data. To work around it, you could do:
mkdir /data0
cp /data/* /data0
Then, outside the container:
docker commit my_container my_image:my_tag
Then you would perhaps want to copy the data on /data0 back to /data, in which case you could make a new image:
On the Dockerfile:
FROM my_image:my_tag
CMD "cp /data0 /data && my_other_CMD"
Notice that trying to copy content to /data in a RUN command will not work, since a new container is created in every layer and, in each of them, the contents of /data are discarded. After the container has been instatiated, you could also do:
docker exec -d my_container /bin/bash -c "cp /data0/* /data"
You have to use the volumes to store your data.
Here you can find the documentation: https://docs.docker.com/storage/volumes/
For example you can do somethink like this in your docker-compose.yml.
version: '3.1'
services:
wordpress:
image: wordpress:php7.2-apache
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: databasename
WORDPRESS_DB_USER: username
WORDPRESS_DB_PASSWORD: password
WORDPRESS_DB_NAME: namedatabase
volumes:
- name_volume:/var/www/html
volumes:
- name_volume:
or
volumes:
- ./yourpath:/var/www/html

Wordpress image with mysql data

Is there any image available that contain wordpress along with mysql data?
When I commit and take backup of the image, mysql data is not included. I will prefer a single image for both.
I tried to create such image using this Dockerfile:
FROM tutum/lamp:latest
RUN rm -fr /app && git clone https://github.com/WordPress/WordPress.git /app
EXPOSE 80
CMD ["/run.sh"]
I can initiate a fresh installation using a command like this...
docker run -p 88:80 shantanuo/wp
But the container can not be moved to another server "as is". I need to take data backup using mysql-dump command and that is something I am trying to avoid. Is it possible?
If I do not volumanize the container, then I am able to copy the wordpress image along with it's data.
https://hub.docker.com/r/shantanuo/lamp/~/dockerfile/
But it does not work on the new server. Adding wordpress tag.
Is there any image available that contain wordpress along with mysql data?
Short answer: not recommended.
An image usually deals with one service (so two images would be involved here: wordpress and MySQL)
And the persistent data would not be "in" the image, but on the host in a volume / bind mount.
For instance, the tutumcloud/lamp image does declare volumes:
# Add volumes for MySQL
VOLUME ["/etc/mysql", "/var/lib/mysql" ]
The docker run command initializes the newly created volume with any data that exists at the specified location within the base image.
Making your own image without those lines might work as you expect (ie, commit a container with its data).
But if the server reboot at any time, or you have to docker run your original container again, it will start anew, without the data.
A typical docker wordpress image would use a mysql one
version: '3.1'
services:
wordpress:
image: wordpress
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_PASSWORD: example
mysql:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
And in turn, that mysql container would use a local host mounted volume in order to persists the database.
docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
See for instance "Quickstart: Compose and WordPress"
So not only should you commit your Wordpress image, but your Mysql one as well, and your volume.
However, committing a volume is not supported: see "Commit content of mounted volumes as well" in order to backup that volume with your WordPress database in it.
With those three backups, you then migrate them to your other server.
However, this seems overly complex, and a fresh WordPress/MySQL docker project on the second server is easier to start.
You would then need, yes, your database dump file.
And some other Wordpress folders (like themes)
See "Easy WordPress Migration with Docker".
That would be the recommended way over trying to commit existing containers form one server and "transplant" them onto another server.
If you want to export your workig dataset to another server, docker has the commit command. This command creates a new image from a running container.
$ docker commit c3f279d17e0a svendowideit/testimage:version3
Documentation.

Docker Data/Named Volumes

I am trying to wrap my mind around Docker volumes but I must have some things missing to understand it.
Let's say I have a Python app that require some initialisation depending on env variables. What I'm trying to achieve is having a "Code only image" from which I can start containers that would be mounted at executions. The entrypoint script of the Main container will then read and generate some files from/on the Code only container.
I tried to create an image to have a copy of the code
FROM ubuntu
COPY ./app /usr/local/code/app
Then docker create --name code_volume
And with docker-compose:
app:
image: python/app
hostname: app
ports:
- "2443:443"
environment:
- ENV=stuff
volumes_from:
- code_volume
I get an error from the app container saying it can't find a file in /usr/local/code/app/src but when I run code_volume with bash then ls into the folder, the file is sitting there...
I tried to change access rights, add /bin/true (seeing it in some examples) but I just can't get what I want to be working. I checked the docker volume create feature but it seems to be for storing/sharing data afterward
What am I missing ? Is the entrypoint script executed before volumes are mounted ? Is there any best practices for cases like this that don't involve mounting folders and keeping one copy for every container ? Should I be thinking my containers over again ?
You do not declare the volume on code_volume container upon creation.
docker create -v /usr/local/code/app --name code_volume

how to create data volume from image for use in nginx container

I have an image (not container) which has the data baked in at /store. The image does not do anything else, it is only a vessel for holding the data in. I want to create a docker-compose file so that my nginx container has access to everything in /store at /usr/share/nginx/html
Do I need to make an intermediary container first? I am not sure how the docker-compose file would look. thanks
This is a quick step process:
docker run -v nginx-store:/store --rm store true
docker run -v nginx-store:/usr/share/nginx/html -d --name nginx nginx
The first run creates a named volume nginx-store from the contents of your store image (this happens any time you mount an empty volume in a container), and immediately exits and deletes the container.
The second run uses that named volume with the future nginx containers. To modify the nginx-store volume in the future, you can run any side container that mounts it with a similar -v nginx-store:/target flag.
The best way for managing that would probably do use a docker volume to store you're /store datas.
You can do it once by creating a container from that image, mount an empty docker volume in it, and then copy the content of /store in the external docker volume.
If you still need to use the /store from an existing image you will need to instanciate a container from if and retrieve the exposed volume from your nginx container. (using volume_from). In this case both containers would need to be on the same host.
You could try using the local-persist volume plugin as follows:
version: '2'
services:
web:
image: nginx
volumes:
- data:/usr/share/nginx/html
volumes:
data:
driver: local-persist
driver_opts:
mountpoint: /data/local-persist/data
Obviously other volume plugin types might offer more flexibility.
https://docs.docker.com/engine/extend/plugins/
I'd suggest you to consider bake /store inside your nginx container. It will reduce number of mounted volumes and thus simplify overall structure. And maybe improves performance.
You could do it in several ways:
Use you data image as base for your nginx image. In this case you will need to write Dockerfile for nginx but it's not very difficult.
You can extract /store from your data image by creating container with true endpoint and docker cp desired data. Then copy it to your nginx image.

how to ignore some container when i run `docker-compose rm`

I have four containers that was node ,redis, mysql, and data. when i run docker-compose rm,it will remove all of my container that include the container data.my data of mysql is in the the container and i don't want to rm the container data.
why i must rm that containers?
Sometime i must change some configure files of node and mysql and rebuild.So
,I must remove containers and start again.
I have searched using google again over again and got nothing.
As things stand, you need to keep your data containers outside of Docker Compose for this reason. A data container shouldn't be running anyway, so this makes sense.
So, to create your data-container do something like:
docker run --name data mysql echo "App Data Container"
The echo command will complete and the container will exit immediately, but as long as you don't docker rm the container you will still be able to use it in --volumes-from commands, so you can do the following in Compose:
db:
image: mysql
volumes-from:
- data
And just remove any code in docker-compose.yml to start up the data container.
An alternative to docker-compose, in Go (https://github.com/michaelsauter/crane), let's you create contianer groups -- including overriding the default group so that you can ignore your data containers when rebuilding your app.
Given you have a "crane.yaml" with the following containers and groups:
containers:
my-app:
...
my-data1:
...
my-data2:
...
groups:
default:
- "my-app"
data:
- "my-data1"
- "my-data2"
You can build your data containers once:
# create your data-only containers (safe to run several times)
crane provision data # needed when building from Dockerfile
crane create data
# build/start your app.
crane lift -r # similar to docker-compose build && docker compose up
# Force re-create off your data-only containers...
crane create --recreate data
PS! Unlike docker-compose, even if building from Dockerfile, you MUST specify an "image" -- when not pulling, this is the name docker will give the image locally! Also note that the container names are global, and not prefixed by the folder name the way they are in docker-compose.
Note that there is at least one major pitfall with crane: It simply ignores misplaced or wrongly spelled fields! This makes it harder to debug that docker-compose yaml.
#AdrianMouat Now , I can specify a *.yml file when I starting all container with the new version 1.2rc of docker-compose (https://github.com/docker/compose/releases). just like follows:
file:data.yml
data:
image: ubuntu
volumes:
- "/var/lib/mysql"
thinks for your much useful answer

Resources