30

I need both java and python in my docker container to run some code.

This is my dockerfile: It works perpectly if I don't add the FROM openjdk:slim

#get python
FROM python:3.6-slim

RUN pip install --trusted-host pypi.python.org flask

#get openjdk

FROM openjdk:slim


COPY . /targetdir
WORKDIR /targetdir

# Make port 81 available to the world outside this container
EXPOSE 81

CMD ["python", "test.py"]

And the test.py app is in the same directory:

from flask import Flask

import os
app = Flask(__name__)


@app.route("/")

def hello():
    html = "<h3>Test:{test}</h3>"
    test = os.environ['JAVA_HOME']

    return html.format(test = test)


if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0',port=81)

I'm getting this error:

D:\MyApps\Docker Toolbox\Docker Toolbox\docker.exe: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"python\": executable file not found in $PATH": unknown.

What exactly am I doing wrong here? I'm new to docker, perhaps I'm missing a step.

Additional details

My goal

I have to run a python program that runs a Java file. The python library I'm using requires the path to JAVA_HOME.

My issues:

  • I do not know Java, so I cannot run the file properly.

  • My entire code is in Python, except this Java bit

  • The Python wrapper runs the file in a way I need it to run.

pajamas
  • 1,194
  • 1
  • 12
  • 25

12 Answers12

35

An easier solution to the above issue is to use multi-stage docker containers where you can copy the content from one to another. In the above case you can have openjdk:slim as the base container and then use content from a python container to be copied over into this base container as follows:

FROM openjdk:slim
COPY --from=python:3.6 / /

... 

<normal instructions for python container continues>

...

This feature is available as of Docker 17.05 and there are more things you can do using multi-stage build as in copying only the content you need from one to another.

Reference documentation

sharma
  • 549
  • 5
  • 5
  • 4
    This is exactly what I was looking for! Granted, you need to use compatible images and stuff but this makes it so much more clear than all the other answers and some of them dont work anymore because openjdk is a pain in the behind. – Subramaniam Ramasubramanian Dec 17 '19 at 11:56
  • this is actually the best practice to do multi-stage builds. thanks for providing this. – liamsuma Aug 07 '20 at 20:53
  • 2
    when i do a similar step `COPY --from=python:3.6 / /`, but the other way around, i.e. i have a Java base image, and then `COPY --from=java_stage / /` I get Python errors when trying to do Python `pip3 install ...` i think the Python files are overwritten by the COPY step? – cryanbhu Jan 14 '21 at 04:12
  • 3
    Copying the entire root from one image to another is bound to be trouble. Who knows what you are clobbering there? If it does work then it's luck and unless you precisely pin those image versions it's probably going to break on you as updates occur. – Davos Dec 16 '21 at 11:31
32

OK it took me a little while to figure it out. And my thanks go to this answer.

I think my approach didn't work because I did not have a basic version of Linux.

So it goes like this:

  1. Get Linux (I'm using Alpine because it's barebones)
  2. Get Java via the package manager
  3. Get Python, PIP

OPTIONAL: find and set JAVA_HOME

  1. Find the path to JAVA_HOME. Perhaps there is a better way to do this, but I did this running the running the container, then I looked inside the container using docker exec -it [COINTAINER ID] bin/bash and found it.
  2. Set JAVA_HOME in dockerfile and build + run it all again

Here is the final Dockerfile ( it should work with the python code in the question) :

### 1. Get Linux
FROM alpine:3.7

### 2. Get Java via the package manager
RUN apk update \
&& apk upgrade \
&& apk add --no-cache bash \
&& apk add --no-cache --virtual=build-dependencies unzip \
&& apk add --no-cache curl \
&& apk add --no-cache openjdk8-jre

### 3. Get Python, PIP

RUN apk add --no-cache python3 \
&& python3 -m ensurepip \
&& pip3 install --upgrade pip setuptools \
&& rm -r /usr/lib/python*/ensurepip && \
if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi && \
if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python3 /usr/bin/python; fi && \
rm -r /root/.cache

### Get Flask for the app
RUN pip install --trusted-host pypi.python.org flask

####
#### OPTIONAL : 4. SET JAVA_HOME environment variable, uncomment the line below if you need it

#ENV JAVA_HOME="/usr/lib/jvm/java-1.8-openjdk"

####

EXPOSE 81    
ADD test.py /
CMD ["python", "test.py"]

I'm new to Docker, so this may not be the best possible solution. I'm open to suggestions.

UPDATE: COMMON ISUUES

  • Difficulty using python packages

As Joabe Lucena pointed out here, Alpine can have issues certain python packages. I recommend that you use a Linux distro that works best for you, e.g. centos.

pajamas
  • 1,194
  • 1
  • 12
  • 25
  • This answer shows how to build a docker container with installed Java and python. When you run python, the Java process will not start. You can ssh your docker and see running processes (`top`) https://stackoverflow.com/questions/30172605/how-do-i-get-into-a-docker-containers-shell – Yan Khonski Feb 05 '19 at 14:43
  • It was a great solution !!!.Really Helped me .I was looking for putting Python 3 and Java 8 in same docker image and i achieved it thru the above way. – Ankur Srivastava Aug 12 '19 at 01:07
  • I needed Java & Python3 as well (but no pip & flask needed) this solution created a 170mb Docker image. Using openjdk:slim and apt-getting python3 I got >400mb. Both images build roughly equally fast. – Matteljay Jun 11 '20 at 14:17
  • Even after setting the java home, in the bash shell when i type java or javac - there is no exec found using above docker file. (Error : Command not found) – AKS Jul 15 '20 at 06:03
  • PEP656 has led to cibuildwheel now supporting Linux distros that use musl i.e. Alpine Linux, so that might be different now see https://discuss.python.org/t/pep-656-platform-tag-for-linux-distributions-using-musl/7165/40 and https://cibuildwheel.readthedocs.io/en/stable/changelog/#v220 – Davos Dec 16 '21 at 11:24
8

I found Sunny Pal's answer very useful but I made the copy more specific and added the necessary environment variables and update-alternatives lines so that Java was accessible from the command line in the Python container.

FROM python:3.9-slim
COPY --from=openjdk:8-jre-slim /usr/local/openjdk-8 /usr/local/openjdk-8

ENV JAVA_HOME /usr/local/openjdk-8

RUN update-alternatives --install /usr/bin/java java /usr/local/openjdk-8/bin/java 1
...
Steven Gillies
  • 471
  • 7
  • 18
  • Using the more specific copy, adding the ENV and using update-alternatives is a very sane approach. – Davos Dec 16 '21 at 14:15
5

Another alternative is to simply use docker-java-python image from docker hub. https://hub.docker.com/r/rappdw/docker-java-python

FROM rappdw/docker-java-python:openjdk1.8.0_171-python3.6.6
RUN java -version
RUN python --version
  • Please note that this package is not actively maintained and is built on java 8. – Sandor Farkas Apr 15 '21 at 21:37
  • It is old but it's crafted sensibly and would be easy to update and maintain, see https://github.com/rappdw/docker-java-python/blob/master/Dockerfile . The Python dockerfiles are complex so starting with the python base container and installing the azul zulu openJDK on top - as it does - looks like a good approach. – Davos Dec 16 '21 at 11:27
2

Oh, let me add my five cents. I took python slim as a base image. Then I found open-jdk-11 (Note, open-jdk-10 will fail because it is not supported) base image code!... And copy-pasted it into my docker file.

Note, copy-paste driven development is cool... ONLY when you understand each line you use in your code!!!

And here it is!

<!-- language: shell -->
FROM python:3.7.2-slim

# Do your stuff, install python.

# and now Jdk
RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get update && apt-get upgrade -y \
    && apt-get install -y --no-install-recommends curl ca-certificates \
    && rm -rf /var/lib/apt/lists/*

ENV JAVA_VERSION jdk-11.0.2+7

COPY slim-java* /usr/local/bin/

RUN set -eux; \
    ARCH="$(dpkg --print-architecture)"; \
    case "${ARCH}" in \
       ppc64el|ppc64le) \
         ESUM='c18364a778b1b990e8e62d094377af48b000f9f6a64ec21baff6a032af06386d'; \
         BINARY_URL='https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.1%2B13/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.1_13.tar.gz'; \
         ;; \
       s390x) \
         ESUM='e39aacc270731dadcdc000aaaf709adae7a08113ccf5b4a045bc87fc13458d71'; \
         BINARY_URL='https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11%2B28/OpenJDK11-jdk_s390x_linux_hotspot_11_28.tar.gz'; \
         ;; \
       amd64|x86_64) \
         ESUM='d89304a971e5186e80b6a48a9415e49583b7a5a9315ba5552d373be7782fc528'; \
         BINARY_URL='https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.2%2B7/OpenJDK11U-jdk_x64_linux_hotspot_11.0.2_7.tar.gz'; \
         ;; \
       aarch64|arm64) \
         ESUM='b66121b9a0c2e7176373e670a499b9d55344bcb326f67140ad6d0dc24d13d3e2'; \
         BINARY_URL='https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.1%2B13/OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.1_13.tar.gz'; \
         ;; \
       *) \
         echo "Unsupported arch: ${ARCH}"; \
         exit 1; \
         ;; \
    esac; \
    curl -Lso /tmp/openjdk.tar.gz ${BINARY_URL}; \
    sha256sum /tmp/openjdk.tar.gz; \
    mkdir -p /opt/java/openjdk; \
    cd /opt/java/openjdk; \
    echo "${ESUM}  /tmp/openjdk.tar.gz" | sha256sum -c -; \
    tar -xf /tmp/openjdk.tar.gz; \
    jdir=$(dirname $(dirname $(find /opt/java/openjdk -name javac))); \
    mv ${jdir}/* /opt/java/openjdk; \
    export PATH="/opt/java/openjdk/bin:$PATH"; \
    apt-get update; apt-get install -y --no-install-recommends binutils; \
    /usr/local/bin/slim-java.sh /opt/java/openjdk; \
    apt-get remove -y binutils; \
    rm -rf /var/lib/apt/lists/*; \
    rm -rf ${jdir} /tmp/openjdk.tar.gz;

ENV JAVA_HOME=/opt/java/openjdk \
    PATH="/opt/java/openjdk/bin:$PATH"
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport"

Now references. https://github.com/AdoptOpenJDK/openjdk-docker/blob/master/11/jdk/ubuntu/Dockerfile.hotspot.releases.slim

https://hub.docker.com/_/python/

https://hub.docker.com/r/adoptopenjdk/openjdk11/

I used them to answer this question, which may help you sometime. Running Python and Java in Docker

Yan Khonski
  • 12,225
  • 15
  • 76
  • 114
1

I believe that by adding FROM openjdk:slim line, you tell docker to execute all of your subsequent commands in openjdk container (which does not have python)

I would approach this by creating two separate containers for openjdk and python and specify individual sets of commands for them.

Docker is made to modularize your solutions and mashing everything into one container is usually a bad practice.

Simas Joneliunas
  • 2,890
  • 20
  • 28
  • 35
  • I updated the question and added the **Additional details** section. Based on your answer. I should create a new container where Java is installed. Then my python code would just require the path to **JAVA_HOME**. How do I do that? I looked at the docker documentation [here](https://docs.docker.com/v17.09/engine/userguide/networking/default_network/container-communication/) and it's really confusing. – pajamas Jul 01 '18 at 11:25
1

I tried pajamas's anwser which worked very well for creating this image. However, when trying to install packages like gensim, pandas or else, I faced some errors like: don't know how to compile Fortran code on platform 'posix'. I searched and tried this, this and that but none worked for me.

So, based on pajamas's anwser I decided to convert his image from Alpine to Centos which worked very well. So here's a Dockerfile that might help someone who's may be struggling in this scenario like I was:

# Get Linux
FROM centos:7

# Install Java
RUN yum update -y \
&& yum install java-1.8.0-openjdk -y \
&& yum clean all \
&& rm -rf /var/cache/yum

# Set JAVA_HOME environment var
ENV JAVA_HOME="/usr/lib/jvm/jre-openjdk"

# Install Python
RUN yum install python3 -y \
&& pip3 install --upgrade pip setuptools wheel \
&& if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi \
&& if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python3 /usr/bin/python; fi \
&& yum clean all \
&& rm -rf /var/cache/yum

CMD ["bash"]
Joabe Lucena
  • 792
  • 8
  • 21
0

you should have one FROM in your dockerfile (unless you use multi-stage build for the docker)

RELW
  • 189
  • 1
  • 14
0

I think i found easiest way to mix java jdk 17 and python3. I is not working on python2

FROM openjdk:17.0.1-jdk-slim


RUN apt-get update && \
    apt-get install -y software-properties-common && \
    apt-get install -y python3-pip

Software Commons have python3 lightweight version. (3.9.1 version)

U can also install some libraries like that.

RUN python3 -m pip install --upgrade pip && \
    python3 -m pip install numpy && \
    python3 -m pip install opencv-python

OR

RUN apt-get update && \
    apt-get install -y ffmpeg
Talha Dilber
  • 117
  • 1
  • 3
0

Easiest is to just start from a Python image and add the OpenJDK. Note that FROM openjdk has been deprecated and replaced with eclipse-temurin

FROM python:3.10

ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:17-jre $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"

RUN pip install --trusted-host pypi.python.org flask

See How to use this Image - Using a different base Image section of https://hub.docker.com/_/eclipse-temurin for details.

Martin Tapp
  • 3,106
  • 3
  • 32
  • 39
0

Here's one I personally use that works with Java 17.

FROM eclipse-temurin:17-jre-alpine

# Install Python and pip, can modify with a specific version of python too
RUN apk add --no-cache python3 py3-pip

# Add your own commands, you can add python packages with pip install

Skipped the security best practices to keep this short.

Phong Phuong
  • 369
  • 4
  • 8
-2

Instead of using FROM openjdk:slim you can separately install Java, please refer below example:

# Install OpenJDK-8
RUN apt-get update && \
apt-get install -y openjdk-8-jdk && \
apt-get install -y ant && \
apt-get clean;

# Fix certificate issues
RUN apt-get update && \
apt-get install ca-certificates-java && \
apt-get clean && \
update-ca-certificates -f;
# Setup JAVA_HOME -- useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME
Hemant Singh
  • 1,487
  • 10
  • 15
  • Does this work for you? I'm getting this : dpkg: error processing package openjdk-8-jre:amd64 (--configure): Package openjdk-7-jre-headless is not installed. Package java7-runtime-headless is not installed. Package openjdk-8-jre-headless:amd64 which provides java7-runtime-headless is not configured yet. Package openjdk-7-jre-headless is not installed. Package java7-runtime-headless is not installed. Package openjdk-8-jre-headless:amd64 which provides java7-runtime-headless is not configured yet. – pajamas Jul 01 '18 at 14:19