如何让 Pipenv 在 Docker 运行?

我正在码头上安装 pipenv:

RUN pip install pipenv
RUN cd /my/app/path/ && pipenv install
RUN cd /my/app/path/ && pipenv shell

我得到了一个错误:

Traceback (most recent call last):
File "/usr/local/bin/pipenv", line 11, in <module>
sys.exit(cli())
File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 722, in __call__
return self.main(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 697, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 1066, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 895, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 535, in invoke
return callback(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/pipenv/cli.py", line 2057, in shell
do_shell(three=three, python=python, fancy=fancy, shell_args=shell_args)
File "/usr/local/lib/python2.7/dist-packages/pipenv/cli.py", line 1952, in do_shell
shell = os.path.abspath(PIPENV_SHELL)
File "/usr/lib/python2.7/posixpath.py", line 360, in abspath
if not isabs(path):
File "/usr/lib/python2.7/posixpath.py", line 54, in isabs
return s.startswith('/')
AttributeError: 'NoneType' object has no attribute 'startswith'

如果我逃跑

RUN cd /my/app/path/ && pipenv install --system

相反,我得到了另一个错误:

build   30-Sep-2017 16:50:45    Step 5/9 : RUN cd /my/app/path &&     pipenv install --system
build   30-Sep-2017 16:50:45     ---> Running in cffd31633074
build   30-Sep-2017 16:50:46    [91mPipfile.lock not found, creating…
build   30-Sep-2017 16:50:46    [0m[91mLocking [dev-packages] dependencies…
build   30-Sep-2017 16:50:46    [0m[91mLocking [packages] dependencies…
build   30-Sep-2017 16:50:49    [0m[91mCRITICAL:pip.utils:Error [Errno 2] No such file or directory while executing command python setup.py egg_info
build   30-Sep-2017 16:50:49    [0m[91mTraceback (most recent call last):
build   30-Sep-2017 16:50:49      File "/usr/local/bin/pipenv", line 11, in <module>
build   30-Sep-2017 16:50:49        sys.exit(cli())
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 722, in __call__
build   30-Sep-2017 16:50:49    [0m[91m    return self.main(*args, **kwargs)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 697, in main
build   30-Sep-2017 16:50:49    [0m[91m    rv = self.invoke(ctx)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 1066, in invoke
build   30-Sep-2017 16:50:49    [0m[91m    return _process_result(sub_ctx.command.invoke(sub_ctx))
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 895, in invoke
build   30-Sep-2017 16:50:49    [0m[91m    return ctx.invoke(self.callback, **ctx.params)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/vendor/click/core.py", line 535, in invoke
build   30-Sep-2017 16:50:49        return callback(*args, **kwargs)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/cli.py", line 1782, in install
build   30-Sep-2017 16:50:49    [0m[91m    do_init(dev=dev, allow_global=system, ignore_pipfile=ignore_pipfile, system=system, skip_lock=skip_lock, verbose=verbose, concurrent=concurrent, deploy=deploy)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/cli.py", line 1290, in do_init
build   30-Sep-2017 16:50:49    [0m[91m    do_lock(system=system)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/cli.py", line 1080, in do_lock
build   30-Sep-2017 16:50:49    [0m[91m    pre=pre
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 421, in resolve_deps
build   30-Sep-2017 16:50:49    [0m[91m    resolved_tree.update(resolver.resolve())
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 101, in resolve
build   30-Sep-2017 16:50:49    [0m[91m    has_changed, best_matches = self._resolve_one_round()
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 199, in _resolve_one_round
build   30-Sep-2017 16:50:49    [0m[91m    for dep in self._iter_dependencies(best_match):
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 293, in _iter_dependencies
build   30-Sep-2017 16:50:49        dependencies = self.repository.get_dependencies(ireq)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 171, in get_dependencies
build   30-Sep-2017 16:50:49        result = reqset._prepare_file(self.finder, ireq)
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/pip/req/req_set.py", line 639, in _prepare_file
build   30-Sep-2017 16:50:49    [0m[91m    abstract_dist.prep_for_dist()
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/pip/req/req_set.py", line 134, in prep_for_dist
build   30-Sep-2017 16:50:49    [0m[91m    self.req_to_install.run_egg_info()
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/pip/req/req_install.py", line 438, in run_egg_info
build   30-Sep-2017 16:50:49    [0m[91m    command_desc='python setup.py egg_info')
build   30-Sep-2017 16:50:49      File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/pip/utils/__init__.py", line 667, in call_subprocess
build   30-Sep-2017 16:50:49    [0m[91m    cwd=cwd, env=env)
build   30-Sep-2017 16:50:49      File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
build   30-Sep-2017 16:50:49    [0m[91m    errread, errwrite)
build   30-Sep-2017 16:50:49      File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
build   30-Sep-2017 16:50:49    [0m[91m    raise child_exception
build   30-Sep-2017 16:50:49    OSError: [Errno 2] No such file or directory
error   30-Sep-2017 16:50:49    The command '/bin/sh -c cd /opt/supercrunch/function-service/lib &&     pipenv install --system' returned a non-zero code: 1
build   30-Sep-2017 16:50:49    [0mSending build context to Docker daemon 40.96 kB

但是当我做下面这些事情的时候:

RUN pip install pipenv
RUN cd /my/app/path && pipenv install
RUN cd /my/app/path && pipenv install --system

起作用了。

因此有两个问题: 第一: 为什么 pipenv shell给我这个错误,第二: 为什么我必须在 pipenv install --system之前做 pipenv install才能让它工作?

我想使用 pipenv 创建具有不同 Python 版本和不同依赖版本的虚拟环境。

61441 次浏览

Considering your problem, as I know it the easiest way is to update to the latest version of pipenv. It is still in development, so problems are fixed very soon.

We are using pipenv with docker in production. And we really like it. There are several things to keep in mind:

  1. You need to use --system flag, so it will install all packages into the system python, and not into the virtualenv. Since docker containers do not need to have virtualenvs
  2. You need to use --deploy flag, so your build will fail if your Pipfile.lock is out of date
  3. You need to use --ignore-pipfile, so it won't mess with our setup

Check the official docs to be sure that this information is up-to-date.

All in all:

pipenv install --system --deploy --ignore-pipfile

There's also one more thing. If you are using the same Dockerfile for both development and production it would be very nice to also use --dev flag for the development environment only.

Also, check out our django project template to see the full example: wemake-django-template

The direct answer to this question is to not use shell, but rather run:

CMD ["pipenv", "run", "python", "my/app.py"]

If you need more flexibility, you could also pipenv run sh init.sh, which would create a shell initialized with all the pipenv environment variables.

I actually prefer the approach C. Sweet mentions. If you can get away with prebuilding your virtual environment and simply copying it over (setting PIPENV_VENV_IN_PROJECT then using a nested FROM followed by COPY --from=builder-image), you don't need python nor pipenv nor pipenv dependencies in your final container. This greatly reduces the size of the final image.

Dockerfile:

WORKDIR /etc/service/
CMD ["sh", "/etc/service/init.sh"]

init.sh:

source /etc/service/my/.venv/bin/activate
python my/app.py

The pipenv documentation no longer officially recommends using the --system flag in docker instances. Instead, they suggest using virtual environments, as "it is for deployment onto a full-fledged OS". This is with the caveat:

...most containers are deployed without virtualenvs as I believe you both note, I believe the purpose is to stay slim and reduce attack surface area by installing as little as possible

As stated in https://github.com/pypa/pipenv/pull/2762.

Instead, the solution would be to run (as stated in a different answer here):

RUN pipenv install --deploy --ignore-pipfile

And then prefix all calls to python with pipenv run, e.g. CMD ["pipenv", "run", "python", "hello.py"]

Ps. I would have wanted to put this as a comment to the accepted answer, but I don't have the reputation.

The best thing is not to use pipenv directly, as you usually don't need a virtualenv in your container.

There is a tool called micropipenv that you can use in your container instead of pipenv.

https://github.com/thoth-station/micropipenv

micropipenv use cases

Why should I use micropipenv instead of Pipenv or Poetry?

  • I would like to have a tool that "rules them all" - one lightweight tool to support all Python dependency lock file managers (pip-tools, Poetry, Pipenv) and lets users decide what they want to use when deploying Python applications in containerized environments (e.g. Kubernetes, OpenShift, ...).

  • I would like to have a fast and minimalistic tool to install software packages in CI.

  • I would like to have containerized Python applications as small as possible with minimum software shipped and required to build and run the Python application in production.

  • I would like to convert files produced by Pipenv/Poetry to a pip-tools compatible output.

  • I don't want to install Pipenv/Poetry, but I would like to run a project that uses Pipenv/Poetry for dependency management (e.g. restricted environments).

  • My Pipenv installation is broken and Pipenv upstream did not issue any new Pipenv release <https://github.com/pypa/pipenv/issues/4058>_.

  • I would like to deploy my application into a production environment and my application dependencies are managed by Pipenv/Poetry (dependencies are already resolved), but I don't want to run Pipenv/Poetry in production (e.g. OpenShift's s2i build process).

Following alternative turned out to be the best for me:

    RUN  pip install pipenv  \
&& pipenv lock --keep-outdated --requirements > requirements.txt \
&& pip install -r requirements.txt

Since this does not install the dependencies in pipenv'a virtualenv, no need to worry about pipenv in docker-compose command.

command: >
- sh
- -c
- |
python myscript.py
python myapp.py

Update [2022-11]

Latest way of creating requiremnts file from pipenv is:

pipenv requirements > requirements.txt

Also, you can skip installing pipenv in Dockerfile by first running above command to create requirements file before building container.

Following reference helped me, includes poetry,pip-tools Reference P.S. I am not the author

Each RUN directive spawns a new shell so it is useless to try to alter it with a pipenv shell command: any following instruction will not benefit from that.

All RUN directive that are meant to use pipenv's environment must be prefixed with pipenv run.

This would also apply to the CMD directive, as well as any command run with a container once the image is built, but you can hack an ENTRYPOINT to save prefixes for commands!

FROM python
RUN pip install pipenv
WORKDIR /app/
COPY Pipfile* /app
RUN mkdir /app/.venv
RUN pipenv install --deploy
RUN pipenv run something
ENTRYPOINT ["python", "-m", "pipenv", "run"]
CMD ["something"]