Skip to content

Commit 7c36aad

Browse files
authored
🧁feat: added the static site chart 🧁 (#24)
🧁 Created a deploy engine for deploying static site onto a Kubernetes cluster with auto-updating capabilities 🧁
1 parent 9fa4031 commit 7c36aad

10 files changed

+563
-0
lines changed

charts/static-site/Chart.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v2
2+
name: static-site
3+
description: A Helm chart for deploying a static-site with auto-updating
4+
type: application
5+
version: 0.0.3
6+
appVersion: 1.17.9-alpine
7+
home: https://github.com/rht-labs/helm-charts
8+
maintainers:
9+
- name: jijiechen

charts/static-site/README-zh.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
## 在 Kubernetes 上部署的可自动更新的静态网站
3+
4+
此项目包含一个 helm chart,它支持向 Kubernetes 部署一个静态网站,并根据 Git 仓库中的最新变更,自动更新网站。
5+
6+
### 用法
7+
8+
```sh
9+
helm install my-cool-site ./ --set "repo.location=https://git-location-of-your-static-site"
10+
```
11+
12+
### 支持的 Helm 设置项
13+
14+
下表列出了这个 Helm Chart 所支持的各项设置及其默认值:
15+
16+
| 参数 | 描述 | 默认值 | 是否必填 |
17+
| -------------------- | ------------------------------------------------- | --------------- | ----------------- |
18+
| `repo.location` | 存储静态网站源代码的 Git 仓库地址的 HTTP(s) 地址 | ||
19+
| `repo.branch` | 要部署的分支名称 | `master` ||
20+
| `repo.credential.username` | Git 仓库的用户名 | ||
21+
| `repo.credential.password` | Git 仓库的密码 | ||
22+
| `site.enableDirectoryListing` | 是否启用目录浏览功能 | `false` ||
23+
| `replicas` | 部署时,要生成的副本数目 | `2` ||
24+
| `autoUpdateCron` | 用以设定检测自动更新频率的 CRON 表达式 | `* * * * *`, 即每分钟检查 ||
25+
26+
27+

charts/static-site/README.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
[查看中文说明文档](README-zh.md)
3+
4+
## Static Site Deployment with Automatic Updating on Kubernetes
5+
6+
This is a helm chart supporting deploying a static website onto Kubernetes and enable auto-polling to its Git repository.
7+
8+
### Usage
9+
10+
```sh
11+
helm install my-cool-site ./ --set "repo.location=https://git-location-of-your-static-site"
12+
```
13+
14+
### Supported Helm Settings
15+
16+
The following table lists the settings of the chart and their default values.
17+
18+
| Parameter | Description | Default | Required |
19+
| -------------------- | ---------------------------------------------------------- | --------------- | ----------------- |
20+
| `repo.location` | HTTP(s) based URL of your git repository that stores source of the site | | Y |
21+
| `repo.branch` | Name of git branch you want to deploy | `master` | N |
22+
| `repo.credential.username` | Username to of the git repository | | N |
23+
| `repo.credential.password` | Password to of the git repository | | N |
24+
| `site.enableDirectoryListing` | Whether enable directory listing for the site | `false` | N |
25+
| `replicas` | Number of replicas of the deployment | `2` | N |
26+
| `autoUpdateCron` | The CRON expression scheduling the auto update polling job | `* * * * *`, which means every minute | N |
27+
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM alpine/git:v2.24.1
2+
RUN apk add --no-cache curl
3+
4+
ENTRYPOINT ["/bin/sh"]
5+
6+
# docker build . -f ./alpine-curl-git.Dockerfile -t jijiechen/alpine-curl-git:v2.24.1
7+
# https://hub.docker.com/r/jijiechen/alpine-curl-git
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
{{/*
3+
Return the proper Storage Class
4+
*/}}
5+
{{- define "tmpl.storageClass" -}}
6+
{{- if .Values.persistence.storage.storageClass -}}
7+
{{- if (eq "-" .Values.persistence.storage.storageClass) -}}
8+
{{- printf "storageClassName: \"\"" -}}
9+
{{- else }}
10+
{{- printf "storageClassName: %s" .Values.persistence.storage.storageClass -}}
11+
{{- end -}}
12+
{{- end -}}
13+
{{- end -}}
14+
15+
16+
{{/*
17+
Return the template of poller/updater pod
18+
*/}}
19+
{{- define "tmpl.pollerPodSpec" -}}
20+
restartPolicy: Never
21+
serviceAccountName: {{ .Release.Name }}-redeployer
22+
volumes:
23+
- name: poller-script
24+
configMap:
25+
name: static-site-{{ .Release.Name }}-poller-script
26+
defaultMode: 0777
27+
containers:
28+
- name: poller
29+
image: jijiechen/alpine-curl-git:v2.24.1
30+
imagePullPolicy: IfNotPresent
31+
volumeMounts:
32+
- mountPath: /var/poller/
33+
name: poller-script
34+
command: ["/bin/sh"]
35+
args:
36+
- /var/poller/poll.sh
37+
env:
38+
- name: REPO_URL
39+
value: {{ .Values.repo.location }}
40+
- name: BRANCH
41+
value: {{ .Values.repo.branch }}
42+
resources:
43+
limits:
44+
cpu: 200m
45+
memory: "200Mi"
46+
requests:
47+
cpu: 50m
48+
memory: "100Mi"
49+
{{- end -}}
50+
+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: static-site-{{ .Release.Name }}-config
5+
labels:
6+
app: static-site
7+
site: {{ .Release.Name }}
8+
data:
9+
nginx.conf: |
10+
user nginx;
11+
worker_processes auto;
12+
13+
error_log /tmp/log/nginx/error.log warn;
14+
pid /tmp/nginx.pid;
15+
16+
17+
events {
18+
worker_connections 1024;
19+
}
20+
21+
http {
22+
include /etc/nginx/mime.types;
23+
default_type application/octet-stream;
24+
25+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
26+
'$status $body_bytes_sent "$http_referer" '
27+
'"$http_user_agent" "$http_x_forwarded_for"';
28+
29+
access_log /tmp/log/nginx/access.log main;
30+
31+
client_body_temp_path /tmp/client_temp;
32+
proxy_temp_path /tmp/proxy_temp_path;
33+
fastcgi_temp_path /tmp/fastcgi_temp;
34+
uwsgi_temp_path /tmp/uwsgi_temp;
35+
scgi_temp_path /tmp/scgi_temp;
36+
37+
38+
sendfile on;
39+
#tcp_nopush on;
40+
41+
keepalive_timeout 65;
42+
43+
gzip on;
44+
include /etc/nginx/conf.d/*.conf;
45+
}
46+
47+
default.conf: |
48+
server {
49+
listen 8080;
50+
server_name localhost;
51+
52+
#charset koi8-r;
53+
#access_log /var/log/nginx/host.access.log main;
54+
55+
location / {
56+
absolute_redirect off;
57+
{{- if .Values.site.enableDirectoryListing }}
58+
autoindex on;
59+
{{- end }}
60+
61+
root /usr/share/nginx/html;
62+
index index.html index.htm;
63+
}
64+
65+
#error_page 404 /404.html;
66+
67+
# redirect server error pages to the static page /50x.html
68+
#
69+
error_page 500 502 503 504 /50x.html;
70+
location = /50x.html {
71+
root /usr/share/nginx/html;
72+
}
73+
74+
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
75+
#
76+
#location ~ \.php$ {
77+
# proxy_pass http://127.0.0.1;
78+
#}
79+
80+
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
81+
#
82+
#location ~ \.php$ {
83+
# root html;
84+
# fastcgi_pass 127.0.0.1:9000;
85+
# fastcgi_index index.php;
86+
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
87+
# include fastcgi_params;
88+
#}
89+
90+
# deny access to .htaccess files, if Apache's document root
91+
# concurs with nginx's one
92+
#
93+
#location ~ /\.ht {
94+
# deny all;
95+
#}
96+
}
97+
98+
clone.sh: |
99+
#!/bin/sh
100+
101+
set -e
102+
103+
REPO_URL='{{ .Values.repo.location }}'
104+
105+
{{- if and .Values.repo.credential.username .Values.repo.credential.password }}
106+
echo 'exec echo $GIT_PASSWORD' > /tmp/git-askpass.sh
107+
chmod +x /tmp/git-askpass.sh
108+
export GIT_ASKPASS=/tmp/git-askpass.sh
109+
110+
if [ ! -z "${REPO_URL##*@*}" ] ; then
111+
REPO_URL=$(echo $REPO_URL | sed 's;//;//{{ .Values.repo.credential.username }}@;')
112+
fi
113+
{{- end }}
114+
115+
INSIDE_TREE=$(git rev-parse --is-inside-work-tree 2>/dev/null || true)
116+
if [ -z "$INSIDE_TREE" ]; then
117+
git init .
118+
fi
119+
120+
git config remote.origin.url $REPO_URL
121+
122+
BRANCH={{ .Values.repo.branch }}
123+
CURRENT=$(git rev-parse --verify HEAD 2>/dev/null || true)
124+
if [ "$CURRENT" != "$GIT_REVISION" ]; then
125+
git fetch --no-tags --progress -- $REPO_URL +refs/heads/$BRANCH:refs/remotes/origin/$BRANCH
126+
127+
git config advice.detachedHead false
128+
git checkout -f $GIT_REVISION
129+
git branch -D $BRANCH 2>/dev/null || true
130+
git checkout -b $BRANCH $GIT_REVISION
131+
fi
132+
133+
git reset --hard
134+
git clean -fdx
135+
136+
# Running at /source
137+
cp -R ./ /website
138+
rm -rf /website/.git
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
{{- if .Values.autoUpdateCron }}
2+
3+
apiVersion: batch/v1beta1
4+
kind: CronJob
5+
metadata:
6+
name: static-site-{{ .Release.Name }}-poller
7+
labels:
8+
app: static-site
9+
site: {{ .Release.Name }}
10+
spec:
11+
schedule: "{{ .Values.autoUpdateCron }}"
12+
concurrencyPolicy: Forbid
13+
successfulJobsHistoryLimit: 1
14+
failedJobsHistoryLimit: 1
15+
jobTemplate:
16+
spec:
17+
backoffLimit: 0
18+
template:
19+
spec:
20+
{{ include "tmpl.pollerPodSpec" . | indent 10 }}
21+
22+
---
23+
{{- end }}
24+
25+
apiVersion: v1
26+
kind: ConfigMap
27+
metadata:
28+
name: static-site-{{ .Release.Name }}-poller-script
29+
labels:
30+
app: static-site
31+
site: {{ .Release.Name }}
32+
data:
33+
poll.sh: |-
34+
#!/bin/sh
35+
36+
set -e
37+
38+
if [ -z "$REPO_URL" ]; then
39+
exit 0
40+
fi
41+
42+
K8S_URL=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT_HTTPS
43+
NAMESPACE=$(cat /run/secrets/kubernetes.io/serviceaccount/namespace)
44+
TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
45+
CACERT=/run/secrets/kubernetes.io/serviceaccount/ca.crt
46+
47+
{{- if .Values.persistence.enabled }}
48+
RES_TYPE=statefulset
49+
{{- else }}
50+
RES_TYPE=deployment
51+
{{- end }}
52+
53+
CURRENT=$(curl --cacert $CACERT -H "Authorization: Bearer $TOKEN" $K8S_URL/apis/apps/v1/namespaces/$NAMESPACE/${RES_TYPE}s/static-site-{{ .Release.Name }} 2>/dev/null | grep '"git_revision"' | tail -n 1 | cut -f 4 -d '"')
54+
LATEST=$(git ls-remote $REPO_URL | grep refs/heads/$BRANCH | awk '{print $1}')
55+
56+
if [ "$LATEST" != "$CURRENT" ] ; then
57+
echo "Redeploying static-site-{{ .Release.Name }}: the current ${RES_TYPE} $CURRENT has expired, as the latest revision is $LATEST"
58+
PATCH_REPLICA=
59+
60+
if [ "$CURRENT" = "pending-init" ]; then
61+
PATCH_REPLICA='"replicas": {{ .Values.replicas }},'
62+
fi
63+
64+
PATCH="{\"spec\": { $PATCH_REPLICA \"template\": {\"metadata\": { \"labels\": { \"git_revision\": \"$LATEST\"}}}}}"
65+
66+
curl --cacert $CACERT -X PATCH -H "Authorization: Bearer $TOKEN" \
67+
-H 'Content-Type: application/strategic-merge-patch+json' --data "$PATCH" \
68+
$K8S_URL/apis/apps/v1/namespaces/$NAMESPACE/${RES_TYPE}s/static-site-{{ .Release.Name }}
69+
else
70+
echo "No change: static-site-{{ .Release.Name }} stayed the same as current ${RES_TYPE} $CURRENT"
71+
fi
72+
---
73+
74+
apiVersion: v1
75+
kind: ServiceAccount
76+
metadata:
77+
name: {{ .Release.Name }}-redeployer
78+
labels:
79+
app: static-site
80+
site: {{ .Release.Name }}
81+
82+
---
83+
84+
apiVersion: rbac.authorization.k8s.io/v1
85+
kind: Role
86+
metadata:
87+
name: {{ .Release.Name }}-static-site-updater
88+
rules:
89+
- apiGroups: ["extensions", "apps"]
90+
resources: ["deployments", "statefulsets"]
91+
verbs: ["get", "patch"]
92+
93+
---
94+
95+
apiVersion: rbac.authorization.k8s.io/v1
96+
kind: RoleBinding
97+
metadata:
98+
name: {{ .Release.Name }}_redeployer
99+
labels:
100+
app: static-site
101+
site: {{ .Release.Name }}
102+
roleRef:
103+
apiGroup: rbac.authorization.k8s.io
104+
kind: Role
105+
name: {{ .Release.Name }}-static-site-updater
106+
subjects:
107+
- kind: ServiceAccount
108+
name: {{ .Release.Name }}-redeployer
109+
110+
111+
---
112+
113+
apiVersion: batch/v1
114+
kind: Job
115+
metadata:
116+
name: static-site-{{ .Release.Name }}-first-poller
117+
labels:
118+
app: static-site
119+
site: {{ .Release.Name }}
120+
annotations:
121+
helm.sh/hook: post-install
122+
helm.sh/hook-delete-policy: "before-hook-creation,hook-succeeded"
123+
spec:
124+
activeDeadlineSeconds: 600
125+
completions: 1
126+
parallelism: 1
127+
template:
128+
spec:
129+
{{ include "tmpl.pollerPodSpec" . | indent 6 }}

0 commit comments

Comments
 (0)