Docker exit witout throwing an error or executing code - java

I'm trying to run talend job inside a docker, build goes ok but when I run container it just exits without any error. Here is my dockerfile:
FROM store/oracle/serverjre:8
ARG talend_job=export_data_xml
ENV TALEND_JOB ${talend_job}
ENV ARGS ""
WORKDIR /opt/talend
COPY . /opt/talend
### Install Talend Job
RUN yum install -y unzip && \
unzip ${TALEND_JOB}.zip && \
rm -rf ${TALEND_JOB}.zip && \
chmod +x ${TALEND_JOB}/${TALEND_JOB}_run.sh
VOLUME /data
CMD ["/bin/sh","-c","${TALEND_JOB}/${TALEND_JOB}_run.sh ${ARGS}"]
Run command:
docker run -it demo:latest
It doesn't execute a code or throw error. Any idea what can be wrong or how to debug it at least?
Thanks.

My best guess it would be that is a problem with the file paths.
You can debug it by running your image with:
docker run -ti demo:latest /bin/sh
In order to go inside the container,
check if export_data_xml_run.sh is in the right path (/opt/talend/export_data_xml) and try to run it form there

Some things worth a try:
Add some echo statements after each command, eg:
RUN echo "About to install unzip..." && yum install -y unzip && echo "unzip installed" \
...
If its failing here, you should at least see the echo statements.
Below, it looks like you're setting pwd to /opt/talend, then performing a COPY of whatever is in pwd to the same dir... effectively a null operation.
WORKDIR /opt/talend
COPY . /opt/talend
This last line, be sure to pass the string args as individual strings. I've been working on something recently and the two strings I passed as "command, input" which was treated as a single string, rather than two individual strings (which is what I actually wanted):
CMD ["/bin/sh","-c","${TALEND_JOB}/${TALEND_JOB}_run.sh ${ARGS}"]
You could also log into the docker container to try and debug it once its running using:
docker exec -it [imageId] sh
Once inside the container you can run various commands to validate everything is as you expect it to be.
HTH

Related

gitlab-runner passes wrong arguments to custom image

I have written a small CLI using Java, Argparse4j, and packaged it in docker using this Dockerfile:
FROM openjdk:18
ENV JAR_NAME "my-jar-with-dependencies.jar"
ENV PROJECT_HOME /opt/app
RUN mkdir -p $PROJECT_HOME
WORKDIR $PROJECT_HOME
COPY run.sh $PROJECT_HOME/run.sh
RUN chmod +x $PROJECT_HOME/run.sh
COPY target/$JAR_NAME $PROJECT_HOME/cli.jar
ENTRYPOINT ["./run.sh"]
The last line of the Dockerfile then invokes a simple bash script:
#!/bin/bash
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./cli.jar "$#"
The CLI I wrote has three main actions: upload, download and apply. Therefore argparse4j expects one of these actions to be passed as the first parameter, i.e.
java -jar cli.jar download #... whatever other argument
This works just fine when running the docker image locally, but completely fails when running in the CI pipeline:
download:
stage: download
image: <url>/my-image:<tag>
variables:
URL: <URL>
API_KEY: <API_KEY>
CI_DEBUG_TRACE: "true"
script:
- download -f zip -u true test-download.zip
This is the error that is returned:
Executing "step_script" stage of the job script 00:01
Using docker image sha256:<sha> for <url>/my-image:<tag> with digest <url>/my-image:<tag>#sha256:<sha> ...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
usage: tool [-h] ACTION ...
tool: error: invalid choice: 'sh' (choose from 'upload', 'download',
'apply')
I have tried following the suggestion in gitlab-runner doesn't run ENTRYPOINT scripts in Dockerfile but I can't seem to get the CI part to work correctly.
I would like to avoid using the entrypoint directive as it needs to be used on multiple files, so I rather fix the issue at the root.
Does anyone have an idea of what is happening or how to fix it?
I would like to avoid using the entrypoint directive as it needs to be used on multiple files, so I rather fix the issue at the root.
You can change your Dockerfile instead to keep default ENTRYPOINT (as openjdk:18 doesn't define any entrypoint, it will be empty):
FROM openjdk:18
# ...
# ENTRYPOINT ["./run.sh"] # remove this
# Add run.sh to path to be able to use `run.sh` from any directory
ENV PATH="${PATH}:/opt/app"
And update your run.sh to specify full path to jar:
#!/bin/bash
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar $PROJECT_HOME/cli.jar "$#"
Now your container will start in Gitlab without having to specify entrypoint keyword for job. You can then setup something like this:
download:
stage: download
image: <url>/my-image:<tag>
variables:
URL: <URL>
API_KEY: <API_KEY>
CI_DEBUG_TRACE: "true"
script:
# Specify command using run.sh
# This command is run from within your container
# Note that script is not an argument passed on your image startup
# But independent commands run within your container using shell
- run.sh download -f zip -u true test-download.zip
Notes:
Gitlab won't run your script in Dockerfile's WORKDIR but in a dedicated directory where your project will be cloned.. Using ./ will look for script and jar in current directory at the moment your command is run, but they wouldn't be found if not run from /opt/app. Specyfing full path to jar and adding your run.sh script to PATH make sure they'll be found wherever your run.sh from. Alternatively you could run cd /opt/app in your job's script but it may cause unwanted side effects.
Without ENTRYPOINT you won't be able to run Docker commands like this
docker run " <url>/my-image:<tag>" download ...
You'll need to specify either COMMAND or --entrypoint such as
docker run "<url>/my-image:<tag>" run.sh download ...
docker run --entrypoint run.sh "<url>/my-image:<tag>" download ...
You specified not wanting to do this, but overriding image entrypoint on your job seems a much simpler and straightforward solution. Using multiple files you may leverage Gitlab's extends and include.
And now for the fun part
what is happening
When Gitlab run your container for a job it will use the entrypoint defined in your Dockerfile by default. From doc:
The runner starts a Docker container using the defined entrypoint. The default from Dockerfile that may be overridden in the
.gitlab-ci.yml file.
The runner attaches itself to a running container.
The runner prepares a script (the combination of before_script, script, and after_script).
The runner sends the script to the container’s shell stdin and receives the output.
And what the doc doesn't say is that Gitlab will try to use various form of sh as Docker command. In short for step 1. it's like running this Docker command:
# Gitlab tries to run container for your job
docker run -it "<url>/my-image:<tag>" sh
It doesn't work as Gitlab will use default entrypoint and the final command run in Docker is:
./run.sh sh
Where ./run.sh is the entrypoint from Dockerfile and sh is the command provided by Gitlab. It causes the error you see:
tool: error: invalid choice: 'sh' (choose from 'upload', 'download', 'apply')
You never reach your job's script (step 4). See ENTRYPOINT vs. CMD for details.
Furthermore, the script you define is a command itself. Even if your container started, it wouldn't work as the following command would be run inside your container:
download -f zip -u true test-download.zip
# 'download' command doesn't exists
# You probably want to run instead something like:
/opt/app/run.sh download -f zip -u true test-download.zip
So, after a bit of research, I have been able to find a solution that works for me.
From my research (and as Pierre B. pointed out in his answer), Gitlab essentially tries to inject a shell script that performs a check for which shell is available.
Now, my solution is in no way elegant, but does achieve what I wanted. I modified the Dockerfile like so:
FROM openjdk:18-bullseye
ENV JAR_NAME "my-jar.jar"
ENV PROJECT_HOME /opt/app
RUN mkdir -p $PROJECT_HOME
WORKDIR $PROJECT_HOME
COPY run.sh $PROJECT_HOME/run.sh
RUN chmod +x $PROJECT_HOME/run.sh
COPY target/$JAR_NAME $PROJECT_HOME/cli.jar
RUN echo '#!/bin/bash \njava $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar $PROJECT_HOME/cli.jar "$#"' > /usr/bin/i18n && \
chmod +x /usr/bin/i18n
ENTRYPOINT ["./run.sh"]
And also modified the run.sh script this way:
#!/bin/bash
if [[ -n "$CI" ]]; then
echo "this block will only execute in a CI environment"
exec /bin/bash
else
echo "Not in CI. Running the image normally"
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./cli.jar "$#"
fi
This works because Gitlab, in its list of predefined variables, provides a CI env var that is set when the script is running on the CI. By doing so, I skip the java invocation but leave it in the case I need to use it when not on a CI.
Now when I need to use my image, all I need to specify in my .gitlab-ci.yml file is this:
download:
stage: download
image: <url>/my-image:<tag>
variables:
URL: <URL>
API_KEY: <API_KEY>
CI_DEBUG_TRACE: "true"
script:
- i18n download -f zip -u true test-download.zip # This calls the script that was injected in the Dockerfile
This way I essentially mimic an actual CLI, and can use it in all my projects that require this tool.
I am not sure though why I need to "echo" the script for the CLI, and I can't simply copy it. For some reason the env variables are not passed down and I couldn't spend any more time debugging it. So for now, it will stay like this.
If you have any recommendations on how to clean this up, please leave some comments and I will edit my answer!
Try to wrap your script in single quotes:
script:
- 'download -f zip -u true test-download.zip'
EDIT:
Oh, this open bug in gitlab could be relevant to you

Use cp in entrypoint for docker run

There is Dockerfile
FROM openjdk:11.0.12-jre-slim
COPY target/app.jar /app.jar
COPY configs configs
ENTRYPOINT ["java","-jar","/app.jar"]
In folder configs contains json configs for java application.
The build docker command is:
docker build --build-arg -f ~/IdeaProjects/app --no-cache -t app:latest
And the run command is:
docker run --entrypoint="cp configs var/opt/configs/ && java -jar app.jar" app:latest
Let's omit the ability to copy configs in the Dockerfile via COPY command. Unfortunately, this must be done using --entrypoint.
An error occurs when the docker run command was executed:
docker: Error response from daemon: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "cp configs var/opt/configs/ && java -jar app.jar": stat cp configs var/opt/configs/ && java -jar app.jar: no such file or directory: unknown.
Could you explain why the error occurred in this case?
I would do this with an entrypoint wrapper script. A Dockerfile can have both an ENTRYPOINT and a CMD; if you do, the CMD gets passed as arguments to the ENTRYPOINT. This means you can make the ENTRYPOINT a shell script that does first-time setup, then ends with exec "$#" to replace itself with the CMD.
#!/bin/sh
# docker-entrypoint.sh
# copy the configuration to the right place
cp configs var/opt/configs/
# run the main container command
exec "$#"
In the Dockerfile, make sure to COPY the script in (it should be checked in to source control as executable) and set it as the ENTRYPOINT.
...
COPY docker-entrypoint.sh .
ENTRYPOINT ["./docker-entrypoint.sh"] # must be JSON-array syntax
CMD ["java", "-jar", "/app.jar"] # what was previously ENTRYPOINT
When you run the container it's straightforward to replace the CMD, so you can double-check that this is doing the right thing by running an interactive shell in place of the java application.
docker run -v "$PWD/alt-configs:/configs" --rm -it my-image sh
If you do need to override the command like this at docker run time, the command you show uses && to run two commands consecutively. This needs to run a shell to be understood correctly, and in this context you need to manually provide a /bin/sh -c wrapper.
I would still recommend changing ENTRYPOINT to CMD in your Dockerfile; then you could run a relatively straightforward
docker run \
... \
-v "$PWD/alt-configs:/configs" \
my-image \
/bin/sh -c 'cp configs var/opt/configs && java -jar /app.jar'
If you use --entrypoint, it only takes the first word out of this command, and it is a Docker options so it needs to come before the image name. I'd recommend designing your image to avoid needing this awkward construct.
docker run \
... \
-v "$PWD/alt-configs:/configs" \
--entrypoint /bin/sh \
my-image \
-c 'cp configs var/opt/configs && java -jar /app.jar'
Your proposed command is having problems because it's trying to pass the entire command, including the embedded spaces and shell operators, as a single word, but that causes the OS-level process handling to try to look for an executable file with spaces and ampersands in the filename, hence the "no such file or directory" error.

"Activemq not found" error after running custom Docker image

We have a legacy application that I am trying to dockerize. The jar of the application has both the application and an activemq bundled together. (We cannot change the way it is built). And has certain installation steps. I created the following initial Dockerfile for this however I am facing an issue (mentioned after the Dockerfile) when I run the image.
The Dockerfile looks like this :
FROM registry:4000/openjdk:8-jre-alpine
RUN addgroup -S appuser && adduser -S -G appuser appuser
ADD ./fe.jar /home/appuser
RUN chmod +x /home/appuser/fe.jar \
&& chown appuser:appuser /home/appuser/fe.jar
USER appuser
RUN ["java", "-jar", "/home/appuser/fe.jar", "-i"]
WORKDIR /home/appuser/fe/activemq/bin
CMD ["/bin/sh", "-c", "activemq"]
The RUN command extracts the application and the activemq at the location into folder called fe.
The WORKDIR seems to be setting the working directly to activemq/bin. I confirmed this by using sh script which triggers when the image is run. In the sh script I trigger an ls and pwd command to see the contents and the location.
However when I run the image which triggers the CMD command I get the error that :
/bin/sh: activemq: not found
What can be the possible issue here?
If activemq is an executable in your bin directory (and not in PATH) then you need to edit your CMD:
CMD ["/bin/sh", "-c", "./activemq"]
Also make sure that your script is executable.
Found the problem. The activemq script starts with #!/bin/bash and I am trying to run it using sh. I need to first install bash in the image and then run the activemq script using one.
I got the hint from this answer : docker alpine /bin/sh script.sh not found
Now it moved ahead however the container dies after running immediately. Not sure what the issue is. Doesn't even give any error.

How can I set up and run a very simple php-apache server in a docker container that's already using another image?

I'm tasked with creating a very simple, web browser accessible gui that can run a specific java file within a docker container. To do this I've chosen to set up a php-apache server that serves an index.php document with the gui. The Dockerfile looks like this:
FROM php:7.0-apache
COPY src /var/www/html
EXPOSE 80
This gets the gui (index.php is inside the src folder) I've written up and running no problem, but it cannot access and run the required java files (obviously, since this creates a separate container).
The Question:
How can I set up a php-apache server inside the existing Dockerfile (provided below) doing the same thing as the Dockerfile above? My aim is to run the java file using php scripts and display the result to the user.
FROM openjdk:8-jre-slim
WORKDIR /usr/src/app
COPY ["./build/libs/*.jar", "./fooBar.jar"]
ENV JAVA_OPTS=${FOO_JAVA_OPTS}
CMD ["/usr/bin/tail", "-f", "/dev/null"]
I have not written the java file myself, only being tasked with running specific commands using it.
As it is Debian based images. one way of doing it, install packages in the container and create the new images from that.
root#310c94d8d75f:/usr/src/app# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
2: root#310c94d8d75f:/usr/src/app# apt update
3- root#310c94d8d75f:/usr/src/app# apt install apache2
4- root#310c94d8d75f:/usr/src/app# apt install php
finally run : docker commit
after this, you will get a new image with the mentioned name.
Ref: https://docs.docker.com/engine/reference/commandline/commit/
2: you can add the same command in Dockerfile and rebuild.
FROM openjdk:8-jre-slim
WORKDIR /usr/src/app
COPY ["./build/libs/*.jar", "./fooBar.jar"]
ENV JAVA_OPTS=${FOO_JAVA_OPTS}
CMD ["/usr/bin/tail", "-f", "/dev/null"]
RUN apt update && apt install apache2 -y && apt install php -y
There appears to be no easy way of merging images like I initially hoped (You cannot have multiple FROM statements in your Dockerfile). What I eventually ended up doing was to manually merge the two images (openjdk and php) into something like this:
FROM php:7.0-apache
ENV LANG C.UTF-8
RUN { \
echo '#!/bin/sh'; \
echo 'set -e'; \
echo; \
echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \
} > /usr/local/bin/docker-java-home \
&& chmod +x /usr/local/bin/docker-java-home
RUN ln -svT "/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)" /docker-java-home
ENV JAVA_HOME /docker-java-home/jre
ENV JAVA_VERSION 8u212
ENV JAVA_DEBIAN_VERSION 8u212-b01-1~deb9u1
RUN set -ex; \
if [ ! -d /usr/share/man/man1 ]; then \
mkdir -p /usr/share/man/man1; \
fi; \
apt-get update; \
apt-get install -y --no-install-recommends openjdk-8-jre-headless="$JAVA_DEBIAN_VERSION"; \
rm -rf /var/lib/apt/lists/*; \
[ "$(readlink -f "$JAVA_HOME")" = "$(docker-java-home)" ]; \
update-alternatives --get-selections | awk -v home="$(readlink -f "$JAVA_HOME")" 'index($3, home) == 1 { $2 = "manual"; print | "update-alternatives --set-selections" }'; \
update-alternatives --query java | grep -q 'Status: manual'
COPY ["./build/libs/*.jar", "./FooBar.jar"]
ENV JAVA_OPTS=${FOO_JAVA_OPTS}
COPY gui/src /var/www/html
EXPOSE 80
Both are Debian based images so the merging were relatively easy (I also removed much of the cluttering comments from the original image source) and since the openjdk image were simpler, I added it on top of the php image instead of the other way around.

mkdir command with docker

Inside my docker container, this command mkdir -p -m 755 directoryName creates a directory (Blue File) at the given path. However, outside docker, when I attempt to create a directory with the same command mkdir -p -m 755 ContainerID:/root/.../directoryName it seems to be creating an Executable (Green File).
This is causing trouble because with my "create directory" command i'm copying stuff to it, and the command is failing when I do it outside of docker.
This is what my full command will be, when I execute outside docker:
mkdir -p -m 755 ContainerID:/root/../dirName && docker cp someImage.jpg ContainerID:/root/../dirName
Any thoughts on how to to make this work?
To be honest, I have never heard of such mkdir syntax, referencing a different host, but in any case (even if it was supported) I would not use it. You should execute anything you want to to inside a docker container as docker exec ContainerID mkdir -p -m 755 /root/../dirName
If you want to put several commands inside the same docker exec call you can do it by executing docker exec ContainerID bash -c "whatever && whatever2 && ... whateverX"
Have in mind that these commands will be executed as the user referenced in the Dockerfile with an USER clause, defaulting to root. There are some images in which the user is set to something different, leading to permission issues while doing stuff like this. The right approach to follow would depend on whatever you want to achieve.
Hope that helps! :)

Categories

Resources