跳到主要内容

Docker实践

阅读量

0

阅读人次

0

以往使用Docker,就是单纯的使用通过Docker构建的各种应用。或者自己构建Docker镜像,用于项目源码的构建,用作就当作虚拟机来看待。

Tini

tini 是一个最小化的 init 系统,运行在容器内部,用于启动一个子进程,并等待进程退出时清理僵尸和执行信号转发。 这是一个替代庞大复杂的systemd体系的解决方案。

使用 tini 有什么好处?作者在 Issues 下给出了一个回答

信息

docker 容器运行必须有一个前台进程, 如果没有前台进程执行,容器认为空闲,就会自行退出。所以我们 Dockerfile 中的 CMD 或者 ENTRYPOINT 指定的进行,不能执行完就退出,否则容器再执行完指定的命令后,就会自行退出。

在常见的Dockerfile中,一般我们会见到如下使用:

ENTRYPOINT ["tini","--","/opt/act/run.sh"]

这行的目的是,以 tini 作为进程ID为1的进程,然后调用 /opt/act/run.sh 脚本运行。

-- 用于分隔 tini 的命令行选项和传递给 tini 执行的命令的参数,防止 tini 本身的选项与由 tini 运行的命令的选项之间的歧义。这也是 Linux 中一种常见的约定。

例如:

tini -s -- ls --help

-- 左侧的 -s 作为选项被 tini 接受,右侧的 --help tini 是不会去处理的,意即 -- 右侧的ls --help作为命令执行。

Ubuntu

用作开发使用。

Dockerfile
FROM ubuntu:24.04

LABEL maintainer="amass <168062547@qq.com>"

ENV DEBIAN_FRONTEND=noninteractive

RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list.d/ubuntu.sources \
&& apt update \
&& apt install -y gcc g++ cmake ninja-build pkg-config openssh-server git nano gpg ca-certificates \
wget curl rsync lsb-release ubuntu-keyring gnupg2 libssl-dev \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& wget -O - https://openresty.org/package/pubkey.gpg | gpg --dearmor -o /usr/share/keyrings/openresty.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/openresty.list > /dev/null \
&& curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt update \
&& apt install -y docker-ce-cli openresty nodejs

RUN git config --global core.quotepath false \
&& git config --global user.email "168062547@qq.com" \
&& git config --global user.name "amass"

RUN mkdir /var/run/sshd \
&& sed -i 's/#Port 22/Port 1022/' /etc/ssh/sshd_config \
&& sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config \
&& mkdir -p /root/.ssh \
&& wget https://frp-by1.wwvvww.cn:44048/s/id_rsa_amass_publickey/download -O /root/.ssh/authorized_keys \
&& chmod 600 /root/.ssh/authorized_keys && chown root:root /root/.ssh/authorized_keys

EXPOSE 1022

RUN git config --global user.email "168062547@qq.com" \
&& git config --global user.name "amass"

RUN cd /root \
&& wget https://frp-by1.wwvvww.cn:44048/s/boost_1_85_0/download -O boost_1_85_0.tar.gz \
&& tar xvf boost_1_85_0.tar.gz > /dev/null \
&& cd /root/boost_1_85_0 \
&& ./bootstrap.sh --prefix=/opt/Libraries/boost_1_85_0 \
&& ./b2 install -q --prefix=/opt/Libraries/boost_1_85_0 threading=multi link=shared runtime-link=shared variant=release cxxstd=17 cxxflags=-fPIC cflags=-fPIC \
&& rm -fr /root/boost_1_85_0*

ENV LD_LIBRARY_PATH=/opt/Libraries/boost_1_85_0/lib:$LD_LIBRARY_PATH

CMD ["service", "ssh", "start", "-D"]

构建镜像:

docker build --progress=plain -f Dockerfile -t ubuntu_dev:24.04 .

frp

构建frp用作内网穿透,除了集成frpc以外,还打算集成nginx。使用场景如下:我们在内网部署了 jellyfin 服务,但是其 https 端口使能起来比较麻烦,这样我们可以将 nginx 作为 https 服务器,然后再将 nginx 作为反向代理将请求转发至 jellyfin。

FROM amd64/alpine:3.18

LABEL maintainer="amass <168062547@qq.com>"

ENV FRP_VERSION 0.58.1

RUN apk add openssl curl ca-certificates \
&& wget 'http://openresty.org/package/admin@openresty.com-5ea678a6.rsa.pub' \
&& mv 'admin@openresty.com-5ea678a6.rsa.pub' /etc/apk/keys/ \
&& . /etc/os-release \
&& MAJOR_VER=`echo $VERSION_ID | sed 's/\.[0-9]\+$//'` \
&& echo "http://openresty.org/package/alpine/v$MAJOR_VER/main" | tee -a /etc/apk/repositories \
&& apk update \
&& apk add openresty openresty-resty openresty-opm \
&& opm get bungle/lua-resty-session

RUN cd /root \
&& wget --no-check-certificate -c https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz \
&& tar zxvf frp_${FRP_VERSION}_linux_amd64.tar.gz \
&& cd frp_${FRP_VERSION}_linux_amd64/ \
&& cp frpc /usr/bin/ \
&& mkdir -p /etc/frp \
&& cp frpc.toml /etc/frp \
&& cd /root \
&& rm frp_${FRP_VERSION}_linux_amd64.tar.gz \
&& rm -rf frp_${FRP_VERSION}_linux_amd64/

ENTRYPOINT if [ -d /app ]; then cd /app && openresty -p /app; fi; /usr/bin/frpc -c /etc/frp/frpc.toml

使用:

docker run --restart=always --network host -d -v /mnt/user/appdata/frpc/wwooww_frpc.toml:/etc/frp/frpc.toml -v /mnt/user/appdata/frpc:/app --name wwooww_frpc frp-by1.wwvvww.cn:45288/frpc:0.58.1
docker run --restart=always --network host -d -v /mnt/user/appdata/frpc/frpc.toml:/etc/frp/frpc.toml --name frpc frp-by1.wwvvww.cn:45288/frpc:0.58.1

Gitea Act Runner

在 unRaid 应用市场提供的 Act Runner 不支持 Docker-in-Docker 功能,那么实际作用就很小了,为了满足实际需求,我们自己构建。

FROM ubuntu:22.04

LABEL maintainer="amass <168062547@qq.com>"

ENV DEBIAN_FRONTEND=noninteractive

RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
&& apt update \
&& apt install -y ca-certificates curl git tini \
&& curl https://gitea.com/gitea/act_runner/releases/download/v0.2.6/act_runner-0.2.6-linux-amd64 -o /usr/local/bin/act_runner \
&& chmod +x /usr/local/bin/act_runner \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
&& chmod a+r /etc/apt/keyrings/docker.asc \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
&& apt update \
&& apt install -y docker-ce-cli

COPY resources/act_runner.sh /opt/act/run.sh

ENTRYPOINT ["tini","--","/opt/act/run.sh"]
resources/act_runner.sh
#!/usr/bin/env bash

if [[ ! -d /data ]]; then
mkdir -p /data
fi

cd /data

RUNNER_STATE_FILE=${RUNNER_STATE_FILE:-'.runner'}

CONFIG_ARG=""
if [[ ! -z "${CONFIG_FILE}" ]]; then
CONFIG_ARG="--config ${CONFIG_FILE}"
fi
EXTRA_ARGS=""
if [[ ! -z "${GITEA_RUNNER_LABELS}" ]]; then
EXTRA_ARGS="${EXTRA_ARGS} --labels ${GITEA_RUNNER_LABELS}"
fi

# In case no token is set, it's possible to read the token from a file, i.e. a Docker Secret
if [[ -z "${GITEA_RUNNER_REGISTRATION_TOKEN}" ]] && [[ -f "${GITEA_RUNNER_REGISTRATION_TOKEN_FILE}" ]]; then
GITEA_RUNNER_REGISTRATION_TOKEN=$(cat "${GITEA_RUNNER_REGISTRATION_TOKEN_FILE}")
fi

# Use the same ENV variable names as https://github.com/vegardit/docker-gitea-act-runner
test -f "$RUNNER_STATE_FILE" || echo "$RUNNER_STATE_FILE is missing or not a regular file"

if [[ ! -s "$RUNNER_STATE_FILE" ]]; then
try=$((try + 1))
success=0

# The point of this loop is to make it simple, when running both act_runner and gitea in docker,
# for the act_runner to wait a moment for gitea to become available before erroring out. Within
# the context of a single docker-compose, something similar could be done via healthchecks, but
# this is more flexible.
while [[ $success -eq 0 ]] && [[ $try -lt ${GITEA_MAX_REG_ATTEMPTS:-10} ]]; do
act_runner register \
--instance "${GITEA_INSTANCE_URL}" \
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
--name "${GITEA_RUNNER_NAME:-`hostname`}" \
${CONFIG_ARG} ${EXTRA_ARGS} --no-interactive 2>&1 | tee /tmp/reg.log

cat /tmp/reg.log | grep 'Runner registered successfully' > /dev/null
if [[ $? -eq 0 ]]; then
echo "SUCCESS"
success=1
else
echo "Waiting to retry ..."
sleep 5
fi
done
fi
# Prevent reading the token from the act_runner process
unset GITEA_RUNNER_REGISTRATION_TOKEN
unset GITEA_RUNNER_REGISTRATION_TOKEN_FILE

act_runner daemon ${CONFIG_ARG}

使用:

docker run -d --name='Gitea-Runner' --restart=always \
-e 'GITEA_RUNNER_REGISTRATION_TOKEN'='your_token' \
-e 'GITEA_RUNNER_NAME'='unraid_act_runner' \
-e 'GITEA_RUNNER_LABELS'='ubuntu-24.04,ubuntu-latest' \
-e 'GITEA_INSTANCE_URL'='https://gitea.amass.fun' \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /mnt/user/appdata/gitea-runner/data:/data \
frp-by1.wwvvww.cn:45288/act_runner:0.2.10

GITEA_RUNNER_REGISTRATION_TOKEN:在Actions设置创建 Runner的下拉框可以找到。

nanopb(i386)

在做嵌入式开发的时候,有时候会碰到很古老的一些应用工具,它只有 i386 的二进制可执行文件。有时候为了保持宿主机的干净,我们不想开启对 i386 可执行文件的支持,然后至少需要安装 libc6:i386libstdc++6:i386等一些安装包,这时我们也可以使用 docker 来帮助我们,让宿主机尽量保持 干净

nanopb 为例,这是笔者在工作中碰到的一个工具。

FROM ubuntu:22.04
LABEL maintainer 168062547@qq.com
ENV DEBIAN_FRONTEND=noninteractive

RUN sed -i "s@http://.*archive.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list && \
sed -i "s@http://.*security.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list && \
dpkg --add-architecture i386 && \
apt-get update && \
apt-get install -y wget libc6:i386 libstdc++6:i386

RUN cd /opt && \
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.3.9.3-linux-x86.tar.gz && \
tar xvf nanopb-0.3.9.3-linux-x86.tar.gz && \
rm nanopb-0.3.9.3-linux-x86.tar.gz

ENV PATH="${PATH}:/opt/nanopb-0.3.9.3-linux-x86/generator-bin"

这样,我们就可以执行以下命令,将 .proto 文件输出我们所需要的源代码文件:

docker run --rm -v .:/app -w /app  nanopb:0.3.9.3 protoc --nanopb_out=. ./PacketModel_all.proto

镜像上传

当我们制作好镜像后,可以上传到阿里云的容器镜像服务中。 这里我们以制作好的ubuntu镜像为例,在 仓库管理➡镜像仓库➡创建镜像仓库里创建名为ubuntu的仓库。然后在对应的仓库页,有如何拉取推送的说明与示例:

docker login --username=168062547@qq.com registry.cn-shenzhen.aliyuncs.com
docker tag [ImageId] registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu:[镜像版本号]
docker push registry.cn-shenzhen.aliyuncs.com/amass_toolset/ubuntu:[镜像版本号]