Ubuntu 22.04使用Podman部署OpenGrok的详细教程

安装必要的依赖:

# 安装 podman
$ sudo apt install podman

# 安装 git
$ sudo apt install git

# 创建一个低权限用户,尽量不要使用root用户,身份操作podman,降低安全风险
$ sudo adduser podman

# 允许用户下的容器在系统启动的时候启动服务
$ sudo loginctl enable-linger podman

# 切换到刚刚创建的低权限用户 ,注意,必须使用 su - user 的方式切换
# 如果使用 su user 切换会导致环境变量被带到新用户,导致执行报错
# ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/1000" is not owned by the current user 

$ su - podman

# 准备本地目录映射
$ mkdir ~/.dockers

$ mkdir ~/.dockers/opengrok

# 准备已经处理过的源代码,源代码需要 src、etc、data 三个目录,src存储源代码,其他目录保持空即可
# 参考 https://www.mobibrw.com/2019/19162
# 更详细的处理参考官方文档即可,后续我们以 Android 4.2.2 的源代码为例子

# Android 4.2.2 源代码存放在 ~/.dockers/opengrok/Android_4.2.2/src
$ mkdir ~/.dockers/opengrok/Android_4.2.2/src

$ mkdir ~/.dockers/opengrok/Android_4.2.2/etc

$ mkdir ~/.dockers/opengrok/Android_4.2.2/data

# 手工拉去部分镜像,或者可以在 /etc/containers/registries.conf 配置镜像服务器,可省略前面的服务器 docker.io
$ podman pull docker.io/library/tomcat:10-jdk17

官方镜像会在报错的时候暴露 Tomcat 10 版本号,错误堆栈,构成安全隐患,我们需要通过构建自定义镜像解决此问题:

# 目前最新代码 commit ad9658443340a6dae425d94ab7cb0cc9d74fb37f
$ git clone https://github.com/oracle/opengrok.git

$ cd opengrok

# 设置 maven 国内代理,否则下载速度太慢了 
$ find . -type f -exec grep -l "repo.maven" {} \; | xargs sed -i "s/repo.maven.apache.org\/maven2\//mirrors.cloud.tencent.com\/nexus\/repository\/maven-public\//"

# 设置 Maven Wrapper 国内源
$ sed -i "s/repo.maven.apache.org\/maven2\//mirrors.cloud.tencent.com\/nexus\/repository\/maven-public\//" .mvn/wrapper/maven-wrapper.properties

# 代码中使用的 linux 系统镜像版本,我们需要明确告知 podman 从 docker.io 获取
$ sed -i "s/ubuntu:jammy/docker.io\/ubuntu:jammy/g" Dockerfile

# 代码中使用的 Tomcat 镜像版本,不能通过 docker.io 获取到,我们直接使用通用 Tomcat 10 版本
$ sed -i "s/tomcat:10.1.19-jdk17/docker.io\/library\/tomcat:10.1-jdk17/g" Dockerfile

内容如下:

# Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved.
# Portions Copyright (c) 2020, Chris Fraire <cfraire@me.com>.

FROM ubuntu:jammy AS build

# hadolint ignore=DL3008
RUN apt-get update && apt-get install --no-install-recommends -y openjdk-17-jdk python3 python3-venv && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update the pom
WORKDIR /mvn
COPY pom.xml /mvn/
COPY mvnw /mvn/
COPY .mvn /mvn/.mvn
COPY opengrok-indexer/pom.xml /mvn/opengrok-indexer/
COPY opengrok-web/pom.xml /mvn/opengrok-web/
COPY plugins/pom.xml /mvn/plugins/
COPY suggester/pom.xml /mvn/suggester/

# distribution and tools do not have dependencies to cache
RUN sed -i 's:<module>distribution</module>::g' /mvn/pom.xml && \
    sed -i 's:<module>tools</module>::g' /mvn/pom.xml && \
    mkdir -p /mvn/opengrok-indexer/target/jflex-sources && \
    mkdir -p /mvn/opengrok-web/src/main/webapp/js && \
    mkdir -p /mvn/opengrok-web/src/main/webapp/WEB-INF/ && \
    touch /mvn/opengrok-web/src/main/webapp/WEB-INF/web.xml

# dummy build to cache the dependencies
RUN ./mvnw -DskipTests -Dcheckstyle.skip -Dmaven.antrun.skip package

# build the project
COPY ./ /opengrok-source
WORKDIR /opengrok-source

RUN /mvn/mvnw -DskipTests=true -Dmaven.javadoc.skip=true -B -V package
# hadolint ignore=SC2012,DL4006
RUN cp `ls -t distribution/target/*.tar.gz | head -1` /opengrok.tar.gz

# Store the version in a file so that the tools can report it.
RUN /mvn/mvnw help:evaluate -Dexpression=project.version -q -DforceStdout > /mvn/VERSION

FROM tomcat:10-jdk17
LABEL maintainer="https://github.com/oracle/opengrok"

# Add Perforce apt source.
# hadolint ignore=DL3008,DL3009
RUN apt-get update && \
    apt-get install --no-install-recommends -y gnupg2
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# hadolint ignore=DL3059
RUN curl -sS https://package.perforce.com/perforce.pubkey | gpg --dearmor > /etc/apt/trusted.gpg.d/perforce.gpg
# hadolint ignore=DL3059
RUN echo 'deb https://package.perforce.com/apt/ubuntu jammy release' > /etc/apt/sources.list.d/perforce.list

# install dependencies and Python tools
# hadolint ignore=DL3008,DL3009
RUN apt-get update && \
    apt-get install --no-install-recommends -y git subversion mercurial cvs cssc bzr rcs rcs-blame \
    unzip python3 python3-pip \
    python3-venv python3-setuptools openssh-client libyaml-dev

# hadolint ignore=DL3008,DL3059
RUN architecture=$(uname -m) && if [[ "$architecture" == "aarch64" ]]; then \
        echo "aarch64: do not install helix-p4d."; else \
        apt-get install --no-install-recommends -y helix-p4d; fi

# compile and install universal-ctags
# hadolint ignore=DL3003,DL3008
RUN apt-get install --no-install-recommends -y pkg-config automake build-essential && \
    git clone https://github.com/universal-ctags/ctags /root/ctags && \
    cd /root/ctags && ./autogen.sh && ./configure && make && make install && \
    apt-get remove -y automake build-essential && \
    apt-get -y autoremove && apt-get -y autoclean && \
    cd /root && rm -rf /root/ctags && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 设置国内源,否则大概率不成功
RUN python3 -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# Update the Python tooling in order to successfully install the opengrok-tools package.
# hadolint ignore=DL3013
RUN python3 -m pip install --no-cache-dir --upgrade pip setuptools

# prepare OpenGrok binaries and directories
# hadolint ignore=DL3010
COPY --from=build opengrok.tar.gz /opengrok.tar.gz
# hadolint ignore=DL3013
RUN mkdir -p /opengrok /opengrok/etc /opengrok/data /opengrok/src && \
    tar -zxvf /opengrok.tar.gz -C /opengrok --strip-components 1 && \
    rm -f /opengrok.tar.gz && \
    python3 -m pip install --no-cache-dir /opengrok/tools/opengrok-tools.tar.gz && \
    python3 -m pip install --no-cache-dir Flask Flask-HTTPAuth waitress # for /reindex REST endpoint handled by start.py

COPY --from=build /mvn/VERSION /opengrok/VERSION

# environment variables
ENV SRC_ROOT /opengrok/src
ENV DATA_ROOT /opengrok/data
ENV URL_ROOT /
ENV CATALINA_HOME /usr/local/tomcat
ENV CATALINA_BASE /usr/local/tomcat
ENV CATALINA_TMPDIR /usr/local/tomcat/temp
ENV PATH $CATALINA_HOME/bin:$PATH
ENV CLASSPATH /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
ENV JAVA_OPTS="--add-exports=java.base/jdk.internal.ref=ALL-UNNAMED --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-opens=jdk.compiler/com.sun.tools.javac=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED \
--add-opens=java.base/java.util=ALL-UNNAMED"

# disable all file logging
COPY docker/logging.properties /usr/local/tomcat/conf/logging.properties
RUN sed -i -e 's/Valve/Disabled/' /usr/local/tomcat/conf/server.xml

# add our scripts and configuration
COPY docker /scripts
RUN chmod -R +x /scripts

# 关闭报错详情,这个会导致安全风险
RUN sed -i "s/<\/Host>/  <Valve className='org.apache.catalina.valves.ErrorReportValve' showReport='false' showServerInfo='false' \/>\n\t<\/Host>/" /usr/local/tomcat/conf/server.xml
 
# 配置AJP协议
# 如果只希望通过 AJP访问,可以参考如下命令 移除原有的 AJP 协议配置
# RUN xmlstarlet ed -L -P -S -d '/Server/Service/Connector' /usr/local/tomcat/conf/server.xml
 
# 增加新的协议配置
RUN sed -i "s/<\/Service>/  <Connector port='8009' protocol='AJP\/1.3' address='0.0.0.0' redirectPort='8443' secretRequired=''\/>\n <\/Service>/" /usr/local/tomcat/conf/server.xml

# run
WORKDIR $CATALINA_HOME
EXPOSE 8080
CMD ["/scripts/start.py"]

构建镜像:

$ podman --cgroup-manager=cgroupfs build -t opengrok-dev .

设置容器:

$ podman stop android-4.2.2

$ podman rm android-4.2.2

# 启动一个 tomcat 容器 8081 HTTP 访问端口 8010 AJP访问端口
# 注意,Docker 镜像会自动 每隔10分钟进行一次自动索引重建,不需要手动进行索引操作,
# 如果需要进行反向代理,则 OPENGROK_WEBAPP_CONTEXT 环境变量是无效的,
# Docker 需要使用 URL_ROOT 进行配置
# 详细配置信息参考源代码目录下的 docker/README.md
# 配置 SYNC_PERIOD_MINUTES=0 禁止周期性重建索引,但是容器重启的时候仍然会自动重建
# 多个容器的情况下,需要限制容器使用的CPU、内存资源,否则会导致宿主机内存不足,这个需要特别注意
# 这里我们分配两个核心给容器,一般限制核心也可以限制并发的Java进程,从而间接可以限制内存占用
# 同时应该限制容器内的同步线程数量 "WORKERS=1",否则极易引起宿主机的OOM 
# podman run -d --cpus=2 --name opengrok-android-4.2.2 -p 8081:8080/tcp -p 8010:8009 -v ~/opengrok-src/:/opengrok/src/ -v ~/opengrok-etc/:/opengrok/etc/ -v ~/opengrok-data/:/opengrok/data/ -e "URL_ROOT=Android_4.2.2" -e "SYNC_PERIOD_MINUTES=0" -e "WORKERS=1" localhost/opengrok-dev

$ podman run -d --cpus=2 --name opengrok-android-4.2.2 -p 8081:8080/tcp -p 8010:8009 -v /home/podman/.dockers/opengrok/Android_4.2.2/src:/opengrok/src/ -v /home/podman/.dockers/opengrok/Android_4.2.2/etc/:/opengrok/etc/ -v /home/podman/.dockers/opengrok/Android_4.2.2/data/:/opengrok/data/ -e "URL_ROOT=Android_4.2.2" -e "SYNC_PERIOD_MINUTES=0" -e "WORKERS=1" localhost/opengrok-dev

# 查看该容器
$ podman ps

# 如果需要进入容器查看执行情况,参考如下命令
# podman exec -it opengrok-android-4.2.2 bash

# 查看日志
# podman logs -f -t opengrok-android-4.2.2

# 每次都启动新容器方式创建servcie //--new参数,每次启动都删除旧容器,启动一个新容器 
$ podman generate systemd --restart-policy=always -n --new -f android-4.2.2

查看启动文件:

$ cat container-opengrok-android-4.2.2.service

内容如下:

# container-opengrok-android-4.2.2.service
# autogenerated by Podman 3.4.4
# Fri Mar 22 10:41:54 CST 2024

[Unit]
Description=Podman container-opengrok-android-4.2.2.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon --replace -d --cpus=2 --name opengrok-android-4.2.2 -p 8081:8080/tcp -p 8010:8009 -v /home/podman/.dockers/opengrok/Android_4.2.2/src:/opengrok/src/ -v /home/podman/.dockers/opengrok/Android_4.2.2/etc/:/opengrok/etc/ -v /home/podman/.dockers/opengrok/Android_4.2.2/data/:/opengrok/data/ -e "URL_ROOT=Android_4.2.2" -e "SYNC_PERIOD_MINUTES=0" -e "WORKERS=1" localhost/opengrok-dev
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=default.target

需要额外注意的一个地方是,给出的路径必须是完整路径 “/home/podman/.dockers/opengrok/Android_4.2.2/src”,不能是 “~/.dockers/opengrok/Android_4.2.2/src”,Systemd不能正确展开 “~” ,导致路径找不到,从而在启动的时候失败,报告错误代码 125 。

另外,Android源代码目录下不能存在 .svn .git  隐藏子目,我们需要手工删除,否则会报错。参考 删除目录下所有的 .svn .git 隐藏子目

Systemd 配置,开机/重启自动启动服务:

# 保存到 ~/.config/systemd/user/
$ mkdir .config

$ mkdir .config/systemd

$ mkdir .config/systemd/user

$ mv container-opengrok-android-4.2.2.service ~/.config/systemd/user/


# 以当前用户身份刷新配置文件,让其生效
$ systemctl --user daemon-reload

# 设置容器开机自启,并且现在启动
$ systemctl --user enable --now ~/.config/systemd/user/container-opengrok-android-4.2.2.service

# 如果需要进入容器查看执行情况,参考如下命令
# podman exec -it opengrok-android-4.2.2 bash

# 查看日志
# podman logs -f -t opengrok-android-4.2.2

# 测试,重启系统,观察是否能开机自动启动
$ sudo reboot

# 启动或重启服务 
# systemctl --user start container-opengrok-android-4.2.2.service 
# systemctl --user restart container-opengrok-android-4.2.2.service

# 如果启动失败,观察服务日志
# sudo journalctl -f
Running containers with resource limits fails with a permissions error

On some systemd-based systems, non-root users do not have resource limit delegation permissions. This causes setting resource limits to fail.

Symptom

Running a container with a resource limit options will fail with an error similar to the following:

--cpus--cpu-period--cpu-quota--cpu-shares:

Error: OCI runtime error: crun: the requested cgroup controller `cpu` is not available

--cpuset-cpus--cpuset-mems:

Error: OCI runtime error: crun: the requested cgroup controller `cpuset` is not available

This means that resource limit delegation is not enabled for the current user.

Solution

You can verify whether resource limit delegation is enabled by running the following command:

$ cat "/sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers"

Example output might be:

memory pids

In the above example, cpu and cpuset are not listed, which means the current user does not have permission to set CPU or CPUSET limits.

If you want to enable CPU or CPUSET limit delegation for all users, you can create the file /etc/systemd/system/user@.service.d/delegate.conf with the contents:

[Service] 
Delegate=memory pids cpu cpuset

After logging out and logging back in, you should have permission to set CPU and CPUSET limits.

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注