Equivalent of volumes_from in Docker Compose v3

2019-02-04 20:34发布

问题:

There are seemingly similar questions here (docker-compose volumes_from equivalent with version 3, How to replace volumes_from in docker-composer v3) but I don't think they answer the question (or at least I don't understand how the answers solve the problem). So let me try to ask again, very specifically.

I have this v2 docker-compose.yml:

version: '2'
services:
  full-tests:
    volumes:
      - ..:/opt/project
      - ../../../ext-libs:/opt/ext-libs
      - ./third-mapping:/opt/third

  unit-tests:
    volumes_from: full-tests

The point is that the set of volumes is defined once and I can easily reuse them using volumes_from.

How would you rewrite this in v3?

回答1:

To answer your question - its impossible with v3 - see the section below. v3 shall not be used as a successor ( also a official statement by docker ) it shall be used in "swarm cases".

nevertheless, what you do should do is using named volumes.

You can combine it with host-mount volumes like this

docker volume create --name volume1 -o type=none -o device=/home/$USER/projects/01 -o o=bind

You can simplify this using the long-syntax introduced in 3.2: https://docs.docker.com/compose/compose-file/#long-syntax-2 so you can define the named volume + bind on the host in the docker-compose file example:

services:
   full-tests:
    volumes:     
      - type: volume
        source: ../
        target: /opt/project
      - type: volume
        source: ../../../ext-libs
        target: /opt/ext-libs

or in short as you had

services:
   full-tests:
     volumes:     
      - ../:/opt/project
      - ../../../ext-libs:/opt/ext-libs

What you cannot do though, putting the long-syntax under the top-level "volumes" definition to give that volume a name and reused it in the volumes section in the services - that is not possible. To do so, you would use a

volumes:
  project:
    external: true
  third-party:
    external: true

And then use the "docker volume create" syntax on the cli to create those volumes with a bind option, as outlines above

but you will never get what volumes_from was doing for you here


There is no equivalent of volumes_from in v3, since v3 is not a successor of v2, its an alternative - please see my comment and the sources here https://github.com/rancher/rancher/issues/3316#issuecomment-310889283

To sum it up - volumes_from and volumes have an overlap in the case volumes_from was just used the wrong way / in the wrong field.

a) If you want data to be persisted across stack upgrades ( down + up ), you pick named volumes - and now, if 2+ services needs to share this, just mount this named volume using volumes:.

b) If you though, do not want the data to persist over stack upgrades ( e.g. because its source code and the image actually includes an upgrades this ) as a in a usual application + httpd/proxy scenario, you will create a anon-volume for this e.g. /var/www in the Dockerfile using Volume /var/www and then use volumes_from to mount it in the httpd service.


the main point with b is, that on stack upgrades, the anon volume will be removed ( `docker-compose down removes anon containers, but not named ones ) and thus the upgrade works as expected, you have a new codebase

Trying to do the same with named volumes will give you a huge suprise on the first upgrade, since the code is on a named volume and that will overlay the codebase on the "fresh" image / new container, thus you will run on the old codebase after the upgrade.



回答2:

You could use extension fields to keep the code short but it isn't quite the same as volumes_from. For example:

version: '3.7'
x-volumes:
  &my-volumes
      - ..:/opt/project
      - ../../../ext-libs:/opt/ext-libs
      - ./third-mapping:/opt/third

services:
  full-tests:
    volumes:
      *my-volumes

  unit-tests:
    volumes:
      *my-volumes


回答3:

I got docker-compose version 3 working with unison. Basically had to replace "volumes_from" and create a global "volumes" declaration.

version: "3"
services:
  unison:
    container_name: xxx_unison
    image: onnimonni/unison
    environment:
      - UNISON_DIR=/var/www/$DOCKER_WEB_DOMAIN/htdocs/
      - UNISON_USER=www-data
      - UNISON_UID=1000
      - UNISON_GID=1001    
    volumes:
      - unison_file:/var/www/$DOCKER_WEB_DOMAIN/htdocs/
    networks: 
      - frontend    
    ports:
      - "5000:5000"    
  database: 
    container_name: xxx_database
    image: percona
    depends_on:
      - unison
    environment:
      - MYSQL_ROOT_PASSWORD=xxx
      - MYSQL_DATABASE=xxx # Assign a database to be created on container first load  
    volumes: 
      - ./mysql/import:/docker-entrypoint-initdb.d
      - ./mysql/data/:/var/lib/mysql/ 
      - ./logs:/var/log/mysql
      - ./mysql/conf:/etc/mysql/conf.d          
    ports: 
      - "3306:3306"
    networks: 
      - frontend
  web:
    container_name: xxx_web
    image: ubuntu
    depends_on:
      - unison    
    volumes: 
      - ./nginx/ssl/:/etc/nginx/ssl/ 
      - ./logs/:/var/log/nginx
      - ./shell/run.sh:/run.sh
      - unison_file:/var/www/$DOCKER_WEB_DOMAIN/htdocs/
    ports:
      - "80:80"
      - "443:443"
    networks: 
      - frontend
    env_file:
      - ./.env      
networks:
  frontend:
volumes:
  unison_file: