Docker + OpenResty - flexible configuration. How to? - docker

I have the openresty application that deploys with docker.
My docker file:
FROM openresty/openresty:alpine-fat
ARG APPLICATION_PATH="/srv/www/my-app"
COPY nginx.conf /usr/local/openresty/nginx/conf
RUN mkdir ${APPLICATION_PATH}
I'm running docker with this command:
docker run -v $(CURRENT_DIRECTORY):/srv/www/my-app -v $(CURRENT_DIRECTORY)/conf:/etc/nginx/conf.d --name="$(APP_NAME)" -p $(PORT):80 -d $(CONTAINER_NAME)
This command stored in the Makefile and variables values like that:
CONTAINER_NAME = my-app
APP_NAME = my-app
override PORT = 8080
ifeq ($(OS),Windows_NT)
CURRENT_DIRECTORY=%cd%
else
CURRENT_DIRECTORY=${PWD}
endif
So also I have my-app.conf stored in the conf directory. This is nginx-configuration file, where I have this line:
content_by_lua_file '/srv/www/my-app/main.lua';
And further I have nginx.conf, where I have this line:
lua_package_path ";;/srv/www/my-app/?.lua;/srv/www/my-app/application/?.lua";
I don't want duplicate /srv/www/my-app in the 3 files. How I can avoid this?

IMO, your approach is not consistent.
You copy nginx.conf file, but mount a volume for my-app.conf (is it included into nginx.conf?)
Curiously that $(CURRENT_DIRECTORY)/conf is mounted twice - as /srv/www/my-app/conf and as /etc/nginx/conf.d.
Below is my approach for OpenResty containers:
Write simple nginx.conf without includes. Copy it into container as you do.
The only reason to mount a folder with nginx.conf is ability to reload nginx configuration after changes. Keep in mind - if you would mount a single file reload may not work. https://github.com/docker/for-win/issues/328
Copy all Lua files mentioned in *_by_lua_file directives into /usr/local/openresty/nginx
Copy all Lua files required from files above (if any) into /usr/local/openresty/lualib
Don't use absolute file paths in *_by_lua_file directives, you may specify relative path to /usr/local/openresty/nginx
Don't use lua_package_path directive, defaults should works.
Here is the simple working example https://gist.github.com/altexy/8f8e08fd13cda25ca47418ab4061ce1b

Related

Populate a volume using multiple containers

I am checking the docker documentation on how to use named volumes to share data between containers.
In Populate a volume using a container it is specified that:
If you start a container which creates a new volume, as above, and the container has files or directories in the directory to be mounted (such as /app/ above), the directory’s contents are copied into the volume. The container then mounts and uses the volume, and other containers which use the volume also have access to the pre-populated content.
So I did a simple example where:
I start a container which creates the volume and mounts it to a directory with existing files
I start a second container on which I mount the volume and indeed I can see the first container's files.
So far so good.
However I wanted to see if it is possible to have pre-populated content from more than one containers.
What I did was
Create two simple images which have their respective configuration files in the same directory
FROM alpine:latest
WORKDIR /opt/test
RUN mkdir -p "/opt/test/conf" && \
echo "container from image 1" > /opt/test/conf/config_1.cfg
FROM alpine:latest
WORKDIR /opt/test
RUN mkdir -p "/opt/test/conf" && \
echo "container from image 2" > /opt/test/conf/config_2.cfg
Create a docker compose which defines a named volume which is mounted on both services
services:
test_container_1:
image:
test_image_1
volumes:
- test_volume:/opt/test/conf
tty: true
test_container_2:
image:
test_image_2
volumes:
- test_volume:/opt/test/conf
tty: true
volumes:
test_volume:
Started the services.
> docker-compose -p example up
Creating network "example_default" with the default driver
Creating volume "example_test_volume" with default driver
Creating example_test_container_2_1 ... done
Creating example_test_container_1_1 ... done
Attaching to example_test_container_1_1, example_test_container_2_1
According to the logs container_2 was created first and it pre-populated the volume. However, the volume was then mounted to container_1 and the only file available on the mount was apparently /opt/test/conf/config_2.cfg effectively removing config_1.
So my question is, if it is possible to have a volume populated with data from 2 or more containers.
The reason I want to explore this, is so that I can have additional app configuration loaded from different containers, to support a multi tenant scenario, without having to rework the app to read the tenant configuration from different folders.
Thank you in advance
Once there is any content in a named volume at all, Docker will never automatically copy content into it. It will not merge content from two different images, update the volume if one of the images changes, or anything else.
I'd advise you to ignore the paragraph you quote in the Docker documentation. Assume any volume you mount into the container is initially empty. This matches the behavior you'll get with Docker bind-mounts (host directories), Kubernetes persistent volumes, and basically any other kind of storage besides Docker named volumes proper. Don't mount a volume over the content in your image.
If you can, restructure your application to avoid sharing files at all. One common use of named volumes I see is trying to republish static assets to a reverse proxy, for example; rather than trying to use a named volume (which will never update itself) you can COPY the static assets into a dedicated Web server image. This avoids the various complexities around trying to use a volume here.
If you really don't have a choice in the matter, then you can approach this with dedicated code in both of the containers. The basic setup here is:
Have a data directory somewhere outside your application directory, and mount the volume there.
Include the original files in the image somewhere different.
In an entrypoint wrapper script, copy the original files into the data directory (the mounted volume).
Let's say for the sake of argument that you've installed the application into /opt/test, and the data directory will be /etc/test. The entrypoint wrapper script can be as little as
#!/bin/sh
# Copy config files from the application tree into the config tree
# (overwriting anything that's already there)
cp /opt/test/* "$TEST_CONFIG_DIR"
# Run the main container command
exec "$#"
In the Dockerfile, you need to make sure that directory exists (and if you'll use a non-root user, that user needs permission to write to it).
FROM alpine
WORKDIR /opt/test
COPY ./ ./
ENV TEST_CONFIG_DIR=/etc/test
RUN mkdir "$TEST_CONFIG_DIR"
ENTRYPOINT ["./entrypoint.sh"]
CMD ["./my_app"]
Finally, in the Compose setup, mount the volume on that data directory (you can't use the environment variable, but consider the filesystem path part of the image's API):
version: '3.8'
volumes:
test_config:
services:
one:
build: ./one
volumes:
- test_config:/etc/test
two:
build: ./two
volumes:
- test_config:/etc/test
You would be able to run, for example,
docker-compose run one ls /etc/test
docker-compose run two ls /etc/test
to see both sets of files appear there.
The entrypoint script is code you control. There's nothing especially magical about it beyond the final exec "$#" line to run the main container command. If you want to ignore files that already exist, for example, or if you have a way to merge in changes, then you can implement something more clever than a simple cp command.

Container based Nginx configuration

Seeking help from developers familiar with Wodby container management. The main objective is changing the MIME Types that are gzipped. I'm confused with the documentation for customizing my Nginx container. The documentation:
https://wodby.com/docs/1.0/stacks/drupal/containers/
suggests I copy "/etc/nginx/conf.d/vhost.conf", modify it, deploy it the repo and use an environment variable to include it. My problem is, even if I could find this file, which is not mounted on the server when created via Wodby, it does not appear that I'm actually able to change the MIME types or the default_type as they are already defined in the nginx.conf file.
I have also attempted to modify the Wodby stack to mount the /etc/ directory so that I could manually edit the nginx.conf file if I had to, but that only freezes the deployment.
Any help would be tremendously appreciated.
Two options
clone a repo https://github.com/wodby/nginx/, change the template file /templates/nginx.conf.tmpl as much as you need and build your own image. See Makefile (/Makefile) for the commands they use to build the image themselves. Use this image as the image for your nginx container from docker-compose.
Run a container with the default settings, shell into the container with docker-compose exec nginx sh and copy the nginx file from the container (use cat /etc/nginx/nginx.conf and copy it somewhere). Create a new file locally and mount it via the docker-compose.yml for the nginx container like
volumes:
- ./nginx-custom.conf:/etc/nginx/nginx.conf

docker-compose only mount files that exist on host to container

Is it possible to to mount a host directory in to a container but only allowing overwriting files that exist on the host?
Example github repo: https://github.com/UniBen/stackoverflow-59031249
E.g
Host:
src/
public/
index.php (200kb)
Container:
src/
public/
index.php (100kb)
vendor/
...
Desired output: (The container file system merged with the mounted host system files which exists. Note the size of the index.php file.)
src/
public/
index.php (200kb)
vendor/
...
Actual output:
src/
public/
index.php (200kb)
Example docker-compose.yml
version: '3.2'
services:
php:
image: php
volumes:
- ./src:/src
Edit: So it looks like overlayfs implemented by docker is only used for bulding docker images and can not be used for volumes what so ever. I still think it's possible to specify a custom driver but not sure how. As a temp fix I've done some fancy stuff with mapping stuff out of container, diffing it and putting it back in but not ideal.
Is it possible to to mount a host directory in to a container but ...
No. The only Docker mounting option is a straight-up "replace this directory in the container with the equivalent directory from the host". There is no way to modify this, selectively hide subdirectories, or implement your "only if it already exists" rule.
In the example you're showing, you probably don't want a volume at all. Files like index.php and a vendor directory are application source code, and in typical use you'd write a Dockerfile, COPY index.php . to move the file into the image, and then RUN composer ... to create the vendor tree. This would be isolated from your host environment, so the vendor directory in the image would be separate from whatever existed on your host system.
Actually, there are no options to control such behavior, e.g, how data between source and dest will be handled. But if David's answer is not really your case you could do something like this:
version: '3.2'
services:
example:
build:
context: .
volumes:
- data:/src
- ./src:/src/host
volumes:
data:
How docker documentation says:
If you start a container which creates a new volume, and the container
has files or directories in the directory to be mounted the
directory’s contents are copied into the volume.
So after that, let's investigate a little bit:
/src # ls
file.a.txt file.c.txt host
/src # cat host/file.a.txt
host
/src # cat file.a.txt
container
/src # cat file.c.txt
container
Data from from container is saved into data named volume. The data from the host machine live in the host folder. Now you can copy from the host folder to the src with cp or rsync with any rules that you want.
This is a quite fishy and artificial example, maybe it's a good idea to rethink current implementation.

Docker-compose exclude from volume and copy

A few lines from docker-compose.yml
volumes:
- ./yii-application:/app/yii-application
- /app/yii-application/common/config/
First line adds to the volume an entire application.
The second one makes some sort of exclude of config folder, so I do not need my config from host machine.
A few lines from Dockerfile
COPY ./config-${APP_ENV}/common /app/yii-application/common/config
Instead of COPY I tried
RUN cp -a /app/config-${APP_ENV}/common/. /app/yii-application/common/config
It does not work either.
I think there is an issue in the order of the commands that are being executed:
When you are building and image with Dockerfile, you are coping the code inside dir /app/yii-application/common/config.
Then, you are mounting a volume: volumes: /app/yii-application/common/config/
and overwriting the existing dir with an empty dir that serves as a volume.
You need to work around that issue.
I moved the config files out of volume directory.
So, now the volume looks like this
volumes:
- ./yii-application:/app/yii-application
Config files in
./

Docker copy files to and mount same folder

I am working on 'tomcat:7.0.75-jre8-alpine' base image and want to deploy my web application alongwith its configurations file. Below is what I am doing in Dockerfile:
......
COPY <my-app-configurations> /org/app/data
COPY <my-app-configurations> /org/app/conf
......
CMD ["catalina.sh", "run"]
And I am using below command to create a container from above image:
$ docker run -p 8080:8080 -v "/c/Users/jaffy/app:/org/app" myapp-image
'/c/Users/jaffy/app' folder is initially empty and I want to get all contents of '/org/app' in it and remains in-sync.
Initially, all configurations are copied in '/org/app' folder but when '/c/Users/jaffy/app' is mounted, '/org/app' gets cleaned/emptied.
How can I solve this issue that host machine folder remains empty initially but afterwards it reflects the exact state of container's '/org/app' folder and its sub-directories.
Thanks a lot in advance.
There is no way to keep the container's folder content when you mount/share a volume over that folder, because the command is replacing the folder not merging it.
If you don't need to replace all the files in the folder you could just mount the file that need to be changed like:
$ docker run -p 8080:8080 -v "/c/Users/jaffy/app/web.xml:/org/app/web.xml" my-app-image

Resources