我在存储库 A 中有一个 CI 作业,用于构建图像(
Dockerfile
,通过 Kaniko),这需要来自存储库 B 的包存储库中的包。
由于这是一项将来会重复进行的任务,因此我创建了一个名为 CI_GROUP_API
的 GAT(组访问令牌,权限:api,角色:
Developer),以及相应的组 CICD 变量,该变量具有相同的值名称和值,以便我的 CI 作业可以使用它访问该组中的所有其他存储库。
我在 CI 工作中使用 GAT 动态生成
.netrc
,并且我还在调整我的 requirements.txt
以添加包(将使此更改在将来永久存在)。 CI作业所需的文件可以在下面的错误日志后看到。当我运行我的工作时,我收到:
Requirement already satisfied: pip in /usr/local/lib/python3.10/site-packages (24.0)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Looking in indexes: https://gitlab.company.com/api/v4/projects/58838/packages/pypi/simple
User for gitlab.company.com: ERROR: Exception:
Traceback (most recent call last):
File "/usr/local/lib/python3.10/site-packages/pip/_internal/cli/base_command.py", line 180, in exc_logging_wrapper
status = run_func(*args)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/cli/req_command.py", line 245, in wrapper
return func(self, options, args)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/commands/install.py", line 377, in run
requirement_set = resolver.resolve(
File "/usr/local/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 95, in resolve
result = self._result = resolver.resolve(
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/resolvelib/resolvers.py", line 546, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/resolvelib/resolvers.py", line 397, in resolve
self._add_to_criteria(self.state.criteria, r, parent=None)
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/resolvelib/resolvers.py", line 173, in _add_to_criteria
if not criterion.candidates:
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/resolvelib/structs.py", line 156, in __bool__
return bool(self._sequence)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 155, in __bool__
return any(self)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 143, in <genexpr>
return (c for c in iterator if id(c) not in self._incompatible_ids)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py", line 44, in _iter_built
for version, func in infos:
File "/usr/local/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py", line 297, in iter_index_candidate_infos
result = self._finder.find_best_candidate(
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/package_finder.py", line 890, in find_best_candidate
candidates = self.find_all_candidates(project_name)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/package_finder.py", line 831, in find_all_candidates
page_candidates = list(page_candidates_it)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/sources.py", line 194, in page_candidates
yield from self._candidates_from_page(self._link)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/package_finder.py", line 791, in process_project_url
index_response = self._link_collector.fetch_response(project_url)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/collector.py", line 461, in fetch_response
return _get_index_content(location, session=self.session)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/collector.py", line 364, in _get_index_content
resp = _get_simple_response(url, session=session)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/index/collector.py", line 135, in _get_simple_response
resp = session.get(
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/requests/sessions.py", line 602, in get
return self.request("GET", url, **kwargs)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/network/session.py", line 520, in request
return super().request(method, url, *args, **kwargs)
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/requests/sessions.py", line 589, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/requests/sessions.py", line 710, in send
r = dispatch_hook("response", hooks, r, **kwargs)
File "/usr/local/lib/python3.10/site-packages/pip/_vendor/requests/hooks.py", line 30, in dispatch_hook
_hook_data = hook(hook_data, **kwargs)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/network/auth.py", line 500, in handle_401
username, password, save = self._prompt_for_password(parsed.netloc)
File "/usr/local/lib/python3.10/site-packages/pip/_internal/network/auth.py", line 455, in _prompt_for_password
username = ask_input(f"User for {netloc}: ") if self.prompting else None
File "/usr/local/lib/python3.10/site-packages/pip/_internal/utils/misc.py", line 251, in ask_input
return input(message)
EOFError: EOF when reading a line
error building image: error building stage: failed to execute command: waiting for process to exit: exit status 2
我的 CI 工作定义如下:
build-service:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: ['']
tags:
- asprunner
rules:
# Build base image only if the Dockerfile or CICD config file have changed and been pushed to the cloud branch
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"'
when: on_success
script:
- echo "Building image for service X using Kaniko"
- |
export BASE_REGISTRY_URL=registry.gitlab.company.com/mygroup/base-service
export BASE_TOKEN_NAME=CI_GROUP_API
export BASE_TOKEN_VALUE=$CI_GROUP_API
cat <<EOT >> .netrc
machine gitlab.company.com
login $BASE_TOKEN_NAME
password $BASE_TOKEN_VALUE
EOT
cat <<EOT >> requirements.txt
--index-url https://gitlab.company.com/api/v4/projects/58838/packages/pypi/simple
base-service
EOT
echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_JOB_TOKEN}" | base64 | tr -d '\n')\"},\"${BASE_REGISTRY_URL}\":{\"username\":\"${BASE_TOKEN_NAME}\",\"password\":\"${BASE_TOKEN_VALUE}\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor
--context .
--dockerfile Dockerfile
--insecure
--skip-tls-verify
--skip-tls-verify-pull
--insecure-pull
--destination "${CI_REGISTRY_IMAGE}:v0.2"
我对应的
Dockerfile
可以在下面看到:
FROM registry.company.com/mygroup/base-service:v1.0
ENV DEBIAN_FRONTEND noninteractive
COPY ./requirements.txt requirements.txt
COPY ./.netrc .netrc
RUN echo "----------- requirements.txt -----------" && cat requirements.txt
RUN echo "----------- .netrc -----------" && cat .netrc
RUN pip3 install --upgrade pip && pip3 install --no-cache -r requirements.txt
RUN mkdir -p service
COPY main.py service/
COPY gltf_transformer service/
RUN rm requirements.txt .netrc
ENTRYPOINT ["sh", "python3 main.py"]
生成的
pip
相关文件如下所示:
需求.txt
pygltflib
--index-url https://gitlab.company.com/api/v4/projects/58838/packages/pypi/simple
x-base-service
.netrc
---------- .netrc -----------
machine gitlab.company.com
login CI_GROUP_API
password [MASKED]
我认为
[MASKED]
(GitLab 在 CI 作业日志中隐藏屏蔽变量的方式)可能已作为实际值写入 .netrc
。然而,首先在读取用户时出现错误,其次我添加了
文物: 路径: - 要求.txt - .netrc 何时:on_failure
我的工作是获取文件并验证令牌的值是否存在。
我还将这两个文件本地下载到我的系统,并使用作业工件中的
requirements.txt
运行 pip 来获取 .netrc
。效果很好。
错误消息非常非常不准确。
首先我需要澄清,它在本地工作的唯一原因是因为
pip
正在使用我的 SSH 连接到软件包存储库,而 .netrc
实际上从未使用过。当我在干净的环境中使用 pip
文件运行 requirements.txt
时,我发现了这一点,它要求我提供使用的名称和密码。
我的下一个想法是编码才是问题所在。毕竟文档指出:
[
但是,使用
iconv
将 UTF-8 转换为 ASCII 没有执行任何操作,错误仍然存在。
然后我在第N次阅读文档时想起来,
.netrc
与特定用户相关。现在,在 Docker 容器内,我们实际上没有用户,但是我们确实有 /root
目录作为主目录。
我添加了
COPY ./.netrc /root/.netrc
到我的 Dockerfile,然后修复引用该文件的所有以下路径并且它起作用了。