feat: チャートをゼロから再設計(外部DB前提のYOURLS+Nginx構成に作り直し)
Helm Chart Release / release-chart (push) Successful in 7s
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:
@@ -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.
|
||||||
@@ -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 "================================"
|
||||||
@@ -2,96 +2,113 @@ name: Update Docker Image Tags and Release Helm Chart
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 4 * * 1' # 毎週月曜日 4:00 AM (JST 1:00 PM)
|
- cron: "0 4 * * 1" # 毎週月曜日 04:00 UTC (JST 13:00)
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-and-release:
|
update-and-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Install Helm
|
- name: Check for new YOURLS FPM Alpine version
|
||||||
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
|
|
||||||
id: yourls
|
id: yourls
|
||||||
run: |
|
run: |
|
||||||
set -e
|
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 ' ')
|
CURRENT=$(grep -A3 "yourls:" values.yaml | grep "tag:" | head -1 | sed 's/.*tag: *"\([^"]*\)".*/\1/' | tr -d ' ')
|
||||||
echo "Current YOURLS: $CURRENT"
|
echo "Current YOURLS: $CURRENT"
|
||||||
|
|
||||||
# Docker Hub API v2を使用してタグを取得
|
# 公式イメージは docker.io/yourls (docker-library配下)。
|
||||||
# パターン: 1.10.2-fpm-alpine 形式
|
# docker.io/yourls/yourls という別名前空間のイメージは非公式のため使用しない。
|
||||||
LATEST=$(curl -s "https://registry.hub.docker.com/v2/repositories/yourls/yourls/tags?page_size=100" | \
|
LATEST=$(curl -s "https://hub.docker.com/v2/repositories/library/yourls/tags?page_size=100&name=fpm-alpine" | \
|
||||||
jq -r '.results[].name' | \
|
jq -r '.results[].name' | \
|
||||||
grep -E '^[0-9]+\.[0-9]+\.[0-9]+-fpm-alpine$' | \
|
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
|
if [ -z "$LATEST" ]; then
|
||||||
echo "Warning: Could not fetch latest YOURLS version, using current"
|
echo "Warning: Could not fetch latest YOURLS version, using current"
|
||||||
LATEST="$CURRENT"
|
LATEST="$CURRENT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# YOURLSバージョンを抽出 (1.10.2の部分)
|
|
||||||
APP_VERSION=$(echo "$LATEST" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
|
APP_VERSION=$(echo "$LATEST" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
|
||||||
if [ -z "$APP_VERSION" ]; then
|
if [ -z "$APP_VERSION" ]; then
|
||||||
echo "Error: Could not extract YOURLS version"
|
echo "Warning: Could not extract YOURLS version from: $LATEST"
|
||||||
exit 1
|
APP_VERSION=$(echo "$CURRENT" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Latest YOURLS: $LATEST"
|
echo "Latest YOURLS: $LATEST"
|
||||||
echo "YOURLS version: $APP_VERSION"
|
echo "YOURLS version: $APP_VERSION"
|
||||||
|
|
||||||
echo "current=$CURRENT" >> $GITHUB_OUTPUT
|
echo "current=$CURRENT" >> $GITHUB_OUTPUT
|
||||||
echo "latest=$LATEST" >> $GITHUB_OUTPUT
|
echo "latest=$LATEST" >> $GITHUB_OUTPUT
|
||||||
echo "app_version=$APP_VERSION" >> $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
|
- name: Determine if update is needed
|
||||||
id: check_update
|
id: check_update
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
NGINX_CURRENT="${{ steps.nginx.outputs.current }}"
|
|
||||||
NGINX_LATEST="${{ steps.nginx.outputs.latest }}"
|
|
||||||
YOURLS_CURRENT="${{ steps.yourls.outputs.current }}"
|
YOURLS_CURRENT="${{ steps.yourls.outputs.current }}"
|
||||||
YOURLS_LATEST="${{ steps.yourls.outputs.latest }}"
|
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 "YOURLS: $YOURLS_CURRENT vs $YOURLS_LATEST"
|
||||||
|
echo "Nginx: $NGINX_CURRENT vs $NGINX_LATEST"
|
||||||
|
|
||||||
if [ "$NGINX_CURRENT" != "$NGINX_LATEST" ] || [ "$YOURLS_CURRENT" != "$YOURLS_LATEST" ]; then
|
UPDATE_NEEDED=false
|
||||||
echo "update_needed=true" >> $GITHUB_OUTPUT
|
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"
|
echo "Update is needed"
|
||||||
else
|
else
|
||||||
echo "update_needed=false" >> $GITHUB_OUTPUT
|
echo "Already up to date - no action required"
|
||||||
echo "Already up to date"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Update values.yaml
|
- name: Update values.yaml
|
||||||
@@ -100,36 +117,29 @@ jobs:
|
|||||||
set -e
|
set -e
|
||||||
echo "Updating values.yaml..."
|
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_OLD="${{ steps.yourls.outputs.current }}"
|
||||||
YOURLS_NEW="${{ steps.yourls.outputs.latest }}"
|
YOURLS_NEW="${{ steps.yourls.outputs.latest }}"
|
||||||
if [ "$YOURLS_OLD" != "$YOURLS_NEW" ]; then
|
if [ "$YOURLS_OLD" != "$YOURLS_NEW" ]; then
|
||||||
# yourlsセクションのtagのみを更新(2つ目のタグ)
|
sed -i "s|tag: \"${YOURLS_OLD}\"|tag: \"${YOURLS_NEW}\"|g" values.yaml
|
||||||
sed -i "0,/tag: \"${NGINX_NEW}\"/b; s/tag: \"${YOURLS_OLD}\"/tag: \"${YOURLS_NEW}\"/" values.yaml
|
|
||||||
echo "YOURLS updated: $YOURLS_OLD -> $YOURLS_NEW"
|
echo "YOURLS updated: $YOURLS_OLD -> $YOURLS_NEW"
|
||||||
fi
|
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"
|
echo "values.yaml updated"
|
||||||
git diff values.yaml
|
git diff values.yaml
|
||||||
|
|
||||||
- name: Update Chart.yaml version
|
- name: Update Chart.yaml version
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.yourls_updated == 'true'
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
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
|
sed -i "s/^appVersion: .*/appVersion: \"$APP_VERSION\"/" Chart.yaml
|
||||||
echo "Chart.yaml updated to version $APP_VERSION"
|
echo "Chart.yaml updated to version $APP_VERSION"
|
||||||
cat Chart.yaml
|
cat Chart.yaml
|
||||||
@@ -138,105 +148,128 @@ jobs:
|
|||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.update_needed == 'true'
|
||||||
run: |
|
run: |
|
||||||
git config user.name "Gitea Actions Bot"
|
git config user.name "Gitea Actions Bot"
|
||||||
git config user.email "actions@gitea.local"
|
git config user.email "actions@git.cafepieters.com"
|
||||||
git add values.yaml Chart.yaml
|
|
||||||
git commit -m "chore: update to YOURLS ${{ steps.yourls.outputs.app_version }}, nginx ${{ steps.nginx.outputs.latest }}"
|
if [ "${{ steps.check_update.outputs.yourls_updated }}" = "true" ]; then
|
||||||
git push origin main
|
git add values.yaml Chart.yaml
|
||||||
|
git commit -m "chore: update to YOURLS ${{ steps.yourls.outputs.app_version }}, nginx ${{ steps.nginx.outputs.latest }}"
|
||||||
|
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
|
- name: Package Helm Chart
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.yourls_updated == 'true'
|
||||||
run: |
|
run: |
|
||||||
helm package .
|
helm package .
|
||||||
echo "Helm chart packaged"
|
echo "Helm chart packaged"
|
||||||
|
|
||||||
- name: Create Git Tag
|
- name: Create Git Tag
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.yourls_updated == 'true'
|
||||||
run: |
|
run: |
|
||||||
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
||||||
git tag -a "v$APP_VERSION" -m "Release YOURLS $APP_VERSION"
|
|
||||||
git push origin "v$APP_VERSION"
|
if git rev-parse "v$APP_VERSION" >/dev/null 2>&1; then
|
||||||
echo "Git tag v$APP_VERSION created"
|
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
|
- name: Create Gitea Release
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.yourls_updated == 'true'
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
||||||
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
|
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
|
||||||
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
|
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
|
||||||
curl -X POST \
|
echo "Release v$APP_VERSION already exists, skipping release creation"
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
else
|
||||||
-H "Content-Type: application/json" \
|
curl -X POST \
|
||||||
-d "{\"tag_name\":\"v${APP_VERSION}\",\"name\":\"v${APP_VERSION}\",\"body\":\"${RELEASE_BODY}\"}" \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases"
|
-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 \
|
||||||
RELEASE_ID=$(curl -s \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/v${APP_VERSION}" | jq -r '.id')
|
||||||
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/v${APP_VERSION}" | jq -r '.id')
|
|
||||||
|
|
||||||
# アセットをアップロード
|
curl -X POST \
|
||||||
curl -X POST \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
-H "Content-Type: application/gzip" \
|
||||||
-H "Content-Type: application/gzip" \
|
--data-binary "@${PACKAGE_FILE}" \
|
||||||
--data-binary "@${PACKAGE_FILE}" \
|
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${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
|
- name: Publish to Gitea Package Registry
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: steps.check_update.outputs.yourls_updated == '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'
|
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
||||||
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
|
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
|
||||||
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
|
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
|
||||||
|
|
||||||
echo "Updating Helm repository..."
|
echo "🚀 Publishing ${PACKAGE_FILE} to Gitea Package Registry..."
|
||||||
|
|
||||||
# パッケージファイルをHelmリポジトリにコピー
|
curl --fail-with-body \
|
||||||
cp "${PACKAGE_FILE}" helm-repo/
|
-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
|
echo "✅ Chart published successfully to Gitea Package Registry"
|
||||||
|
|
||||||
# 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"
|
|
||||||
|
|
||||||
- name: Summary
|
- name: Summary
|
||||||
if: steps.check_update.outputs.update_needed == 'true'
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
APP_VERSION="${{ steps.yourls.outputs.app_version }}"
|
||||||
NGINX_VERSION="${{ steps.nginx.outputs.latest }}"
|
NGINX_VERSION="${{ steps.nginx.outputs.latest }}"
|
||||||
YOURLS_VERSION="${{ steps.yourls.outputs.latest }}"
|
YOURLS_UPDATED="${{ steps.check_update.outputs.yourls_updated }}"
|
||||||
echo "========================================"
|
|
||||||
echo "Update completed successfully!"
|
echo "================================"
|
||||||
echo "========================================"
|
echo "Update completed!"
|
||||||
echo "Chart Version: ${APP_VERSION}"
|
echo "- YOURLS: ${APP_VERSION}"
|
||||||
echo "YOURLS: ${YOURLS_VERSION}"
|
echo "- Nginx: ${NGINX_VERSION}"
|
||||||
echo "Nginx: ${NGINX_VERSION}"
|
echo ""
|
||||||
echo "========================================"
|
|
||||||
echo "Helm repository updated at:"
|
if [ "$YOURLS_UPDATED" = "true" ]; then
|
||||||
echo "https://git.cafepieters.com/helmchart/repo"
|
echo "✅ YOURLS version updated - Release created (v${APP_VERSION})"
|
||||||
echo "========================================"
|
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 "================================"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Claude Code local settings
|
||||||
|
.claude/
|
||||||
+22
@@ -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
|
||||||
@@ -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
@@ -1,21 +1,23 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: yourls-nginx
|
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
|
type: application
|
||||||
version: 1.0.0
|
version: 1.10.6
|
||||||
appVersion: "1.10.2"
|
appVersion: "1.10.6"
|
||||||
keywords:
|
keywords:
|
||||||
- yourls
|
- yourls
|
||||||
- url-shortener
|
- url-shortener
|
||||||
- nginx
|
- nginx
|
||||||
- php-fpm
|
- php-fpm
|
||||||
|
maintainers:
|
||||||
|
- name: Pieter
|
||||||
|
url: https://git.cafepieters.com/helmchart/repo/
|
||||||
home: https://yourls.org/
|
home: https://yourls.org/
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/YOURLS/YOURLS
|
- https://github.com/YOURLS/YOURLS
|
||||||
maintainers:
|
- https://github.com/YOURLS/containers
|
||||||
- name: Pieter
|
icon: https://yourls.org/images/yourls-logo.png
|
||||||
dependencies:
|
kubeVersion: ">=1.19.0-0"
|
||||||
- name: mysql
|
annotations:
|
||||||
version: "9.x.x"
|
category: Networking
|
||||||
repository: "https://charts.bitnami.com/bitnami"
|
licenses: MIT
|
||||||
condition: mysql.enabled
|
|
||||||
|
|||||||
@@ -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` は参照だけを保持し、内容を増やさない。
|
||||||
@@ -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構成
|
- Nginx(alpine-perl) + YOURLS公式イメージ(fpm-alpine)のマルチコンテナPod構成
|
||||||
- MySQL/MariaDBのサブチャートによるデータベース管理
|
- 外部MySQL/MariaDBを利用(サブチャート非同梱。WordPress/phpfpmチャートと同一方針)
|
||||||
- 永続ボリュームによるデータ保存
|
- YOURLS本体はemptyDirに展開する使い捨て構成(bitnami方式)。管理画面から追加した
|
||||||
- Ingressサポート (オプション)
|
プラグイン・カスタムページ・言語ファイルのみ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+
|
- Kubernetes 1.19+
|
||||||
- Helm 3.0+
|
- Helm 3.0+
|
||||||
- PersistentVolumeプロビジョナーのサポート (永続化を有効にする場合)
|
- 到達可能な外部MySQL/MariaDB(YOURLS用データベースを別途用意)
|
||||||
|
- PersistentVolumeプロビジョナー(永続化を有効にする場合)
|
||||||
|
|
||||||
## ディレクトリ構造
|
## ディレクトリ構造
|
||||||
|
|
||||||
```
|
```
|
||||||
yourls-nginx/
|
yourls/
|
||||||
├── Chart.yaml
|
├── Chart.yaml
|
||||||
├── values.yaml
|
├── values.yaml
|
||||||
├── templates/
|
├── templates/
|
||||||
@@ -35,100 +62,163 @@ yourls-nginx/
|
|||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## インストール
|
## インストール方法
|
||||||
|
|
||||||
### 1. 依存関係の更新
|
### 1. データベースの準備
|
||||||
|
|
||||||
```bash
|
外部MySQL/MariaDB上に、YOURLS用のデータベースとユーザーを作成してください(テーブルは初回admin画面アクセス時に自動作成されるため、当該ユーザーに`CREATE`/`ALTER`権限が必要です)。
|
||||||
helm dependency update
|
|
||||||
|
```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
|
```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
|
```bash
|
||||||
helm install my-yourls ./yourls-nginx \
|
helm install my-yourls . -f values.yaml
|
||||||
--set yourls.config.site=https://短縮.example.com \
|
|
||||||
--set yourls.config.username=admin \
|
|
||||||
--set yourls.config.password=SecurePassword123 \
|
|
||||||
--set mysql.auth.password=SecureMySQLPassword
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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.tag` | Nginxイメージのタグ | `1.31.5-alpine-perl` |
|
||||||
| `image.nginx.repository` | NGINXイメージのリポジトリ | `nginx` |
|
| `image.yourls.tag` | YOURLSイメージのタグ(`docker.io/yourls`) | `1.10.6-fpm-alpine` |
|
||||||
| `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` |
|
|
||||||
|
|
||||||
### YOURLS設定
|
### YOURLS設定
|
||||||
|
|
||||||
| パラメータ | 説明 | デフォルト値 |
|
| パラメータ | 説明 | デフォルト値 |
|
||||||
|-----------|------|-------------|
|
|-----------|------|-------------|
|
||||||
| `yourls.config.site` | YOURLSのサイトURL | `https://yourls.example.com` |
|
| `yourls.site` | YOURLSインスタンスのURL(固定値。末尾スラッシュなし) | `https://your-own-domain-here.com` |
|
||||||
| `yourls.config.username` | 管理者ユーザー名 | `admin` |
|
| `yourls.admin.username` | 管理者ユーザー名 | `admin` |
|
||||||
| `yourls.config.password` | 管理者パスワード | `admin` |
|
| `yourls.admin.password` | 管理者パスワード(空の場合は自動生成・Secretに保存) | `""` |
|
||||||
| `yourls.config.cookiekey` | Cookieの暗号化キー | `modify_this_random_string` |
|
| `yourls.admin.existingSecret` | 管理者パスワードを既存Secretから取得する場合の名前 | `""` |
|
||||||
| `yourls.persistence.enabled` | データの永続化 | `true` |
|
| `yourls.admin.existingSecretKey` | 上記Secret内のキー名 | `password` |
|
||||||
| `yourls.persistence.size` | PVCのサイズ | `1Gi` |
|
| `yourls.cookiekey` | Cookie暗号化キー(空の場合は自動生成・以後のupgradeでも保持) | `""` |
|
||||||
|
| `yourls.privateMode` | `true`: 管理画面ログイン必須(推奨) / `false`: 統計等を公開 | `true` |
|
||||||
|
| `yourls.hoursOffset` | サーバーのタイムゾーンオフセット(時間) | `0` |
|
||||||
|
| `yourls.lang` | 管理画面の表示言語 | `""` |
|
||||||
|
|
||||||
### MySQL設定
|
### データベース設定
|
||||||
|
|
||||||
| パラメータ | 説明 | デフォルト値 |
|
| パラメータ | 説明 | デフォルト値 |
|
||||||
|-----------|------|-------------|
|
|-----------|------|-------------|
|
||||||
| `mysql.enabled` | MySQLサブチャートの有効化 | `true` |
|
| `database.host` | MySQL/MariaDBのホスト名 | `mysql.example.svc.cluster.local` |
|
||||||
| `mysql.auth.database` | データベース名 | `yourls` |
|
| `database.port` | ポート番号 | `3306` |
|
||||||
| `mysql.auth.username` | データベースユーザー名 | `yourls` |
|
| `database.name` | データベース名 | `yourls` |
|
||||||
| `mysql.auth.password` | データベースパスワード | `yourls` |
|
| `database.user` | DBユーザー名 | `yourls` |
|
||||||
| `mysql.primary.persistence.size` | PVCのサイズ | `8Gi` |
|
| `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.enabled` | Ingressの有効化 | `false` |
|
||||||
| `ingress.className` | Ingress class名 | `nginx` |
|
| `ingress.hostname` | ホスト名 | `yourls.example.com` |
|
||||||
| `ingress.hosts[0].host` | ホスト名 | `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
|
```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
|
```bash
|
||||||
helm uninstall my-yourls
|
helm uninstall my-yourls
|
||||||
```
|
```
|
||||||
|
|
||||||
## セキュリティに関する注意事項
|
PVCは`helm uninstall`では削除されません。完全にデータを削除する場合は手動で削除してください。
|
||||||
|
|
||||||
1. **デフォルトパスワードの変更**: インストール後すぐにデフォルトの管理者パスワードを変更してください
|
```bash
|
||||||
2. **COOKIEKEY**: `yourls.config.cookiekey`をランダムな文字列に変更してください
|
kubectl delete pvc my-yourls-yourls-nginx-pvc
|
||||||
3. **データベースパスワード**: 本番環境では強力なパスワードを設定してください
|
```
|
||||||
4. **HTTPS**: 本番環境では必ずIngressでTLS/HTTPSを有効にしてください
|
|
||||||
|
|
||||||
## トラブルシューティング
|
## トラブルシューティング
|
||||||
|
|
||||||
@@ -136,6 +226,7 @@ helm uninstall my-yourls
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get pods -l app.kubernetes.io/name=yourls-nginx
|
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 nginx
|
||||||
kubectl logs <pod-name> -c yourls
|
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
|
kubectl exec -it <pod-name> -c yourls -- env | grep YOURLS_DB
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 管理画面にアクセスしてもテーブルが作成されない
|
||||||
|
|
||||||
|
`database.user`に指定したDBユーザーに`CREATE`/`ALTER`権限があるか確認してください(初回アクセス時の自動テーブル作成に必要です)。
|
||||||
|
|
||||||
### 永続ボリュームの確認
|
### 永続ボリュームの確認
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get pvc
|
kubectl get pvc
|
||||||
kubectl describe pvc <pvc-name>
|
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
@@ -9,36 +9,44 @@ To learn more about the release, try:
|
|||||||
|
|
||||||
1. Get the application URL by running these commands:
|
1. Get the application URL by running these commands:
|
||||||
{{- if .Values.ingress.enabled }}
|
{{- if .Values.ingress.enabled }}
|
||||||
{{- range $host := .Values.ingress.hosts }}
|
http{{ if or .Values.ingress.tls .Values.ingress.extraTls }}s{{ end }}://{{ .Values.ingress.hostname }}{{ .Values.ingress.path }}
|
||||||
{{- range .paths }}
|
|
||||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- else if contains "NodePort" .Values.service.type }}
|
{{- 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_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}")
|
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||||
echo http://$NODE_IP:$NODE_PORT
|
echo http://$NODE_IP:$NODE_PORT
|
||||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
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 }}"}}")
|
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 }}
|
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
{{- 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 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"
|
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 }}
|
{{- end }}
|
||||||
|
|
||||||
2. Default credentials (CHANGE IMMEDIATELY):
|
2. Important: YOURLS_SITE must match the URL you access this instance from.
|
||||||
Username: {{ .Values.yourls.config.username }}
|
Current value: {{ .Values.yourls.site }}
|
||||||
Password: {{ .Values.yourls.config.password }}
|
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:
|
3. Default admin credentials:
|
||||||
http://your-domain/admin/
|
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:
|
IMPORTANT SECURITY NOTES:
|
||||||
- Change the default admin password immediately
|
- Change the default admin username/password before exposing this publicly.
|
||||||
- Update the YOURLS_COOKIEKEY in values.yaml to a random string
|
- yourls.privateMode is {{ .Values.yourls.privateMode }} (true = admin login required to
|
||||||
- Consider enabling HTTPS via Ingress with TLS
|
create/manage short URLs; recommended default).
|
||||||
- Review and adjust resource limits based on your needs
|
- Enable HTTPS via Ingress (ingress.tls: true) or your LoadBalancer/CDN in production.
|
||||||
|
|||||||
@@ -47,3 +47,31 @@ Selector labels
|
|||||||
app.kubernetes.io/name: {{ include "yourls-nginx.name" . }}
|
app.kubernetes.io/name: {{ include "yourls-nginx.name" . }}
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
{{- end }}
|
{{- 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
@@ -6,51 +6,117 @@ metadata:
|
|||||||
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
||||||
data:
|
data:
|
||||||
default.conf: |
|
default.conf: |
|
||||||
upstream php-fpm {
|
upstream php {
|
||||||
server localhost:9000;
|
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 {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
root /var/www/html;
|
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 / {
|
location / {
|
||||||
try_files $uri $uri/ /yourls-loader.php$is_args$args;
|
try_files $uri $uri/ /yourls-loader.php$is_args$args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PHPファイルの処理
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
fastcgi_pass php-fpm;
|
fastcgi_pass php;
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
|
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
fastcgi_buffering off;
|
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
|
||||||
fastcgi_read_timeout 300;
|
|
||||||
|
# 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 ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
}
|
expires 7d;
|
||||||
|
add_header Cache-Control "public";
|
||||||
location = /favicon.ico {
|
|
||||||
log_not_found off;
|
log_not_found off;
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /robots.txt {
|
{{- with .Values.nginx.extraConfig }}
|
||||||
log_not_found off;
|
{{- . | nindent 8 }}
|
||||||
access_log off;
|
{{- end }}
|
||||||
allow all;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(css|gif|ico|jpeg|jpg|js|png|svg|woff|woff2|ttf|eot)$ {
|
|
||||||
expires max;
|
|
||||||
log_not_found off;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+152
-60
@@ -14,19 +14,74 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
{{- include "yourls-nginx.selectorLabels" . | nindent 8 }}
|
{{- include "yourls-nginx.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 82
|
||||||
|
fsGroupChangePolicy: "OnRootMismatch"
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: wait-for-mysql
|
- name: yourls-init
|
||||||
image: busybox:1.36
|
image: "{{ .Values.image.yourls.registry }}/{{ .Values.image.yourls.repository }}:{{ .Values.image.yourls.tag }}"
|
||||||
command:
|
imagePullPolicy: {{ .Values.image.yourls.pullPolicy }}
|
||||||
- sh
|
command: ["/bin/sh", "-c"]
|
||||||
- -c
|
args:
|
||||||
- |
|
- |
|
||||||
until nc -z {{ include "yourls-nginx.fullname" . }}-mysql 3306; do
|
set -e
|
||||||
echo "Waiting for MySQL..."
|
echo "=== YOURLS initialization started ==="
|
||||||
sleep 2
|
|
||||||
|
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
|
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:
|
containers:
|
||||||
# NGINX Container
|
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: "{{ .Values.image.nginx.registry }}/{{ .Values.image.nginx.repository }}:{{ .Values.image.nginx.tag }}"
|
image: "{{ .Values.image.nginx.registry }}/{{ .Values.image.nginx.repository }}:{{ .Values.image.nginx.tag }}"
|
||||||
imagePullPolicy: {{ .Values.image.nginx.pullPolicy }}
|
imagePullPolicy: {{ .Values.image.nginx.pullPolicy }}
|
||||||
@@ -35,87 +90,124 @@ spec:
|
|||||||
containerPort: 80
|
containerPort: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: nginx-config
|
|
||||||
mountPath: /etc/nginx/conf.d
|
|
||||||
- name: yourls-data
|
- name: yourls-data
|
||||||
mountPath: /var/www/html
|
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:
|
resources:
|
||||||
{{- toYaml .Values.resources.nginx | nindent 12 }}
|
{{- 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
|
- name: yourls
|
||||||
image: "{{ .Values.image.yourls.registry }}/{{ .Values.image.yourls.repository }}:{{ .Values.image.yourls.tag }}"
|
image: "{{ .Values.image.yourls.registry }}/{{ .Values.image.yourls.repository }}:{{ .Values.image.yourls.tag }}"
|
||||||
imagePullPolicy: {{ .Values.image.yourls.pullPolicy }}
|
imagePullPolicy: {{ .Values.image.yourls.pullPolicy }}
|
||||||
|
command: ["php-fpm"]
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 82
|
||||||
|
runAsGroup: 82
|
||||||
|
runAsNonRoot: true
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop: ["ALL"]
|
||||||
env:
|
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
|
- name: YOURLS_SITE
|
||||||
value: {{ .Values.yourls.config.site | quote }}
|
value: {{ .Values.yourls.site | quote }}
|
||||||
- name: YOURLS_USER
|
- name: YOURLS_USER
|
||||||
value: {{ .Values.yourls.config.username | quote }}
|
value: {{ .Values.yourls.admin.username | quote }}
|
||||||
- name: YOURLS_PASS
|
- name: YOURLS_PASS
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "yourls-nginx.fullname" . }}-secret
|
{{- if .Values.yourls.admin.existingSecret }}
|
||||||
key: yourls-password
|
name: {{ .Values.yourls.admin.existingSecret }}
|
||||||
|
key: {{ .Values.yourls.admin.existingSecretKey }}
|
||||||
|
{{- else }}
|
||||||
|
name: {{ include "yourls-nginx.secretName" . }}
|
||||||
|
key: admin-password
|
||||||
|
{{- end }}
|
||||||
- name: YOURLS_COOKIEKEY
|
- name: YOURLS_COOKIEKEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "yourls-nginx.fullname" . }}-secret
|
name: {{ include "yourls-nginx.secretName" . }}
|
||||||
key: yourls-cookiekey
|
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
|
- name: YOURLS_HOURS_OFFSET
|
||||||
value: {{ .Values.yourls.config.hours_offset | quote }}
|
value: {{ .Values.yourls.hoursOffset | quote }}
|
||||||
- name: YOURLS_LANG
|
- 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:
|
volumeMounts:
|
||||||
|
# YOURLS本体はinitContainerが書き込んだ後は変更不要なため読み取り専用でマウントする
|
||||||
|
# (実行時に侵害された場合でも配信コードの改ざん・永続化を防ぐ多層防御)。
|
||||||
- name: yourls-data
|
- name: yourls-data
|
||||||
mountPath: /var/www/html
|
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:
|
resources:
|
||||||
{{- toYaml .Values.resources.yourls | nindent 12 }}
|
{{- toYaml .Values.resources.yourls | nindent 12 }}
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 9000
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 9000
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 5
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: nginx-config
|
|
||||||
configMap:
|
|
||||||
name: {{ include "yourls-nginx.fullname" . }}-nginx-config
|
|
||||||
- name: yourls-data
|
- name: yourls-data
|
||||||
{{- if .Values.yourls.persistence.enabled }}
|
emptyDir: {}
|
||||||
|
- name: yourls-persistent
|
||||||
|
{{- if .Values.persistence.enabled }}
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: {{ include "yourls-nginx.fullname" . }}-pvc
|
claimName: {{ include "yourls-nginx.fullname" . }}-pvc
|
||||||
{{- else }}
|
{{- else }}
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
- name: nginx-config
|
||||||
|
configMap:
|
||||||
|
name: {{ include "yourls-nginx.fullname" . }}-nginx-config
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|||||||
+23
-8
@@ -1,4 +1,4 @@
|
|||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.ingress.enabled }}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
@@ -13,29 +13,44 @@ spec:
|
|||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if or .Values.ingress.tls .Values.ingress.extraTls }}
|
||||||
tls:
|
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:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
secretName: {{ .secretName }}
|
secretName: {{ .secretName }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
- host: {{ .Values.ingress.hostname | quote }}
|
||||||
- host: {{ .host | 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:
|
http:
|
||||||
paths:
|
paths:
|
||||||
{{- range .paths }}
|
|
||||||
- path: {{ .path }}
|
- path: {{ .path }}
|
||||||
pathType: {{ .pathType }}
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
name: {{ include "yourls-nginx.fullname" $ }}
|
name: {{ include "yourls-nginx.fullname" $ }}
|
||||||
port:
|
port:
|
||||||
number: {{ $.Values.service.port }}
|
number: {{ $.Values.service.port }}
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
{{- if .Values.yourls.persistence.enabled }}
|
{{- if .Values.persistence.enabled }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
@@ -7,11 +7,11 @@ metadata:
|
|||||||
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.yourls.persistence.accessMode }}
|
- {{ .Values.persistence.accessMode }}
|
||||||
{{- if .Values.yourls.persistence.storageClass }}
|
{{- if .Values.persistence.storageClass }}
|
||||||
storageClassName: {{ .Values.yourls.persistence.storageClass }}
|
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.yourls.persistence.size }}
|
storage: {{ .Values.persistence.size }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "yourls-nginx.fullname" . }}-secret
|
name: {{ include "yourls-nginx.secretName" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
stringData:
|
||||||
yourls-password: {{ .Values.yourls.config.password | b64enc | quote }}
|
{{- if not .Values.database.existingSecret }}
|
||||||
yourls-cookiekey: {{ .Values.yourls.config.cookiekey | b64enc | quote }}
|
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 }}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ metadata:
|
|||||||
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
{{- include "yourls-nginx.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
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:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.service.port }}
|
||||||
targetPort: http
|
targetPort: http
|
||||||
|
|||||||
+159
-59
@@ -1,95 +1,195 @@
|
|||||||
# Default values for yourls-nginx chart
|
# デフォルト値設定
|
||||||
# This is a YAML-formatted file.
|
replicaCount: 1
|
||||||
|
|
||||||
# Image configurations
|
|
||||||
image:
|
image:
|
||||||
nginx:
|
nginx:
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
repository: nginx
|
repository: nginx
|
||||||
tag: "1.29.3-alpine-perl"
|
tag: "1.31.5-alpine-perl"
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
yourls:
|
yourls:
|
||||||
|
# 公式イメージ(docker-library/YOURLS)。fpm-alpineバリアントはPHP-FPMのみで
|
||||||
|
# リバースプロキシ(nginx)が別途必要な構成。
|
||||||
registry: docker.io
|
registry: docker.io
|
||||||
repository: yourls
|
repository: yourls
|
||||||
tag: "1.10.2-fpm-alpine"
|
tag: "1.10.6-fpm-alpine"
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
# Replica count
|
# YOURLS設定
|
||||||
replicaCount: 1
|
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:
|
service:
|
||||||
|
# VIP/グローバルIPが希少な環境を想定し、既定はClusterIP + Ingress(共有)としています。
|
||||||
|
# 専用のLoadBalancer IPを払い出す場合のみ type: LoadBalancer に変更してください。
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 80
|
port: 80
|
||||||
targetPort: 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:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
className: "nginx"
|
className: nginx
|
||||||
annotations: {}
|
annotations: {}
|
||||||
# {
|
# コメントを外すだけで有効化できる設定例
|
||||||
# acme.cert-manager.io/http01-ingress-class: "nginx",
|
# cert-manager.io/cluster-issuer: "letsencrypt-issuer",
|
||||||
# cert-manager.io/cluster-issuer: "letsencrypt-issuer",
|
# nginx.ingress.kubernetes.io/ssl-redirect: "true",
|
||||||
# nginx.ingress.kubernetes.io/from-to-www-redirect: "true",
|
# nginx.ingress.kubernetes.io/force-ssl-redirect: "true",
|
||||||
# nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
# nginx.ingress.kubernetes.io/proxy-body-size: "10m",
|
||||||
# }
|
# IP取得用(Nginx Ingress Controllerの場合)
|
||||||
hosts:
|
# nginx.ingress.kubernetes.io/use-forwarded-headers: "true",
|
||||||
- host: example.tld
|
# nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true",
|
||||||
paths:
|
hostname: yourls.example.com
|
||||||
- path: /
|
path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
tls: []
|
# TLS設定(true にするだけで hostname を使用して自動設定)
|
||||||
# - secretName: yourls-tls
|
tls: false
|
||||||
# hosts:
|
# tlsを有効にすると自動的に以下が設定されます:
|
||||||
# - example.tld
|
# - hosts: [hostname]
|
||||||
|
# - secretName: {hostname}-tls
|
||||||
|
|
||||||
# MySQL/MariaDB configuration
|
# 追加のホスト設定
|
||||||
mysql:
|
extraHosts: []
|
||||||
|
# - name: go.example.com
|
||||||
|
# path: /
|
||||||
|
|
||||||
|
# 追加の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
|
enabled: true
|
||||||
auth:
|
storageClass: ""
|
||||||
rootPassword: "changeme"
|
accessMode: ReadWriteOnce
|
||||||
database: "yourls"
|
size: 1Gi
|
||||||
username: "yourls"
|
|
||||||
password: "yourls"
|
|
||||||
primary:
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
size: 8Gi
|
|
||||||
|
|
||||||
# YOURLS configuration
|
# リソース設定
|
||||||
yourls:
|
|
||||||
config:
|
|
||||||
site: "https://yourls.example.com"
|
|
||||||
username: "admin"
|
|
||||||
password: "admin"
|
|
||||||
cookiekey: "modify_this_random_string"
|
|
||||||
hours_offset: 0
|
|
||||||
lang: "en"
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClass: ""
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 1Gi
|
|
||||||
|
|
||||||
# Resources
|
|
||||||
resources:
|
resources:
|
||||||
nginx:
|
nginx:
|
||||||
limits:
|
limits:
|
||||||
cpu: 200m
|
cpu: 200m
|
||||||
memory: 256Mi
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 32Mi
|
||||||
yourls:
|
yourls:
|
||||||
limits:
|
limits:
|
||||||
cpu: 500m
|
cpu: 500m
|
||||||
memory: 512Mi
|
|
||||||
requests:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 256Mi
|
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: {}
|
nodeSelector: {}
|
||||||
|
|
||||||
# Tolerations
|
# Tolerations
|
||||||
|
|||||||
Reference in New Issue
Block a user