feat: チャートをゼロから再設計(外部DB前提のYOURLS+Nginx構成に作り直し)
Helm Chart Release / release-chart (push) Successful in 7s

旧チャートは存在しないDocker Hubイメージ(yourls/yourls)を参照し、Bitnami MySQL
サブチャートに依存するなど正しく動作しない状態だったため、YOURLS公式イメージ
(docker.io/yourls, container-entrypoint.sh/config-container.php)の実装を確認した上で
WordPressチャートと同一方針(外部DB、emptyDir+PVC分離、fsGroup非root実行、
real IP取得対応)でスクラッチから再構築した。
This commit is contained in:
2026-09-12 07:56:17 +09:00
parent a1cf9dced9
commit de1a4144b5
18 changed files with 1056 additions and 397 deletions
+153 -61
View File
@@ -14,19 +14,74 @@ spec:
labels:
{{- include "yourls-nginx.selectorLabels" . | nindent 8 }}
spec:
automountServiceAccountToken: false
securityContext:
fsGroup: 82
fsGroupChangePolicy: "OnRootMismatch"
initContainers:
- name: wait-for-mysql
image: busybox:1.36
command:
- sh
- -c
- name: yourls-init
image: "{{ .Values.image.yourls.registry }}/{{ .Values.image.yourls.repository }}:{{ .Values.image.yourls.tag }}"
imagePullPolicy: {{ .Values.image.yourls.pullPolicy }}
command: ["/bin/sh", "-c"]
args:
- |
until nc -z {{ include "yourls-nginx.fullname" . }}-mysql 3306; do
echo "Waiting for MySQL..."
sleep 2
set -e
echo "=== YOURLS initialization started ==="
echo "Copying YOURLS core files..."
# user/ ディレクトリ(プラグイン・ページ・言語・設定)は別ボリュームで扱うため、
# コアファイルのコピー対象から除外する。
for item in /usr/src/yourls/*; do
name=$(basename "$item")
if [ "$name" = "user" ]; then
continue
fi
cp -r "$item" /var/www/html/
done
echo "Core files copied"
mkdir -p /var/www/html/user
# user/plugins, user/pages, user/languages はPVC(またはemptyDir)。
# 初回(空)の場合のみYOURLS同梱のデフォルト内容(サンプルプラグイン等)を投入し、
# 以降は管理画面経由で追加/削除された内容をPod再起動のたびに上書きしない。
for sub in plugins pages languages; do
target="/var/www/html/user/$sub"
mkdir -p "$target"
if [ -z "$(ls -A "$target" 2>/dev/null)" ]; then
echo "Seeding default user/$sub (first run)"
cp -rp "/usr/src/yourls/user/$sub/"* "$target/" 2>/dev/null || true
else
echo "user/$sub already has content, preserving"
fi
done
# user/config.phpは環境変数(getenv)を読むだけのテンプレートで、YOURLS自体が
# 書き込むことはないため、永続化せず毎回生成し直す(values.yaml変更を確実に反映)。
echo "Generating user/config.php from environment variables..."
cp /usr/src/yourls/user/config-container.php /var/www/html/user/config.php
echo "=== YOURLS initialization completed ==="
securityContext:
runAsUser: 82
runAsGroup: 82
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: yourls-data
mountPath: /var/www/html
- name: yourls-persistent
mountPath: /var/www/html/user/plugins
subPath: plugins
- name: yourls-persistent
mountPath: /var/www/html/user/pages
subPath: pages
- name: yourls-persistent
mountPath: /var/www/html/user/languages
subPath: languages
containers:
# NGINX Container
- name: nginx
image: "{{ .Values.image.nginx.registry }}/{{ .Values.image.nginx.repository }}:{{ .Values.image.nginx.tag }}"
imagePullPolicy: {{ .Values.image.nginx.pullPolicy }}
@@ -35,87 +90,124 @@ spec:
containerPort: 80
protocol: TCP
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
- name: yourls-data
mountPath: /var/www/html
readOnly: true
- name: yourls-persistent
mountPath: /var/www/html/user/plugins
subPath: plugins
readOnly: true
- name: yourls-persistent
mountPath: /var/www/html/user/pages
subPath: pages
readOnly: true
- name: yourls-persistent
mountPath: /var/www/html/user/languages
subPath: languages
readOnly: true
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
{{- if .Values.healthCheck.enabled }}
livenessProbe:
{{- toYaml .Values.healthCheck.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.healthCheck.readinessProbe | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources.nginx | nindent 12 }}
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 5
# YOURLS PHP-FPM Container
- name: yourls
image: "{{ .Values.image.yourls.registry }}/{{ .Values.image.yourls.repository }}:{{ .Values.image.yourls.tag }}"
imagePullPolicy: {{ .Values.image.yourls.pullPolicy }}
command: ["php-fpm"]
securityContext:
runAsUser: 82
runAsGroup: 82
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
env:
- name: YOURLS_DB_HOST
value: "{{ include "yourls-nginx.fullname" . }}-mysql"
- name: YOURLS_DB_USER
value: {{ .Values.mysql.auth.username | quote }}
- name: YOURLS_DB_PASS
valueFrom:
secretKeyRef:
name: {{ include "yourls-nginx.fullname" . }}-mysql
key: mysql-password
- name: YOURLS_DB_NAME
value: {{ .Values.mysql.auth.database | quote }}
- name: YOURLS_SITE
value: {{ .Values.yourls.config.site | quote }}
value: {{ .Values.yourls.site | quote }}
- name: YOURLS_USER
value: {{ .Values.yourls.config.username | quote }}
value: {{ .Values.yourls.admin.username | quote }}
- name: YOURLS_PASS
valueFrom:
secretKeyRef:
name: {{ include "yourls-nginx.fullname" . }}-secret
key: yourls-password
{{- if .Values.yourls.admin.existingSecret }}
name: {{ .Values.yourls.admin.existingSecret }}
key: {{ .Values.yourls.admin.existingSecretKey }}
{{- else }}
name: {{ include "yourls-nginx.secretName" . }}
key: admin-password
{{- end }}
- name: YOURLS_COOKIEKEY
valueFrom:
secretKeyRef:
name: {{ include "yourls-nginx.fullname" . }}-secret
key: yourls-cookiekey
name: {{ include "yourls-nginx.secretName" . }}
key: cookiekey
- name: YOURLS_DB_HOST
value: {{ printf "%s:%v" .Values.database.host .Values.database.port | quote }}
- name: YOURLS_DB_USER
value: {{ .Values.database.user | quote }}
- name: YOURLS_DB_PASS
valueFrom:
secretKeyRef:
{{- if .Values.database.existingSecret }}
name: {{ .Values.database.existingSecret }}
key: {{ .Values.database.existingSecretKey }}
{{- else }}
name: {{ include "yourls-nginx.secretName" . }}
key: db-password
{{- end }}
- name: YOURLS_DB_NAME
value: {{ .Values.database.name | quote }}
- name: YOURLS_DB_PREFIX
value: {{ .Values.database.tablePrefix | quote }}
- name: YOURLS_HOURS_OFFSET
value: {{ .Values.yourls.config.hours_offset | quote }}
value: {{ .Values.yourls.hoursOffset | quote }}
- name: YOURLS_LANG
value: {{ .Values.yourls.config.lang | quote }}
value: {{ .Values.yourls.lang | quote }}
- name: YOURLS_PRIVATE
value: {{ .Values.yourls.privateMode | quote }}
{{- with .Values.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.extraEnvFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
# YOURLS本体はinitContainerが書き込んだ後は変更不要なため読み取り専用でマウントする
# (実行時に侵害された場合でも配信コードの改ざん・永続化を防ぐ多層防御)。
- name: yourls-data
mountPath: /var/www/html
readOnly: true
- name: yourls-persistent
mountPath: /var/www/html/user/plugins
subPath: plugins
- name: yourls-persistent
mountPath: /var/www/html/user/pages
subPath: pages
- name: yourls-persistent
mountPath: /var/www/html/user/languages
subPath: languages
resources:
{{- toYaml .Values.resources.yourls | nindent 12 }}
livenessProbe:
tcpSocket:
port: 9000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 9000
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: nginx-config
configMap:
name: {{ include "yourls-nginx.fullname" . }}-nginx-config
- name: yourls-data
{{- if .Values.yourls.persistence.enabled }}
emptyDir: {}
- name: yourls-persistent
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "yourls-nginx.fullname" . }}-pvc
{{- else }}
emptyDir: {}
{{- end }}
- name: nginx-config
configMap:
name: {{ include "yourls-nginx.fullname" . }}-nginx-config
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
@@ -127,4 +219,4 @@ spec:
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}