1

Im trying to run a python3 application in a docker container using CentOS 7 as the base image. So if I'm just playing with it interactively, I type scl enable rh-python36 bash

That obviously switches my standard python2 environment to the python3.6 environment I install earlier(in the Dockerfile) Now, earlier in the dockerfile I run the following:

SHELL ["scl", "enable", "rh-python36"] (and many variations of this)

This enables me to do all of my pip installations in this python3 environment. However, when I actually want to run my app.py with CMD, it defaults to the python2. I've tried playing with ENTRYPOINT and variations of CMD, but I cant seem to make the python3 environment active when the container finally runs. How can I get this running correctly with python3?

Here's the dockerfile:

FROM centos:7
RUN mkdir -p /usr/src/app && \
  yum install -y centos-release-scl && \
  yum install -y rh-python36 && \
  yum install -y rh-python36-python-tkinter
SHELL ["scl", "enable", "rh-python36"]
WORKDIR /usr/src/app
COPY . .
WORKDIR /usr/src/app/codeBase
RUN pip install --no-cache-dir -r /usr/src/app/codeBase/requirements.txt
EXPOSE 9000
CMD ["python",  "run.py"]

I've also tried the alias solution, but I'm afraid it doesnt change the python exe for the CMD: Here's the totally runnable version with that that still prints out python 2.7.5:

FROM centos:7
RUN mkdir -p /usr/src/app && \  
  yum install -y centos-release-scl && \
  yum install -y rh-python36 && \
  yum install -y rh-python36-python-tkinter
WORKDIR /usr/src/app
RUN alias python=$(find / -type f -name "python*" | grep "python3.6$")
CMD ["python",  "-V"]

It just seems as though none of this persists in the new shell created with CMD

jzeef
  • 731
  • 1
  • 13
  • 25
  • Putting an `alias` in your `Dockerfile` is completely misdirected. You should probably avoid aliases entirely anyway; but the way to define an alias in a Dockerfile is to add the alias definition to the shell's initialization file in the Docker image. – tripleee Jun 04 '19 at 15:23
  • @tripleee Yea, I really don't like the alias as a solution. I figured there would be a more docker friendly solution to this. I just posted an answer below that works for me. I don't see any less convoluted way to achieve this though. – jzeef Jun 04 '19 at 15:35

4 Answers4

2

Note:

I am leaving this answer up as context to the question, but this does not solve the issue. My attempt was to modify where python is pointing, but if executing in a RUN command, the shell will exit and you'll lose the alias. Modifying the PYTHONPATH via bash is also a no-go, per this question. Thanks to @tripleee for the catch

Unfortunately, it looks like yum install -y rh-python36 puts that python3.6 in a really weird spot:

find / -type f -name "python*" | grep "python3.6$"
/opt/rh/rh-python36/root/usr/bin/python3.6

You can use that to alias your python command in your Dockerfile:

RUN mkdir -p /usr/src/app && \
  yum install -y centos-release-scl && \
  yum install -y rh-python36 && \
  yum install -y rh-python36-python-tkinter

# Here
RUN alias python=$(find / -type f -name "python*" | grep "python3.6$")

Which should allow you to retain your CMD and also ties it to the correct site-packages:

import sys

sys.path
['', '/opt/rh/rh-python36/root/usr/lib64/python36.zip', '/opt/rh/rh-python36/root/usr/lib64/python3.6', '/opt/rh/rh-python36/root/usr/lib64/python3.6/lib-dynload', '/opt/rh/rh-python36/root/usr/lib64/python3.6/site-packages', '/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages']

It looks like this also might remove the need to scl enable that python environment, as you can then just use python -m pip...:

# No scl has been run
python -m pip install requests
# running python in the terminal
import requests

r = requests.get('http://google.com')
# runs like a charm
C.Nivs
  • 12,353
  • 2
  • 19
  • 44
  • It looks like, for me, the alias does not work. Wit the new minimal dockerfile example I posted above, I'm still getting python 2 when I run the CMD – jzeef Jun 04 '19 at 15:13
  • 1
    And I guess merely overriding the Python executable won't provide you with the correct `PYTHONPATH` etc (and of course as pointed out elsewhere, interactively defining an alias in your Dockerfile will simply define it in a sh ll instance which then immediately exits). – tripleee Jun 04 '19 at 15:40
  • @tripleee you're right, worked for me because I was running the command in a single bash session and not taking into account that the `RUN` will exit that shell to start a new one in a different layer. In that case, you could modify `PYTHONPATH` with an environment variable in that directory. I'll edit my answer – C.Nivs Jun 04 '19 at 16:46
  • 1
    Though you'll still need to reverse engineer what *exactly* `scl enable` does and reimplement everything. So I'm thinking better to figure out how to use the command itself. I'm not a Red Hat / CentOS person so I'm not too familiar with this command. – tripleee Jun 04 '19 at 16:49
  • I'm not either, which is why I went this way – C.Nivs Jun 04 '19 at 16:52
  • @tripleee Looking at [this](https://stackoverflow.com/questions/34911622/dockerfile-set-env-to-result-of-command) question, looks like docker does not support exporting env vars as output of shell commands. So, learning to actually use `scl enable` is the way forward. I'll leave my answer, but I'll add the caveat in an edit – C.Nivs Jun 04 '19 at 16:58
2

SHELL is completely the wrong Dockerfile command for this. You'll probably want to put that in a RUN stanza instead.

The purpose of SHELL is to define the shell used to execute RUN commands. So something like

SHELL ["sh", "-c"] # The default
RUN echo "foo"

ends up running

sh -c 'echo "foo"'

Of course, replacing SHELL with a command which doesn't support this use case will simply break the RUN command for you.

Maybe try something like

FROM centos:7
RUN mkdir -p /usr/src/app && \
  yum install -y centos-release-scl && \
  yum install -y rh-python36 && \
  yum install -y rh-python36-python-tkinter
WORKDIR /usr/src/app
COPY . .
WORKDIR /usr/src/app/codeBase
RUN scl enable rh-python36 pip install --no-cache-dir -r ./requirements.txt
EXPOSE 9000
CMD ["scl", "enable", "rh-python36", "python",  "run.py"]
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Ah, so that explains why all the packages install correctly with pip after running that, but not the CMD itself – jzeef Jun 04 '19 at 15:22
  • Unfortunately putting it all in the CMD didn't work for me. scl seems to create a new shell, where the python runs in the same shell that scl ran in. – jzeef Jun 04 '19 at 15:36
  • Wait, try `CMD ["scl", "enable", "rh-python36", "sh", "-c", "python ./run.py"]` – tripleee Jun 04 '19 at 15:57
2

So I figured this out thanks to the answer from C.Nivs and an answer here. The alias works in an interactive shell, but not for the CMD. What I ended up doing was similar, only in my case I'm creating a new executable in /usr/bin that calls the special python36 exe:

RUN echo -e '#!/bin/bash\n$(find / -type f -name "python*" | grep "python3.6$") "$@"' > /usr/bin/py3 && \
    chmod +x /usr/bin/py3
CMD ["py3",  "-V"]

now py3 runs a script calling the python3 install specifically with whatever argument

jzeef
  • 731
  • 1
  • 13
  • 25
  • But then you get the wrong versions of the modules you just installed because your `PYYHONPATH` is wrong, don't you? – tripleee Jun 04 '19 at 15:44
  • @tripleee interestingly, it's looking like it ties to the correct directories. Looking at `sys.path`: `['', "/usr/src/app/$(find / -type f -name 'python*' | grep 'python3.6$')", '/opt/rh/rh-python36/root/usr/lib64/python36.zip', '/opt/rh/rh-python36/root/usr/lib64/python3.6', '/opt/rh/rh-python36/root/usr/lib64/python3.6/lib-dynload', '/opt/rh/rh-python36/root/usr/lib64/python3.6/site-packages', '/opt/rh/rh-python36/root/usr/lib/python3.6/site-packages']`. Though the second entry is... concerning but doesn't seem to throw `FileNotFoundError` – C.Nivs Jun 04 '19 at 17:08
  • Nah, you can have any junk you want in there, it will just silently ignore directories which don't exist. – tripleee Jun 04 '19 at 17:13
  • 1
    @tripleee What ended up getting this working for me is that before installing with pip, I left in `SHELL ["scl", "enable", "rh-python36"]` Which seems to then let everything install in the right place with the right pip, because i can then access everything i downloaded in my py3 – jzeef Jun 05 '19 at 18:15
0

Configuring the shell environment variables by overriding them all to use scl_source, as seen from following "Method #2" from Austin Dewey's blog works for me.

FROM centos:7
SHELL ["/usr/bin/env", "bash", "-c"]
RUN \
    yum install -y centos-release-scl && \
    yum install -y rh-python38-python-devel
ENV \
    BASH_ENV="/usr/bin/scl_enable" \
    ENV="/usr/bin/scl_enable" \
    PROMPT_COMMAND=". /usr/bin/scl_enable"
RUN echo -e "\n\
unset BASH_ENV PROMPT_COMMAND ENV\n\
source scl_source enable rh-python38\n\
" > /usr/bin/scl_enable
RUN \
    python3 -m ensurepip && \
    python3 -m pip install --upgrade pip && \
    python3 -m pip install setuptools wheel
docker build --tag python:3.8-centos7 - < Dockerfile
docker run --rm -i -t python:3.8-centos7
python3 --version

Python 3.8.11

NOTE: This does not work when giving python3 --version as command. I have not been able to figure out a work-around.

docker run --rm -i -t python:3.8-centos7 python3 --version
Kevin
  • 2,234
  • 2
  • 21
  • 26