External SpringBoot properties file on Docker - docker

I'm currently trying to automatically externalize my application.yml file from my Spring Boot app's default location /src/main/resouces/application.yml. I know currently Spring Cloud Server is a good or prefered way to do so, but that may not be an option for my case at this time.
I'm currently trying to extract the .yml file from it's .jar and then copy it to my desired folder.
Unfortunately, I don't seem to get it at all! At some point I try to run RUN ls -lrt /tmp/config and even though I get a success message from COPY command, it's always empty.
This is my curreny setup:
Dockerfile:
FROM openjdk:8-jdk-alpine
VOLUME ["/tmp", "/tmp/config", "/tmp/logs"]
ADD /target/*.jar app.jar
RUN apk add --update unzip && unzip app.jar "*application.yml" && ls -lrt
RUN ls -lrt /BOOT-INF/classes
RUN cp /BOOT-INF/classes/application.yml tmp/config
RUN ls -lrt tmp/config
# ----> Total 0
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar", "--spring.config.location=file:/tmp/config/application.yml"]
And in my docker-compose.yml I have a mapping for all three VOLUMES I'm defining above.
Do you guys have any idea on how to solve this issue without making the user drop the .yml file in the directory at first deploy?
Best regards,
Enrico Bergamo

In the end I have just decided keeping it simple and have a volume mounted just for an additional application.yml file. Before running the container, I'm creating the directory and placing my new .yml file in this dir and it did the trick :)
FROM openjdk:8-jre-alpine
VOLUME ["/tmp", "/tmp/config", "/tmp/logs"]
ADD /target/*.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar", "--spring.config.location=classpath:/application.yml,file:/tmp/config/application.yml"]

Related

No file found when using ENTRYPOINT

I am trying to use ENTRYPOINT and whenever I do that I am getting an error as no such file or directory
Dockerfile:
FROM ubuntu:18.04
COPY . /home
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s /usr/local/bin/docker-entrypoint.sh
WORKDIR /home
RUN chmod 777 /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/bin/bash"]
I have tried giving it permission, tried running it with absolute path also tried this, tried it with #!/bin/bash & #!/bin/sh and in the end, I still get the file not found error.
I am not sure what the problem is.
The question you asked:
I don't remember exactly why, but the file isn't being found because you're calling it docker-entrypoint.sh rather than ./docker-entrypoint.sh.
The question you'll ask soon:
That doesn't entirely fix your problem. You've added execute privileges to the copy of docker-entrypoint.sh in /usr/local/bin, but there's another copy of the file in /home that gets found first and doesn't have execute privileges. You'll get a permissions error when you try to use it. An easy workaround (depending on what you want to do) consists of a modified entrypoint:
ENTRYPOINT ["/bin/bash", "docker-entrypoint.sh"]
Extra details if you'll be using Docker a lot:
Being able to enter a container or image to examine its contents is invaluable. For ubuntu-based images, write down the following line somewhere (replace bash with sh for basically every other linux OS):
docker run -it --rm --entrypoint=bash my_image_name
This will open up a shell in that image and let you play around in the same environment the Dockerfile is running in and debug whatever is causing you problems.

How to write files in Docker Images?

I've copied a file into a docker image with:
COPY dbconfig.xml /var/app/dbconfig.xml
After that I tried to replace some values in the file with:
RUN sed -i "s/PASSWD/$dbpasswd/" /var/app/dbconfig.xml
Note that $dbpassword is an ENV Variable.
When I check the contents of config.xml, by starting a container of that image and running a bash inside it, nothing has changed in the dbconfig.xml.
Now I think I misunderstand some fundamentals of docker images..
I even tested to create a simple file:
RUN echo "test" > newfile.txt
which seems to be deleted after the call..
I know that each RUN statement creates an new layer and after the statement it gets removed(?).
I'm confused. Why does something like installing software with
RUN apt-get install -y some-package
doesn't get removed and creating a simple file does get removed?
So.. how can I change files inside docker images at image-build-time?
Dockerfile:
FROM dchevell/jira-software:8.0
COPY dbconfig.xml /var/atlassian/application-data/jira/dbconfig.xml
WORKDIR /var/atlassian/application-data/jira
# set default password to admin
ENV dbpasswd=admin
RUN sed -i "s/PASSWD/$dbpasswd/" dbconfig.xml \
&& cat dbconfig.xml
RUN echo "test" > newfile.txt
dbconfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<jira-database-config>
<name>defaultDS</name>
<delegator-name>default</delegator-name>
<database-type>postgres72</database-type>
<schema-name>public</schema-name>
<jdbc-datasource>
<url>jdbc:postgresql://docker-postgres:5432/jiradb</url>
<driver-class>org.postgresql.Driver</driver-class>
<username>atlasdb</username>
<password>PASSWD</password>
<pool-test-while-idle>true</pool-test-while-idle>
</jdbc-datasource>
</jira-database-config>
Update 1
Confusingly, when I COPY something in the WORKDIR folder, it persists, but when I try to modify it afterwards with SED, these changes do not persist! I think there is some really dark magic happening in the background..
Maybe I try to bind mount my preconfigured dbconfig.xml within docker-compose and see if that helps..
Update 2
From the Docker Documentation:
Changing the volume from within the Dockerfile: If any build steps
change the data within the volume after it has been declared, those
changes will be discarded.
I totally missed that! Thanks David for pointing me there:) So creating and writing Files DOES work as expected, but be careful with VOLUME directories. RUN statements do not work here.
So to address this issue, the best practice would be to bind mount the file into that volume.
If you look at the Dockerfile for that base image, it says in part
ENV JIRA_HOME /var/atlassian/application-data/jira
VOLUME ["${JIRA_HOME}"]
Once you execute a VOLUME statement in a Dockerfile, later Dockerfile statements can't make any more changes in that specific directory.
Given that the sorts of things you're trying to change are very installation-specific settings (admin password, database settings) I wouldn't try to build an image out of these. Instead I'd use the docker run -v option to inject the configuration file at runtime.
Each RUN statement does not create an intermediate container but creates a new layer on union file system, which is read only. When you run an image, a special writable layer is created for this container and all the changes you make on this container are written to this layer. (except the volumes. which is a different concept). That is why docker is able to share the same image (or even layers) between containers safely, without affecting each other. You can check docker documentation for more information.
For your question, you should see every change you make on build time in the running instance of this image, unless you somehow delete or overwrite them.
See this question.
The commands you are running are correct and they should create the files. What I suspect is that when you run your container, the jira application is overwriting the WORKDIR you have specified.
Try this Dockerfile:
WORKDIR /var/atlassian/application-data/jira
# set default password to admin
ENV dbpasswd=admin
RUN sed -i "s/PASSWD/$dbpasswd/" dbconfig.xml \
&& cat dbconfig.xml
WORKDIR /testtest
RUN touch test.txt
RUN echo "test" > newfile.txt
WORKDIR /var/atlassian/application-data/jira
Now if you start the container, you can see that the files are being created inside the /testtest folder.
If you want your changes to the dbconfig.xml file to persist you should try using volumes to bind the local dbconfig.xml with the jira folder.
Thanks for this interesting question :)

Docker COPY issue while building the docker image

I have a project which is Maven based multi module project
It has various modules with in, like common-utils, web, theme, etc, etc
And also in the root location it has the Dockerfile which is not default one but I have named it Dockerfile.cli due to some requirements
Dockerfile.cli contents here:-
FROM tomcat
ENV NEUW_LOG_HOME /neuw/web/logs
RUN echo "running the image, making it a container :-)"
RUN mkdir -p "/neuw/web/theme-v4"
COPY web/target/web.war /usr/local/tomcat/webapps/ROOT.war
COPY theme-v4 /neuw/web/theme-v4
CMD ["catalina.sh", "jpda", "run"]
Now why I am here -> am getting the below error while building the image:-
COPY failed: stat /var/lib/docker/tmp/docker-builder838877607/web/target/web.war: no such file or directory
The command I use to run the image build is like below and running it on the root of the project which contains both theme and web folder:-
docker build -f Dockerfile.cli -t neuw/web:snapshot-30 .
Any hints and help for the issue?
Which directory are you in when you run this command? Could you do ls /web/target/ from that directory? I ask because I think your Dockerfile is expecting to find a web.war in ./web/target relative to the directory you are running in.
Edit (to save anyone digging through the comments on this): The target directory did contain the file but it was invisible to docker due to a .dockerignore file with **/target.

Dockerfile not enabling autoptimize

Im pulling a wordpress image and everything is working fine but when I go to the wordpress editor page the following error is on the top of screen.
Autoptimize cannot write to the cache directory (/var/www/html/wp-content/cache/autoptimize/), please fix to enable CSS/ JS optimization!
I assumed RUN chown -R www-data:www-data wp-content/ would solve that issue but its not working. Any ideas would be appreciated. My Dockerfile is below.
FROM wordpress:4.9.2-php7.2-apache
RUN chown -R www-data:www-data wp-content/
COPY ./src /var/www/html/
# Install the new entry-point script
COPY secrets-entrypoint.sh /secrets-entrypoint.sh
RUN chmod +x /secrets-entrypoint.sh
ENTRYPOINT ["/secrets-entrypoint.sh"]
EXPOSE 80
CMD ["apache2-foreground"]
I'm not sure the exact permission but you don't want to be writing inside a container so you should define a volume. Since you don't need the data to persist, you can do this in your dockerfile like:
VOLUME /var/www/html/wp-content/cache
This will set up a default volume where Docker will choose the location on your host, but you can mount it to a named volume instead when the container is created if you like.
You could also use a tmpfs volume which is good for things like cache files.

Docker mount happens before or after entrypoint execution

I'm building a Docker image to run my Spring Boot based application. I want to have user to be able to feed a run time properties file by mounting the folder containing application.properties into container. Here is my Dockerfile,
FROM java:8
RUN mkdir /app
RUN mkdir /app/config
ADD myapp.jar /app/
ENTRYPOINT ["java","-jar","/app/myapp.jar"]
When kicking off container, I run this,
docker run -d -v /home/user/config:/app/config myapp:latest
where /home/user/config contains the application.properties I want the jar file to pick up during run time.
However this doesn't work, the app run doesn't pick up this mounted properties file, it's using the default one packed inside the jar. But when I exec into the started container and manually run the entrypoint cmd again, it works as expected by picking up the file I mounted in. So I'm wondering is this something related to how mount works with entrypoint? Or I just didn't write the Dockerfile correctly for this case?
Spring Boot searches for application.properties inside a /config subdirectory of the current directory (among other locations). In your case, current directory is / (docker default), so you need to change it to /app. To do that, add
WORKDIR /app
before the ENTRYPOINT line.
And to answer your original question: mounts are done before anything inside the container is run.

Resources