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
+7
View File
@@ -0,0 +1,7 @@
---
trigger: always_on
description: Read the CLAUDE.md hierarchy via GEMINI.md before doing anything in this workspace
---
最初に、このディレクトリの `GEMINI.md` を読み、そこに列挙された CLAUDE.md を上から順にすべて読んで従うこと。
Read `GEMINI.md` in this directory first, then read and follow every CLAUDE.md it lists, in order.
+106
View File
@@ -0,0 +1,106 @@
# .gitea/workflows/helm-release.yaml
# 汎用Helmチャート公開ワークフロー(全リポジトリ共通)
name: Helm Chart Release
on:
push:
branches:
- main
- master
workflow_dispatch:
env:
REGISTRY_URL: https://git.cafepieters.com
OWNER: helmchart
jobs:
release-chart:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: |
echo "🔄 Cloning repository..."
git clone --depth 1 $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git .
echo "✅ Repository cloned"
- name: Install Helm
run: |
echo "📦 Installing Helm..."
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
echo "✅ Helm installed"
- name: Get Chart Info
id: chart_info
run: |
# Chart.yamlからチャート名とバージョンを取得
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
CHART_VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}')
echo "Chart Name: ${CHART_NAME}"
echo "Chart Version: ${CHART_VERSION}"
# GitHub Actionsの環境変数として設定
echo "CHART_NAME=${CHART_NAME}" >> $GITHUB_ENV
echo "CHART_VERSION=${CHART_VERSION}" >> $GITHUB_ENV
- name: Validate Chart
run: |
echo "🔍 Validating Helm chart..."
helm lint .
echo "✅ Chart validation passed"
- name: Package Chart
run: |
echo "📦 Packaging Helm chart..."
helm package .
CHART_FILE=$(ls *.tgz)
echo "✅ Packaged: ${CHART_FILE}"
echo "CHART_FILE=${CHART_FILE}" >> $GITHUB_ENV
- name: Publish to Gitea Package Registry
run: |
echo "🚀 Publishing ${CHART_FILE} to Gitea Package Registry..."
curl --fail-with-body \
-u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" \
-X POST \
--upload-file "${CHART_FILE}" \
"${REGISTRY_URL}/api/packages/${OWNER}/helm/api/charts"
echo "✅ Chart published successfully!"
- name: Summary
if: success()
run: |
echo "================================"
echo "✅ Deployment Successful!"
echo "================================"
echo "Repository: ${{ github.repository }}"
echo "Chart Name: ${CHART_NAME}"
echo "Chart Version: ${CHART_VERSION}"
echo "Chart File: ${CHART_FILE}"
echo "Branch: ${{ github.ref }}"
echo "Commit: ${{ github.sha }}"
echo "================================"
echo ""
echo "📦 Install with:"
echo "helm repo add cafepieters ${REGISTRY_URL}/api/packages/${OWNER}/helm"
echo "helm repo update"
echo "helm install my-${CHART_NAME} cafepieters/${CHART_NAME}"
- name: Error Report
if: failure()
run: |
echo "================================"
echo "❌ Deployment Failed!"
echo "================================"
echo "Check the logs above for details"
echo "Common issues:"
echo "- Missing Chart.yaml"
echo "- Invalid Helm chart structure"
echo "- Missing REGISTRY_USER or REGISTRY_TOKEN secrets"
echo "- Insufficient permissions on Personal Access Token"
echo "================================"
+145 -112
View File
@@ -2,96 +2,113 @@ name: Update Docker Image Tags and Release Helm Chart
on:
schedule:
- cron: '0 4 * * 1' # 毎週月曜日 4:00 AM (JST 1:00 PM)
- cron: "0 4 * * 1" # 毎週月曜日 04:00 UTC (JST 13:00)
workflow_dispatch:
jobs:
update-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.12.0'
- name: Check for new nginx version
id: nginx
run: |
set -e
echo "Checking nginx versions..."
CURRENT=$(grep -A3 "nginx:" values.yaml | grep "tag:" | head -1 | sed 's/.*tag: *"\([^"]*\)".*/\1/' | tr -d ' ')
echo "Current nginx: $CURRENT"
# Docker Hub API v2を使用してタグを取得
LATEST=$(curl -s "https://registry.hub.docker.com/v2/repositories/library/nginx/tags?page_size=100" | \
jq -r '.results[].name' | \
grep -E '^[0-9]+\.[0-9]+\.[0-9]+-alpine-perl$' | \
sort -V | tail -1)
if [ -z "$LATEST" ]; then
echo "Warning: Could not fetch latest nginx version, using current"
LATEST="$CURRENT"
fi
echo "Latest nginx: $LATEST"
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "latest=$LATEST" >> $GITHUB_OUTPUT
- name: Check for new YOURLS version
- name: Check for new YOURLS FPM Alpine version
id: yourls
run: |
set -e
echo "Checking YOURLS versions..."
echo "Checking YOURLS fpm-alpine versions..."
CURRENT=$(grep -A3 "yourls:" values.yaml | grep "tag:" | head -1 | sed 's/.*tag: *"\([^"]*\)".*/\1/' | tr -d ' ')
echo "Current YOURLS: $CURRENT"
# Docker Hub API v2を使用してタグを取得
# パターン: 1.10.2-fpm-alpine 形式
LATEST=$(curl -s "https://registry.hub.docker.com/v2/repositories/yourls/yourls/tags?page_size=100" | \
# 公式イメージは docker.io/yourls (docker-library配下)。
# docker.io/yourls/yourls という別名前空間のイメージは非公式のため使用しない。
LATEST=$(curl -s "https://hub.docker.com/v2/repositories/library/yourls/tags?page_size=100&name=fpm-alpine" | \
jq -r '.results[].name' | \
grep -E '^[0-9]+\.[0-9]+\.[0-9]+-fpm-alpine$' | \
sort -V | tail -1)
grep -v 'rc' | \
grep -v 'beta' | \
grep -v 'alpha' | \
sort -V | \
tail -n 1)
if [ -z "$LATEST" ]; then
echo "Warning: Could not fetch latest YOURLS version, using current"
LATEST="$CURRENT"
fi
# YOURLSバージョンを抽出 (1.10.2の部分)
APP_VERSION=$(echo "$LATEST" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
if [ -z "$APP_VERSION" ]; then
echo "Error: Could not extract YOURLS version"
exit 1
echo "Warning: Could not extract YOURLS version from: $LATEST"
APP_VERSION=$(echo "$CURRENT" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
fi
echo "Latest YOURLS: $LATEST"
echo "YOURLS version: $APP_VERSION"
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "latest=$LATEST" >> $GITHUB_OUTPUT
echo "app_version=$APP_VERSION" >> $GITHUB_OUTPUT
- name: Check for new Nginx Alpine Perl version
id: nginx
run: |
set -e
echo "Checking Nginx Alpine Perl versions..."
CURRENT=$(grep -A3 "nginx:" values.yaml | grep "tag:" | head -1 | sed 's/.*tag: *"\([^"]*\)".*/\1/' | tr -d ' ')
echo "Current Nginx: $CURRENT"
LATEST=$(curl -s "https://hub.docker.com/v2/repositories/library/nginx/tags?page_size=100&name=alpine-perl" | \
jq -r '.results[].name' | \
grep -E '^[0-9]+\.[0-9]+\.[0-9]+-alpine-perl$' | \
grep -v 'rc' | \
grep -v 'beta' | \
grep -v 'alpha' | \
sort -V | \
tail -n 1)
if [ -z "$LATEST" ]; then
echo "Warning: Could not fetch latest Nginx version, using current"
LATEST="$CURRENT"
fi
echo "Latest Nginx: $LATEST"
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "latest=$LATEST" >> $GITHUB_OUTPUT
- name: Determine if update is needed
id: check_update
run: |
set -e
NGINX_CURRENT="${{ steps.nginx.outputs.current }}"
NGINX_LATEST="${{ steps.nginx.outputs.latest }}"
YOURLS_CURRENT="${{ steps.yourls.outputs.current }}"
YOURLS_LATEST="${{ steps.yourls.outputs.latest }}"
NGINX_CURRENT="${{ steps.nginx.outputs.current }}"
NGINX_LATEST="${{ steps.nginx.outputs.latest }}"
echo "Nginx: $NGINX_CURRENT vs $NGINX_LATEST"
echo "YOURLS: $YOURLS_CURRENT vs $YOURLS_LATEST"
echo "Nginx: $NGINX_CURRENT vs $NGINX_LATEST"
if [ "$NGINX_CURRENT" != "$NGINX_LATEST" ] || [ "$YOURLS_CURRENT" != "$YOURLS_LATEST" ]; then
echo "update_needed=true" >> $GITHUB_OUTPUT
UPDATE_NEEDED=false
YOURLS_UPDATED=false
if [ "$YOURLS_CURRENT" != "$YOURLS_LATEST" ]; then
UPDATE_NEEDED=true
YOURLS_UPDATED=true
echo "YOURLS update detected"
fi
if [ "$NGINX_CURRENT" != "$NGINX_LATEST" ]; then
UPDATE_NEEDED=true
echo "Nginx update detected"
fi
echo "update_needed=$UPDATE_NEEDED" >> $GITHUB_OUTPUT
echo "yourls_updated=$YOURLS_UPDATED" >> $GITHUB_OUTPUT
if [ "$UPDATE_NEEDED" = "true" ]; then
echo "Update is needed"
else
echo "update_needed=false" >> $GITHUB_OUTPUT
echo "Already up to date"
echo "Already up to date - no action required"
fi
- name: Update values.yaml
@@ -100,36 +117,29 @@ jobs:
set -e
echo "Updating values.yaml..."
# YOURLSバージョンを取得
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
# Nginx更新
NGINX_OLD="${{ steps.nginx.outputs.current }}"
NGINX_NEW="${{ steps.nginx.outputs.latest }}"
if [ "$NGINX_OLD" != "$NGINX_NEW" ]; then
# nginxセクションのtagのみを更新(1つ目のタグ)
sed -i "0,/tag: \"${NGINX_OLD}\"/s//tag: \"${NGINX_NEW}\"/" values.yaml
echo "Nginx updated: $NGINX_OLD -> $NGINX_NEW"
fi
# YOURLS更新
YOURLS_OLD="${{ steps.yourls.outputs.current }}"
YOURLS_NEW="${{ steps.yourls.outputs.latest }}"
if [ "$YOURLS_OLD" != "$YOURLS_NEW" ]; then
# yourlsセクションのtagのみを更新(2つ目のタグ)
sed -i "0,/tag: \"${NGINX_NEW}\"/b; s/tag: \"${YOURLS_OLD}\"/tag: \"${YOURLS_NEW}\"/" values.yaml
sed -i "s|tag: \"${YOURLS_OLD}\"|tag: \"${YOURLS_NEW}\"|g" values.yaml
echo "YOURLS updated: $YOURLS_OLD -> $YOURLS_NEW"
fi
NGINX_OLD="${{ steps.nginx.outputs.current }}"
NGINX_NEW="${{ steps.nginx.outputs.latest }}"
if [ "$NGINX_OLD" != "$NGINX_NEW" ]; then
sed -i "s|tag: \"${NGINX_OLD}\"|tag: \"${NGINX_NEW}\"|g" values.yaml
echo "Nginx updated: $NGINX_OLD -> $NGINX_NEW"
fi
echo "values.yaml updated"
git diff values.yaml
- name: Update Chart.yaml version
if: steps.check_update.outputs.update_needed == 'true'
if: steps.check_update.outputs.yourls_updated == 'true'
run: |
set -e
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
sed -i "s/^version: .*/version: \"$APP_VERSION\"/" Chart.yaml
sed -i "s/^version: .*/version: $APP_VERSION/" Chart.yaml
sed -i "s/^appVersion: .*/appVersion: \"$APP_VERSION\"/" Chart.yaml
echo "Chart.yaml updated to version $APP_VERSION"
cat Chart.yaml
@@ -138,105 +148,128 @@ jobs:
if: steps.check_update.outputs.update_needed == 'true'
run: |
git config user.name "Gitea Actions Bot"
git config user.email "actions@gitea.local"
git config user.email "actions@git.cafepieters.com"
if [ "${{ steps.check_update.outputs.yourls_updated }}" = "true" ]; then
git add values.yaml Chart.yaml
git commit -m "chore: update to YOURLS ${{ steps.yourls.outputs.app_version }}, nginx ${{ steps.nginx.outputs.latest }}"
git push origin main
else
git add values.yaml
git commit -m "chore: update nginx to ${{ steps.nginx.outputs.latest }} (no release)"
fi
MAX_RETRIES=3
RETRY_COUNT=0
until git push origin main || [ $RETRY_COUNT -eq $MAX_RETRIES ]; do
RETRY_COUNT=$((RETRY_COUNT+1))
echo "Push failed, retrying ($RETRY_COUNT/$MAX_RETRIES)..."
sleep 5
git pull --rebase origin main
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "ERROR: Failed to push after $MAX_RETRIES attempts"
exit 1
fi
echo "Successfully pushed changes to main branch"
- name: Install Helm
if: steps.check_update.outputs.yourls_updated == 'true'
uses: azure/setup-helm@v3
with:
version: 'latest'
- name: Package Helm Chart
if: steps.check_update.outputs.update_needed == 'true'
if: steps.check_update.outputs.yourls_updated == 'true'
run: |
helm package .
echo "Helm chart packaged"
- name: Create Git Tag
if: steps.check_update.outputs.update_needed == 'true'
if: steps.check_update.outputs.yourls_updated == 'true'
run: |
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
if git rev-parse "v$APP_VERSION" >/dev/null 2>&1; then
echo "Tag v$APP_VERSION already exists, skipping tag creation"
else
git tag -a "v$APP_VERSION" -m "Release YOURLS $APP_VERSION"
git push origin "v$APP_VERSION"
echo "Git tag v$APP_VERSION created"
fi
- name: Create Gitea Release
if: steps.check_update.outputs.update_needed == 'true'
if: steps.check_update.outputs.yourls_updated == 'true'
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
RELEASE_BODY="YOURLS Helm Chart v${APP_VERSION} - YOURLS: ${{ steps.yourls.outputs.latest }}, Nginx: ${{ steps.nginx.outputs.latest }}"
RELEASE_BODY="YOURLS Helm Chart v${APP_VERSION} - Automated release"
# リリースを作成
if curl -s -H "Authorization: token ${GITEA_TOKEN}" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/v${APP_VERSION}" | jq -e '.id' >/dev/null 2>&1; then
echo "Release v$APP_VERSION already exists, skipping release creation"
else
curl -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"v${APP_VERSION}\",\"name\":\"v${APP_VERSION}\",\"body\":\"${RELEASE_BODY}\"}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases"
# リリースIDを取得
RELEASE_ID=$(curl -s \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/v${APP_VERSION}" | jq -r '.id')
# アセットをアップロード
curl -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/gzip" \
--data-binary "@${PACKAGE_FILE}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${PACKAGE_FILE}"
echo "Release created with asset: ${PACKAGE_FILE}"
echo "Release v${APP_VERSION} created with asset ${PACKAGE_FILE}"
fi
- name: Checkout Helm Repository
if: steps.check_update.outputs.update_needed == 'true'
uses: actions/checkout@v3
with:
repository: helmchart/repo
token: ${{ secrets.GITEA_TOKEN }}
path: helm-repo
fetch-depth: 0
- name: Update Helm Repository Index
if: steps.check_update.outputs.update_needed == 'true'
- name: Publish to Gitea Package Registry
if: steps.check_update.outputs.yourls_updated == 'true'
run: |
set -e
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
echo "Updating Helm repository..."
echo "🚀 Publishing ${PACKAGE_FILE} to Gitea Package Registry..."
# パッケージファイルをHelmリポジトリにコピー
cp "${PACKAGE_FILE}" helm-repo/
curl --fail-with-body \
-u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" \
-X POST \
--upload-file "${PACKAGE_FILE}" \
"https://git.cafepieters.com/api/packages/helmchart/helm/api/charts"
cd helm-repo
# index.yamlを生成/更新
helm repo index . --url "https://git.cafepieters.com/helmchart/repo/raw/branch/main"
# コミットしてプッシュ
git config user.name "Gitea Actions Bot"
git config user.email "actions@gitea.local"
git add "${PACKAGE_FILE}" index.yaml
git commit -m "chore: add ${CHART_NAME} v${APP_VERSION}" || echo "No changes to commit"
git push origin main
echo "Helm repository updated successfully"
echo "✅ Chart published successfully to Gitea Package Registry"
- name: Summary
if: steps.check_update.outputs.update_needed == 'true'
if: always()
run: |
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
NGINX_VERSION="${{ steps.nginx.outputs.latest }}"
YOURLS_VERSION="${{ steps.yourls.outputs.latest }}"
echo "========================================"
echo "Update completed successfully!"
echo "========================================"
echo "Chart Version: ${APP_VERSION}"
echo "YOURLS: ${YOURLS_VERSION}"
echo "Nginx: ${NGINX_VERSION}"
echo "========================================"
echo "Helm repository updated at:"
echo "https://git.cafepieters.com/helmchart/repo"
echo "========================================"
YOURLS_UPDATED="${{ steps.check_update.outputs.yourls_updated }}"
echo "================================"
echo "Update completed!"
echo "- YOURLS: ${APP_VERSION}"
echo "- Nginx: ${NGINX_VERSION}"
echo ""
if [ "$YOURLS_UPDATED" = "true" ]; then
echo "✅ YOURLS version updated - Release created (v${APP_VERSION})"
echo "YOURLS updated: ${{ steps.yourls.outputs.current }} -> ${{ steps.yourls.outputs.latest }}"
echo "Nginx updated: ${{ steps.nginx.outputs.current }} -> ${{ steps.nginx.outputs.latest }}"
elif [ "${{ steps.check_update.outputs.update_needed }}" = "true" ]; then
echo "ℹ️ Nginx only update - No release (waiting for next YOURLS update)"
echo "Nginx updated: ${{ steps.nginx.outputs.current }} -> ${{ steps.nginx.outputs.latest }}"
else
echo "ℹ️ Already up to date - no action required"
fi
echo "================================"
+2
View File
@@ -0,0 +1,2 @@
# Claude Code local settings
.claude/
+22
View File
@@ -0,0 +1,22 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.gitea/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
# Helm packages directory (pre-built packages)
packages/
*.tgz
+43
View File
@@ -0,0 +1,43 @@
# CLAUDE.md — yourls Helm Chart
> **共通ルール(要約)** — 詳細は `P:\Claude\CLAUDE.md`(全体共通)と `P:\Claude\HelmChart\CLAUDE.md`(Helm チャート共通: リリースフロー・ワークフロー・クラスタ構成)を参照。
> 作業前に pull / 機能変更時は README 更新 / 作業単位ごとに commit → push まで / コミットに AI クレジットを付けない / pull で入った変更を revert しない / Helm・PHP はローカル実行不可(Python のみ可)。
## リポジトリ概要
Raspberry Pi などのベアメタルで稼働することを想定した、Kubernetes 上で動作する Alpine Nginx + YOURLS(URL短縮サービス、PHP-FPM)の Helm チャート。
- **Gitea リポジトリ**: `ssh://git@192.168.9.65/helmchart/yourls`
- **Helm リポジトリ**: `https://git.cafepieters.com/api/packages/helmchart/helm`
- **公式イメージ**: `docker.io/yourls`(`docker-library`配下の公式イメージ。`yourls/yourls`という別名前空間のイメージは非公式であり誤り)
## 実行環境について
YOURLS本体(コアファイル一式)はイメージ内蔵のもの(`docker.io/yourls:<version>-fpm-alpine`)をemptyDirにコピーして使い捨てで起動する(WordPressチャートと同じbitnami方式)。`user/plugins`・`user/pages`・`user/languages`(管理画面経由で追加されるプラグイン・カスタムページ・言語ファイル)のみPVCで永続化する。`user/config.php`はYOURLS公式イメージ同梱の`config-container.php`(環境変数を`getenv()`で読むだけのテンプレート。YOURLS本体はこのファイルに書き込まない)をPod起動のたびにコピーするだけで、永続化はしない。PHPの実行環境はイメージに依存し、本リポジトリにPHPコードは含まれない。
データベースは外部MySQL/MariaDBを前提とし、サブチャートは同梱しない(WordPress/phpfpmチャートと同一方針)。
## 設計上の注意(このチャート固有)
- **YOURLS_SITEは固定値**: WordPressの`WP_HOME`/`WP_SITEURL`のように実行時のHTTP Hostヘッダーから自動導出されない。短縮URL生成に使う固定文字列のため、`values.yourls.site`を実際のドメインに必ず設定する必要がある。
- **Cookie暗号化キー・自動生成パスワードの安定化**: `templates/_helpers.tpl`の`yourls-nginx.existingSecretValue`が`lookup`関数で既存Secretの値を参照し、`helm upgrade`のたびに`cookiekey`や自動生成された`admin-password`がローテーションされない(ログインセッション・既知のパスワードが無効化されるのを防ぐ)。`helm template`(クラスタ未接続)ではlookupが空を返すため、その場合のみ新しい値が生成される(クラスタに適用されないため実害はない)。
- **liveness/readinessの使い分け**: `livenessProbe`は`/robots.txt`(YOURLSがDB接続前に無条件で200を返す固定応答。`yourls-loader.php`のソースで確認済み)を見ており、DB接続の一時的な断でPodが再起動ループに陥らないようにしている。`readinessProbe`は`/`(DB接続を要する)を見ており、DB未接続時はServiceの振り分けから外れる。
- **VIP/グローバルIPの希少性**: 組織CLAUDE.mdの「実運用クラスタ構成」を踏まえ、`service.type`の既定は`ClusterIP`(+Ingress共有)。専用LoadBalancer IPが必要な場合のみ明示的に`LoadBalancer`へ変更する運用とする。
- **非root実行**: `yourls`/`yourls-init`コンテナは`runAsUser/runAsGroup: 82`(www-data)で実行し、Pod全体に`fsGroup: 82`を設定してPVC/emptyDirの書き込み権限を確保する。`nginx`コンテナは公式イメージの挙動(masterはroot、workerは`nginx`ユーザー)に委ね、org内WordPressチャートと同じ方針でsecurityContextの上書きは行わない。
## チャート改修履歴
### ゼロからの再設計(2026-09-12, v1.10.6)
**背景**: 旧チャートが正しく動作しないとの報告を受け、スクラッチで設計をやり直した。
**旧チャートの問題点**(調査により判明):
- `.gitea/workflows/image-update-and-release.yaml`が存在しない`docker.io/yourls/yourls`(個人名前空間)を追跡しており、正しい公式イメージ`docker.io/yourls`(`library/yourls`)ではなかった。
- `image.yourls.tag`が`1.10.2-fpm-alpine`と古く(最新は`1.10.6-fpm-alpine`)、追跡ワークフローの参照先が誤っていたため自動更新も機能していなかった。
- Bitnami MySQLサブチャートに依存しており、org内の他チャート(WordPress/phpfpm)が採用する「外部DB前提・サブチャート非同梱」の方針と乖離していた。ARM(Raspberry Pi)環境でのBitnamiサブチャートの動作も未検証だった。
- セキュリティコンテキストが一切設定されておらず(root実行)、実IP取得・ヘルスチェックの設計(liveness/readinessの使い分け)も無かった。
- `.gitea/workflows/helm-release.yaml`が存在せず、mainブランチpush時のチャート公開が自動化されていなかった。
**新チャートの設計方針**: [YOURLS/containers](https://github.com/YOURLS/containers)(公式Dockerイメージのソース)の`container-entrypoint.sh`・`Dockerfile.template`・`config-container.php`を実際に確認した上で、この組織で実績のあるWordPressチャートの構成パターン(emptyDir+PVC分離、fsGroup非root実行、real IP取得対応、extraEnvFrom/extraEnvによるsecret-manager連携)を踏襲して再構築した。
**対象ファイル**: 全ファイル(スクラッチから再作成)。詳細は README.md を参照。
+12 -10
View File
@@ -1,21 +1,23 @@
apiVersion: v2
name: yourls-nginx
description: A Helm chart for YOURLS URL shortener with NGINX
description: YOURLS (Your Own URL Shortener) with Nginx and PHP-FPM on Kubernetes
type: application
version: 1.0.0
appVersion: "1.10.2"
version: 1.10.6
appVersion: "1.10.6"
keywords:
- yourls
- url-shortener
- nginx
- php-fpm
maintainers:
- name: Pieter
url: https://git.cafepieters.com/helmchart/repo/
home: https://yourls.org/
sources:
- https://github.com/YOURLS/YOURLS
maintainers:
- name: Pieter
dependencies:
- name: mysql
version: "9.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: mysql.enabled
- https://github.com/YOURLS/containers
icon: https://yourls.org/images/yourls-logo.png
kubeVersion: ">=1.19.0-0"
annotations:
category: Networking
licenses: MIT
+23
View File
@@ -0,0 +1,23 @@
# GEMINI.md — Antigravity (Gemini) 用エントリポイント(HelmChart\yourls)
このディレクトリのルールはすべて **`CLAUDE.md`** に記載されている。本ファイルにはルール本文を書かず、読むべき CLAUDE.md への参照だけを置く(正本は常に CLAUDE.md)。
## 必ず最初に実行すること
以下のファイルを**上から順に**読み込み、すべての指示に従うこと。上位ほど広い範囲に適用される共通ルールで、下位は上位を前提とした補足・例外である。
1. `P:\Claude\CLAUDE.md` — 全組織共通ルール(Git 運用・コミット書式・実行環境・情報記載の原則)
2. `P:\Claude\HelmChart\CLAUDE.md` — HelmChart 組織共通ルール
3. `P:\Claude\HelmChart\yourls\CLAUDE.md` — yourls リポジトリ固有ルール(このディレクトリ)
> Antigravity はワークスペース(または git ルート)直下のルールしか自動で読まず、親フォルダを遡らない。そのため上位階層のパスをここに明示している。`P:\Claude` はクラウド同期されており、デスクトップ・ノート PC とも同じパスで存在する。
## 読み替え
- CLAUDE.md 中の「Claude」「Claude Code」は、Antigravity で作業する場合は Gemini 自身のことと読み替える。
- Git のコミット者は Antigravity でも `user.name = Claude` / `user.email = claude@cafepieters.com` のまま使う(変更しない)。コミットメッセージに AI クレジットを付けない。
- Claude Code 固有の機能(`.claude/` 設定、スキル、メモリ等)に関する記述は無視してよい。
## 保守ルール
- ルールの追加・変更は **CLAUDE.md にのみ**行う。本ファイルと `.agents/rules/claude-md.md` は参照だけを保持し、内容を増やさない。
+163 -61
View File
@@ -1,26 +1,53 @@
# YOURLS with NGINX Helm Chart
# YOURLS with Nginx Helm Chart
このHelmチャートは、YOURLS (Your Own URL Shortener) をNGINXとPHP-FPMの構成でKubernetesにデプロイします。
[YOURLS](https://yourls.org/)(Your Own URL Shortener)をNginx + PHP-FPMの構成でKubernetesにデプロイするHelmチャートです。Raspberry Piなどのベアメタル(ARM)クラスタでの運用を想定しています。
## 特徴
## 主な機能
- NGINX (1.29.3-alpine-perl) + YOURLS (1.10.2-fpm-alpine) のマルチコンテナPod構成
- MySQL/MariaDBのサブチャートによるデータベース管理
- 永続ボリュームによるデータ保存
- Ingressサポート (オプション)
- セキュアな環境変数管理
- リソース制限とヘルスチェック
- Nginx(alpine-perl) + YOURLS公式イメージ(fpm-alpine)のマルチコンテナPod構成
- 外部MySQL/MariaDBを利用(サブチャート非同梱。WordPress/phpfpmチャートと同一方針)
- YOURLS本体はemptyDirに展開する使い捨て構成(bitnami方式)。管理画面から追加した
プラグイン・カスタムページ・言語ファイルのみPVCで永続化
- 非root実行(uid/gid 82 = www-data)+ 読み取り専用ルートファイルシステムマウントによる多層防御
- Ingress/LoadBalancer両対応、ベアメタル環境での実クライアントIP取得に対応
- secret-manager等の外部Secretからのパスワード参照(`existingSecret`)、`extraEnvFrom`/`extraEnv`による環境変数の追加注入
- helm upgradeを跨いでもCookie暗号化キー・自動生成パスワードがローテーションしない設計
## アーキテクチャとセキュリティ
### ストレージ構成
```
/var/www/html/ ← emptyDir (Pod再起動で消える。YOURLS本体を都度展開)
├── admin/, includes/, css/, js/, images/, yourls-*.php ... (イメージから展開)
└── user/
├── config.php ← 環境変数から都度生成(永続化しない)
├── plugins/ ┐
├── pages/ ├─ PVCの該当パスをsubPathマウント(永続化)
└── languages/ ┘
```
`user/config.php`はYOURLS公式イメージ同梱の`config-container.php`をコピーしただけのもので、中身は`getenv()`で環境変数を読むだけのテンプレートです。YOURLS本体がこのファイルに書き込むことはないため、Pod起動のたびに生成し直しても情報は失われません。これにより`values.yaml`の変更が再起動のたびに確実に反映されます。
### セキュリティ上の利点
- YOURLS本体のコードはPodごとに公式イメージからクリーンに展開され、実行時に改ざんされても再起動で元に戻る
- `yourls`/`yourls-init`コンテナは非root(uid/gid 82)・`allowPrivilegeEscalation: false`・全capability drop
- `yourls`コンテナの`/var/www/html`マウントは読み取り専用(`user/plugins`等の永続領域のみ書き込み可)
- DB接続情報を保持する`user/config.php`・内部処理用の`includes/`ディレクトリへの直接アクセスをNginx側で拒否(多層防御。通常はfastcgi経由でPHPとして実行されるため到達しない)
- `automountServiceAccountToken: false`(不要なKubernetes APIトークンをPodにマウントしない)
## 前提条件
- Kubernetes 1.19+
- Helm 3.0+
- PersistentVolumeプロビジョナーのサポート (永続化を有効にする場合)
- 到達可能な外部MySQL/MariaDB(YOURLS用データベースを別途用意)
- PersistentVolumeプロビジョナー(永続化を有効にする場合)
## ディレクトリ構造
```
yourls-nginx/
yourls/
├── Chart.yaml
├── values.yaml
├── templates/
@@ -35,100 +62,163 @@ yourls-nginx/
└── README.md
```
## インストール
## インストール方法
### 1. 依存関係の更新
### 1. データベースの準備
```bash
helm dependency update
外部MySQL/MariaDB上に、YOURLS用のデータベースとユーザーを作成してください(テーブルは初回admin画面アクセス時に自動作成されるため、当該ユーザーに`CREATE`/`ALTER`権限が必要です)。
```sql
CREATE DATABASE yourls CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'yourls'@'%' IDENTIFIED BY 'securepassword';
GRANT ALL PRIVILEGES ON yourls.* TO 'yourls'@'%';
FLUSH PRIVILEGES;
```
### 2. 基本インストール
### 2. インストール
```bash
helm install my-yourls ./yourls-nginx
helm install my-yourls . \
--set yourls.site="https://sho.rt" \
--set yourls.admin.username=admin \
--set yourls.admin.password='SecureAdminPassword' \
--set database.host=mysql.example.svc.cluster.local \
--set database.user=yourls \
--set database.password='securepassword'
```
### 3. カスタム設定でインストール
または `values.yaml` をカスタマイズしてインストール:
```bash
helm install my-yourls ./yourls-nginx \
--set yourls.config.site=https://短縮.example.com \
--set yourls.config.username=admin \
--set yourls.config.password=SecurePassword123 \
--set mysql.auth.password=SecureMySQLPassword
helm install my-yourls . -f values.yaml
```
### 4. values.yamlをカスタマイズしてインストール
```bash
# values.yamlを編集
vim values.yaml
# インストール
helm install my-yourls ./yourls-nginx -f values.yaml
```
## 設定項目
## 設定パラメータ
### イメージ設定
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `image.nginx.registry` | NGINXイメージのレジストリ | `docker.io` |
| `image.nginx.repository` | NGINXイメージのリポジトリ | `nginx` |
| `image.nginx.tag` | NGINXイメージのタグ | `1.29.3-alpine-perl` |
| `image.yourls.registry` | YOURLSイメージのレジストリ | `docker.io` |
| `image.yourls.repository` | YOURLSイメージのリポジトリ | `yourls` |
| `image.yourls.tag` | YOURLSイメージのタグ | `1.10.2-fpm-alpine` |
| `image.nginx.tag` | Nginxイメージのタグ | `1.31.5-alpine-perl` |
| `image.yourls.tag` | YOURLSイメージのタグ(`docker.io/yourls`) | `1.10.6-fpm-alpine` |
### YOURLS設定
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `yourls.config.site` | YOURLSのサイトURL | `https://yourls.example.com` |
| `yourls.config.username` | 管理者ユーザー名 | `admin` |
| `yourls.config.password` | 管理者パスワード | `admin` |
| `yourls.config.cookiekey` | Cookieの暗号化キー | `modify_this_random_string` |
| `yourls.persistence.enabled` | データの永続化 | `true` |
| `yourls.persistence.size` | PVCのサイズ | `1Gi` |
| `yourls.site` | YOURLSインスタンスのURL(固定値。末尾スラッシュなし) | `https://your-own-domain-here.com` |
| `yourls.admin.username` | 管理者ユーザー名 | `admin` |
| `yourls.admin.password` | 管理者パスワード(空の場合は自動生成・Secretに保存) | `""` |
| `yourls.admin.existingSecret` | 管理者パスワードを既存Secretから取得する場合の名前 | `""` |
| `yourls.admin.existingSecretKey` | 上記Secret内のキー名 | `password` |
| `yourls.cookiekey` | Cookie暗号化キー(空の場合は自動生成・以後のupgradeでも保持) | `""` |
| `yourls.privateMode` | `true`: 管理画面ログイン必須(推奨) / `false`: 統計等を公開 | `true` |
| `yourls.hoursOffset` | サーバーのタイムゾーンオフセット(時間) | `0` |
| `yourls.lang` | 管理画面の表示言語 | `""` |
### MySQL設定
### データベース設定
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `mysql.enabled` | MySQLサブチャートの有効化 | `true` |
| `mysql.auth.database` | データベース名 | `yourls` |
| `mysql.auth.username` | データベースユーザー名 | `yourls` |
| `mysql.auth.password` | データベースパスワード | `yourls` |
| `mysql.primary.persistence.size` | PVCのサイズ | `8Gi` |
| `database.host` | MySQL/MariaDBのホスト名 | `mysql.example.svc.cluster.local` |
| `database.port` | ポート番号 | `3306` |
| `database.name` | データベース名 | `yourls` |
| `database.user` | DBユーザー名 | `yourls` |
| `database.password` | DBパスワード(本番はexistingSecret推奨) | `changeme` |
| `database.existingSecret` | DBパスワードを既存Secretから取得する場合の名前 | `""` |
| `database.existingSecretKey` | 上記Secret内のキー名 | `password` |
| `database.tablePrefix` | テーブル接頭辞 | `yourls_` |
### Ingress設定
### 外部Secret連携・追加環境変数
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `extraEnvFrom` | 既存Secret/ConfigMapを丸ごとenvFromで注入(`cred-*`等) | `[]` |
| `extraEnv` | 個別の環境変数を追加(value/valueFrom使用可)。`YOURLS_DEBUG`等、values化していない項目はこちら | `[]` |
### Service / Ingress設定
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `service.type` | Serviceタイプ(VIPが希少なため既定はClusterIP) | `ClusterIP` |
| `service.externalTrafficPolicy` | LoadBalancer/NodePort時のみ有効。`Local`で実IPを透過 | `Local` |
| `ingress.enabled` | Ingressの有効化 | `false` |
| `ingress.className` | Ingress class名 | `nginx` |
| `ingress.hosts[0].host` | ホスト名 | `yourls.example.com` |
| `ingress.hostname` | ホスト名 | `yourls.example.com` |
| `ingress.tls` | `true`でTLSを自動設定(hostnameを使用) | `false` |
### 永続化設定
| パラメータ | 説明 | デフォルト値 |
|-----------|------|-------------|
| `persistence.enabled` | `user/plugins`,`user/pages`,`user/languages`の永続化 | `true` |
| `persistence.size` | PVCのサイズ | `1Gi` |
| `persistence.storageClass` | StorageClass名 | `""`(クラスタ既定) |
## 使用例
### Ingressでの公開(TLS付き)
```bash
helm install my-yourls . \
--set yourls.site="https://sho.rt" \
--set database.host=mysql.example.svc.cluster.local \
--set database.password='securepassword' \
--set ingress.enabled=true \
--set ingress.hostname=sho.rt \
--set ingress.className=nginx \
--set ingress.tls=true \
--set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-issuer
```
### secret-managerチャートが配布するSecretをDBパスワードに使う
```bash
helm install my-yourls . \
--set database.existingSecret=cred-mysql \
--set database.existingSecretKey=MYSQL_PASSWORD
```
### 管理者パスワードの確認(自動生成時)
```bash
kubectl get secret my-yourls-yourls-nginx-secret \
-o jsonpath="{.data.admin-password}" | base64 -d; echo
```
## 初期化の動作
### 新規インストール時
1. `yourls-init` initContainerがYOURLS本体のコアファイルをemptyDirへ展開
2. `user/plugins`,`user/pages`,`user/languages`のPVCが空であれば、YOURLS同梱のデフォルト内容(サンプルプラグイン等)を投入
3. `user/config.php`を環境変数駆動のテンプレートから生成
4. Pod起動後、`{yourls.site}/admin/`へ初回アクセスするとYOURLSが自動的にテーブルを作成
### 再起動時
- コアファイルは毎回クリーンに再展開される(emptyDirは使い捨てのため)
- `user/plugins`等のPVCは既に中身があればそのまま保持され、デフォルト内容で上書きされない
- `user/config.php`も毎回再生成されるため、`values.yaml`のDB接続情報等の変更が確実に反映される
## アップグレード
```bash
helm upgrade my-yourls ./yourls-nginx -f values.yaml
helm upgrade my-yourls . -f values.yaml
```
`yourls.cookiekey`や自動生成された`yourls.admin.password`は、Secretの既存値を参照して引き継がれるため、明示的に固定していなくても`helm upgrade`のたびに変わることはありません。
## アンインストール
```bash
helm uninstall my-yourls
```
## セキュリティに関する注意事項
PVCは`helm uninstall`では削除されません。完全にデータを削除する場合は手動で削除してください。
1. **デフォルトパスワードの変更**: インストール後すぐにデフォルトの管理者パスワードを変更してください
2. **COOKIEKEY**: `yourls.config.cookiekey`をランダムな文字列に変更してください
3. **データベースパスワード**: 本番環境では強力なパスワードを設定してください
4. **HTTPS**: 本番環境では必ずIngressでTLS/HTTPSを有効にしてください
```bash
kubectl delete pvc my-yourls-yourls-nginx-pvc
```
## トラブルシューティング
@@ -136,6 +226,7 @@ helm uninstall my-yourls
```bash
kubectl get pods -l app.kubernetes.io/name=yourls-nginx
kubectl logs <pod-name> -c yourls-init # 初期化ログ(初回のみ)
kubectl logs <pod-name> -c nginx
kubectl logs <pod-name> -c yourls
```
@@ -146,9 +237,20 @@ kubectl logs <pod-name> -c yourls
kubectl exec -it <pod-name> -c yourls -- env | grep YOURLS_DB
```
### 管理画面にアクセスしてもテーブルが作成されない
`database.user`に指定したDBユーザーに`CREATE`/`ALTER`権限があるか確認してください(初回アクセス時の自動テーブル作成に必要です)。
### 永続ボリュームの確認
```bash
kubectl get pvc
kubectl describe pvc <pvc-name>
```
## セキュリティに関する注意事項
1. **デフォルトの管理者パスワード・DBパスワードは必ず変更してください**(`yourls.admin.password`/`database.password`を明示指定するか、`existingSecret`を利用)
2. **`yourls.site`を実際のドメインに設定してください**(短縮URL生成に使う固定値です)
3. **本番環境では必ずHTTPSを有効化してください**(`ingress.tls: true`、または前段のLB/CDNでTLS終端)
4. **`yourls.privateMode`は既定で`true`です**(管理画面ログインなしでの短縮URL作成・統計閲覧を許可する場合のみ`false`に変更してください。詳細: https://yourls.org/privatepublic)
+25 -17
View File
@@ -9,36 +9,44 @@ To learn more about the release, try:
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
http{{ if or .Values.ingress.tls .Values.ingress.extraTls }}s{{ end }}://{{ .Values.ingress.hostname }}{{ .Values.ingress.path }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "yourls-nginx.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "yourls-nginx.fullname" . }}'
You can watch the status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "yourls-nginx.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "yourls-nginx.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "yourls-nginx.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80
{{- end }}
2. Default credentials (CHANGE IMMEDIATELY):
Username: {{ .Values.yourls.config.username }}
Password: {{ .Values.yourls.config.password }}
2. Important: YOURLS_SITE must match the URL you access this instance from.
Current value: {{ .Values.yourls.site }}
Short URLs are generated using this fixed value, unlike WordPress it is NOT
derived automatically from the request. Update .Values.yourls.site (and
re-run helm upgrade) if it does not match your real domain yet.
3. Access the admin interface at:
http://your-domain/admin/
3. Default admin credentials:
Username: {{ .Values.yourls.admin.username }}
{{- if .Values.yourls.admin.existingSecret }}
Password: stored in existing Secret "{{ .Values.yourls.admin.existingSecret }}" (key: {{ .Values.yourls.admin.existingSecretKey }})
{{- else }}
Password: check the generated Secret if you did not set yourls.admin.password:
kubectl get secret --namespace {{ .Release.Namespace }} {{ include "yourls-nginx.secretName" . }} -o jsonpath="{.data.admin-password}" | base64 -d; echo
{{- end }}
4. Access the admin interface at:
{{ .Values.yourls.site }}/admin/
(first visit creates the database tables automatically; the DB user needs
CREATE/ALTER privileges on {{ .Values.database.name }} for this to succeed)
IMPORTANT SECURITY NOTES:
- Change the default admin password immediately
- Update the YOURLS_COOKIEKEY in values.yaml to a random string
- Consider enabling HTTPS via Ingress with TLS
- Review and adjust resource limits based on your needs
- Change the default admin username/password before exposing this publicly.
- yourls.privateMode is {{ .Values.yourls.privateMode }} (true = admin login required to
create/manage short URLs; recommended default).
- Enable HTTPS via Ingress (ingress.tls: true) or your LoadBalancer/CDN in production.
+28
View File
@@ -47,3 +47,31 @@ Selector labels
app.kubernetes.io/name: {{ include "yourls-nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Secret name used for chart-managed credentials.
*/}}
{{- define "yourls-nginx.secretName" -}}
{{- printf "%s-secret" (include "yourls-nginx.fullname" .) }}
{{- end }}
{{/*
Fetch a previously-generated value from the existing chart-managed Secret, if any.
Used so that random values (cookiekey, auto-generated admin password) survive
"helm upgrade" instead of rotating on every release, which would otherwise
invalidate admin login cookies and previously-issued passwords.
Usage: {{ include "yourls-nginx.existingSecretValue" (list . "cookiekey") }}
Returns an empty string on first install (no Secret exists yet) or when
running "helm template" without cluster access.
*/}}
{{- define "yourls-nginx.existingSecretValue" -}}
{{- $ctx := index . 0 -}}
{{- $key := index . 1 -}}
{{- $secret := lookup "v1" "Secret" $ctx.Release.Namespace (include "yourls-nginx.secretName" $ctx) -}}
{{- if $secret -}}
{{- $value := index $secret.data $key -}}
{{- if $value -}}
{{- $value | b64dec -}}
{{- end -}}
{{- end -}}
{{- end }}
+88 -22
View File
@@ -6,51 +6,117 @@ metadata:
{{- include "yourls-nginx.labels" . | nindent 4 }}
data:
default.conf: |
upstream php-fpm {
server localhost:9000;
upstream php {
server 127.0.0.1:9000;
}
{{- if .Values.nginx.forwardRealIP.enabled }}
# 実IPアドレスの抽出(X-Forwarded-Forから最初のIPを取得)
map $http_x_forwarded_for $real_ip {
~^(\d+\.\d+\.\d+\.\d+) $1;
default $remote_addr;
}
{{- end }}
# HTTPSプロトコルの判定
map $http_x_forwarded_proto $fastcgi_https {
default '';
https on;
}
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
# ルート直下は yourls-loader.php が、/admin/ 配下は admin/index.php がエントリポイント。
# ディレクトリ末尾スラッシュでのアクセス(例: /admin/)を両方とも正しく解決するため
# 2つ指定する(存在する方が使われる)。
index yourls-loader.php index.php;
client_max_body_size 100M;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 10M;
{{- if .Values.nginx.forwardRealIP.enabled }}
# 信頼できるプロキシからのX-Forwarded-Forヘッダーを使用
real_ip_header X-Forwarded-For;
{{- range .Values.nginx.forwardRealIP.trustedProxies }}
set_real_ip_from {{ . }};
{{- end }}
real_ip_recursive on;
{{- end }}
# DB接続情報等を保持する設定ファイルへの直接アクセスを拒否
# (通常はfastcgiで.phpとして実行されるため到達しないが、多層防御として明示的に拒否する)
location = /user/config.php {
deny all;
}
# 内部処理用ファイル群(直接リクエストされることを想定しない)へのアクセスを拒否
location ^~ /includes/ {
deny all;
}
# 隠しファイルへのアクセス拒否
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 短縮URLのリダイレクト・favicon/robots.txt・管理画面を含む全ての動的処理は
# yourls-loader.php が担う(favicon.ico/robots.txtは物理ファイルが存在しないため、
# ここで意図的にPHPへフォールバックさせて動的応答させる)
location / {
try_files $uri $uri/ /yourls-loader.php$is_args$args;
}
# PHPファイルの処理
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm;
fastcgi_pass php;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_buffering off;
fastcgi_read_timeout 300;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
# HTTPS判定をPHP側に伝える
fastcgi_param HTTPS $fastcgi_https if_not_empty;
{{- if .Values.nginx.forwardRealIP.enabled }}
# プロキシ経由のリクエスト情報をPHPに伝える(リアルIP取得有効時)
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for;
fastcgi_param REMOTE_ADDR $real_ip;
{{- else }}
fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
fastcgi_param HTTP_X_FORWARDED_FOR $http_x_forwarded_for;
fastcgi_param REMOTE_ADDR $remote_addr;
{{- end }}
fastcgi_read_timeout 60;
fastcgi_send_timeout 60;
fastcgi_connect_timeout 60;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
# 静的ファイルのキャッシュ
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 7d;
add_header Cache-Control "public";
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png|svg|woff|woff2|ttf|eot)$ {
expires max;
log_not_found off;
}
{{- with .Values.nginx.extraConfig }}
{{- . | nindent 8 }}
{{- end }}
}
+152 -60
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 }}
+23 -8
View File
@@ -1,4 +1,4 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@@ -13,9 +13,15 @@ spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if or .Values.ingress.tls .Values.ingress.extraTls }}
tls:
{{- range .Values.ingress.tls }}
{{- if .Values.ingress.tls }}
- hosts:
- {{ .Values.ingress.hostname | quote }}
secretName: {{ .Values.ingress.hostname | replace "." "-" }}-tls
{{- end }}
{{- if .Values.ingress.extraTls }}
{{- range .Values.ingress.extraTls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@@ -23,19 +29,28 @@ spec:
secretName: {{ .secretName }}
{{- end }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
- host: {{ .Values.ingress.hostname | quote }}
http:
paths:
- path: {{ .Values.ingress.path }}
pathType: {{ .Values.ingress.pathType }}
backend:
service:
name: {{ include "yourls-nginx.fullname" . }}
port:
number: {{ $.Values.service.port }}
{{- range .Values.ingress.extraHosts }}
- host: {{ .name | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
pathType: Prefix
backend:
service:
name: {{ include "yourls-nginx.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
+5 -5
View File
@@ -1,4 +1,4 @@
{{- if .Values.yourls.persistence.enabled }}
{{- if .Values.persistence.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
@@ -7,11 +7,11 @@ metadata:
{{- include "yourls-nginx.labels" . | nindent 4 }}
spec:
accessModes:
- {{ .Values.yourls.persistence.accessMode }}
{{- if .Values.yourls.persistence.storageClass }}
storageClassName: {{ .Values.yourls.persistence.storageClass }}
- {{ .Values.persistence.accessMode }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.yourls.persistence.size }}
storage: {{ .Values.persistence.size }}
{{- end }}
+9 -4
View File
@@ -1,10 +1,15 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "yourls-nginx.fullname" . }}-secret
name: {{ include "yourls-nginx.secretName" . }}
labels:
{{- include "yourls-nginx.labels" . | nindent 4 }}
type: Opaque
data:
yourls-password: {{ .Values.yourls.config.password | b64enc | quote }}
yourls-cookiekey: {{ .Values.yourls.config.cookiekey | b64enc | quote }}
stringData:
{{- if not .Values.database.existingSecret }}
db-password: {{ .Values.database.password | quote }}
{{- end }}
{{- if not .Values.yourls.admin.existingSecret }}
admin-password: {{ .Values.yourls.admin.password | default (include "yourls-nginx.existingSecretValue" (list . "admin-password")) | default (randAlphaNum 20) | quote }}
{{- end }}
cookiekey: {{ .Values.yourls.cookiekey | default (include "yourls-nginx.existingSecretValue" (list . "cookiekey")) | default (randAlphaNum 64) | quote }}
+5
View File
@@ -6,6 +6,11 @@ metadata:
{{- include "yourls-nginx.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
{{- if and .Values.service.externalTrafficPolicy (ne .Values.service.type "ClusterIP") }}
# ベアメタル環境(MetalLB等)でkube-proxyのSNATを回避し、クライアントの実IPを
# $remote_addr にそのまま渡すための設定。LoadBalancer/NodePortでのみ有効。
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
{{- end }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
+153 -53
View File
@@ -1,95 +1,195 @@
# Default values for yourls-nginx chart
# This is a YAML-formatted file.
# デフォルト値設定
replicaCount: 1
# Image configurations
image:
nginx:
registry: docker.io
repository: nginx
tag: "1.29.3-alpine-perl"
tag: "1.31.5-alpine-perl"
pullPolicy: IfNotPresent
yourls:
# 公式イメージ(docker-library/YOURLS)。fpm-alpineバリアントはPHP-FPMのみで
# リバースプロキシ(nginx)が別途必要な構成。
registry: docker.io
repository: yourls
tag: "1.10.2-fpm-alpine"
tag: "1.10.6-fpm-alpine"
pullPolicy: IfNotPresent
# Replica count
replicaCount: 1
# YOURLS設定
yourls:
# YOURLSインスタンスのURL。末尾スラッシュなし、小文字。
# 必ず実際のドメインに変更してください(短縮URL生成に使用される固定値のため、
# WordPressのように実行時のHTTP Hostヘッダーから自動判定されません)。
site: "https://your-own-domain-here.com"
# Service configuration
admin:
username: "admin"
# 空の場合はSecret作成時にランダム生成されます(helm upgradeを跨いで値は保持されます)。
# 本番環境では明示的に設定するか、existingSecretの使用を推奨します。
password: ""
# 【外部Secret連携】secret-managerチャート等、既存のSecretからパスワードを取得する場合に指定。
# 指定時は上記 password は無視されます。
existingSecret: ""
existingSecretKey: "password"
# Cookie暗号化キー。空の場合はSecret作成時にランダム生成され、以後のhelm upgradeでも
# 同じ値が再利用されます(lookup関数でSecretの既存値を参照するため、ログインセッションが
# アップグレードのたびに無効化されることはありません)。
cookiekey: ""
# true(デフォルト): 管理画面へのアクセスにログインが必須(YOURLS推奨の安全な既定値)。
# false: 統計ページ等を公開する場合に変更してください。詳細: https://yourls.org/privatepublic
privateMode: true
# サーバーのタイムゾーンオフセット(時間)
hoursOffset: 0
# 管理画面の表示言語。user/languages に該当する.moファイルがある場合のみ有効。
lang: ""
# データベース設定(外部MySQL/MariaDBを利用する構成。サブチャートは同梱しません)
database:
host: "mysql.example.svc.cluster.local"
port: 3306
name: "yourls"
user: "yourls"
# 本番環境ではexistingSecretの使用を推奨します
password: "changeme"
# 【外部Secret連携】secret-managerチャート等、既存のSecretからパスワードを取得する場合に指定。
# 指定時は上記 password は無視されます。
existingSecret: ""
existingSecretKey: "password"
tablePrefix: "yourls_"
# 既存の Secret / ConfigMap をまるごと環境変数として yourls コンテナへ注入する(envFrom)。
# 別リポジトリの secret-manager チャートが各ネームスペースへ配布する
# cred-<名前> Secret(API キー等)をそのまま読み込む用途を想定。
extraEnvFrom: []
# - secretRef:
# name: cred-gemini
# 個別の環境変数を追加する(生の env エントリ配列。value / valueFrom(secretKeyRef 等) 使用可)。
# YOURLS_DEBUG, YOURLS_UNIQUE_URLS, YOURLS_URL_CONVERT, YOURLS_NOSTATS 等、
# 本チャートが直接values化していないYOURLSの環境変数はこちらで指定してください。
extraEnv: []
# - name: YOURLS_UNIQUE_URLS
# value: "true"
nginx:
# Ingress ControllerやCDN(Cloudflare等)、外部LB等、X-Forwarded-Forヘッダーを
# 付与するリバースプロキシを前段に置く場合にのみ有効にしてください。
# Service直下にPodがぶら下がるベアメタル/MetalLB構成では、代わりに下記の
# service.externalTrafficPolicy: Local を使用してください。
forwardRealIP:
enabled: false
trustedProxies:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
# CloudflareやAWS ALB等を使っている場合は以下のIPレンジも追加してください
# - 173.245.48.0/20
extraConfig: |
# Service設定
service:
# VIP/グローバルIPが希少な環境を想定し、既定はClusterIP + Ingress(共有)としています。
# 専用のLoadBalancer IPを払い出す場合のみ type: LoadBalancer に変更してください。
type: ClusterIP
port: 80
targetPort: 80
# ベアメタルクラスター(MetalLB等)でクライアントの実IPを取得するための設定。
# kube-proxyはデフォルト(Cluster)だと送信元IPをノードIPにSNATしてしまうため、
# Local にすることでSNATを回避し、Nginxの$remote_addrに実IPがそのまま渡ります。
# LoadBalancer / NodePort でのみ有効(ClusterIPでは無視されます)。
externalTrafficPolicy: Local
# Ingress configuration
# Ingress設定
ingress:
enabled: false
className: "nginx"
className: nginx
annotations: {}
# {
# acme.cert-manager.io/http01-ingress-class: "nginx",
# コメントを外すだけで有効化できる設定例
# cert-manager.io/cluster-issuer: "letsencrypt-issuer",
# nginx.ingress.kubernetes.io/from-to-www-redirect: "true",
# nginx.ingress.kubernetes.io/proxy-body-size: "100m"
# }
hosts:
- host: example.tld
paths:
- path: /
# nginx.ingress.kubernetes.io/ssl-redirect: "true",
# nginx.ingress.kubernetes.io/force-ssl-redirect: "true",
# nginx.ingress.kubernetes.io/proxy-body-size: "10m",
# IP取得用(Nginx Ingress Controllerの場合)
# nginx.ingress.kubernetes.io/use-forwarded-headers: "true",
# nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true",
hostname: yourls.example.com
path: /
pathType: Prefix
tls: []
# - secretName: yourls-tls
# hosts:
# - example.tld
# TLS設定(true にするだけで hostname を使用して自動設定)
tls: false
# tlsを有効にすると自動的に以下が設定されます:
# - hosts: [hostname]
# - secretName: {hostname}-tls
# MySQL/MariaDB configuration
mysql:
enabled: true
auth:
rootPassword: "changeme"
database: "yourls"
username: "yourls"
password: "yourls"
primary:
persistence:
enabled: true
size: 8Gi
# 追加のホスト設定
extraHosts: []
# - name: go.example.com
# path: /
# YOURLS configuration
yourls:
config:
site: "https://yourls.example.com"
username: "admin"
password: "admin"
cookiekey: "modify_this_random_string"
hours_offset: 0
lang: "en"
persistence:
# 追加のTLS設定
extraTls: []
# - hosts:
# - go.example.com
# secretName: go-example-com-tls
# PersistentVolume設定
# YOURLS本体(コアファイル)はemptyDirに配置(使い捨て)。
# user/plugins, user/pages, user/languages(管理画面から追加したプラグイン・ページ・
# 言語ファイル)のみPVCに永続化します。user/config.phpは環境変数から都度生成されるため
# 永続化しません(値を変更した場合に確実に反映されるようにするため)。
persistence:
enabled: true
storageClass: ""
accessMode: ReadWriteOnce
size: 1Gi
# Resources
# リソース設定
resources:
nginx:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 32Mi
yourls:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
requests:
cpu: 100m
memory: 64Mi
# Node selector
# Health Check
# livenessProbeは/robots.txt(YOURLSがDB接続前に無条件で200を返す固定応答)を見ており、
# DB接続の一時的な断でコンテナが再起動ループに陥らないようにしています。
# readinessProbeは/(DB接続を要する通常ページ)を見ており、DBに接続できない間は
# Serviceの振り分け対象から外れるようにしています。
healthCheck:
enabled: true
livenessProbe:
httpGet:
path: /robots.txt
port: 80
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# Node Selector
nodeSelector: {}
# Tolerations