Skip to content

Commit 458b51b

Browse files
committed
feat(hyperf): add OTEL collector variants and unify hyperf-otel
Restructures hyperf/ to follow the {image}/{version} pattern used by aws-cli, node, quasar, vue and merges the standalone hyperf-otel image into hyperf/8.3 as a multi-stage variant. Targets: - hyperf -> devitools/hyperf:8.3 (lean, ~97MB, ENTRYPOINT=php) - hyperf-otel -> devitools/hyperf:8.3-<col> (~463MB, ENTRYPOINT=supervisord) Collectors live in hyperf/8.3/otel/collectors/*.yaml. New variants can be added by dropping a yaml file. Two ship in this PR: - debug (vendor-neutral, logs spans to stderr) - google (googlecloud exporter, replaces previous gcloud-specific default) hyperf/latest is a symlink to 8.3. Adds .github/workflows/build-images.yml: detects changed {image}/{version} folders on push to main, follows latest symlinks, and publishes one tag per (version, target) combo to Docker Hub via DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets.
1 parent 3697c8c commit 458b51b

14 files changed

Lines changed: 344 additions & 7 deletions

File tree

‎.github/workflows/build-images.yml‎

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Build and push images
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- '*/*/Dockerfile'
9+
- '*/*/rootfs/**'
10+
- '*/*/.scripts/**'
11+
- '*/*/otel/**'
12+
- '*/latest'
13+
workflow_dispatch:
14+
inputs:
15+
target:
16+
description: 'Image/version to build (e.g. hyperf/8.3 or hyperf/latest). Leave empty to detect.'
17+
required: false
18+
19+
jobs:
20+
detect:
21+
runs-on: ubuntu-latest
22+
outputs:
23+
builds: ${{ steps.list.outputs.builds }}
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
28+
- name: Get changed files
29+
id: changed
30+
if: github.event_name == 'push'
31+
uses: tj-actions/changed-files@v44
32+
33+
- name: Build matrix
34+
id: list
35+
shell: bash
36+
run: |
37+
set -euo pipefail
38+
39+
declare -a versions=()
40+
41+
collect_version() {
42+
local v="$1"
43+
[ -z "$v" ] && return
44+
local depth
45+
depth=$(awk -F/ '{print NF}' <<< "$v")
46+
if [ "$depth" != "2" ]; then return; fi
47+
local image_dir="$v"
48+
if [ -L "$image_dir" ]; then
49+
local link_target
50+
link_target=$(readlink "$image_dir")
51+
local parent="${image_dir%/*}"
52+
image_dir="${parent}/${link_target}"
53+
fi
54+
if [ ! -f "${image_dir}/Dockerfile" ]; then return; fi
55+
for existing in "${versions[@]+"${versions[@]}"}"; do
56+
if [ "$existing" = "$v" ]; then return; fi
57+
done
58+
versions+=("$v")
59+
}
60+
61+
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.target }}" ]; then
62+
collect_version "${{ inputs.target }}"
63+
else
64+
declare -a changed_dirs=()
65+
for f in ${{ steps.changed.outputs.all_changed_files || '' }}; do
66+
dir=$(awk -F/ 'NF>=2 {print $1"/"$2}' <<< "$f")
67+
[ -z "$dir" ] && continue
68+
for existing in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do
69+
if [ "$existing" = "$dir" ]; then dir=""; break; fi
70+
done
71+
[ -n "$dir" ] && changed_dirs+=("$dir")
72+
done
73+
74+
for v in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do
75+
collect_version "$v"
76+
done
77+
78+
for symlink in */latest; do
79+
[ -L "$symlink" ] || continue
80+
target=$(readlink "$symlink")
81+
parent="${symlink%/*}"
82+
resolved="${parent}/${target}"
83+
for changed in "${changed_dirs[@]+"${changed_dirs[@]}"}"; do
84+
if [ "$changed" = "$resolved" ]; then
85+
collect_version "$symlink"
86+
break
87+
fi
88+
done
89+
done
90+
fi
91+
92+
declare -a builds=()
93+
for v in "${versions[@]+"${versions[@]}"}"; do
94+
image="${v%%/*}"
95+
tag="${v##*/}"
96+
ctx="$v"
97+
if [ -L "$ctx" ]; then
98+
link_target=$(readlink "$ctx")
99+
ctx="${ctx%/*}/${link_target}"
100+
fi
101+
102+
builds+=("{\"image\":\"${image}\",\"tag\":\"${tag}\",\"context\":\"${ctx}\",\"target\":\"${image}\",\"variant\":\"\"}")
103+
104+
if [ -d "${ctx}/otel/collectors" ]; then
105+
for cfg in "${ctx}"/otel/collectors/*.yaml; do
106+
[ -f "$cfg" ] || continue
107+
variant=$(basename "$cfg" .yaml)
108+
builds+=("{\"image\":\"${image}\",\"tag\":\"${tag}-${variant}\",\"context\":\"${ctx}\",\"target\":\"${image}-otel\",\"variant\":\"${variant}\"}")
109+
done
110+
fi
111+
done
112+
113+
if [ ${#builds[@]} -eq 0 ]; then
114+
echo "builds=[]" >> "$GITHUB_OUTPUT"
115+
else
116+
joined=$(IFS=,; echo "${builds[*]}")
117+
echo "builds=[${joined}]" >> "$GITHUB_OUTPUT"
118+
fi
119+
120+
build:
121+
needs: detect
122+
if: needs.detect.outputs.builds != '[]'
123+
runs-on: ubuntu-latest
124+
strategy:
125+
fail-fast: false
126+
matrix:
127+
build: ${{ fromJson(needs.detect.outputs.builds) }}
128+
steps:
129+
- name: Checkout
130+
uses: actions/checkout@v4
131+
132+
- name: Set up Docker Buildx
133+
uses: docker/setup-buildx-action@v3
134+
135+
- name: Log in to Docker Hub
136+
uses: docker/login-action@v3
137+
with:
138+
username: ${{ secrets.DOCKERHUB_USERNAME }}
139+
password: ${{ secrets.DOCKERHUB_TOKEN }}
140+
141+
- name: Build and push
142+
uses: docker/build-push-action@v6
143+
with:
144+
context: ./${{ matrix.build.context }}
145+
target: ${{ matrix.build.target }}
146+
build-args: |
147+
${{ matrix.build.variant && format('COLLECTOR={0}', matrix.build.variant) || '' }}
148+
platforms: linux/amd64
149+
push: true
150+
tags: devitools/${{ matrix.build.image }}:${{ matrix.build.tag }}
151+
cache-from: type=gha,scope=${{ matrix.build.image }}-${{ matrix.build.tag }}
152+
cache-to: type=gha,mode=max,scope=${{ matrix.build.image }}-${{ matrix.build.tag }}
Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.20
1+
FROM alpine:3.20 AS hyperf
22

33
ARG CONTEXT
44
ARG TIMEZONE
@@ -57,18 +57,34 @@ COPY --from=composer/composer:2.8.5-bin /composer /usr/local/bin/composer
5757

5858
COPY .scripts /devitools/.scripts
5959

60-
# update
6160
RUN set -ex \
62-
# ---------- apply settings -------\
6361
&& bash /devitools/.scripts/setup.sh "$TIMEZONE" \
6462
&& bash /devitools/.scripts/setup-dev.sh "$APP_TARGET" \
65-
# ---------- clear works ----------\
6663
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man
6764

6865
WORKDIR /opt/www
69-
7066
EXPOSE 9501
67+
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php"]
68+
CMD ["start"]
69+
70+
# --- Variant: with OTEL Collector + pgbouncer + supervisor ---
71+
FROM hyperf AS hyperf-otel
72+
73+
ARG COLLECTOR=debug
74+
ARG OTEL_COLLECTOR_VERSION=0.121.0
75+
76+
RUN apk add --no-cache --upgrade expat \
77+
&& apk add --no-cache supervisor wget pgbouncer \
78+
&& wget -q "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_COLLECTOR_VERSION}/otelcol-contrib_${OTEL_COLLECTOR_VERSION}_linux_amd64.tar.gz" -O /tmp/otelcol.tar.gz \
79+
&& tar -xzf /tmp/otelcol.tar.gz -C /usr/local/bin otelcol-contrib \
80+
&& rm /tmp/otelcol.tar.gz \
81+
&& chmod +x /usr/local/bin/otelcol-contrib \
82+
&& mkdir -p /etc/pgbouncer /var/log/pgbouncer /var/run/pgbouncer /etc/supervisor.d /var/run/supervisor
7183

72-
ENTRYPOINT [ "php", "/opt/www/bin/hyperf.php" ]
84+
COPY otel/collectors/${COLLECTOR}.yaml /etc/otel-collector-config.yaml
85+
COPY otel/supervisord.conf /etc/supervisord.conf
86+
COPY otel/entrypoint.sh /entrypoint.sh
87+
RUN chmod +x /entrypoint.sh
7388

74-
CMD [ "start" ]
89+
ENTRYPOINT ["/entrypoint.sh"]
90+
CMD []
File renamed without changes.
File renamed without changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
receivers:
2+
zipkin:
3+
endpoint: "0.0.0.0:9411"
4+
5+
processors:
6+
batch:
7+
send_batch_size: 200
8+
timeout: 5s
9+
memory_limiter:
10+
check_interval: 1s
11+
limit_mib: 256
12+
13+
exporters:
14+
debug:
15+
verbosity: normal
16+
17+
service:
18+
pipelines:
19+
traces:
20+
receivers: [zipkin]
21+
processors: [memory_limiter, batch]
22+
exporters: [debug]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
receivers:
2+
zipkin:
3+
endpoint: "0.0.0.0:9411"
4+
5+
processors:
6+
batch:
7+
send_batch_size: 200
8+
timeout: 5s
9+
memory_limiter:
10+
check_interval: 1s
11+
limit_mib: 256
12+
13+
exporters:
14+
googlecloud:
15+
project: ${GOOGLE_CLOUD_PROJECT}
16+
17+
service:
18+
pipelines:
19+
traces:
20+
receivers: [zipkin]
21+
processors: [memory_limiter, batch]
22+
exporters: [googlecloud]

‎hyperf/8.3/otel/entrypoint.sh‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/sh
2+
set -e
3+
4+
if [ "$PGBOUNCER_ENABLED" != "true" ]; then
5+
exec supervisord -c /etc/supervisord.conf
6+
fi
7+
8+
PGBOUNCER_DATABASES="${PGBOUNCER_DATABASES:-default}"
9+
10+
DEFAULT_POOL_SIZE="${PGBOUNCER_DEFAULT_POOL_SIZE:-5}"
11+
MIN_POOL_SIZE="${PGBOUNCER_MIN_POOL_SIZE:-1}"
12+
RESERVE_POOL_SIZE="${PGBOUNCER_RESERVE_POOL_SIZE:-2}"
13+
MAX_CLIENT_CONN="${PGBOUNCER_MAX_CLIENT_CONN:-1000}"
14+
MAX_PREPARED_STATEMENTS="${PGBOUNCER_MAX_PREPARED_STATEMENTS:-100}"
15+
SERVER_IDLE_TIMEOUT="${PGBOUNCER_SERVER_IDLE_TIMEOUT:-300}"
16+
SERVER_LIFETIME="${PGBOUNCER_SERVER_LIFETIME:-3600}"
17+
18+
resolve_prefix() {
19+
if [ "$1" = "default" ]; then
20+
echo "POSTGRES_DB"
21+
else
22+
echo "POSTGRES_DB_$(echo "$1" | tr '[:lower:]' '[:upper:]')"
23+
fi
24+
}
25+
26+
echo "[databases]" > /etc/pgbouncer/pgbouncer.ini
27+
28+
OLD_IFS="$IFS"
29+
IFS=','
30+
for alias in $PGBOUNCER_DATABASES; do
31+
IFS="$OLD_IFS"
32+
alias=$(echo "$alias" | tr -d ' ')
33+
if [ -n "$alias" ]; then
34+
prefix=$(resolve_prefix "$alias")
35+
host=$(eval "printf '%s' \"\${${prefix}_HOST:-}\"")
36+
port=$(eval "printf '%s' \"\${${prefix}_PORT:-5432}\"")
37+
name=$(eval "printf '%s' \"\${${prefix}_NAME:-}\"")
38+
user=$(eval "printf '%s' \"\${${prefix}_USERNAME:-}\"")
39+
pass=$(eval "printf '%s' \"\${${prefix}_PASSWORD:-}\"")
40+
41+
echo "pgb_${alias} = host=${host} port=${port} dbname=${name} user=${user} password=${pass}" >> /etc/pgbouncer/pgbouncer.ini
42+
fi
43+
IFS=','
44+
done
45+
IFS="$OLD_IFS"
46+
47+
cat >> /etc/pgbouncer/pgbouncer.ini <<EOF
48+
49+
[pgbouncer]
50+
listen_addr = 127.0.0.1
51+
listen_port = 6432
52+
auth_type = any
53+
pool_mode = transaction
54+
default_pool_size = ${DEFAULT_POOL_SIZE}
55+
min_pool_size = ${MIN_POOL_SIZE}
56+
reserve_pool_size = ${RESERVE_POOL_SIZE}
57+
max_client_conn = ${MAX_CLIENT_CONN}
58+
max_prepared_statements = ${MAX_PREPARED_STATEMENTS}
59+
server_idle_timeout = ${SERVER_IDLE_TIMEOUT}
60+
server_lifetime = ${SERVER_LIFETIME}
61+
log_connections = 0
62+
log_disconnections = 0
63+
log_stats = 0
64+
logfile = /dev/stderr
65+
pidfile = /var/run/pgbouncer/pgbouncer.pid
66+
EOF
67+
68+
cat > /etc/supervisor.d/pgbouncer.ini <<EOF
69+
[program:pgbouncer]
70+
command=pgbouncer /etc/pgbouncer/pgbouncer.ini
71+
autostart=true
72+
autorestart=true
73+
priority=0
74+
stdout_logfile=/dev/stdout
75+
stdout_logfile_maxbytes=0
76+
stderr_logfile=/dev/stderr
77+
stderr_logfile_maxbytes=0
78+
EOF
79+
80+
IFS=','
81+
for alias in $PGBOUNCER_DATABASES; do
82+
IFS="$OLD_IFS"
83+
alias=$(echo "$alias" | tr -d ' ')
84+
if [ -n "$alias" ]; then
85+
prefix=$(resolve_prefix "$alias")
86+
eval "export ${prefix}_HOST=127.0.0.1"
87+
eval "export ${prefix}_PORT=6432"
88+
eval "export ${prefix}_NAME=pgb_${alias}"
89+
eval "export ${prefix}_READ_HOST=127.0.0.1"
90+
eval "export ${prefix}_READ_PORT=6432"
91+
fi
92+
IFS=','
93+
done
94+
IFS="$OLD_IFS"
95+
96+
exec supervisord -c /etc/supervisord.conf

‎hyperf/8.3/otel/supervisord.conf‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[supervisord]
2+
nodaemon=true
3+
logfile=/dev/null
4+
logfile_maxbytes=0
5+
pidfile=/var/run/supervisor/supervisord.pid
6+
7+
[program:hyperf]
8+
command=php /opt/www/bin/hyperf.php start
9+
autostart=true
10+
autorestart=true
11+
priority=1
12+
stdout_logfile=/dev/stdout
13+
stdout_logfile_maxbytes=0
14+
stderr_logfile=/dev/stderr
15+
stderr_logfile_maxbytes=0
16+
17+
[program:otel-collector]
18+
command=otelcol-contrib --config=/etc/otel-collector-config.yaml
19+
autostart=true
20+
autorestart=true
21+
priority=2
22+
stdout_logfile=/dev/stdout
23+
stdout_logfile_maxbytes=0
24+
stderr_logfile=/dev/stderr
25+
stderr_logfile_maxbytes=0
26+
27+
[include]
28+
files = /etc/supervisor.d/*.ini

0 commit comments

Comments
 (0)