GitLab CI/CD构建容器无法运行systemd问题求助
背景
我是一名SRE,GitLab流水线和Docker容器经验有限,接手了一个长期无人维护的老旧项目,需协助开发者将应用从Ubuntu 14升级至Ubuntu 20.04。目前正在搭建新的GitLab CI/CD流水线:代码合并至master分支时,流水线触发构建任务启动Runner,Runner从独立仓库拉取Containerfile构建的镜像并启动容器,在容器内完成应用构建。此前查询到的类似问题解决方案多为启动容器时添加特权标志或手动用podman启动,但由于流程由GitLab Runner自动化执行,这两种方式均不可行。希望通过修改.gitlab-ci.yml的build阶段配置,或调整Containerfile解决问题(知晓容器运行systemd存在安全风险,但构建容器仅在任务期间活跃,任务结束后会被GitLab Runner清理)。
当前Build阶段配置
build: stage: build image: [Removed for privacy, points to base docker image created by Containerfile] rules: - !reference [.run without tags, rules] variables: GIT_SUBMODULE_STRATEGY: recursive script: - sudo apt-get clean # Run local script from repository to install salt on the build container. - ./salt_me.sh - make clean - make debpkg - mv ../*.deb . artifacts: expire_in: 3 mos paths: [ '*.deb', deploy.env, 'build/cov' ] reports: dotenv: deploy.env
报错信息
构建过程中调用Salt状态初始化PostgreSQL时失败,具体报错如下:
[INFO ] Running state [postgresql] at time 17:48:37.982497 [INFO ] Executing state service.mod_watch for [postgresql] [INFO ] Executing command /usr/bin/systemctl in directory '/root' [INFO ] Executing command /usr/bin/systemctl in directory '/root' [INFO ] Executing command /usr/bin/systemctl in directory '/root' [ERROR ] Command '/usr/bin/systemctl' failed with return code: 1 [ERROR ] stderr: System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down [ERROR ] retcode: 1
当前Containerfile片段
FROM ubuntu:focal ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ && apt-get install -y tzdata git-lfs gnupg2 wget \ && ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime \ && dpkg-reconfigure --frontend noninteractive tzdata \ && wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \ && apt-get install -y --no-install-recommends --no-install-suggests \ [various dependencies] RUN git config --global user.email "[removed from question]" RUN git config --global user.name "Dyna Bot" RUN echo "#!/bin/bash\n\$@" > /usr/bin/sudo RUN chmod +x /usr/bin/sudo
此前尝试的无效配置
曾给Containerfile添加以下内容,但未解决问题:
RUN echo 'root:root' | chpasswd RUN printf '#!/bin/sh\nexit 0' > /usr/sbin/policy-rc.d RUN apt-get update...[the stuff that was already there up through the dependencies] RUN printf "systemctl start systemd-logind" >> /etc/profile ENTRYPOINT ["/sbin/init"] [and then the RUN git config commands]
解决方案
方案1:修改Containerfile让容器以systemd为PID 1运行
要让Ubuntu容器正常运行systemd,需完成以下配置:
- 安装systemd依赖包
- 禁用不必要的systemd服务,减少资源占用
- 配置
policy-rc.d避免安装包时自动启动服务 - 设置ENTRYPOINT为systemd,并确保GitLab Runner启动容器时挂载必要的cgroup
修改后的Containerfile关键部分:
FROM ubuntu:focal ENV DEBIAN_FRONTEND=noninteractive ENV container docker # 安装systemd及依赖 RUN apt-get update \ && apt-get install -y systemd systemd-sysv \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # 禁用不需要的systemd服务 RUN systemctl mask dev-hugepages.mount sys-fs-fuse-connections.mount \ && systemctl disable systemd-networkd.service systemd-resolved.service # 配置policy-rc.d,阻止安装包时自动启动服务 RUN printf '#!/bin/sh\nexit 101' > /usr/sbin/policy-rc.d \ && chmod +x /usr/sbin/policy-rc.d # 保留原有的依赖安装、git配置等步骤... # 设置容器启动入口为systemd ENTRYPOINT ["/sbin/init"]
同时,在.gitlab-ci.yml的build阶段添加等待systemd启动完成的步骤,若Runner使用Docker executor,需确保启用特权模式(这是systemd运行的必要条件):
build: stage: build image: [your modified image] rules: - !reference [.run without tags, rules] variables: GIT_SUBMODULE_STRATEGY: recursive # 若Runner支持,添加变量启用特权模式 # DOCKER_OPTS: "--privileged --cgroupns=host" script: # 等待systemd完全启动 - until systemctl is-system-running; do sleep 1; done - sudo apt-get clean - ./salt_me.sh - make clean - make debpkg - mv ../*.deb . artifacts: expire_in: 3 mos paths: [ '*.deb', deploy.env, 'build/cov' ] reports: dotenv: deploy.env
方案2:绕过systemd,直接启动PostgreSQL服务
如果不想在容器中运行systemd,可修改Salt状态或构建脚本,直接使用PostgreSQL原生命令启动服务:
- 在
salt_me.sh或Salt状态中,将systemctl start postgresql替换为pg_ctl start -D /var/lib/postgresql/[版本号]/main - 确保PostgreSQL数据目录存在且权限配置正确
- 构建前手动初始化数据库(若需要):
initdb -D /var/lib/postgresql/[版本号]/main
这种方式无需修改容器的init系统,更轻量,也规避了systemd的安全风险。
方案3:使用GitLab Services单独启动PostgreSQL
更符合容器化最佳实践的方式是,不在构建容器内运行PostgreSQL,而是通过GitLab CI的services功能单独启动一个PostgreSQL容器,让构建容器连接到外部数据库:
build: stage: build image: [your original image] services: - name: postgres:12 variables: POSTGRES_USER: your_user POSTGRES_PASSWORD: your_password POSTGRES_DB: your_db rules: - !reference [.run without tags, rules] variables: GIT_SUBMODULE_STRATEGY: recursive # 构建脚本中连接数据库的地址为services的别名postgres DB_HOST: postgres DB_USER: your_user DB_PASSWORD: your_password DB_NAME: your_db script: - sudo apt-get clean - ./salt_me.sh - make clean - make debpkg - mv ../*.deb . artifacts: expire_in: 3 mos paths: [ '*.deb', deploy.env, 'build/cov' ] reports: dotenv: deploy.env
这种方式完全避免了在构建容器内运行数据库服务的问题,也是GitLab CI中推荐的做法。
内容的提问来源于stack exchange,提问作者kat.is.konfused

