How to manage permissions for a volume mounted into a docker container?



  • I'm developing a Wordpress theme, and want to use Docker in my dev setup. What I've done is pretty simple:

    • create a database service running MySQL 5.7
    • create a wordpress service, where I mount my theme folder as a volume into /var/www/html/wp-content/themes

    I'm struggling with the volume permissions, however.

    I'm working with the following project folder structure:

    .
    ├── docker-compose.yml
    └── my-theme
    
    

    My docker-compose.yml file looks like this:

    version: '3.2'
    
    services:
      database:
        image: mysql:5.7
        volumes:
          - my_data:/var/lib/mysql
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordpress
          MYSQL_PASSWORD: root
    
      wordpress:
        depends_on:
          - database
        image: wordpress:php7.3-apache
        ports:
          - '8000:80'
        restart: always
        environment:
          WORDPRESS_DB_HOST: database:3306
          WORDPRESS_DB_USER: wordpress
          WORDPRESS_DB_PASSWORD: root
        working_dir: /var/www/html
        volumes:
          - type: volume
            source: ./my-theme
            target: /var/www/html/wp-content/themes/my-theme
    volumes:
      my_data: {}
    
    

    When I run docker-compose up, everything works as expected: containers are created, and I can access Wordpress in the browser. However, the theme I mounted as a volume doesn't render anything when I activate it.

    When I sh into the wordpress container (docker-compose exec wordpress sh) I can see that the wp-content/themes folder is owned by root. So I figured that was the problem.

    I verified this being a permissions issue by manually and recursively chowning the wp-content folder in the container:

    <pre class="lang-sh prettyprint-override">```
    chown -R www-data:www-data /var/www/html/wp-content
    
    
    
    Once that was done, my theme rendered as expected. So now I'm looking for a way to avoid this `chown` process (the idea is that any other dev can clone this project, simply run `docker-compose up` and start working).
    
    The first thing I tried was to make a **Dockerfile** where I would build a slightly customized Wordpress image:
    
    

    FROM wordpress:php7.3-apache
    RUN mkdir -p /var/www/html/wp-content/themes/test-theme
    && chown -R /var/www/html/wp-content

    
    My reasoning behind this was that by creating the directory and `chown`ing it beforehand, the volume would inherit the user:group mapping. Alas, no such thing; mounting the volume overrides this mapping and sets it back to `root:root`.
    
    After that, I tried to set the `APACHE_RUN_USER` and `APACHE_RUN_GROUP` environment variables in my `docker-compose.yml` file:
    
    

    version: '3.2'

    services:
    database:
    ...

    wordpress:
    ...
    environment:
    WORDPRESS_DB_HOST: database:3306
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: root
    APACHE_RUN_USER: '$(id -u)'
    APACHE_RUN_GROUP: '$(id -g)'
    working_dir: /var/www/html
    volumes:
    - type: volume
    source: ./my-theme
    target: /var/www/html/wp-content/themes/my-theme
    volumes:
    my_data: {}

    
    However, this threw a bunch of apache errors on build.
    
    I'm at a bit of a loss here now. Is there any best practice for managing permissions of mounted volumes in Docker? I've googled a lot for this, but the solutions I do find go a little bit over my head.


  • You can do this by overwriting the entrypoint for the wordpress image.

    Create a file startup.sh in your project and make is executable:

    #!/bin/bash
    
    chown -R www-data:www-data /var/www/html/wp-content
    docker-entrypoint.sh apache2-foreground
    
    

    Then in your docker-compose.yml:

    ...
      wordpress:
    ...
        working_dir: /var/www/html
        volumes:
            - './my-theme:/var/www/html/wp-content/themes/my-theme'
            - './startup.sh:/startup.sh'
        entrypoint: /startup.sh
    
    

    This worked for me, let me know if you have problems implementing it.



最新帖子

最新内容

  • S

    This will not work as intended because you call makeGetVisibleTodos only once, when creating the structured selector. The resulting selector will be shared between all component instances. If this is a problem (because the selector result depends on component props, you need to add a level of indirection as described in the reselect docs section you linked:

    <pre class="lang-js prettyprint-override">``` const mapStateToProps = () => createStructuredSelector({ visibleTodos: makeGetVisibleTodos() });

    read more
  • S

    I wonder something if you give an idea I will be grateful.

    I am using reselect package first time in one of my projects. I create makeGetVisibleTodos selector base on the link below then if I link this selector to the component like below using createStructuredSelector;

    const mapStateToProps = createStructuredSelector({ visibleTodos: makeGetVisibleTodos() });

    Does it work? I mean if I use this component several times, have I got any problems because of sharing the component? In this way, I did not create a function for mapStateToProps like makeMapStateToProps.

    https://github.com/reduxjs/reselect#sharing-selectors-with-props-across-multiple-component-instances

    Thank you

    read more
  • S

    You can achieve it using a combination of concatMap, filter and take:

    const URLS = ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com']; // ... public getResponse() { return from(URLS).pipe( concatMap(url => this.http.get<{ isReady: boolean }>(url)), filter(({ isReady }) => isReady), take(1), ); }

    DEMO

    read more
  • S

    I have an array of URL's for which i need to perform a http.get sequentially. And, depending on the result, carry on to the next item in the array.

    For example:

    var myArray = this.myService.getUrls(); // myArray can be something like this: // ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com'] // Or... // ['http://www.us.randomurl.com'] // Or... // ['http://www.us.someurl.com', 'http://www.hk.notaurl.com', 'http://www.pl.someurl.com', 'http://www.in.boringurl.com]

    When i perform a http.get on each of these, my server will respond within its data something along the line of {isReady: true} or {isReady: false}

    What i want to be able to do it for each item in myArray, perform the http.get, and if the result of that individual call is isReady = true, dont perform the next http.get in the array. If isReady = false, then move on to the next URL in the array.

    I have tried flatMap, but its seems i cannot get around hard coding the URLs:

    var myArray = ['http://www.us.someurl.com', 'http://www.hk.anotherurl.com']; this.http.get('http://www.us.someurl.com') .map(res => res.json()) .flatMap(group => this.http.get('http://www.hk.anotherurl.com') ) .map(res => res.json()) .subscribe((res) => { console.log(res); }); });

    read more
  • S

    I think you might have an error with the data, not the python file.

    8/6/2018 6:45 Does not match the format %d/%m/%Y %H:%M:%S because there are no seconds.

    I would test with a :00 added to your time data and test again.

    read more

推荐阅读